You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ah...@apache.org on 2022/09/08 15:28:37 UTC
[commons-numbers] branch complex-gsoc-2022 updated: NUMBERS-186: Added additional complex list operations
This is an automated email from the ASF dual-hosted git repository.
aherbert pushed a commit to branch complex-gsoc-2022
in repository https://gitbox.apache.org/repos/asf/commons-numbers.git
The following commit(s) were added to refs/heads/complex-gsoc-2022 by this push:
new ddd2c063 NUMBERS-186: Added additional complex list operations
ddd2c063 is described below
commit ddd2c06356cbfb4847196e54a02cc9509cf5212f
Author: sumanth-rajkumar <53...@users.noreply.github.com>
AuthorDate: Thu Sep 8 11:28:32 2022 -0400
NUMBERS-186: Added additional complex list operations
* Add a ComplexConsumer and dedicated forEach method
* get/set methods for only the real or imaginary part of the number
* consistent javadoc: replace the generic 'element' with 'complex number'
* consistent use of index << 1
---
.../numbers/complex/arrays/ComplexList.java | 154 ++++++++++++++++-----
.../numbers/complex/arrays/ComplexListTest.java | 88 +++++++++++-
.../{ComplexSink.java => ComplexConsumer.java} | 14 +-
.../commons/numbers/complex/ComplexSink.java | 1 +
4 files changed, 211 insertions(+), 46 deletions(-)
diff --git a/commons-numbers-complex-arrays/src/main/java/org/apache/commons/numbers/complex/arrays/ComplexList.java b/commons-numbers-complex-arrays/src/main/java/org/apache/commons/numbers/complex/arrays/ComplexList.java
index 8a9b42db..28a9dace 100644
--- a/commons-numbers-complex-arrays/src/main/java/org/apache/commons/numbers/complex/arrays/ComplexList.java
+++ b/commons-numbers-complex-arrays/src/main/java/org/apache/commons/numbers/complex/arrays/ComplexList.java
@@ -18,6 +18,7 @@
package org.apache.commons.numbers.complex.arrays;
import org.apache.commons.numbers.complex.Complex;
+import org.apache.commons.numbers.complex.ComplexConsumer;
import org.apache.commons.numbers.complex.ComplexUnaryOperator;
import java.util.AbstractList;
@@ -28,17 +29,17 @@ import java.util.Objects;
/**
* Resizable-double array implementation of the List interface. Implements all optional list operations,
- * and permits all elements. In addition to implementing the List interface,
+ * and permits all complex numbers. In addition to implementing the List interface,
* this class provides methods to manipulate the size of the array that is used internally to store the list.
*
- * <p>Each ComplexList instance has a capacity. The capacity is half the size of the double array used to store the elements
- * in the list. As elements are added to an ComplexList, its capacity grows automatically.
- * The complex number is stored using an interleaved format and so the maximum number of elements that may be added is
+ * <p>Each ComplexList instance has a capacity. The capacity is half the size of the double array used to store the complex numbers
+ * in the list. As complex numbers are added to an ComplexList, its capacity grows automatically.
+ * The complex number is stored using an interleaved format and so the maximum number of complex numbers that may be added is
* approximately 2^30. This is half the maximum capacity of java.util.ArrayList.
* The memory usage is more efficient than using a List of Complex objects as the underlying numbers are not stored
* using instances of Complex.</p>
*
- * <p>An application can increase the capacity of an ComplexList instance before adding a large number of elements
+ * <p>An application can increase the capacity of an ComplexList instance before adding a large number of complex numbers
* using the ensureCapacity operation. This may reduce the amount of incremental reallocation.</p>
*/
public class ComplexList extends AbstractList<Complex> {
@@ -55,7 +56,7 @@ public class ComplexList extends AbstractList<Complex> {
private static final int MAX_CAPACITY = MAX_ARRAY_SIZE / 2;
/**
- * error in case of allocation above max capacity.
+ * Error in case of allocation above max capacity.
*/
private static final String OOM_ERROR_STRING = "cannot allocate capacity %s greater than max " + MAX_CAPACITY;
@@ -74,7 +75,7 @@ public class ComplexList extends AbstractList<Complex> {
private static final String INDEX_MSG = "Index: ";
/**
- * The double array buffer into which the elements of the ComplexList are stored.
+ * The double array buffer into which the complex numbers of the ComplexList are stored.
*/
private double[] realAndImagParts;
@@ -115,7 +116,7 @@ public class ComplexList extends AbstractList<Complex> {
/**
* Checks if the given index is in range.
*
- * @param index Index of the element to range check.
+ * @param index Index of the complex number to range check.
* @throws IndexOutOfBoundsException if index isn't within the range.
*/
private void rangeCheck(int index) {
@@ -127,7 +128,7 @@ public class ComplexList extends AbstractList<Complex> {
/**
* A version of rangeCheck used by add and addAll.
*
- * @param index Index of the element to range check.
+ * @param index Index of the complex number to range check.
* @throws IndexOutOfBoundsException if index isn't within the range of list.
*/
private void rangeCheckForInsert(int index) {
@@ -138,8 +139,9 @@ public class ComplexList extends AbstractList<Complex> {
/**
* Gets the complex number \( (a + i b) \) at the indexed position of the list.
- *
+ * <p>
* {@inheritDoc}
+ *
* @return the complex number.
*/
@Override
@@ -150,36 +152,99 @@ public class ComplexList extends AbstractList<Complex> {
}
/**
- * Replaces the element at the specified position in this list with the specified element's
- * real and imaginary parts. No range checks are done.
+ * Gets the real part \( a \) of the complex number \( (a + i b) \) at the indexed position of the list.
*
- * @param index Index of the element to replace.
- * @param real Real part \( a \) of the complex number \( (a +ib) \).
- * @param imaginary Imaginary part \( b \) of the complex number \( (a +ib) \).
+ * @param index Index of the complex number.
+ * @return The real part.
*/
- private void setNoRangeCheck(int index, double real, double imaginary) {
- final int i = index << 1;
- realAndImagParts[i] = real;
- realAndImagParts[i + 1] = imaginary;
+ public double getReal(int index) {
+ rangeCheck(index);
+ return realAndImagParts[index << 1];
+ }
+
+ /**
+ * Gets the imaginary part \( b \) of the complex number \( (a + i b) \) at the indexed position of the list.
+ *
+ * @param index Index of the complex number.
+ * @return The imaginary part.
+ */
+ public double getImaginary(int index) {
+ rangeCheck(index);
+ return realAndImagParts[(index << 1) + 1];
}
/**
* {@inheritDoc}
*
- * @param element Complex element to be set.
+ * @param number Complex number to be set.
*/
@Override
- public Complex set(int index, Complex element) {
+ public Complex set(int index, Complex number) {
rangeCheck(index);
final int i = index << 1;
final Complex oldValue = Complex.ofCartesian(realAndImagParts[i], realAndImagParts[i + 1]);
- setNoRangeCheck(index, element.getReal(), element.getImaginary());
+ realAndImagParts[i] = number.getReal();
+ realAndImagParts[i + 1] = number.getImaginary();
return oldValue;
}
+ /**
+ * Replaces the complex number's real part at the specified position
+ * in the list with the specified real number.
+ *
+ * @param index Index of the complex number.
+ * @param real Real part \( a \) of the complex number \( (a +ib) \).
+ */
+ public void setReal(int index, double real) {
+ rangeCheck(index);
+ realAndImagParts[index << 1] = real;
+ }
+
+ /**
+ * Replaces the complex number's imaginary part at the specified position
+ * in the list with the specified imaginary number.
+ *
+ * @param index Index of the complex number.
+ * @param imaginary Imaginary part \( b \) of the complex number \( (a +ib) \).
+ */
+ public void setImaginary(int index, double imaginary) {
+ rangeCheck(index);
+ realAndImagParts[(index << 1) + 1] = imaginary;
+ }
+
+ /**
+ * Returns an array containing all the complex number's real parts in
+ * the list in proper sequence (from first to last number).
+ *
+ * @return Array of real parts.
+ */
+ double[] toArrayReal() {
+ final int length = size;
+ double[] real = new double[length];
+ for (int i = 0; i < length; i++) {
+ real[i] = realAndImagParts[i << 1];
+ }
+ return real;
+ }
+
+ /**
+ * Returns an array containing all the complex number's imaginary parts in
+ * the list in proper sequence (from first to last number).
+ *
+ * @return Array of imaginary parts.
+ */
+ double[] toArrayImaginary() {
+ final int length = size;
+ double[] imag = new double[length];
+ for (int i = 0; i < length; i++) {
+ imag[i] = realAndImagParts[(i << 1) + 1];
+ }
+ return imag;
+ }
+
/**
* Increases the capacity of this ComplexList instance, if necessary, to ensure that it can hold at
- * least the number of elements specified by the minimum capacity argument.
+ * least the amount of complex numbers specified by the minimum capacity argument.
*
* @param minCapacity Desired minimum capacity.
* @return the backing double array.
@@ -211,7 +276,7 @@ public class ComplexList extends AbstractList<Complex> {
/**
* Increases the capacity of this ComplexList instance, if necessary, to ensure that it can hold at
- * least an additional number of elements specified by the capacity argument.
+ * least an additional amount of complex numbers specified by the capacity argument.
*
* @param capacity Desired capacity.
*/
@@ -222,17 +287,17 @@ public class ComplexList extends AbstractList<Complex> {
/**
* {@inheritDoc}
*
- * @param element Complex element to be appended to this list
+ * @param number Complex number to be appended to this list
*/
@Override
- public boolean add(Complex element) {
+ public boolean add(Complex number) {
double[] e = realAndImagParts;
if (size == (e.length >>> 1)) {
e = ensureCapacityInternal(size + 1);
}
final int i = size << 1;
- e[i] = element.real();
- e[i + 1] = element.imag();
+ e[i] = number.real();
+ e[i + 1] = number.imag();
size++;
return true;
}
@@ -241,7 +306,7 @@ public class ComplexList extends AbstractList<Complex> {
* {@inheritDoc}
*/
@Override
- public void add(int index, Complex element) {
+ public void add(int index, Complex number) {
rangeCheckForInsert(index);
double[] e = realAndImagParts;
if (size == e.length >>> 1) {
@@ -250,8 +315,8 @@ public class ComplexList extends AbstractList<Complex> {
final int i = index << 1;
final int s = size << 1;
System.arraycopy(e, i, e, i + 2, s - i);
- e[i] = element.real();
- e[i + 1] = element.imag();
+ e[i] = number.real();
+ e[i + 1] = number.imag();
size++;
}
@@ -318,7 +383,7 @@ public class ComplexList extends AbstractList<Complex> {
/**
* Constructs an IndexOutOfBoundsException detail message.
*
- * @param index Index of the element.
+ * @param index Index of the complex number.
* @return message detailing the exception.
*/
private String outOfBoundsMsg(int index) {
@@ -326,8 +391,11 @@ public class ComplexList extends AbstractList<Complex> {
}
/**
- * Replaces each element of the list with the result of applying the operator to that element.
- * @param operator The operator to apply to each element.
+ * Replaces each complex number of the list with the result of applying the operator to that complex number.
+ *
+ * @param operator The operator to apply to each complex number.
+ * @throws ConcurrentModificationException if expected modCount isn't equal to modCount.
+ * @throws NullPointerException if the specified operator is null.
*/
public void replaceAll(ComplexUnaryOperator<Void> operator) {
Objects.requireNonNull(operator);
@@ -348,4 +416,22 @@ public class ComplexList extends AbstractList<Complex> {
}
modCount++;
}
+
+ /**
+ * Performs the given action for each complex number of the list until all complex numbers
+ * have been processed or the action throws an exception. Unless otherwise specified by the
+ * implementing class, actions are performed in the order of iteration.
+ *
+ * @param action The action to apply to each complex number.
+ * @throws NullPointerException if the specified action is null.
+ */
+ public void forEach(ComplexConsumer action) {
+ Objects.requireNonNull(action);
+ final double[] parts = this.realAndImagParts;
+ final int m = size;
+ for (int i = 0; i < m; i++) {
+ final int index = i << 1;
+ action.accept(parts[index], parts[index + 1]);
+ }
+ }
}
diff --git a/commons-numbers-complex-arrays/src/test/java/org/apache/commons/numbers/complex/arrays/ComplexListTest.java b/commons-numbers-complex-arrays/src/test/java/org/apache/commons/numbers/complex/arrays/ComplexListTest.java
index 4384aaf0..433d2df3 100644
--- a/commons-numbers-complex-arrays/src/test/java/org/apache/commons/numbers/complex/arrays/ComplexListTest.java
+++ b/commons-numbers-complex-arrays/src/test/java/org/apache/commons/numbers/complex/arrays/ComplexListTest.java
@@ -18,6 +18,7 @@
package org.apache.commons.numbers.complex.arrays;
import org.apache.commons.numbers.complex.Complex;
+import org.apache.commons.numbers.complex.ComplexConsumer;
import org.apache.commons.numbers.complex.ComplexFunctions;
import org.apache.commons.numbers.complex.ComplexUnaryOperator;
import org.junit.jupiter.api.Assertions;
@@ -152,9 +153,7 @@ public class ComplexListTest {
@Test
void testRemoveIndexOutOfBoundExceptions() {
- ComplexList list = new ComplexList();
- list.add(Complex.ofCartesian(42, 13));
- list.addAll(list);
+ ComplexList list = generateComplexList(2);
list.remove(0);
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> list.remove(1));
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> list.remove(-1));
@@ -176,7 +175,6 @@ public class ComplexListTest {
@Test
void testCapacityExceptions() {
-
Assertions.assertThrows(IllegalArgumentException.class, () -> new ComplexList(MAX_CAPACITY + 1));
// Set-up required sizes
@@ -223,6 +221,88 @@ public class ComplexListTest {
Assertions.assertEquals(objectList, actualList);
}
+ @ParameterizedTest
+ @ValueSource(ints = {0, 10})
+ void testForEachComplexConsumer(int size) {
+ ComplexList expected = generateComplexList(size);
+ ArrayList<Complex> actual = new ArrayList<>();
+ Assertions.assertThrows(NullPointerException.class, () -> expected.forEach((ComplexConsumer) null));
+ expected.forEach((real, imaginary) -> actual.add(Complex.ofCartesian(real, imaginary)));
+ Assertions.assertEquals(expected, actual);
+ }
+
+ @Test
+ void testGetRealAndImaginary() {
+ ComplexList list = generateComplexList(10);
+ for (int i = 0; i < list.size(); i++) {
+ Assertions.assertEquals(list.get(i).getReal(), list.getReal(i), "real");
+ Assertions.assertEquals(list.get(i).getImaginary(), list.getImaginary(i), "imaginary");
+ }
+ }
+
+ @Test
+ void testSetRealAndImaginary() {
+ ComplexList list = generateComplexList(10);
+ for (int i = 0; i < list.size(); i++) {
+ final double value = Math.PI * i;
+ list.setReal(i, value);
+ list.setImaginary(i, value);
+ Assertions.assertEquals(value, list.get(i).getReal());
+ Assertions.assertEquals(value, list.get(i).getImaginary());
+ }
+ }
+
+ @Test
+ void testGetAndSetRealAndImaginaryIndexOutOfBoundsException() {
+ ComplexList list = new ComplexList();
+ // Empty list throws
+ Assertions.assertThrows(IndexOutOfBoundsException.class, () -> list.getReal(0));
+ Assertions.assertThrows(IndexOutOfBoundsException.class, () -> list.getImaginary(0));
+
+ int size = 5;
+ IntStream.range(0, size).mapToObj(i -> Complex.ofCartesian(i, -i)).forEach(list::add);
+
+ Assertions.assertThrows(IndexOutOfBoundsException.class, () -> list.getReal(-1));
+ Assertions.assertThrows(IndexOutOfBoundsException.class, () -> list.getReal(size));
+ Assertions.assertThrows(IndexOutOfBoundsException.class, () -> list.getReal(size + 1));
+
+ Assertions.assertThrows(IndexOutOfBoundsException.class, () -> list.getImaginary(-1));
+ Assertions.assertThrows(IndexOutOfBoundsException.class, () -> list.getImaginary(size));
+ Assertions.assertThrows(IndexOutOfBoundsException.class, () -> list.getImaginary(size + 1));
+
+ Assertions.assertThrows(IndexOutOfBoundsException.class, () -> list.setReal(-2, 200));
+ Assertions.assertThrows(IndexOutOfBoundsException.class, () -> list.setReal(size, 200));
+ Assertions.assertThrows(IndexOutOfBoundsException.class, () -> list.setReal(size + 1, 200));
+
+ Assertions.assertThrows(IndexOutOfBoundsException.class, () -> list.setImaginary(-2, 200));
+ Assertions.assertThrows(IndexOutOfBoundsException.class, () -> list.setImaginary(size, 200));
+ Assertions.assertThrows(IndexOutOfBoundsException.class, () -> list.setImaginary(size + 1, 200));
+ }
+
+ @Test
+ void testToArrayRealAndImaginary() {
+ ComplexList list = generateComplexList(10);
+ double[] expectedReal = list.stream().mapToDouble(Complex::getReal).toArray();
+ double[] actualReal = list.toArrayReal();
+ Assertions.assertArrayEquals(expectedReal, actualReal);
+ double[] expectedImaginary = list.stream().mapToDouble(Complex::getImaginary).toArray();
+ double[] actualImaginary = list.toArrayImaginary();
+ Assertions.assertArrayEquals(expectedImaginary, actualImaginary);
+ }
+
+ /**
+ * Generates a ComplexList of random complex numbers of the given size.
+ * @param size number of complex numbers in the list.
+ * @return the ComplexList of random complex numbers.
+ */
+ private static ComplexList generateComplexList(int size) {
+ List<Complex> objectList = generateList(size);
+ ComplexList list = new ComplexList();
+ list.addAll(objectList);
+ Assertions.assertEquals(objectList, list);
+ return list;
+ }
+
/**
* Generates a list of random complex numbers of the given size.
* @param size number of complex numbers in the list.
diff --git a/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexSink.java b/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexConsumer.java
similarity index 76%
copy from commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexSink.java
copy to commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexConsumer.java
index 0bfca066..d1b7652e 100644
--- a/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexSink.java
+++ b/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexConsumer.java
@@ -18,23 +18,21 @@
package org.apache.commons.numbers.complex;
/**
- * Represents a data sink for a complex number \( (a + i b) \).
- * Operations return a result of type {@code R}.
+ * Represents an operation that accepts two double input arguments and returns no result.
*
* <p>This is a functional interface whose functional method is
- * {@link #apply(double, double)}.
+ * {@link #accept(double, double)}.
*
- * @param <R> The type of the result
* @since 1.1
*/
@FunctionalInterface
-public interface ComplexSink<R> {
+public interface ComplexConsumer {
/**
- * Represents a function that accepts real and imaginary part of complex number and returns an object.
+ * Represents a function that accepts real and imaginary part of complex number and returns no result.
+ *
* @param real Real part \( a \) of the complex number \( (a + ib) \).
* @param imaginary Imaginary part \( b \) of the complex number \( (a + ib) \).
- * @return R the object encapsulating the complex result
*/
- R apply(double real, double imaginary);
+ void accept(double real, double imaginary);
}
diff --git a/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexSink.java b/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexSink.java
index 0bfca066..5550062a 100644
--- a/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexSink.java
+++ b/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexSink.java
@@ -32,6 +32,7 @@ public interface ComplexSink<R> {
/**
* Represents a function that accepts real and imaginary part of complex number and returns an object.
+ *
* @param real Real part \( a \) of the complex number \( (a + ib) \).
* @param imaginary Imaginary part \( b \) of the complex number \( (a + ib) \).
* @return R the object encapsulating the complex result