-
Notifications
You must be signed in to change notification settings - Fork 109
/
Copy pathMathUtilities.java
379 lines (346 loc) · 11.7 KB
/
MathUtilities.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
package com.cedarsoftware.util;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
import static java.util.Collections.swap;
/**
* Mathematical utility class providing enhanced numeric operations and algorithms.
* <p>
* This class provides:
* </p>
* <ul>
* <li>Minimum/Maximum calculations for various numeric types</li>
* <li>Smart numeric parsing with minimal type selection</li>
* <li>Permutation generation</li>
* <li>Common mathematical constants</li>
* </ul>
*
* <p><strong>Features:</strong></p>
* <ul>
* <li>Support for primitive types (long, double)</li>
* <li>Support for BigInteger and BigDecimal</li>
* <li>Null-safe operations</li>
* <li>Efficient implementations</li>
* <li>Thread-safe operations</li>
* </ul>
*
* @author John DeRegnaucourt ([email protected])
* <br>
* Copyright (c) Cedar Software LLC
* <br><br>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <br><br>
* <a href="http://www.apache.org/licenses/LICENSE-2.0">License</a>
* <br><br>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public final class MathUtilities
{
public static final BigInteger BIG_INT_LONG_MIN = BigInteger.valueOf(Long.MIN_VALUE);
public static final BigInteger BIG_INT_LONG_MAX = BigInteger.valueOf(Long.MAX_VALUE);
public static final BigDecimal BIG_DEC_DOUBLE_MIN = BigDecimal.valueOf(-Double.MAX_VALUE);
public static final BigDecimal BIG_DEC_DOUBLE_MAX = BigDecimal.valueOf(Double.MAX_VALUE);
private MathUtilities()
{
super();
}
/**
* Calculate the minimum value from an array of values.
*
* @param values Array of values.
* @return minimum value of the provided set.
*/
public static long minimum(long... values)
{
final int len = values.length;
long current = values[0];
for (int i=1; i < len; i++)
{
current = Math.min(values[i], current);
}
return current;
}
/**
* Calculate the minimum value from an array of values.
*
* @param values Array of values.
* @return minimum value of the provided set.
*/
public static long maximum(long... values)
{
final int len = values.length;
long current = values[0];
for (int i=1; i < len; i++)
{
current = Math.max(values[i], current);
}
return current;
}
/**
* Calculate the minimum value from an array of values.
*
* @param values Array of values.
* @return minimum value of the provided set.
*/
public static double minimum(double... values)
{
final int len =values.length;
double current = values[0];
for (int i=1; i < len; i++)
{
current = Math.min(values[i], current);
}
return current;
}
/**
* Calculate the minimum value from an array of values.
*
* @param values Array of values.
* @return minimum value of the provided set.
*/
public static double maximum(double... values)
{
final int len = values.length;
double current = values[0];
for (int i=1; i < len; i++)
{
current = Math.max(values[i], current);
}
return current;
}
/**
* Calculate the minimum value from an array of values.
*
* @param values Array of values.
* @return minimum value of the provided set.
*/
public static BigInteger minimum(BigInteger... values)
{
final int len = values.length;
if (len == 1)
{
if (values[0] == null)
{
throw new IllegalArgumentException("Cannot passed null BigInteger entry to minimum()");
}
return values[0];
}
BigInteger current = values[0];
for (int i=1; i < len; i++)
{
if (values[i] == null)
{
throw new IllegalArgumentException("Cannot passed null BigInteger entry to minimum()");
}
current = values[i].min(current);
}
return current;
}
/**
* Calculate the minimum value from an array of values.
*
* @param values Array of values.
* @return minimum value of the provided set.
*/
public static BigInteger maximum(BigInteger... values)
{
final int len = values.length;
if (len == 1)
{
if (values[0] == null)
{
throw new IllegalArgumentException("Cannot passed null BigInteger entry to maximum()");
}
return values[0];
}
BigInteger current = values[0];
for (int i=1; i < len; i++)
{
if (values[i] == null)
{
throw new IllegalArgumentException("Cannot passed null BigInteger entry to maximum()");
}
current = values[i].max(current);
}
return current;
}
/**
* Calculate the minimum value from an array of values.
*
* @param values Array of values.
* @return minimum value of the provided set.
*/
public static BigDecimal minimum(BigDecimal... values)
{
final int len = values.length;
if (len == 1)
{
if (values[0] == null)
{
throw new IllegalArgumentException("Cannot passed null BigDecimal entry to minimum()");
}
return values[0];
}
BigDecimal current = values[0];
for (int i=1; i < len; i++)
{
if (values[i] == null)
{
throw new IllegalArgumentException("Cannot passed null BigDecimal entry to minimum()");
}
current = values[i].min(current);
}
return current;
}
/**
* Calculate the maximum value from an array of values.
*
* @param values Array of values.
* @return maximum value of the provided set.
*/
public static BigDecimal maximum(BigDecimal... values)
{
final int len = values.length;
if (len == 1)
{
if (values[0] == null)
{
throw new IllegalArgumentException("Cannot pass null BigDecimal entry to maximum()");
}
return values[0];
}
BigDecimal current = values[0];
for (int i=1; i < len; i++)
{
if (values[i] == null)
{
throw new IllegalArgumentException("Cannot pass null BigDecimal entry to maximum()");
}
current = values[i].max(current);
}
return current;
}
/**
* Parses a string representation of a number into the most appropriate numeric type.
* <p>
* This method intelligently selects the smallest possible numeric type that can accurately
* represent the value, following these rules:
* </p>
* <ul>
* <li>Integer values within Long range: returns {@link Long}</li>
* <li>Integer values outside Long range: returns {@link BigInteger}</li>
* <li>Decimal values within Double precision: returns {@link Double}</li>
* <li>Decimal values requiring more precision: returns {@link BigDecimal}</li>
* </ul>
*
* <p><strong>Examples:</strong></p>
* <pre>{@code
* parseToMinimalNumericType("123") → Long(123)
* parseToMinimalNumericType("1.23") → Double(1.23)
* parseToMinimalNumericType("1e308") → BigDecimal
* parseToMinimalNumericType("999999999999999999999") → BigInteger
* }</pre>
*
* @param numStr the string to parse, must not be null
* @return the parsed number in its most appropriate type
* @throws NumberFormatException if the string cannot be parsed as a number
* @throws IllegalArgumentException if numStr is null
*/
public static Number parseToMinimalNumericType(String numStr) {
// Handle and preserve negative signs correctly while removing leading zeros
boolean isNegative = numStr.startsWith("-");
if (isNegative || numStr.startsWith("+")) {
char sign = numStr.charAt(0);
numStr = sign + numStr.substring(1).replaceFirst("^0+", "");
} else {
numStr = numStr.replaceFirst("^0+", "");
}
boolean hasDecimalPoint = false;
boolean hasExponent = false;
int mantissaSize = 0;
StringBuilder exponentValue = new StringBuilder();
int len = numStr.length();
for (int i = 0; i < len; i++) {
char c = numStr.charAt(i);
if (c == '.') {
hasDecimalPoint = true;
} else if (c == 'e' || c == 'E') {
hasExponent = true;
} else if (c >= '0' && c <= '9') {
if (!hasExponent) {
mantissaSize++; // Count digits in the mantissa only
} else {
exponentValue.append(c);
}
}
}
if (hasDecimalPoint || hasExponent) {
if (mantissaSize < 17 && (exponentValue.length() == 0 || Math.abs(Integer.parseInt(exponentValue.toString())) < 308)) {
return Double.parseDouble(numStr);
} else {
return new BigDecimal(numStr);
}
} else {
if (numStr.length() < 19) {
return Long.parseLong(numStr);
}
BigInteger bigInt = new BigInteger(numStr);
if (bigInt.compareTo(BIG_INT_LONG_MIN) >= 0 && bigInt.compareTo(BIG_INT_LONG_MAX) <= 0) {
return bigInt.longValue(); // Correctly convert BigInteger back to Long if within range
} else {
return bigInt;
}
}
}
/**
* Generates the next lexicographically ordered permutation of the given list.
* <p>
* This method modifies the input list in-place to produce the next permutation.
* If there are no more permutations possible, it returns false.
* </p>
*
* <p><strong>Example:</strong></p>
* <pre>{@code
* List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3));
* do {
* System.out.println(list); // Prints each permutation
* } while (nextPermutation(list));
* // Output:
* // [1, 2, 3]
* // [1, 3, 2]
* // [2, 1, 3]
* // [2, 3, 1]
* // [3, 1, 2]
* // [3, 2, 1]
* }</pre>
*
* @param <T> type of elements in the list, must implement Comparable
* @param list the list to permute, will be modified in-place
* @return true if a next permutation exists and was generated, false if no more permutations exist
* @throws IllegalArgumentException if list is null
*/
public static <T extends Comparable<? super T>> boolean nextPermutation(List<T> list) {
int k = list.size() - 2;
while (k >= 0 && list.get(k).compareTo(list.get(k + 1)) >= 0) {
k--;
}
if (k < 0) {
return false; // No more permutations
}
int l = list.size() - 1;
while (list.get(k).compareTo(list.get(l)) >= 0) {
l--;
}
swap(list, k, l);
for (int i = k + 1, j = list.size() - 1; i < j; i++, j--) {
swap(list, i, j);
}
return true;
}
}