major optimization updates
[debian/openrocket] / src / net / sf / openrocket / util / MathUtil.java
1 package net.sf.openrocket.util;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.Collections;
6 import java.util.Comparator;
7 import java.util.List;
8
9 import net.sf.openrocket.logging.LogHelper;
10 import net.sf.openrocket.startup.Application;
11
12 public class MathUtil {
13         private static final LogHelper log = Application.getLogger();
14         
15         public static final double EPSILON = 0.00000001; // 10mm^3 in m^3
16         
17         /**
18          * The square of x (x^2).  On Sun's JRE using this method is as fast as typing x*x. 
19          * @param x  x
20          * @return   x^2
21          */
22         public static double pow2(double x) {
23                 return x * x;
24         }
25         
26         /**
27          * The cube of x (x^3).
28          * @param x  x
29          * @return   x^3
30          */
31         public static double pow3(double x) {
32                 return x * x * x;
33         }
34         
35         public static double pow4(double x) {
36                 return (x * x) * (x * x);
37         }
38         
39         /**
40          * Clamps the value x to the range min - max.  
41          * @param x    Original value.
42          * @param min  Minimum value to return.
43          * @param max  Maximum value to return.
44          * @return     The clamped value.
45          */
46         public static double clamp(double x, double min, double max) {
47                 if (x < min)
48                         return min;
49                 if (x > max)
50                         return max;
51                 return x;
52         }
53         
54         public static float clamp(float x, float min, float max) {
55                 if (x < min)
56                         return min;
57                 if (x > max)
58                         return max;
59                 return x;
60         }
61         
62         public static int clamp(int x, int min, int max) {
63                 if (x < min)
64                         return min;
65                 if (x > max)
66                         return max;
67                 return x;
68         }
69         
70         
71         /**
72          * Maps a value from one value range to another.
73          * 
74          * @param value         the value to map.
75          * @param fromMin       the minimum of the starting range.
76          * @param fromMax       the maximum of the starting range.
77          * @param toMin         the minimum of the destination range.
78          * @param toMax         the maximum of the destination range.
79          * @return                      the mapped value.
80          * @throws      IllegalArgumentException  if fromMin == fromMax, but toMin != toMax.
81          */
82         public static double map(double value, double fromMin, double fromMax,
83                         double toMin, double toMax) {
84                 if (equals(toMin, toMax))
85                         return toMin;
86                 if (equals(fromMin, fromMax)) {
87                         throw new IllegalArgumentException("from range is singular and to range is not: " +
88                                         "value=" + value + " fromMin=" + fromMin + " fromMax=" + fromMax +
89                                         "toMin=" + toMin + " toMax=" + toMax);
90                 }
91                 return (value - fromMin) / (fromMax - fromMin) * (toMax - toMin) + toMin;
92         }
93         
94         
95         /**
96          * Maps a coordinate from one value range to another.
97          * 
98          * @param value         the value to map.
99          * @param fromMin       the minimum of the starting range.
100          * @param fromMax       the maximum of the starting range.
101          * @param toMin         the minimum coordinate of the destination;
102          * @param toMax         the maximum coordinate of the destination;
103          * @return                      the mapped value.
104          * @throws      IllegalArgumentException  if fromMin == fromMax, but toMin != toMax.
105          */
106         public static Coordinate map(double value, double fromMin, double fromMax,
107                         Coordinate toMin, Coordinate toMax) {
108                 if (toMin.equals(toMax))
109                         return toMin;
110                 if (equals(fromMin, fromMax)) {
111                         throw new IllegalArgumentException("from range is singular and to range is not: " +
112                                         "value=" + value + " fromMin=" + fromMin + " fromMax=" + fromMax +
113                                         "toMin=" + toMin + " toMax=" + toMax);
114                 }
115                 double a = (value - fromMin) / (fromMax - fromMin);
116                 return toMax.multiply(a).add(toMin.multiply(1 - a));
117         }
118         
119         
120         /**
121          * Compute the minimum of two values.  This is performed by direct comparison. 
122          * However, if one of the values is NaN and the other is not, the non-NaN value is
123          * returned.
124          */
125         public static double min(double x, double y) {
126                 if (Double.isNaN(y))
127                         return x;
128                 return (x < y) ? x : y;
129         }
130         
131         /**
132          * Compute the maximum of two values.  This is performed by direct comparison. 
133          * However, if one of the values is NaN and the other is not, the non-NaN value is
134          * returned.
135          */
136         public static double max(double x, double y) {
137                 if (Double.isNaN(x))
138                         return y;
139                 return (x < y) ? y : x;
140         }
141         
142         /**
143          * Compute the minimum of three values.  This is performed by direct comparison. 
144          * However, if one of the values is NaN and the other is not, the non-NaN value is
145          * returned.
146          */
147         public static double min(double x, double y, double z) {
148                 if (x < y || Double.isNaN(y)) {
149                         return min(x, z);
150                 } else {
151                         return min(y, z);
152                 }
153         }
154         
155         /**
156          * Compute the maximum of three values.  This is performed by direct comparison. 
157          * However, if one of the values is NaN and the other is not, the non-NaN value is
158          * returned.
159          */
160         public static double max(double x, double y, double z) {
161                 if (x > y || Double.isNaN(y)) {
162                         return max(x, z);
163                 } else {
164                         return max(y, z);
165                 }
166         }
167         
168         /**
169          * Calculates the hypotenuse <code>sqrt(x^2+y^2)</code>.  This method is SIGNIFICANTLY
170          * faster than <code>Math.hypot(x,y)</code>.
171          */
172         public static double hypot(double x, double y) {
173                 return Math.sqrt(x * x + y * y);
174         }
175         
176         /**
177          * Reduce the angle x to the range 0 - 2*PI.
178          * @param x  Original angle.
179          * @return   The equivalent angle in the range 0 ... 2*PI.
180          */
181         public static double reduce360(double x) {
182                 double d = Math.floor(x / (2 * Math.PI));
183                 return x - d * 2 * Math.PI;
184         }
185         
186         /**
187          * Reduce the angle x to the range -PI - PI.
188          * 
189          * Either -PI and PI might be returned, depending on the rounding function. 
190          * 
191          * @param x  Original angle.
192          * @return   The equivalent angle in the range -PI ... PI.
193          */
194         public static double reduce180(double x) {
195                 double d = Math.rint(x / (2 * Math.PI));
196                 return x - d * 2 * Math.PI;
197         }
198         
199         
200         /**
201          * Return the square root of a value.  If the value is negative, zero is returned.
202          * This is safer in cases where rounding errors might make a value slightly negative.
203          * 
204          * @param d             the value of which the square root is to be taken.
205          * @return              the square root of the value.
206          */
207         public static double safeSqrt(double d) {
208                 if (d < 0) {
209                         if (d < 0.01) {
210                                 log.warn(1, "Attempting to compute sqrt(" + d + ")");
211                         }
212                         return 0;
213                 }
214                 return Math.sqrt(d);
215         }
216         
217         
218
219         public static boolean equals(double a, double b) {
220                 double absb = Math.abs(b);
221                 
222                 if (absb < EPSILON / 2) {
223                         // Near zero
224                         return Math.abs(a) < EPSILON / 2;
225                 }
226                 return Math.abs(a - b) < EPSILON * absb;
227         }
228         
229         
230         /**
231          * Return the sign of the number.  This corresponds to Math.signum, but ignores
232          * the special cases of zero and NaN.  The value returned for those is arbitrary.
233          * <p>
234          * This method is about 4 times faster than Math.signum().
235          * 
236          * @param x             the checked value.
237          * @return              -1.0 if x<0; 1.0 if x>0; otherwise either -1.0 or 1.0.
238          */
239         public static double sign(double x) {
240                 return (x < 0) ? -1.0 : 1.0;
241         }
242         
243         /* Math.abs() is about 3x as fast as this:
244         
245         public static double abs(double x) {
246                 return (x<0) ? -x : x;
247         }
248         */
249
250
251         public static double average(Collection<? extends Number> values) {
252                 if (values.isEmpty()) {
253                         return Double.NaN;
254                 }
255                 
256                 double avg = 0.0;
257                 int count = 0;
258                 for (Number n : values) {
259                         avg += n.doubleValue();
260                         count++;
261                 }
262                 return avg / count;
263         }
264         
265         public static double stddev(Collection<? extends Number> values) {
266                 if (values.size() < 2) {
267                         return Double.NaN;
268                 }
269                 
270                 double avg = average(values);
271                 double stddev = 0.0;
272                 int count = 0;
273                 for (Number n : values) {
274                         stddev += pow2(n.doubleValue() - avg);
275                         count++;
276                 }
277                 stddev = Math.sqrt(stddev / (count - 1));
278                 return stddev;
279         }
280         
281         public static double median(Collection<? extends Number> values) {
282                 if (values.isEmpty()) {
283                         return Double.NaN;
284                 }
285                 
286                 List<Number> sorted = new ArrayList<Number>(values);
287                 Collections.sort(sorted, new Comparator<Number>() {
288                         @Override
289                         public int compare(Number o1, Number o2) {
290                                 return Double.compare(o1.doubleValue(), o2.doubleValue());
291                         }
292                 });
293                 
294                 int n = sorted.size();
295                 if (n % 2 == 0) {
296                         return (sorted.get(n / 2).doubleValue() + sorted.get(n / 2 - 1).doubleValue()) / 2;
297                 } else {
298                         return sorted.get(n / 2).doubleValue();
299                 }
300         }
301         
302 }