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