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