diff options
Diffstat (limited to 'src/main/java/org/apache/commons/math3/util/MathArrays.java')
-rw-r--r-- | src/main/java/org/apache/commons/math3/util/MathArrays.java | 1912 |
1 files changed, 1912 insertions, 0 deletions
diff --git a/src/main/java/org/apache/commons/math3/util/MathArrays.java b/src/main/java/org/apache/commons/math3/util/MathArrays.java new file mode 100644 index 0000000..968e626 --- /dev/null +++ b/src/main/java/org/apache/commons/math3/util/MathArrays.java @@ -0,0 +1,1912 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math3.util; + +import org.apache.commons.math3.Field; +import org.apache.commons.math3.distribution.UniformIntegerDistribution; +import org.apache.commons.math3.exception.DimensionMismatchException; +import org.apache.commons.math3.exception.MathArithmeticException; +import org.apache.commons.math3.exception.MathIllegalArgumentException; +import org.apache.commons.math3.exception.MathInternalError; +import org.apache.commons.math3.exception.NoDataException; +import org.apache.commons.math3.exception.NonMonotonicSequenceException; +import org.apache.commons.math3.exception.NotANumberException; +import org.apache.commons.math3.exception.NotPositiveException; +import org.apache.commons.math3.exception.NotStrictlyPositiveException; +import org.apache.commons.math3.exception.NullArgumentException; +import org.apache.commons.math3.exception.NumberIsTooLargeException; +import org.apache.commons.math3.exception.util.LocalizedFormats; +import org.apache.commons.math3.random.RandomGenerator; +import org.apache.commons.math3.random.Well19937c; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.TreeSet; + +/** + * Arrays utilities. + * + * @since 3.0 + */ +public class MathArrays { + + /** Private constructor. */ + private MathArrays() {} + + /** + * Real-valued function that operate on an array or a part of it. + * + * @since 3.1 + */ + public interface Function { + /** + * Operates on an entire array. + * + * @param array Array to operate on. + * @return the result of the operation. + */ + double evaluate(double[] array); + + /** + * @param array Array to operate on. + * @param startIndex Index of the first element to take into account. + * @param numElements Number of elements to take into account. + * @return the result of the operation. + */ + double evaluate(double[] array, int startIndex, int numElements); + } + + /** + * Create a copy of an array scaled by a value. + * + * @param arr Array to scale. + * @param val Scalar. + * @return scaled copy of array with each entry multiplied by val. + * @since 3.2 + */ + public static double[] scale(double val, final double[] arr) { + double[] newArr = new double[arr.length]; + for (int i = 0; i < arr.length; i++) { + newArr[i] = arr[i] * val; + } + return newArr; + } + + /** + * Multiply each element of an array by a value. + * + * <p>The array is modified in place (no copy is created). + * + * @param arr Array to scale + * @param val Scalar + * @since 3.2 + */ + public static void scaleInPlace(double val, final double[] arr) { + for (int i = 0; i < arr.length; i++) { + arr[i] *= val; + } + } + + /** + * Creates an array whose contents will be the element-by-element addition of the arguments. + * + * @param a First term of the addition. + * @param b Second term of the addition. + * @return a new array {@code r} where {@code r[i] = a[i] + b[i]}. + * @throws DimensionMismatchException if the array lengths differ. + * @since 3.1 + */ + public static double[] ebeAdd(double[] a, double[] b) throws DimensionMismatchException { + checkEqualLength(a, b); + + final double[] result = a.clone(); + for (int i = 0; i < a.length; i++) { + result[i] += b[i]; + } + return result; + } + + /** + * Creates an array whose contents will be the element-by-element subtraction of the second + * argument from the first. + * + * @param a First term. + * @param b Element to be subtracted. + * @return a new array {@code r} where {@code r[i] = a[i] - b[i]}. + * @throws DimensionMismatchException if the array lengths differ. + * @since 3.1 + */ + public static double[] ebeSubtract(double[] a, double[] b) throws DimensionMismatchException { + checkEqualLength(a, b); + + final double[] result = a.clone(); + for (int i = 0; i < a.length; i++) { + result[i] -= b[i]; + } + return result; + } + + /** + * Creates an array whose contents will be the element-by-element multiplication of the + * arguments. + * + * @param a First factor of the multiplication. + * @param b Second factor of the multiplication. + * @return a new array {@code r} where {@code r[i] = a[i] * b[i]}. + * @throws DimensionMismatchException if the array lengths differ. + * @since 3.1 + */ + public static double[] ebeMultiply(double[] a, double[] b) throws DimensionMismatchException { + checkEqualLength(a, b); + + final double[] result = a.clone(); + for (int i = 0; i < a.length; i++) { + result[i] *= b[i]; + } + return result; + } + + /** + * Creates an array whose contents will be the element-by-element division of the first argument + * by the second. + * + * @param a Numerator of the division. + * @param b Denominator of the division. + * @return a new array {@code r} where {@code r[i] = a[i] / b[i]}. + * @throws DimensionMismatchException if the array lengths differ. + * @since 3.1 + */ + public static double[] ebeDivide(double[] a, double[] b) throws DimensionMismatchException { + checkEqualLength(a, b); + + final double[] result = a.clone(); + for (int i = 0; i < a.length; i++) { + result[i] /= b[i]; + } + return result; + } + + /** + * Calculates the L<sub>1</sub> (sum of abs) distance between two points. + * + * @param p1 the first point + * @param p2 the second point + * @return the L<sub>1</sub> distance between the two points + * @throws DimensionMismatchException if the array lengths differ. + */ + public static double distance1(double[] p1, double[] p2) throws DimensionMismatchException { + checkEqualLength(p1, p2); + double sum = 0; + for (int i = 0; i < p1.length; i++) { + sum += FastMath.abs(p1[i] - p2[i]); + } + return sum; + } + + /** + * Calculates the L<sub>1</sub> (sum of abs) distance between two points. + * + * @param p1 the first point + * @param p2 the second point + * @return the L<sub>1</sub> distance between the two points + * @throws DimensionMismatchException if the array lengths differ. + */ + public static int distance1(int[] p1, int[] p2) throws DimensionMismatchException { + checkEqualLength(p1, p2); + int sum = 0; + for (int i = 0; i < p1.length; i++) { + sum += FastMath.abs(p1[i] - p2[i]); + } + return sum; + } + + /** + * Calculates the L<sub>2</sub> (Euclidean) distance between two points. + * + * @param p1 the first point + * @param p2 the second point + * @return the L<sub>2</sub> distance between the two points + * @throws DimensionMismatchException if the array lengths differ. + */ + public static double distance(double[] p1, double[] p2) throws DimensionMismatchException { + checkEqualLength(p1, p2); + double sum = 0; + for (int i = 0; i < p1.length; i++) { + final double dp = p1[i] - p2[i]; + sum += dp * dp; + } + return FastMath.sqrt(sum); + } + + /** + * Calculates the cosine of the angle between two vectors. + * + * @param v1 Cartesian coordinates of the first vector. + * @param v2 Cartesian coordinates of the second vector. + * @return the cosine of the angle between the vectors. + * @since 3.6 + */ + public static double cosAngle(double[] v1, double[] v2) { + return linearCombination(v1, v2) / (safeNorm(v1) * safeNorm(v2)); + } + + /** + * Calculates the L<sub>2</sub> (Euclidean) distance between two points. + * + * @param p1 the first point + * @param p2 the second point + * @return the L<sub>2</sub> distance between the two points + * @throws DimensionMismatchException if the array lengths differ. + */ + public static double distance(int[] p1, int[] p2) throws DimensionMismatchException { + checkEqualLength(p1, p2); + double sum = 0; + for (int i = 0; i < p1.length; i++) { + final double dp = p1[i] - p2[i]; + sum += dp * dp; + } + return FastMath.sqrt(sum); + } + + /** + * Calculates the L<sub>∞</sub> (max of abs) distance between two points. + * + * @param p1 the first point + * @param p2 the second point + * @return the L<sub>∞</sub> distance between the two points + * @throws DimensionMismatchException if the array lengths differ. + */ + public static double distanceInf(double[] p1, double[] p2) throws DimensionMismatchException { + checkEqualLength(p1, p2); + double max = 0; + for (int i = 0; i < p1.length; i++) { + max = FastMath.max(max, FastMath.abs(p1[i] - p2[i])); + } + return max; + } + + /** + * Calculates the L<sub>∞</sub> (max of abs) distance between two points. + * + * @param p1 the first point + * @param p2 the second point + * @return the L<sub>∞</sub> distance between the two points + * @throws DimensionMismatchException if the array lengths differ. + */ + public static int distanceInf(int[] p1, int[] p2) throws DimensionMismatchException { + checkEqualLength(p1, p2); + int max = 0; + for (int i = 0; i < p1.length; i++) { + max = FastMath.max(max, FastMath.abs(p1[i] - p2[i])); + } + return max; + } + + /** Specification of ordering direction. */ + public enum OrderDirection { + /** Constant for increasing direction. */ + INCREASING, + /** Constant for decreasing direction. */ + DECREASING + } + + /** + * Check that an array is monotonically increasing or decreasing. + * + * @param <T> the type of the elements in the specified array + * @param val Values. + * @param dir Ordering direction. + * @param strict Whether the order should be strict. + * @return {@code true} if sorted, {@code false} otherwise. + */ + public static <T extends Comparable<? super T>> boolean isMonotonic( + T[] val, OrderDirection dir, boolean strict) { + T previous = val[0]; + final int max = val.length; + for (int i = 1; i < max; i++) { + final int comp; + switch (dir) { + case INCREASING: + comp = previous.compareTo(val[i]); + if (strict) { + if (comp >= 0) { + return false; + } + } else { + if (comp > 0) { + return false; + } + } + break; + case DECREASING: + comp = val[i].compareTo(previous); + if (strict) { + if (comp >= 0) { + return false; + } + } else { + if (comp > 0) { + return false; + } + } + break; + default: + // Should never happen. + throw new MathInternalError(); + } + + previous = val[i]; + } + return true; + } + + /** + * Check that an array is monotonically increasing or decreasing. + * + * @param val Values. + * @param dir Ordering direction. + * @param strict Whether the order should be strict. + * @return {@code true} if sorted, {@code false} otherwise. + */ + public static boolean isMonotonic(double[] val, OrderDirection dir, boolean strict) { + return checkOrder(val, dir, strict, false); + } + + /** + * Check that both arrays have the same length. + * + * @param a Array. + * @param b Array. + * @param abort Whether to throw an exception if the check fails. + * @return {@code true} if the arrays have the same length. + * @throws DimensionMismatchException if the lengths differ and {@code abort} is {@code true}. + * @since 3.6 + */ + public static boolean checkEqualLength(double[] a, double[] b, boolean abort) { + if (a.length == b.length) { + return true; + } else { + if (abort) { + throw new DimensionMismatchException(a.length, b.length); + } + return false; + } + } + + /** + * Check that both arrays have the same length. + * + * @param a Array. + * @param b Array. + * @throws DimensionMismatchException if the lengths differ. + * @since 3.6 + */ + public static void checkEqualLength(double[] a, double[] b) { + checkEqualLength(a, b, true); + } + + /** + * Check that both arrays have the same length. + * + * @param a Array. + * @param b Array. + * @param abort Whether to throw an exception if the check fails. + * @return {@code true} if the arrays have the same length. + * @throws DimensionMismatchException if the lengths differ and {@code abort} is {@code true}. + * @since 3.6 + */ + public static boolean checkEqualLength(int[] a, int[] b, boolean abort) { + if (a.length == b.length) { + return true; + } else { + if (abort) { + throw new DimensionMismatchException(a.length, b.length); + } + return false; + } + } + + /** + * Check that both arrays have the same length. + * + * @param a Array. + * @param b Array. + * @throws DimensionMismatchException if the lengths differ. + * @since 3.6 + */ + public static void checkEqualLength(int[] a, int[] b) { + checkEqualLength(a, b, true); + } + + /** + * Check that the given array is sorted. + * + * @param val Values. + * @param dir Ordering direction. + * @param strict Whether the order should be strict. + * @param abort Whether to throw an exception if the check fails. + * @return {@code true} if the array is sorted. + * @throws NonMonotonicSequenceException if the array is not sorted and {@code abort} is {@code + * true}. + */ + public static boolean checkOrder( + double[] val, OrderDirection dir, boolean strict, boolean abort) + throws NonMonotonicSequenceException { + double previous = val[0]; + final int max = val.length; + + int index; + ITEM: + for (index = 1; index < max; index++) { + switch (dir) { + case INCREASING: + if (strict) { + if (val[index] <= previous) { + break ITEM; + } + } else { + if (val[index] < previous) { + break ITEM; + } + } + break; + case DECREASING: + if (strict) { + if (val[index] >= previous) { + break ITEM; + } + } else { + if (val[index] > previous) { + break ITEM; + } + } + break; + default: + // Should never happen. + throw new MathInternalError(); + } + + previous = val[index]; + } + + if (index == max) { + // Loop completed. + return true; + } + + // Loop early exit means wrong ordering. + if (abort) { + throw new NonMonotonicSequenceException(val[index], previous, index, dir, strict); + } else { + return false; + } + } + + /** + * Check that the given array is sorted. + * + * @param val Values. + * @param dir Ordering direction. + * @param strict Whether the order should be strict. + * @throws NonMonotonicSequenceException if the array is not sorted. + * @since 2.2 + */ + public static void checkOrder(double[] val, OrderDirection dir, boolean strict) + throws NonMonotonicSequenceException { + checkOrder(val, dir, strict, true); + } + + /** + * Check that the given array is sorted in strictly increasing order. + * + * @param val Values. + * @throws NonMonotonicSequenceException if the array is not sorted. + * @since 2.2 + */ + public static void checkOrder(double[] val) throws NonMonotonicSequenceException { + checkOrder(val, OrderDirection.INCREASING, true); + } + + /** + * Throws DimensionMismatchException if the input array is not rectangular. + * + * @param in array to be tested + * @throws NullArgumentException if input array is null + * @throws DimensionMismatchException if input array is not rectangular + * @since 3.1 + */ + public static void checkRectangular(final long[][] in) + throws NullArgumentException, DimensionMismatchException { + MathUtils.checkNotNull(in); + for (int i = 1; i < in.length; i++) { + if (in[i].length != in[0].length) { + throw new DimensionMismatchException( + LocalizedFormats.DIFFERENT_ROWS_LENGTHS, in[i].length, in[0].length); + } + } + } + + /** + * Check that all entries of the input array are strictly positive. + * + * @param in Array to be tested + * @throws NotStrictlyPositiveException if any entries of the array are not strictly positive. + * @since 3.1 + */ + public static void checkPositive(final double[] in) throws NotStrictlyPositiveException { + for (int i = 0; i < in.length; i++) { + if (in[i] <= 0) { + throw new NotStrictlyPositiveException(in[i]); + } + } + } + + /** + * Check that no entry of the input array is {@code NaN}. + * + * @param in Array to be tested. + * @throws NotANumberException if an entry is {@code NaN}. + * @since 3.4 + */ + public static void checkNotNaN(final double[] in) throws NotANumberException { + for (int i = 0; i < in.length; i++) { + if (Double.isNaN(in[i])) { + throw new NotANumberException(); + } + } + } + + /** + * Check that all entries of the input array are >= 0. + * + * @param in Array to be tested + * @throws NotPositiveException if any array entries are less than 0. + * @since 3.1 + */ + public static void checkNonNegative(final long[] in) throws NotPositiveException { + for (int i = 0; i < in.length; i++) { + if (in[i] < 0) { + throw new NotPositiveException(in[i]); + } + } + } + + /** + * Check all entries of the input array are >= 0. + * + * @param in Array to be tested + * @throws NotPositiveException if any array entries are less than 0. + * @since 3.1 + */ + public static void checkNonNegative(final long[][] in) throws NotPositiveException { + for (int i = 0; i < in.length; i++) { + for (int j = 0; j < in[i].length; j++) { + if (in[i][j] < 0) { + throw new NotPositiveException(in[i][j]); + } + } + } + } + + /** + * Returns the Cartesian norm (2-norm), handling both overflow and underflow. Translation of the + * minpack enorm subroutine. + * + * <p>The redistribution policy for MINPACK is available <a + * href="http://www.netlib.org/minpack/disclaimer">here</a>, for convenience, it is reproduced + * below. + * + * <table border="0" width="80%" cellpadding="10" align="center" bgcolor="#E0E0E0"> + * <tr><td> + * Minpack Copyright Notice (1999) University of Chicago. + * All rights reserved + * </td></tr> + * <tr><td> + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * <ol> + * <li>Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer.</li> + * <li>Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution.</li> + * <li>The end-user documentation included with the redistribution, if any, + * must include the following acknowledgment: + * {@code This product includes software developed by the University of + * Chicago, as Operator of Argonne National Laboratory.} + * Alternately, this acknowledgment may appear in the software itself, + * if and wherever such third-party acknowledgments normally appear.</li> + * <li><strong>WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS" + * WITHOUT WARRANTY OF ANY KIND. THE COPYRIGHT HOLDER, THE + * UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND + * THEIR EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE + * OR NON-INFRINGEMENT, (2) DO NOT ASSUME ANY LEGAL LIABILITY + * OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR + * USEFULNESS OF THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF + * THE SOFTWARE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS, (4) + * DO NOT WARRANT THAT THE SOFTWARE WILL FUNCTION + * UNINTERRUPTED, THAT IT IS ERROR-FREE OR THAT ANY ERRORS WILL + * BE CORRECTED.</strong></li> + * <li><strong>LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT + * HOLDER, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF + * ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT, + * INCIDENTAL, CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF + * ANY KIND OR NATURE, INCLUDING BUT NOT LIMITED TO LOSS OF + * PROFITS OR LOSS OF DATA, FOR ANY REASON WHATSOEVER, WHETHER + * SUCH LIABILITY IS ASSERTED ON THE BASIS OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE OR STRICT LIABILITY), OR OTHERWISE, + * EVEN IF ANY OF SAID PARTIES HAS BEEN WARNED OF THE + * POSSIBILITY OF SUCH LOSS OR DAMAGES.</strong></li> + * <ol></td></tr> + * </table> + * + * @param v Vector of doubles. + * @return the 2-norm of the vector. + * @since 2.2 + */ + public static double safeNorm(double[] v) { + double rdwarf = 3.834e-20; + double rgiant = 1.304e+19; + double s1 = 0; + double s2 = 0; + double s3 = 0; + double x1max = 0; + double x3max = 0; + double floatn = v.length; + double agiant = rgiant / floatn; + for (int i = 0; i < v.length; i++) { + double xabs = FastMath.abs(v[i]); + if (xabs < rdwarf || xabs > agiant) { + if (xabs > rdwarf) { + if (xabs > x1max) { + double r = x1max / xabs; + s1 = 1 + s1 * r * r; + x1max = xabs; + } else { + double r = xabs / x1max; + s1 += r * r; + } + } else { + if (xabs > x3max) { + double r = x3max / xabs; + s3 = 1 + s3 * r * r; + x3max = xabs; + } else { + if (xabs != 0) { + double r = xabs / x3max; + s3 += r * r; + } + } + } + } else { + s2 += xabs * xabs; + } + } + double norm; + if (s1 != 0) { + norm = x1max * Math.sqrt(s1 + (s2 / x1max) / x1max); + } else { + if (s2 == 0) { + norm = x3max * Math.sqrt(s3); + } else { + if (s2 >= x3max) { + norm = Math.sqrt(s2 * (1 + (x3max / s2) * (x3max * s3))); + } else { + norm = Math.sqrt(x3max * ((s2 / x3max) + (x3max * s3))); + } + } + } + return norm; + } + + /** A helper data structure holding a double and an integer value. */ + private static class PairDoubleInteger { + /** Key */ + private final double key; + + /** Value */ + private final int value; + + /** + * @param key Key. + * @param value Value. + */ + PairDoubleInteger(double key, int value) { + this.key = key; + this.value = value; + } + + /** + * @return the key. + */ + public double getKey() { + return key; + } + + /** + * @return the value. + */ + public int getValue() { + return value; + } + } + + /** + * Sort an array in ascending order in place and perform the same reordering of entries on other + * arrays. For example, if {@code x = [3, 1, 2], y = [1, 2, 3]} and {@code z = [0, 5, 7]}, then + * {@code sortInPlace(x, y, z)} will update {@code x} to {@code [1, 2, 3]}, {@code y} to {@code + * [2, 3, 1]} and {@code z} to {@code [5, 7, 0]}. + * + * @param x Array to be sorted and used as a pattern for permutation of the other arrays. + * @param yList Set of arrays whose permutations of entries will follow those performed on + * {@code x}. + * @throws DimensionMismatchException if any {@code y} is not the same size as {@code x}. + * @throws NullArgumentException if {@code x} or any {@code y} is null. + * @since 3.0 + */ + public static void sortInPlace(double[] x, double[]... yList) + throws DimensionMismatchException, NullArgumentException { + sortInPlace(x, OrderDirection.INCREASING, yList); + } + + /** + * Sort an array in place and perform the same reordering of entries on other arrays. This + * method works the same as the other {@link #sortInPlace(double[], double[][]) sortInPlace} + * method, but allows the order of the sort to be provided in the {@code dir} parameter. + * + * @param x Array to be sorted and used as a pattern for permutation of the other arrays. + * @param dir Order direction. + * @param yList Set of arrays whose permutations of entries will follow those performed on + * {@code x}. + * @throws DimensionMismatchException if any {@code y} is not the same size as {@code x}. + * @throws NullArgumentException if {@code x} or any {@code y} is null + * @since 3.0 + */ + public static void sortInPlace(double[] x, final OrderDirection dir, double[]... yList) + throws NullArgumentException, DimensionMismatchException { + + // Consistency checks. + if (x == null) { + throw new NullArgumentException(); + } + + final int yListLen = yList.length; + final int len = x.length; + + for (int j = 0; j < yListLen; j++) { + final double[] y = yList[j]; + if (y == null) { + throw new NullArgumentException(); + } + if (y.length != len) { + throw new DimensionMismatchException(y.length, len); + } + } + + // Associate each abscissa "x[i]" with its index "i". + final List<PairDoubleInteger> list = new ArrayList<PairDoubleInteger>(len); + for (int i = 0; i < len; i++) { + list.add(new PairDoubleInteger(x[i], i)); + } + + // Create comparators for increasing and decreasing orders. + final Comparator<PairDoubleInteger> comp = + dir == MathArrays.OrderDirection.INCREASING + ? new Comparator<PairDoubleInteger>() { + /** {@inheritDoc} */ + public int compare(PairDoubleInteger o1, PairDoubleInteger o2) { + return Double.compare(o1.getKey(), o2.getKey()); + } + } + : new Comparator<PairDoubleInteger>() { + /** {@inheritDoc} */ + public int compare(PairDoubleInteger o1, PairDoubleInteger o2) { + return Double.compare(o2.getKey(), o1.getKey()); + } + }; + + // Sort. + Collections.sort(list, comp); + + // Modify the original array so that its elements are in + // the prescribed order. + // Retrieve indices of original locations. + final int[] indices = new int[len]; + for (int i = 0; i < len; i++) { + final PairDoubleInteger e = list.get(i); + x[i] = e.getKey(); + indices[i] = e.getValue(); + } + + // In each of the associated arrays, move the + // elements to their new location. + for (int j = 0; j < yListLen; j++) { + // Input array will be modified in place. + final double[] yInPlace = yList[j]; + final double[] yOrig = yInPlace.clone(); + + for (int i = 0; i < len; i++) { + yInPlace[i] = yOrig[indices[i]]; + } + } + } + + /** + * Creates a copy of the {@code source} array. + * + * @param source Array to be copied. + * @return the copied array. + */ + public static int[] copyOf(int[] source) { + return copyOf(source, source.length); + } + + /** + * Creates a copy of the {@code source} array. + * + * @param source Array to be copied. + * @return the copied array. + */ + public static double[] copyOf(double[] source) { + return copyOf(source, source.length); + } + + /** + * Creates a copy of the {@code source} array. + * + * @param source Array to be copied. + * @param len Number of entries to copy. If smaller then the source length, the copy will be + * truncated, if larger it will padded with zeroes. + * @return the copied array. + */ + public static int[] copyOf(int[] source, int len) { + final int[] output = new int[len]; + System.arraycopy(source, 0, output, 0, FastMath.min(len, source.length)); + return output; + } + + /** + * Creates a copy of the {@code source} array. + * + * @param source Array to be copied. + * @param len Number of entries to copy. If smaller then the source length, the copy will be + * truncated, if larger it will padded with zeroes. + * @return the copied array. + */ + public static double[] copyOf(double[] source, int len) { + final double[] output = new double[len]; + System.arraycopy(source, 0, output, 0, FastMath.min(len, source.length)); + return output; + } + + /** + * Creates a copy of the {@code source} array. + * + * @param source Array to be copied. + * @param from Initial index of the range to be copied, inclusive. + * @param to Final index of the range to be copied, exclusive. (This index may lie outside the + * array.) + * @return the copied array. + */ + public static double[] copyOfRange(double[] source, int from, int to) { + final int len = to - from; + final double[] output = new double[len]; + System.arraycopy(source, from, output, 0, FastMath.min(len, source.length - from)); + return output; + } + + /** + * Compute a linear combination accurately. This method computes the sum of the products <code> + * a<sub>i</sub> b<sub>i</sub></code> to high accuracy. It does so by using specific + * multiplication and addition algorithms to preserve accuracy and reduce cancellation effects. + * <br> + * It is based on the 2005 paper <a + * href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.2.1547">Accurate Sum and Dot + * Product</a> by Takeshi Ogita, Siegfried M. Rump, and Shin'ichi Oishi published in SIAM J. + * Sci. Comput. + * + * @param a Factors. + * @param b Factors. + * @return <code>Σ<sub>i</sub> a<sub>i</sub> b<sub>i</sub></code>. + * @throws DimensionMismatchException if arrays dimensions don't match + */ + public static double linearCombination(final double[] a, final double[] b) + throws DimensionMismatchException { + checkEqualLength(a, b); + final int len = a.length; + + if (len == 1) { + // Revert to scalar multiplication. + return a[0] * b[0]; + } + + final double[] prodHigh = new double[len]; + double prodLowSum = 0; + + for (int i = 0; i < len; i++) { + final double ai = a[i]; + final double aHigh = + Double.longBitsToDouble(Double.doubleToRawLongBits(ai) & ((-1L) << 27)); + final double aLow = ai - aHigh; + + final double bi = b[i]; + final double bHigh = + Double.longBitsToDouble(Double.doubleToRawLongBits(bi) & ((-1L) << 27)); + final double bLow = bi - bHigh; + prodHigh[i] = ai * bi; + final double prodLow = + aLow * bLow - (((prodHigh[i] - aHigh * bHigh) - aLow * bHigh) - aHigh * bLow); + prodLowSum += prodLow; + } + + final double prodHighCur = prodHigh[0]; + double prodHighNext = prodHigh[1]; + double sHighPrev = prodHighCur + prodHighNext; + double sPrime = sHighPrev - prodHighNext; + double sLowSum = (prodHighNext - (sHighPrev - sPrime)) + (prodHighCur - sPrime); + + final int lenMinusOne = len - 1; + for (int i = 1; i < lenMinusOne; i++) { + prodHighNext = prodHigh[i + 1]; + final double sHighCur = sHighPrev + prodHighNext; + sPrime = sHighCur - prodHighNext; + sLowSum += (prodHighNext - (sHighCur - sPrime)) + (sHighPrev - sPrime); + sHighPrev = sHighCur; + } + + double result = sHighPrev + (prodLowSum + sLowSum); + + if (Double.isNaN(result)) { + // either we have split infinite numbers or some coefficients were NaNs, + // just rely on the naive implementation and let IEEE754 handle this + result = 0; + for (int i = 0; i < len; ++i) { + result += a[i] * b[i]; + } + } + + return result; + } + + /** + * Compute a linear combination accurately. + * + * <p>This method computes a<sub>1</sub>×b<sub>1</sub> + a<sub>2</sub>×b<sub>2</sub> + * to high accuracy. It does so by using specific multiplication and addition algorithms to + * preserve accuracy and reduce cancellation effects. It is based on the 2005 paper <a + * href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.2.1547">Accurate Sum and Dot + * Product</a> by Takeshi Ogita, Siegfried M. Rump, and Shin'ichi Oishi published in SIAM J. + * Sci. Comput. + * + * @param a1 first factor of the first term + * @param b1 second factor of the first term + * @param a2 first factor of the second term + * @param b2 second factor of the second term + * @return a<sub>1</sub>×b<sub>1</sub> + a<sub>2</sub>×b<sub>2</sub> + * @see #linearCombination(double, double, double, double, double, double) + * @see #linearCombination(double, double, double, double, double, double, double, double) + */ + public static double linearCombination( + final double a1, final double b1, final double a2, final double b2) { + + // the code below is split in many additions/subtractions that may + // appear redundant. However, they should NOT be simplified, as they + // use IEEE754 floating point arithmetic rounding properties. + // The variable naming conventions are that xyzHigh contains the most significant + // bits of xyz and xyzLow contains its least significant bits. So theoretically + // xyz is the sum xyzHigh + xyzLow, but in many cases below, this sum cannot + // be represented in only one double precision number so we preserve two numbers + // to hold it as long as we can, combining the high and low order bits together + // only at the end, after cancellation may have occurred on high order bits + + // split a1 and b1 as one 26 bits number and one 27 bits number + final double a1High = + Double.longBitsToDouble(Double.doubleToRawLongBits(a1) & ((-1L) << 27)); + final double a1Low = a1 - a1High; + final double b1High = + Double.longBitsToDouble(Double.doubleToRawLongBits(b1) & ((-1L) << 27)); + final double b1Low = b1 - b1High; + + // accurate multiplication a1 * b1 + final double prod1High = a1 * b1; + final double prod1Low = + a1Low * b1Low - (((prod1High - a1High * b1High) - a1Low * b1High) - a1High * b1Low); + + // split a2 and b2 as one 26 bits number and one 27 bits number + final double a2High = + Double.longBitsToDouble(Double.doubleToRawLongBits(a2) & ((-1L) << 27)); + final double a2Low = a2 - a2High; + final double b2High = + Double.longBitsToDouble(Double.doubleToRawLongBits(b2) & ((-1L) << 27)); + final double b2Low = b2 - b2High; + + // accurate multiplication a2 * b2 + final double prod2High = a2 * b2; + final double prod2Low = + a2Low * b2Low - (((prod2High - a2High * b2High) - a2Low * b2High) - a2High * b2Low); + + // accurate addition a1 * b1 + a2 * b2 + final double s12High = prod1High + prod2High; + final double s12Prime = s12High - prod2High; + final double s12Low = (prod2High - (s12High - s12Prime)) + (prod1High - s12Prime); + + // final rounding, s12 may have suffered many cancellations, we try + // to recover some bits from the extra words we have saved up to now + double result = s12High + (prod1Low + prod2Low + s12Low); + + if (Double.isNaN(result)) { + // either we have split infinite numbers or some coefficients were NaNs, + // just rely on the naive implementation and let IEEE754 handle this + result = a1 * b1 + a2 * b2; + } + + return result; + } + + /** + * Compute a linear combination accurately. + * + * <p>This method computes a<sub>1</sub>×b<sub>1</sub> + a<sub>2</sub>×b<sub>2</sub> + * + a<sub>3</sub>×b<sub>3</sub> to high accuracy. It does so by using specific + * multiplication and addition algorithms to preserve accuracy and reduce cancellation effects. + * It is based on the 2005 paper <a + * href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.2.1547">Accurate Sum and Dot + * Product</a> by Takeshi Ogita, Siegfried M. Rump, and Shin'ichi Oishi published in SIAM J. + * Sci. Comput. + * + * @param a1 first factor of the first term + * @param b1 second factor of the first term + * @param a2 first factor of the second term + * @param b2 second factor of the second term + * @param a3 first factor of the third term + * @param b3 second factor of the third term + * @return a<sub>1</sub>×b<sub>1</sub> + a<sub>2</sub>×b<sub>2</sub> + + * a<sub>3</sub>×b<sub>3</sub> + * @see #linearCombination(double, double, double, double) + * @see #linearCombination(double, double, double, double, double, double, double, double) + */ + public static double linearCombination( + final double a1, + final double b1, + final double a2, + final double b2, + final double a3, + final double b3) { + + // the code below is split in many additions/subtractions that may + // appear redundant. However, they should NOT be simplified, as they + // do use IEEE754 floating point arithmetic rounding properties. + // The variables naming conventions are that xyzHigh contains the most significant + // bits of xyz and xyzLow contains its least significant bits. So theoretically + // xyz is the sum xyzHigh + xyzLow, but in many cases below, this sum cannot + // be represented in only one double precision number so we preserve two numbers + // to hold it as long as we can, combining the high and low order bits together + // only at the end, after cancellation may have occurred on high order bits + + // split a1 and b1 as one 26 bits number and one 27 bits number + final double a1High = + Double.longBitsToDouble(Double.doubleToRawLongBits(a1) & ((-1L) << 27)); + final double a1Low = a1 - a1High; + final double b1High = + Double.longBitsToDouble(Double.doubleToRawLongBits(b1) & ((-1L) << 27)); + final double b1Low = b1 - b1High; + + // accurate multiplication a1 * b1 + final double prod1High = a1 * b1; + final double prod1Low = + a1Low * b1Low - (((prod1High - a1High * b1High) - a1Low * b1High) - a1High * b1Low); + + // split a2 and b2 as one 26 bits number and one 27 bits number + final double a2High = + Double.longBitsToDouble(Double.doubleToRawLongBits(a2) & ((-1L) << 27)); + final double a2Low = a2 - a2High; + final double b2High = + Double.longBitsToDouble(Double.doubleToRawLongBits(b2) & ((-1L) << 27)); + final double b2Low = b2 - b2High; + + // accurate multiplication a2 * b2 + final double prod2High = a2 * b2; + final double prod2Low = + a2Low * b2Low - (((prod2High - a2High * b2High) - a2Low * b2High) - a2High * b2Low); + + // split a3 and b3 as one 26 bits number and one 27 bits number + final double a3High = + Double.longBitsToDouble(Double.doubleToRawLongBits(a3) & ((-1L) << 27)); + final double a3Low = a3 - a3High; + final double b3High = + Double.longBitsToDouble(Double.doubleToRawLongBits(b3) & ((-1L) << 27)); + final double b3Low = b3 - b3High; + + // accurate multiplication a3 * b3 + final double prod3High = a3 * b3; + final double prod3Low = + a3Low * b3Low - (((prod3High - a3High * b3High) - a3Low * b3High) - a3High * b3Low); + + // accurate addition a1 * b1 + a2 * b2 + final double s12High = prod1High + prod2High; + final double s12Prime = s12High - prod2High; + final double s12Low = (prod2High - (s12High - s12Prime)) + (prod1High - s12Prime); + + // accurate addition a1 * b1 + a2 * b2 + a3 * b3 + final double s123High = s12High + prod3High; + final double s123Prime = s123High - prod3High; + final double s123Low = (prod3High - (s123High - s123Prime)) + (s12High - s123Prime); + + // final rounding, s123 may have suffered many cancellations, we try + // to recover some bits from the extra words we have saved up to now + double result = s123High + (prod1Low + prod2Low + prod3Low + s12Low + s123Low); + + if (Double.isNaN(result)) { + // either we have split infinite numbers or some coefficients were NaNs, + // just rely on the naive implementation and let IEEE754 handle this + result = a1 * b1 + a2 * b2 + a3 * b3; + } + + return result; + } + + /** + * Compute a linear combination accurately. + * + * <p>This method computes a<sub>1</sub>×b<sub>1</sub> + a<sub>2</sub>×b<sub>2</sub> + * + a<sub>3</sub>×b<sub>3</sub> + a<sub>4</sub>×b<sub>4</sub> to high accuracy. It + * does so by using specific multiplication and addition algorithms to preserve accuracy and + * reduce cancellation effects. It is based on the 2005 paper <a + * href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.2.1547">Accurate Sum and Dot + * Product</a> by Takeshi Ogita, Siegfried M. Rump, and Shin'ichi Oishi published in SIAM J. + * Sci. Comput. + * + * @param a1 first factor of the first term + * @param b1 second factor of the first term + * @param a2 first factor of the second term + * @param b2 second factor of the second term + * @param a3 first factor of the third term + * @param b3 second factor of the third term + * @param a4 first factor of the third term + * @param b4 second factor of the third term + * @return a<sub>1</sub>×b<sub>1</sub> + a<sub>2</sub>×b<sub>2</sub> + + * a<sub>3</sub>×b<sub>3</sub> + a<sub>4</sub>×b<sub>4</sub> + * @see #linearCombination(double, double, double, double) + * @see #linearCombination(double, double, double, double, double, double) + */ + public static double linearCombination( + final double a1, + final double b1, + final double a2, + final double b2, + final double a3, + final double b3, + final double a4, + final double b4) { + + // the code below is split in many additions/subtractions that may + // appear redundant. However, they should NOT be simplified, as they + // do use IEEE754 floating point arithmetic rounding properties. + // The variables naming conventions are that xyzHigh contains the most significant + // bits of xyz and xyzLow contains its least significant bits. So theoretically + // xyz is the sum xyzHigh + xyzLow, but in many cases below, this sum cannot + // be represented in only one double precision number so we preserve two numbers + // to hold it as long as we can, combining the high and low order bits together + // only at the end, after cancellation may have occurred on high order bits + + // split a1 and b1 as one 26 bits number and one 27 bits number + final double a1High = + Double.longBitsToDouble(Double.doubleToRawLongBits(a1) & ((-1L) << 27)); + final double a1Low = a1 - a1High; + final double b1High = + Double.longBitsToDouble(Double.doubleToRawLongBits(b1) & ((-1L) << 27)); + final double b1Low = b1 - b1High; + + // accurate multiplication a1 * b1 + final double prod1High = a1 * b1; + final double prod1Low = + a1Low * b1Low - (((prod1High - a1High * b1High) - a1Low * b1High) - a1High * b1Low); + + // split a2 and b2 as one 26 bits number and one 27 bits number + final double a2High = + Double.longBitsToDouble(Double.doubleToRawLongBits(a2) & ((-1L) << 27)); + final double a2Low = a2 - a2High; + final double b2High = + Double.longBitsToDouble(Double.doubleToRawLongBits(b2) & ((-1L) << 27)); + final double b2Low = b2 - b2High; + + // accurate multiplication a2 * b2 + final double prod2High = a2 * b2; + final double prod2Low = + a2Low * b2Low - (((prod2High - a2High * b2High) - a2Low * b2High) - a2High * b2Low); + + // split a3 and b3 as one 26 bits number and one 27 bits number + final double a3High = + Double.longBitsToDouble(Double.doubleToRawLongBits(a3) & ((-1L) << 27)); + final double a3Low = a3 - a3High; + final double b3High = + Double.longBitsToDouble(Double.doubleToRawLongBits(b3) & ((-1L) << 27)); + final double b3Low = b3 - b3High; + + // accurate multiplication a3 * b3 + final double prod3High = a3 * b3; + final double prod3Low = + a3Low * b3Low - (((prod3High - a3High * b3High) - a3Low * b3High) - a3High * b3Low); + + // split a4 and b4 as one 26 bits number and one 27 bits number + final double a4High = + Double.longBitsToDouble(Double.doubleToRawLongBits(a4) & ((-1L) << 27)); + final double a4Low = a4 - a4High; + final double b4High = + Double.longBitsToDouble(Double.doubleToRawLongBits(b4) & ((-1L) << 27)); + final double b4Low = b4 - b4High; + + // accurate multiplication a4 * b4 + final double prod4High = a4 * b4; + final double prod4Low = + a4Low * b4Low - (((prod4High - a4High * b4High) - a4Low * b4High) - a4High * b4Low); + + // accurate addition a1 * b1 + a2 * b2 + final double s12High = prod1High + prod2High; + final double s12Prime = s12High - prod2High; + final double s12Low = (prod2High - (s12High - s12Prime)) + (prod1High - s12Prime); + + // accurate addition a1 * b1 + a2 * b2 + a3 * b3 + final double s123High = s12High + prod3High; + final double s123Prime = s123High - prod3High; + final double s123Low = (prod3High - (s123High - s123Prime)) + (s12High - s123Prime); + + // accurate addition a1 * b1 + a2 * b2 + a3 * b3 + a4 * b4 + final double s1234High = s123High + prod4High; + final double s1234Prime = s1234High - prod4High; + final double s1234Low = (prod4High - (s1234High - s1234Prime)) + (s123High - s1234Prime); + + // final rounding, s1234 may have suffered many cancellations, we try + // to recover some bits from the extra words we have saved up to now + double result = + s1234High + + (prod1Low + prod2Low + prod3Low + prod4Low + s12Low + s123Low + s1234Low); + + if (Double.isNaN(result)) { + // either we have split infinite numbers or some coefficients were NaNs, + // just rely on the naive implementation and let IEEE754 handle this + result = a1 * b1 + a2 * b2 + a3 * b3 + a4 * b4; + } + + return result; + } + + /** + * Returns true iff both arguments are null or have same dimensions and all their elements are + * equal as defined by {@link Precision#equals(float,float)}. + * + * @param x first array + * @param y second array + * @return true if the values are both null or have same dimension and equal elements. + */ + public static boolean equals(float[] x, float[] y) { + if ((x == null) || (y == null)) { + return !((x == null) ^ (y == null)); + } + if (x.length != y.length) { + return false; + } + for (int i = 0; i < x.length; ++i) { + if (!Precision.equals(x[i], y[i])) { + return false; + } + } + return true; + } + + /** + * Returns true iff both arguments are null or have same dimensions and all their elements are + * equal as defined by {@link Precision#equalsIncludingNaN(double,double) this method}. + * + * @param x first array + * @param y second array + * @return true if the values are both null or have same dimension and equal elements + * @since 2.2 + */ + public static boolean equalsIncludingNaN(float[] x, float[] y) { + if ((x == null) || (y == null)) { + return !((x == null) ^ (y == null)); + } + if (x.length != y.length) { + return false; + } + for (int i = 0; i < x.length; ++i) { + if (!Precision.equalsIncludingNaN(x[i], y[i])) { + return false; + } + } + return true; + } + + /** + * Returns {@code true} iff both arguments are {@code null} or have same dimensions and all + * their elements are equal as defined by {@link Precision#equals(double,double)}. + * + * @param x First array. + * @param y Second array. + * @return {@code true} if the values are both {@code null} or have same dimension and equal + * elements. + */ + public static boolean equals(double[] x, double[] y) { + if ((x == null) || (y == null)) { + return !((x == null) ^ (y == null)); + } + if (x.length != y.length) { + return false; + } + for (int i = 0; i < x.length; ++i) { + if (!Precision.equals(x[i], y[i])) { + return false; + } + } + return true; + } + + /** + * Returns {@code true} iff both arguments are {@code null} or have same dimensions and all + * their elements are equal as defined by {@link Precision#equalsIncludingNaN(double,double) + * this method}. + * + * @param x First array. + * @param y Second array. + * @return {@code true} if the values are both {@code null} or have same dimension and equal + * elements. + * @since 2.2 + */ + public static boolean equalsIncludingNaN(double[] x, double[] y) { + if ((x == null) || (y == null)) { + return !((x == null) ^ (y == null)); + } + if (x.length != y.length) { + return false; + } + for (int i = 0; i < x.length; ++i) { + if (!Precision.equalsIncludingNaN(x[i], y[i])) { + return false; + } + } + return true; + } + + /** + * Normalizes an array to make it sum to a specified value. Returns the result of the + * transformation + * + * <pre> + * x |-> x * normalizedSum / sum + * </pre> + * + * applied to each non-NaN element x of the input array, where sum is the sum of the non-NaN + * entries in the input array. + * + * <p>Throws IllegalArgumentException if {@code normalizedSum} is infinite or NaN and + * ArithmeticException if the input array contains any infinite elements or sums to 0. + * + * <p>Ignores (i.e., copies unchanged to the output array) NaNs in the input array. + * + * @param values Input array to be normalized + * @param normalizedSum Target sum for the normalized array + * @return the normalized array. + * @throws MathArithmeticException if the input array contains infinite elements or sums to + * zero. + * @throws MathIllegalArgumentException if the target sum is infinite or {@code NaN}. + * @since 2.1 + */ + public static double[] normalizeArray(double[] values, double normalizedSum) + throws MathIllegalArgumentException, MathArithmeticException { + if (Double.isInfinite(normalizedSum)) { + throw new MathIllegalArgumentException(LocalizedFormats.NORMALIZE_INFINITE); + } + if (Double.isNaN(normalizedSum)) { + throw new MathIllegalArgumentException(LocalizedFormats.NORMALIZE_NAN); + } + double sum = 0d; + final int len = values.length; + double[] out = new double[len]; + for (int i = 0; i < len; i++) { + if (Double.isInfinite(values[i])) { + throw new MathIllegalArgumentException( + LocalizedFormats.INFINITE_ARRAY_ELEMENT, values[i], i); + } + if (!Double.isNaN(values[i])) { + sum += values[i]; + } + } + if (sum == 0) { + throw new MathArithmeticException(LocalizedFormats.ARRAY_SUMS_TO_ZERO); + } + for (int i = 0; i < len; i++) { + if (Double.isNaN(values[i])) { + out[i] = Double.NaN; + } else { + out[i] = values[i] * normalizedSum / sum; + } + } + return out; + } + + /** + * Build an array of elements. + * + * <p>Arrays are filled with field.getZero() + * + * @param <T> the type of the field elements + * @param field field to which array elements belong + * @param length of the array + * @return a new array + * @since 3.2 + */ + public static <T> T[] buildArray(final Field<T> field, final int length) { + @SuppressWarnings("unchecked") // OK because field must be correct class + T[] array = (T[]) Array.newInstance(field.getRuntimeClass(), length); + Arrays.fill(array, field.getZero()); + return array; + } + + /** + * Build a double dimension array of elements. + * + * <p>Arrays are filled with field.getZero() + * + * @param <T> the type of the field elements + * @param field field to which array elements belong + * @param rows number of rows in the array + * @param columns number of columns (may be negative to build partial arrays in the same way + * <code>new Field[rows][]</code> works) + * @return a new array + * @since 3.2 + */ + @SuppressWarnings("unchecked") + public static <T> T[][] buildArray(final Field<T> field, final int rows, final int columns) { + final T[][] array; + if (columns < 0) { + T[] dummyRow = buildArray(field, 0); + array = (T[][]) Array.newInstance(dummyRow.getClass(), rows); + } else { + array = (T[][]) Array.newInstance(field.getRuntimeClass(), new int[] {rows, columns}); + for (int i = 0; i < rows; ++i) { + Arrays.fill(array[i], field.getZero()); + } + } + return array; + } + + /** + * Calculates the <a href="http://en.wikipedia.org/wiki/Convolution">convolution</a> between two + * sequences. + * + * <p>The solution is obtained via straightforward computation of the convolution sum (and not + * via FFT). Whenever the computation needs an element that would be located at an index outside + * the input arrays, the value is assumed to be zero. + * + * @param x First sequence. Typically, this sequence will represent an input signal to a system. + * @param h Second sequence. Typically, this sequence will represent the impulse response of the + * system. + * @return the convolution of {@code x} and {@code h}. This array's length will be {@code + * x.length + h.length - 1}. + * @throws NullArgumentException if either {@code x} or {@code h} is {@code null}. + * @throws NoDataException if either {@code x} or {@code h} is empty. + * @since 3.3 + */ + public static double[] convolve(double[] x, double[] h) + throws NullArgumentException, NoDataException { + MathUtils.checkNotNull(x); + MathUtils.checkNotNull(h); + + final int xLen = x.length; + final int hLen = h.length; + + if (xLen == 0 || hLen == 0) { + throw new NoDataException(); + } + + // initialize the output array + final int totalLength = xLen + hLen - 1; + final double[] y = new double[totalLength]; + + // straightforward implementation of the convolution sum + for (int n = 0; n < totalLength; n++) { + double yn = 0; + int k = FastMath.max(0, n + 1 - xLen); + int j = n - k; + while (k < hLen && j >= 0) { + yn += x[j--] * h[k++]; + } + y[n] = yn; + } + + return y; + } + + /** Specification for indicating that some operation applies before or after a given index. */ + public enum Position { + /** Designates the beginning of the array (near index 0). */ + HEAD, + /** Designates the end of the array. */ + TAIL + } + + /** + * Shuffle the entries of the given array. The {@code start} and {@code pos} parameters select + * which portion of the array is randomized and which is left untouched. + * + * @see #shuffle(int[],int,Position,RandomGenerator) + * @param list Array whose entries will be shuffled (in-place). + * @param start Index at which shuffling begins. + * @param pos Shuffling is performed for index positions between {@code start} and either the + * end (if {@link Position#TAIL}) or the beginning (if {@link Position#HEAD}) of the array. + */ + public static void shuffle(int[] list, int start, Position pos) { + shuffle(list, start, pos, new Well19937c()); + } + + /** + * Shuffle the entries of the given array, using the <a + * href="http://en.wikipedia.org/wiki/Fisher–Yates_shuffle#The_modern_algorithm"> + * Fisher–Yates</a> algorithm. The {@code start} and {@code pos} parameters select which portion + * of the array is randomized and which is left untouched. + * + * @param list Array whose entries will be shuffled (in-place). + * @param start Index at which shuffling begins. + * @param pos Shuffling is performed for index positions between {@code start} and either the + * end (if {@link Position#TAIL}) or the beginning (if {@link Position#HEAD}) of the array. + * @param rng Random number generator. + */ + public static void shuffle(int[] list, int start, Position pos, RandomGenerator rng) { + switch (pos) { + case TAIL: + { + for (int i = list.length - 1; i >= start; i--) { + final int target; + if (i == start) { + target = start; + } else { + // NumberIsTooLargeException cannot occur. + target = new UniformIntegerDistribution(rng, start, i).sample(); + } + final int temp = list[target]; + list[target] = list[i]; + list[i] = temp; + } + } + break; + case HEAD: + { + for (int i = 0; i <= start; i++) { + final int target; + if (i == start) { + target = start; + } else { + // NumberIsTooLargeException cannot occur. + target = new UniformIntegerDistribution(rng, i, start).sample(); + } + final int temp = list[target]; + list[target] = list[i]; + list[i] = temp; + } + } + break; + default: + throw new MathInternalError(); // Should never happen. + } + } + + /** + * Shuffle the entries of the given array. + * + * @see #shuffle(int[],int,Position,RandomGenerator) + * @param list Array whose entries will be shuffled (in-place). + * @param rng Random number generator. + */ + public static void shuffle(int[] list, RandomGenerator rng) { + shuffle(list, 0, Position.TAIL, rng); + } + + /** + * Shuffle the entries of the given array. + * + * @see #shuffle(int[],int,Position,RandomGenerator) + * @param list Array whose entries will be shuffled (in-place). + */ + public static void shuffle(int[] list) { + shuffle(list, new Well19937c()); + } + + /** + * Returns an array representing the natural number {@code n}. + * + * @param n Natural number. + * @return an array whose entries are the numbers 0, 1, ..., {@code n}-1. If {@code n == 0}, the + * returned array is empty. + */ + public static int[] natural(int n) { + return sequence(n, 0, 1); + } + + /** + * Returns an array of {@code size} integers starting at {@code start}, skipping {@code stride} + * numbers. + * + * @param size Natural number. + * @param start Natural number. + * @param stride Natural number. + * @return an array whose entries are the numbers {@code start, start + stride, ..., start + + * (size - 1) * stride}. If {@code size == 0}, the returned array is empty. + * @since 3.4 + */ + public static int[] sequence(int size, int start, int stride) { + final int[] a = new int[size]; + for (int i = 0; i < size; i++) { + a[i] = start + i * stride; + } + return a; + } + + /** + * This method is used + * to verify that the input parameters designate a subarray of positive length. + * <p> + * <ul> + * <li>returns <code>true</code> iff the parameters designate a subarray of + * positive length</li> + * <li>throws <code>MathIllegalArgumentException</code> if the array is null or + * or the indices are invalid</li> + * <li>returns <code>false</li> if the array is non-null, but + * <code>length</code> is 0. + * </ul></p> + * + * @param values the input array + * @param begin index of the first array element to include + * @param length the number of elements to include + * @return true if the parameters are valid and designate a subarray of positive length + * @throws MathIllegalArgumentException if the indices are invalid or the array is null + * @since 3.3 + */ + public static boolean verifyValues(final double[] values, final int begin, final int length) + throws MathIllegalArgumentException { + return verifyValues(values, begin, length, false); + } + + /** + * This method is used + * to verify that the input parameters designate a subarray of positive length. + * <p> + * <ul> + * <li>returns <code>true</code> iff the parameters designate a subarray of + * non-negative length</li> + * <li>throws <code>IllegalArgumentException</code> if the array is null or + * or the indices are invalid</li> + * <li>returns <code>false</li> if the array is non-null, but + * <code>length</code> is 0 unless <code>allowEmpty</code> is <code>true</code> + * </ul></p> + * + * @param values the input array + * @param begin index of the first array element to include + * @param length the number of elements to include + * @param allowEmpty if <code>true</code> then zero length arrays are allowed + * @return true if the parameters are valid + * @throws MathIllegalArgumentException if the indices are invalid or the array is null + * @since 3.3 + */ + public static boolean verifyValues( + final double[] values, final int begin, final int length, final boolean allowEmpty) + throws MathIllegalArgumentException { + + if (values == null) { + throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY); + } + + if (begin < 0) { + throw new NotPositiveException(LocalizedFormats.START_POSITION, Integer.valueOf(begin)); + } + + if (length < 0) { + throw new NotPositiveException(LocalizedFormats.LENGTH, Integer.valueOf(length)); + } + + if (begin + length > values.length) { + throw new NumberIsTooLargeException( + LocalizedFormats.SUBARRAY_ENDS_AFTER_ARRAY_END, + Integer.valueOf(begin + length), + Integer.valueOf(values.length), + true); + } + + if (length == 0 && !allowEmpty) { + return false; + } + + return true; + } + + /** + * This method is used + * to verify that the begin and length parameters designate a subarray of positive length + * and the weights are all non-negative, non-NaN, finite, and not all zero. + * <p> + * <ul> + * <li>returns <code>true</code> iff the parameters designate a subarray of + * positive length and the weights array contains legitimate values.</li> + * <li>throws <code>IllegalArgumentException</code> if any of the following are true: + * <ul><li>the values array is null</li> + * <li>the weights array is null</li> + * <li>the weights array does not have the same length as the values array</li> + * <li>the weights array contains one or more infinite values</li> + * <li>the weights array contains one or more NaN values</li> + * <li>the weights array contains negative values</li> + * <li>the start and length arguments do not determine a valid array</li></ul> + * </li> + * <li>returns <code>false</li> if the array is non-null, but + * <code>length</code> is 0. + * </ul></p> + * + * @param values the input array + * @param weights the weights array + * @param begin index of the first array element to include + * @param length the number of elements to include + * @return true if the parameters are valid and designate a subarray of positive length + * @throws MathIllegalArgumentException if the indices are invalid or the array is null + * @since 3.3 + */ + public static boolean verifyValues( + final double[] values, final double[] weights, final int begin, final int length) + throws MathIllegalArgumentException { + return verifyValues(values, weights, begin, length, false); + } + + /** + * This method is used + * to verify that the begin and length parameters designate a subarray of positive length + * and the weights are all non-negative, non-NaN, finite, and not all zero. + * <p> + * <ul> + * <li>returns <code>true</code> iff the parameters designate a subarray of + * non-negative length and the weights array contains legitimate values.</li> + * <li>throws <code>MathIllegalArgumentException</code> if any of the following are true: + * <ul><li>the values array is null</li> + * <li>the weights array is null</li> + * <li>the weights array does not have the same length as the values array</li> + * <li>the weights array contains one or more infinite values</li> + * <li>the weights array contains one or more NaN values</li> + * <li>the weights array contains negative values</li> + * <li>the start and length arguments do not determine a valid array</li></ul> + * </li> + * <li>returns <code>false</li> if the array is non-null, but + * <code>length</code> is 0 unless <code>allowEmpty</code> is <code>true</code>. + * </ul></p> + * + * @param values the input array. + * @param weights the weights array. + * @param begin index of the first array element to include. + * @param length the number of elements to include. + * @param allowEmpty if {@code true} than allow zero length arrays to pass. + * @return {@code true} if the parameters are valid. + * @throws NullArgumentException if either of the arrays are null + * @throws MathIllegalArgumentException if the array indices are not valid, + * the weights array contains NaN, infinite or negative elements, or there + * are no positive weights. + * @since 3.3 + */ + public static boolean verifyValues( + final double[] values, + final double[] weights, + final int begin, + final int length, + final boolean allowEmpty) + throws MathIllegalArgumentException { + + if (weights == null || values == null) { + throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY); + } + + checkEqualLength(weights, values); + + boolean containsPositiveWeight = false; + for (int i = begin; i < begin + length; i++) { + final double weight = weights[i]; + if (Double.isNaN(weight)) { + throw new MathIllegalArgumentException( + LocalizedFormats.NAN_ELEMENT_AT_INDEX, Integer.valueOf(i)); + } + if (Double.isInfinite(weight)) { + throw new MathIllegalArgumentException( + LocalizedFormats.INFINITE_ARRAY_ELEMENT, + Double.valueOf(weight), + Integer.valueOf(i)); + } + if (weight < 0) { + throw new MathIllegalArgumentException( + LocalizedFormats.NEGATIVE_ELEMENT_AT_INDEX, + Integer.valueOf(i), + Double.valueOf(weight)); + } + if (!containsPositiveWeight && weight > 0.0) { + containsPositiveWeight = true; + } + } + + if (!containsPositiveWeight) { + throw new MathIllegalArgumentException(LocalizedFormats.WEIGHT_AT_LEAST_ONE_NON_ZERO); + } + + return verifyValues(values, begin, length, allowEmpty); + } + + /** + * Concatenates a sequence of arrays. The return array consists of the entries of the input + * arrays concatenated in the order they appear in the argument list. Null arrays cause + * NullPointerExceptions; zero length arrays are allowed (contributing nothing to the output + * array). + * + * @param x list of double[] arrays to concatenate + * @return a new array consisting of the entries of the argument arrays + * @throws NullPointerException if any of the arrays are null + * @since 3.6 + */ + public static double[] concatenate(double[]... x) { + int combinedLength = 0; + for (double[] a : x) { + combinedLength += a.length; + } + int offset = 0; + int curLength = 0; + final double[] combined = new double[combinedLength]; + for (int i = 0; i < x.length; i++) { + curLength = x[i].length; + System.arraycopy(x[i], 0, combined, offset, curLength); + offset += curLength; + } + return combined; + } + + /** + * Returns an array consisting of the unique values in {@code data}. The return array is sorted + * in descending order. Empty arrays are allowed, but null arrays result in + * NullPointerException. Infinities are allowed. NaN values are allowed with maximum sort order + * - i.e., if there are NaN values in {@code data}, {@code Double.NaN} will be the first element + * of the output array, even if the array also contains {@code Double.POSITIVE_INFINITY}. + * + * @param data array to scan + * @return descending list of values included in the input array + * @throws NullPointerException if data is null + * @since 3.6 + */ + public static double[] unique(double[] data) { + TreeSet<Double> values = new TreeSet<Double>(); + for (int i = 0; i < data.length; i++) { + values.add(data[i]); + } + final int count = values.size(); + final double[] out = new double[count]; + Iterator<Double> iterator = values.iterator(); + int i = 0; + while (iterator.hasNext()) { + out[count - ++i] = iterator.next(); + } + return out; + } +} |