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()); } }