MathUtils.java
package com.tradecloud.domain.base.utils;
import org.apache.commons.lang3.StringUtils;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.Locale;
import java.util.Map;
/**
*
*/
public class MathUtils {
public final static int SCALE_DISPLAY = 2;
public final static int SCALE_FOUR = 4;
public final static int SCALE_ACCURATE = 12;
public final static int SCALE = SCALE_ACCURATE;
public final static int SCALE_VERY_ACCURATE = 19;
public final static int SCALE_VERY_VERY_ACCURATE = 19;
public final static RoundingMode ROUNDING_MODE = RoundingMode.HALF_UP;
// Use this for calculations so accuracy is not lost
public final static MathContext MC_ACCURATE = new MathContext(SCALE_ACCURATE, ROUNDING_MODE);
public final static MathContext MC_VERY_VERY_ACCURATE = new MathContext(SCALE_VERY_VERY_ACCURATE, ROUNDING_MODE);
// Use this MathContext for results that get displayed to the user
public final static MathContext MC = new MathContext(SCALE, ROUNDING_MODE);
// Use this MathContext for results that get displayed to the user
public final static MathContext MC_DISPLAY = new MathContext(SCALE_DISPLAY, ROUNDING_MODE);
public static final BigDecimal ZERO = BigDecimal.ZERO.setScale(2);
public static BigDecimal ONE_HUNDRED = new BigDecimal(100);
public static BigDecimal multiply(BigDecimal bd1, BigDecimal bd2) {
BigDecimal bd = bd1.multiply(bd2);
return bd.setScale(SCALE, ROUNDING_MODE);
}
public static BigDecimal multiplyVA(BigDecimal bd1, BigDecimal bd2) {
BigDecimal bd = bd1.multiply(bd2);
return bd.setScale(SCALE_VERY_ACCURATE, ROUNDING_MODE);
}
public static BigDecimal divide(BigDecimal bd1, BigDecimal bd2) {
return bd1.divide(bd2, SCALE, ROUNDING_MODE);
}
public static BigDecimal safeDivide(BigDecimal bd1, BigDecimal bd2) {
return bd2.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO : divide(bd1, bd2);
}
public static BigDecimal divideVA(BigDecimal bd1, BigDecimal bd2) {
return bd1.divide(bd2, SCALE_VERY_ACCURATE, ROUNDING_MODE);
}
public static BigDecimal safeDivideVA(BigDecimal bd1, BigDecimal bd2) {
return bd2.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO : divideVA(bd1, bd2);
}
public static BigDecimal safeDivide(BigDecimal quantity, BigDecimal bigDecimal, int scale) {
return bigDecimal.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO : quantity.divide(bigDecimal, scale, ROUNDING_MODE);
}
public static BigDecimal setScale(BigDecimal bg, MathContext mc) {
return bg.setScale(mc.getPrecision(), mc.getRoundingMode());
}
public static BigDecimal setScale(BigDecimal bg, int scale) {
return bg.setScale(scale, ROUNDING_MODE);
}
public static BigDecimal setDisplayScale(BigDecimal bg) {
if (bg != null) {
return bg.setScale(MC_DISPLAY.getPrecision(), MC_DISPLAY.getRoundingMode());
} else {
return bg;
}
}
public static BigDecimal setAccurateScale(BigDecimal bg) {
return bg.setScale(MC_ACCURATE.getPrecision(), MC_ACCURATE.getRoundingMode());
}
public static BigDecimal max(BigDecimal bg1, BigDecimal bg2) {
return bg1.compareTo(bg2) == 1 ? bg1 : bg2;
}
public static BigDecimal min(BigDecimal bg1, BigDecimal bg2) {
return bg1.compareTo(bg2) == -1 ? bg1 : bg2;
}
public static boolean gt(BigDecimal bg1, BigDecimal bg2) {
return bg1.compareTo(bg2) == 1;
}
public static boolean gte(BigDecimal bg1, BigDecimal bg2) {
return bg1.compareTo(bg2) != -1;
}
public static boolean lt(BigDecimal bg1, BigDecimal bg2) {
return bg1.compareTo(bg2) == -1;
}
public static boolean lte(BigDecimal bg1, BigDecimal bg2) {
return lt(bg1, bg2) || areNumbersCloselyEqual(bg1, bg2, SCALE_FOUR);
}
public static BigDecimal toPercent(BigDecimal bg) {
return bg.divide(ONE_HUNDRED, MC_ACCURATE);
}
public static BigDecimal fromPercent(BigDecimal bd) {
return bd.multiply(ONE_HUNDRED).setScale(SCALE, ROUNDING_MODE);
}
public static BigDecimal zeroIfNull(BigDecimal bg) {
return bg != null ? bg : BigDecimal.ZERO;
}
public static BigDecimal zeroIntIfNull(Integer bg) {
return bg != null ? new BigDecimal(bg) : BigDecimal.ZERO;
}
public static boolean isZero(BigDecimal bg) {
// do not use equals, it will not factor in scale and precision
return bg.compareTo(BigDecimal.ZERO) == 0;
}
public static boolean isNonZero(BigDecimal bg) {
// do not use equals, it will not factor in scale and precision
return bg != null && !isZero(bg);
}
public static boolean isNegative(BigDecimal bg) {
return bg.compareTo(BigDecimal.ZERO) == -1;
}
public static boolean isPositive(BigDecimal bg) {
return bg.compareTo(BigDecimal.ZERO) == 1;
}
public static boolean isPercentage(BigDecimal bg) {
// is not greater than 100 and is not less than 0
return bg.compareTo(ONE_HUNDRED) != 1 && bg.compareTo(BigDecimal.ZERO) != -1;
}
/**
* Used when keeping a map against a value. Put a 0 in if the map entry does not exist.
*/
public static <X> void zeroIfEmpty(Map<X, BigDecimal> map, X key) {
if (!map.containsKey(key)) {
map.put(key, BigDecimal.ZERO);
}
}
/**
* Used when keeping a map against a value. Add the new value to the new or existing key value.
*/
public static <X> void add(Map<X, BigDecimal> map, X key, BigDecimal value) {
zeroIfEmpty(map, key);
map.put(key, map.get(key).add(value));
}
/**
* Used when keeping a map against a value. Subtract the new value to the new or existing key value.
*/
public static <X> void subtract(Map<X, BigDecimal> map, X key, BigDecimal value) {
zeroIfEmpty(map, key);
map.put(key, map.get(key).subtract(value));
}
public static BigDecimal subtract(BigDecimal a, BigDecimal b) {
return a.subtract(b).setScale(SCALE, ROUNDING_MODE);
}
public static BigDecimal subtractNoScale(BigDecimal a, BigDecimal b) {
return a.subtract(b);
}
public static BigDecimal sum(BigDecimal a, BigDecimal b) {
return a.add(b).setScale(SCALE_VERY_ACCURATE, ROUNDING_MODE);
}
public static BigDecimal sumSafe(BigDecimal a, BigDecimal b) {
if (a == null) a = BigDecimal.ZERO;
if (b == null) b = BigDecimal.ZERO;
return a.add(b).setScale(SCALE_VERY_ACCURATE, ROUNDING_MODE);
}
public static boolean areNumbersCloselyEqual(BigDecimal lhsNumber, BigDecimal rhsNumber, int precision) {
if (lhsNumber != rhsNumber) {
if (lhsNumber != null && rhsNumber != null) {
return areNumbersCloselyEqual(lhsNumber.doubleValue(), rhsNumber.doubleValue(), precision);
} else {
return false;
}
} else {
return true;
}
}
public static boolean areNumbersCloselyEqual(double lhsNumber, double rhsNumber, int precision) {
if (Double.isNaN(lhsNumber) && Double.isNaN(rhsNumber)) {
return true;
}
double tolerance = calculateFactor((-1) * (precision + 1)) * 9;
return Math.abs(lhsNumber - rhsNumber) < tolerance;
}
public static double calculateFactor(int decimals) {
return Math.pow(10, decimals);
}
public static String toNumberFormat(BigDecimal amountValue, String format) {
if (StringUtils.isEmpty(format)) {
return amountValue != null ? amountValue.toPlainString() : "";
}
NumberFormat numberFormat = getNumberFormat(format);
return amountValue != null ? numberFormat.format(amountValue) : "";
}
public static DecimalFormat getNumberFormat(String format) {
DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.ENGLISH);
symbols.setDecimalSeparator('.');
//symbols.setGroupingSeparator(' ');
return new DecimalFormat(format, symbols);
}
public static BigDecimal defaultIfBlank(BigDecimal value, BigDecimal zero) {
return value == null ? zero : value;
}
public static BigDecimal getPercentageOrZero(BigDecimal value) {
BigDecimal lessPercentage = MathUtils.defaultIfBlank(value, BigDecimal.ZERO);
lessPercentage = MathUtils.toPercent(lessPercentage);
return lessPercentage;
}
public static BigDecimal roundTo2DecimalPlaces(BigDecimal number) {
return roundTo(number, 2);
}
public static BigDecimal roundTo4DecimalPlaces(BigDecimal number) {
return roundTo(number, 4);
}
public static BigDecimal roundTo(BigDecimal number, int newScale) {
if (number == null) {
return null;
}
return number.setScale(newScale, ROUNDING_MODE);
}
public static BigDecimal divideContainerUsage(BigDecimal numberAtPOD, BigDecimal oldContainerQuantity,
BigDecimal newContainerQuantity) {
BigDecimal numberAtPODToPercentage = safeDivide(numberAtPOD, oldContainerQuantity, 1);
return numberAtPODToPercentage.divide(ONE_HUNDRED).multiply(newContainerQuantity).multiply(ONE_HUNDRED);
}
public static boolean allValidNonZero(BigDecimal... bigDecimals) {
if (bigDecimals != null && bigDecimals.length > 0) {
for (BigDecimal bigDecimal : bigDecimals) {
if (!isNonZero(bigDecimal)) {
return false;
}
}
return true;
}
return false;
}
public static boolean inRange(double lowerLimit, double upperLimit, BigDecimal numberToCheck) {
double v = numberToCheck.setScale(2, ROUNDING_MODE).doubleValue();
return v >= lowerLimit && v <= upperLimit;
}
public static int adjustQuantityToNearest10(int forecast) {
if (forecast <= 0) return 0;
return ((forecast + 9) / 10) * 10;
}
}