org.apache.commons.math4.util.MathArrays Maven / Gradle / Ivy
Show all versions of virtdata-lib-curves4 Show documentation
/*
* 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.math4.util;
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;
import org.apache.commons.numbers.core.Precision;
import org.apache.commons.math4.Field;
import org.apache.commons.math4.exception.DimensionMismatchException;
import org.apache.commons.math4.exception.MathArithmeticException;
import org.apache.commons.math4.exception.MathIllegalArgumentException;
import org.apache.commons.math4.exception.MathInternalError;
import org.apache.commons.math4.exception.NoDataException;
import org.apache.commons.math4.exception.NonMonotonicSequenceException;
import org.apache.commons.math4.exception.NotANumberException;
import org.apache.commons.math4.exception.NotPositiveException;
import org.apache.commons.math4.exception.NotStrictlyPositiveException;
import org.apache.commons.math4.exception.NullArgumentException;
import org.apache.commons.math4.exception.NumberIsTooLargeException;
import org.apache.commons.math4.exception.util.LocalizedFormats;
/**
* 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.
*
* 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 L1 (sum of abs) distance between two points.
*
* @param p1 the first point
* @param p2 the second point
* @return the L1 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 L1 (sum of abs) distance between two points.
*
* @param p1 the first point
* @param p2 the second point
* @return the L1 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 L2 (Euclidean) distance between two points.
*
* @param p1 the first point
* @param p2 the second point
* @return the L2 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 L2 (Euclidean) distance between two points.
*
* @param p1 the first point
* @param p2 the second point
* @return the L2 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∞ (max of abs) distance between two points.
*
* @param p1 the first point
* @param p2 the second point
* @return the L∞ 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∞ (max of abs) distance between two points.
*
* @param p1 the first point
* @param p2 the second point
* @return the L∞ 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 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 > 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]);
}
}
}
}
/**
* 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);
}
/**
* Helper data structure holding a (double, integer) pair.
*/
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 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 list
= new ArrayList<>(len);
for (int i = 0; i < len; i++) {
list.add(new PairDoubleInteger(x[i], i));
}
// Create comparators for increasing and decreasing orders.
final Comparator comp
= dir == OrderDirection.INCREASING ?
new Comparator() {
/** {@inheritDoc} */
@Override
public int compare(PairDoubleInteger o1,
PairDoubleInteger o2) {
return Double.compare(o1.getKey(), o2.getKey());
}
} : new Comparator() {
/** {@inheritDoc} */
@Override
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;
}
/**
* 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
*
* x |-> x * normalizedSum / sum
*
* 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.
*
* Throws IllegalArgumentException if {@code normalizedSum} is infinite
* or NaN and ArithmeticException if the input array contains any infinite elements
* or sums to 0.
*
* 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.
*
* Arrays are filled with field.getZero()
*
* @param 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[] buildArray(final Field 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.
*
* Arrays are filled with field.getZero()
*
* @param 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 new Field[rows][]
works)
* @return a new array
* @since 3.2
*/
@SuppressWarnings("unchecked")
public static T[][] buildArray(final Field 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
* convolution between two sequences.
*
* 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;
}
/**
* 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.
*
* - returns
true
iff the parameters designate a subarray of
* positive length
* - throws
MathIllegalArgumentException
if the array is null or
* or the indices are invalid
* - returns
false
if the array is non-null, but
* length
is 0.
*
*
* @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.
*
* - returns
true
iff the parameters designate a subarray of
* non-negative length
* - throws
IllegalArgumentException
if the array is null or
* or the indices are invalid
* - returns
false
if the array is non-null, but
* length
is 0 unless allowEmpty
is true
*
*
* @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 true
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.
*
* - returns
true
iff the parameters designate a subarray of
* positive length and the weights array contains legitimate values.
* - throws
IllegalArgumentException
if any of the following are true:
* - the values array is null
* - the weights array is null
* - the weights array does not have the same length as the values array
* - the weights array contains one or more infinite values
* - the weights array contains one or more NaN values
* - the weights array contains negative values
* - the start and length arguments do not determine a valid array
*
* - returns
false
if the array is non-null, but
* length
is 0.
*
*
* @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.
*
* - returns
true
iff the parameters designate a subarray of
* non-negative length and the weights array contains legitimate values.
* - throws
MathIllegalArgumentException
if any of the following are true:
* - the values array is null
* - the weights array is null
* - the weights array does not have the same length as the values array
* - the weights array contains one or more infinite values
* - the weights array contains one or more NaN values
* - the weights array contains negative values
* - the start and length arguments do not determine a valid array
*
* - returns
false
if the array is non-null, but
* length
is 0 unless allowEmpty
is true
.
*
*
* @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 values = new TreeSet<>();
for (int i = 0; i < data.length; i++) {
values.add(data[i]);
}
final int count = values.size();
final double[] out = new double[count];
Iterator iterator = values.descendingIterator();
int i = 0;
while (iterator.hasNext()) {
out[i++] = iterator.next();
}
return out;
}
}