package net.sf.openrocket.util;
-public class MathUtil {
- public static final double EPSILON = 0.00000001; // 10mm^3 in m^3
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+public class MathUtil {
+ public static final double EPSILON = 0.00000001; // 10mm^3 in m^3
+
/**
* The square of x (x^2). On Sun's JRE using this method is as fast as typing x*x.
* @param x x
* @return x^2
*/
public static double pow2(double x) {
- return x*x;
+ return x * x;
}
/**
* @return x^3
*/
public static double pow3(double x) {
- return x*x*x;
+ return x * x * x;
}
public static double pow4(double x) {
- return (x*x)*(x*x);
+ return (x * x) * (x * x);
}
/**
if (equals(toMin, toMax))
return toMin;
if (equals(fromMin, fromMax)) {
- throw new IllegalArgumentException("from range is singular an to range is not.");
+ throw new IllegalArgumentException("from range is singular and to range is not: " +
+ "value=" + value + " fromMin=" + fromMin + " fromMax=" + fromMax +
+ "toMin=" + toMin + " toMax=" + toMax);
}
- return (value - fromMin)/(fromMax-fromMin) * (toMax - toMin) + toMin;
+ return (value - fromMin) / (fromMax - fromMin) * (toMax - toMin) + toMin;
}
+
+ /**
+ * Maps a coordinate from one value range to another.
+ *
+ * @param value the value to map.
+ * @param fromMin the minimum of the starting range.
+ * @param fromMax the maximum of the starting range.
+ * @param toMin the minimum coordinate of the destination;
+ * @param toMax the maximum coordinate of the destination;
+ * @return the mapped value.
+ * @throws IllegalArgumentException if fromMin == fromMax, but toMin != toMax.
+ */
+ public static Coordinate map(double value, double fromMin, double fromMax,
+ Coordinate toMin, Coordinate toMax) {
+ if (toMin.equals(toMax))
+ return toMin;
+ if (equals(fromMin, fromMax)) {
+ throw new IllegalArgumentException("from range is singular and to range is not: " +
+ "value=" + value + " fromMin=" + fromMin + " fromMax=" + fromMax +
+ "toMin=" + toMin + " toMax=" + toMax);
+ }
+ double a = (value - fromMin) / (fromMax - fromMin);
+ return toMax.multiply(a).add(toMin.multiply(1 - a));
+ }
+
+
/**
* Compute the minimum of two values. This is performed by direct comparison.
* However, if one of the values is NaN and the other is not, the non-NaN value is
*/
public static double min(double x, double y, double z) {
if (x < y || Double.isNaN(y)) {
- return min(x,z);
+ return min(x, z);
} else {
- return min(y,z);
+ return min(y, z);
}
}
*/
public static double max(double x, double y, double z) {
if (x > y || Double.isNaN(y)) {
- return max(x,z);
+ return max(x, z);
} else {
- return max(y,z);
+ return max(y, z);
}
}
* faster than <code>Math.hypot(x,y)</code>.
*/
public static double hypot(double x, double y) {
- return Math.sqrt(x*x + y*y);
+ return Math.sqrt(x * x + y * y);
}
-
+
/**
* Reduce the angle x to the range 0 - 2*PI.
* @param x Original angle.
* @return The equivalent angle in the range 0 ... 2*PI.
*/
public static double reduce360(double x) {
- double d = Math.floor(x / (2*Math.PI));
- return x - d*2*Math.PI;
+ double d = Math.floor(x / (2 * Math.PI));
+ return x - d * 2 * Math.PI;
}
-
+
/**
* Reduce the angle x to the range -PI - PI.
*
* @return The equivalent angle in the range -PI ... PI.
*/
public static double reduce180(double x) {
- double d = Math.rint(x / (2*Math.PI));
- return x - d*2*Math.PI;
+ double d = Math.rint(x / (2 * Math.PI));
+ return x - d * 2 * Math.PI;
}
public static boolean equals(double a, double b) {
double absb = Math.abs(b);
- if (absb < EPSILON/2) {
+ if (absb < EPSILON / 2) {
// Near zero
- return Math.abs(a) < EPSILON/2;
+ return Math.abs(a) < EPSILON / 2;
}
- return Math.abs(a-b) < EPSILON*absb;
+ return Math.abs(a - b) < EPSILON * absb;
}
+
+ /**
+ * Return the sign of the number. This corresponds to Math.signum, but ignores
+ * the special cases of zero and NaN. The value returned for those is arbitrary.
+ * <p>
+ * This method is about 4 times faster than Math.signum().
+ *
+ * @param x the checked value.
+ * @return -1.0 if x<0; 1.0 if x>0; otherwise either -1.0 or 1.0.
+ */
public static double sign(double x) {
- return (x<0) ? -1.0 : 1.0;
+ return (x < 0) ? -1.0 : 1.0;
}
-
+
/* Math.abs() is about 3x as fast as this:
public static double abs(double x) {
return (x<0) ? -x : x;
}
*/
+
+
+ public static double average(Collection<? extends Number> values) {
+ if (values.isEmpty()) {
+ return Double.NaN;
+ }
+
+ double avg = 0.0;
+ int count = 0;
+ for (Number n : values) {
+ avg += n.doubleValue();
+ count++;
+ }
+ return avg / count;
+ }
+ public static double stddev(Collection<? extends Number> values) {
+ if (values.size() < 2) {
+ return Double.NaN;
+ }
+
+ double avg = average(values);
+ double stddev = 0.0;
+ int count = 0;
+ for (Number n : values) {
+ stddev += pow2(n.doubleValue() - avg);
+ count++;
+ }
+ stddev = Math.sqrt(stddev / (count - 1));
+ return stddev;
+ }
- public static void main(String[] arg) {
- double nan = Double.NaN;
- System.out.println("min(5,6) = " + min(5, 6));
- System.out.println("min(5,nan) = " + min(5, nan));
- System.out.println("min(nan,6) = " + min(nan, 6));
- System.out.println("min(nan,nan) = " + min(nan, nan));
- System.out.println();
- System.out.println("max(5,6) = " + max(5, 6));
- System.out.println("max(5,nan) = " + max(5, nan));
- System.out.println("max(nan,6) = " + max(nan, 6));
- System.out.println("max(nan,nan) = " + max(nan, nan));
- System.out.println();
- System.out.println("min(5,6,7) = " + min(5, 6, 7));
- System.out.println("min(5,6,nan) = " + min(5, 6, nan));
- System.out.println("min(5,nan,7) = " + min(5, nan, 7));
- System.out.println("min(5,nan,nan) = " + min(5, nan, nan));
- System.out.println("min(nan,6,7) = " + min(nan, 6, 7));
- System.out.println("min(nan,6,nan) = " + min(nan, 6, nan));
- System.out.println("min(nan,nan,7) = " + min(nan, nan, 7));
- System.out.println("min(nan,nan,nan) = " + min(nan, nan, nan));
- System.out.println();
- System.out.println("max(5,6,7) = " + max(5, 6, 7));
- System.out.println("max(5,6,nan) = " + max(5, 6, nan));
- System.out.println("max(5,nan,7) = " + max(5, nan, 7));
- System.out.println("max(5,nan,nan) = " + max(5, nan, nan));
- System.out.println("max(nan,6,7) = " + max(nan, 6, 7));
- System.out.println("max(nan,6,nan) = " + max(nan, 6, nan));
- System.out.println("max(nan,nan,7) = " + max(nan, nan, 7));
- System.out.println("max(nan,nan,nan) = " + max(nan, nan, nan));
- System.out.println();
+ public static double median(Collection<? extends Number> values) {
+ if (values.isEmpty()) {
+ return Double.NaN;
+ }
+ List<Number> sorted = new ArrayList<Number>(values);
+ Collections.sort(sorted, new Comparator<Number>() {
+ @Override
+ public int compare(Number o1, Number o2) {
+ return Double.compare(o1.doubleValue(), o2.doubleValue());
+ }
+ });
+ int n = sorted.size();
+ if (n % 2 == 0) {
+ return (sorted.get(n / 2).doubleValue() + sorted.get(n / 2 - 1).doubleValue()) / 2;
+ } else {
+ return sorted.get(n / 2).doubleValue();
+ }
}
}