enhanced motor handling, bug fixes, initial optimization code
[debian/openrocket] / src / net / sf / openrocket / util / MathUtil.java
index a714f81275a8b8ad8f3d520c8f60b9d988902d8f..58585d7717c66d54b62c2d18bda811d966939fc0 100644 (file)
@@ -1,15 +1,21 @@
 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;
        }
        
        /**
@@ -18,11 +24,11 @@ public class MathUtil {
         * @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);
        }
        
        /**
@@ -73,11 +79,39 @@ public class MathUtil {
                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
@@ -107,9 +141,9 @@ public class MathUtil {
         */
        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);
                }
        }
        
@@ -120,9 +154,9 @@ public class MathUtil {
         */
        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);
                }
        }
        
@@ -131,19 +165,19 @@ public class MathUtil {
         * 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.
         * 
@@ -153,65 +187,92 @@ public class MathUtil {
         * @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();
+               }
        }
        
 }