From e89db38df8a93104796dc13ce5327988984fefac Mon Sep 17 00:00:00 2001
From: Phil Steitz
Date: Sun, 30 Aug 2009 18:10:16 +0000
Subject: [PATCH] Added normalizeArray method to MathUtils.
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@809394 13f79535-47bb-0310-9956-ffa450edef68
---
.../apache/commons/math/util/MathUtils.java | 56 +++++++++++++++++++
src/site/xdoc/changes.xml | 3 +
.../commons/math/util/MathUtilsTest.java | 48 ++++++++++++++++
3 files changed, 107 insertions(+)
diff --git a/src/main/java/org/apache/commons/math/util/MathUtils.java b/src/main/java/org/apache/commons/math/util/MathUtils.java
index 28ee4a937..d5c331de0 100644
--- a/src/main/java/org/apache/commons/math/util/MathUtils.java
+++ b/src/main/java/org/apache/commons/math/util/MathUtils.java
@@ -987,6 +987,62 @@ public final class MathUtils {
public static double normalizeAngle(double a, double center) {
return a - TWO_PI * Math.floor((a + Math.PI - center) / TWO_PI);
}
+
+ /**
+ * 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 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 normalized array
+ * @throws ArithmeticException if the input array contains infinite elements or sums to zero
+ * @throws IllegalArgumentException if the target sum is infinite or NaN
+ */
+ public static double[] normalizeArray(double[] values, double normalizedSum)
+ throws ArithmeticException, IllegalArgumentException {
+ if (Double.isInfinite(normalizedSum)) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ "Cannot normalize to an infinite value");
+ }
+ if (Double.isNaN(normalizedSum)) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ "Cannot normalize to 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 MathRuntimeException.createArithmeticException(
+ "Array contains an infinite element", values[i], i);
+ }
+ if (!Double.isNaN(values[i])) {
+ sum += values[i];
+ }
+ }
+ if (sum == 0) {
+ throw MathRuntimeException.createArithmeticException(
+ "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;
+ }
/**
* Round the given value to the specified number of decimal places. The
diff --git a/src/site/xdoc/changes.xml b/src/site/xdoc/changes.xml
index d303f9acd..933cfa4db 100644
--- a/src/site/xdoc/changes.xml
+++ b/src/site/xdoc/changes.xml
@@ -39,6 +39,9 @@ The type attribute can be add,update,fix,remove.
+
+ Added normalizeArray method to MathUtils.
+
Fixed a NullPointerException in simplex solver when no solution is possible
and some constraints are negative.
diff --git a/src/test/java/org/apache/commons/math/util/MathUtilsTest.java b/src/test/java/org/apache/commons/math/util/MathUtilsTest.java
index d048f5f71..1486c9d1d 100644
--- a/src/test/java/org/apache/commons/math/util/MathUtilsTest.java
+++ b/src/test/java/org/apache/commons/math/util/MathUtilsTest.java
@@ -796,6 +796,54 @@ public final class MathUtilsTest extends TestCase {
}
}
}
+
+ public void testNormalizeArray() {
+ double[] testValues1 = new double[] {1, 1, 2};
+ TestUtils.assertEquals(
+ new double[] {.25, .25, .5},
+ MathUtils.normalizeArray(testValues1, 1),
+ Double.MIN_VALUE);
+
+ double[] testValues2 = new double[] {-1, -1, 1};
+ TestUtils.assertEquals(
+ new double[] {1, 1, -1},
+ MathUtils.normalizeArray(testValues2, 1),
+ Double.MIN_VALUE);
+
+ // Ignore NaNs
+ double[] testValues3 = new double[] {-1, -1, Double.NaN, 1, Double.NaN};
+ TestUtils.assertEquals(
+ new double[] {1, 1,Double.NaN, -1, Double.NaN},
+ MathUtils.normalizeArray(testValues3, 1),
+ Double.MIN_VALUE);
+
+ // Zero sum -> ArithmeticException
+ double[] zeroSum = new double[] {-1, 1};
+ try {
+ MathUtils.normalizeArray(zeroSum, 1);
+ fail("expecting ArithmeticException");
+ } catch (ArithmeticException ex) {}
+
+ // Infinite elements -> ArithmeticException
+ double[] hasInf = new double[] {1, 2, 1, Double.NEGATIVE_INFINITY};
+ try {
+ MathUtils.normalizeArray(hasInf, 1);
+ fail("expecting ArithmeticException");
+ } catch (ArithmeticException ex) {}
+
+ // Infinite target -> IllegalArgumentException
+ try {
+ MathUtils.normalizeArray(testValues1, Double.POSITIVE_INFINITY);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {}
+
+ // NaN target -> IllegalArgumentException
+ try {
+ MathUtils.normalizeArray(testValues1, Double.NaN);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {}
+
+ }
public void testRoundDouble() {
double x = 1.234567890;