154 lines
5.6 KiB
Java
154 lines
5.6 KiB
Java
package tc.oc.commons.core.util;
|
|
|
|
import com.google.common.collect.Range;
|
|
|
|
public class Numbers {
|
|
|
|
/**
|
|
* Get the value of the given numeric type that best represents positive infinity.
|
|
* @throws java.lang.ReflectiveOperationException if this fails, which should not happen with the primitive types
|
|
*/
|
|
public static <T extends Number> T positiveInfinity(Class<T> type) throws ReflectiveOperationException {
|
|
try {
|
|
return type.cast(type.getField("POSITIVE_INFINITY").get(null));
|
|
}
|
|
catch(NoSuchFieldException e) {
|
|
return type.cast(type.getField("MAX_VALUE").get(null));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the value of the given numeric type that best represents negative infinity.
|
|
* @throws java.lang.ReflectiveOperationException if this fails, which should not happen with the primitive types
|
|
*/
|
|
public static <T extends Number> T negativeInfinity(Class<T> type) throws ReflectiveOperationException {
|
|
try {
|
|
return type.cast(type.getField("NEGATIVE_INFINITY").get(null));
|
|
}
|
|
catch(NoSuchFieldException e) {
|
|
return type.cast(type.getField("MIN_VALUE").get(null));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Try to parse the given text as a number of the given type
|
|
* @param text string representation of a number
|
|
* @param type numeric type to parse
|
|
* @param infinity whether infinities should be allowed
|
|
* @return a parsed number
|
|
* @throws NumberFormatException if a number could not be parsed for whatever reason
|
|
*/
|
|
public static <T extends Number> T parse(String text, Class<T> type, boolean infinity) throws NumberFormatException {
|
|
try {
|
|
if(infinity) {
|
|
String trimmed = text.trim();
|
|
if("oo".equals(trimmed) || "+oo".equals(trimmed)) {
|
|
return positiveInfinity(type);
|
|
} else if("-oo".equals(trimmed)) {
|
|
return negativeInfinity(type);
|
|
}
|
|
}
|
|
return type.cast(type.getMethod("valueOf", String.class).invoke(null, text));
|
|
} catch(ReflectiveOperationException e) {
|
|
if(e.getCause() instanceof NumberFormatException) {
|
|
throw (NumberFormatException) e.getCause();
|
|
} else {
|
|
throw new IllegalArgumentException("cannot parse type " + type.getName(), e);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static <T extends Number> T coerce(Object obj, Class<T> type, boolean infinity) throws NumberFormatException {
|
|
if(type.isInstance(obj)) {
|
|
return type.cast(obj);
|
|
} else if(obj instanceof String) {
|
|
return parse((String) obj, type, infinity);
|
|
} else if(obj instanceof Number) {
|
|
Number n = (Number) obj;
|
|
if(type.equals(Double.class)) {
|
|
return type.cast(n.doubleValue());
|
|
} else if(type.equals(Float.class)) {
|
|
return type.cast(n.floatValue());
|
|
} else if(type.equals(Long.class)) {
|
|
return type.cast(n.longValue());
|
|
} else if(type.equals(Integer.class)) {
|
|
return type.cast(n.intValue());
|
|
} else if(type.equals(Short.class)) {
|
|
return type.cast(n.shortValue());
|
|
} else if(type.equals(Byte.class)) {
|
|
return type.cast(n.byteValue());
|
|
}
|
|
}
|
|
|
|
throw new NumberFormatException("Cannot coerce " + obj + " to " + type.getSimpleName());
|
|
}
|
|
|
|
public static double clamp(double value, double min, double max) {
|
|
return value < min ? min : (value > max ? max : value);
|
|
}
|
|
|
|
public static int clamp(int value, int min, int max) {
|
|
return value < min ? min : (value > max ? max : value);
|
|
}
|
|
|
|
public static double clamp(double value, Range<Double> range) {
|
|
return clamp(value,
|
|
range.hasLowerBound() ? range.lowerEndpoint() : Double.NEGATIVE_INFINITY,
|
|
range.hasUpperBound() ? range.upperEndpoint() : Double.POSITIVE_INFINITY);
|
|
}
|
|
|
|
/**
|
|
* Divide the first argument by the second and round the result up to the next integer.
|
|
* @param numerator Assumed to be >= 0
|
|
* @param denominator Assumed to be > 0
|
|
*/
|
|
public static int divideRoundingUp(int numerator, int denominator) {
|
|
return (numerator + denominator - 1) / denominator;
|
|
}
|
|
|
|
/**
|
|
* Round the first argument up to the next multiple of the second argument
|
|
* @param value Assumed to be >= 0
|
|
* @param modulus Assumed to be > 0
|
|
*/
|
|
public static int roundUp(int value, int modulus) {
|
|
return divideRoundingUp(value, modulus) * modulus;
|
|
}
|
|
|
|
/**
|
|
* Round the first argument up to the next multiple of the second argument
|
|
* @param value Assumed to be >= 0
|
|
* @param modulus Assumed to be > 0
|
|
*/
|
|
public static int roundDown(int value, int modulus) {
|
|
return (value / modulus) * modulus;
|
|
}
|
|
|
|
/**
|
|
* Convert a value in the range 0..1 to a percentage in the range 0..100.
|
|
* The result will only be 0 if the input is exactly 0, and will only be 100
|
|
* if the input is exactly 1.
|
|
*/
|
|
public static int percentage(double n) {
|
|
int percent = (int) Math.round(n * 100);
|
|
if(percent == 0 && n != 0) {
|
|
percent = 1;
|
|
} else if(percent == 100 && n != 1) {
|
|
percent = 99;
|
|
}
|
|
return percent;
|
|
}
|
|
|
|
public static double square(double n) {
|
|
return n * n;
|
|
}
|
|
|
|
public static int ceil(Number n) {
|
|
return (int) Math.ceil(n.doubleValue());
|
|
}
|
|
|
|
public static int floor(Number n) {
|
|
return (int) Math.floor(n.doubleValue());
|
|
}
|
|
}
|