|
|
@@ -0,0 +1,260 @@
|
|
|
+package net.ranides.assira.math;
|
|
|
+
|
|
|
+import lombok.EqualsAndHashCode;
|
|
|
+
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.math.BigInteger;
|
|
|
+
|
|
|
+@EqualsAndHashCode
|
|
|
+public class NumberPair {
|
|
|
+
|
|
|
+ private final Number a;
|
|
|
+ private final Number b;
|
|
|
+
|
|
|
+ private static final Class<?>[] type2real = {
|
|
|
+ Float.class, // 0
|
|
|
+ Float.class, // 1
|
|
|
+ Float.class, // 2
|
|
|
+ Double.class, // 3
|
|
|
+ BigDecimal.class // 4
|
|
|
+ };
|
|
|
+
|
|
|
+ private static final Class<?>[] type2int = {
|
|
|
+ Byte.class, // 0
|
|
|
+ Short.class, // 1
|
|
|
+ Integer.class, // 2
|
|
|
+ Long.class, // 3
|
|
|
+ BigInteger.class // 4
|
|
|
+ };
|
|
|
+
|
|
|
+ public NumberPair(Number a, Number b) {
|
|
|
+ this.a = a;
|
|
|
+ this.b = b;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static NumberPair of(Number a, Number b) {
|
|
|
+ return new NumberPair(a,b);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static int real2type(Class<?> a) {
|
|
|
+ if(Float.class.equals(a)) {
|
|
|
+ return 2;
|
|
|
+ }
|
|
|
+ if(Double.class.equals(a)) {
|
|
|
+ return 3;
|
|
|
+ }
|
|
|
+ if(BigDecimal.class.equals(a)) {
|
|
|
+ return 4;
|
|
|
+ }
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static int int2type(Class<?> a) {
|
|
|
+ if(Byte.class.equals(a)) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ if(Short.class.equals(a)) {
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ if(Integer.class.equals(a)) {
|
|
|
+ return 2;
|
|
|
+ }
|
|
|
+ if(Long.class.equals(a)) {
|
|
|
+ return 3;
|
|
|
+ }
|
|
|
+ if(BigInteger.class.equals(a)) {
|
|
|
+ return 4;
|
|
|
+ }
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static Class<? extends Number> wider(Class<?> a, Class<?> b) {
|
|
|
+ int ai = int2type(a);
|
|
|
+ int bi = int2type(b);
|
|
|
+ int mi = Math.max(ai, bi);
|
|
|
+
|
|
|
+ if (ai != -1 && bi != -1) {
|
|
|
+ return (Class)type2int[mi];
|
|
|
+ }
|
|
|
+
|
|
|
+ int af = real2type(a);
|
|
|
+ int bf = real2type(b);
|
|
|
+ int mf = Math.max(af, bf);
|
|
|
+
|
|
|
+ return (Class)type2real[Math.max(mi,mf)];
|
|
|
+ }
|
|
|
+
|
|
|
+ private static <T extends Number> T cast(Class<T> target, Number value) {
|
|
|
+ if(target.isInstance(value)) {
|
|
|
+ return target.cast(value);
|
|
|
+ }
|
|
|
+ if(Float.class.equals(target)) {
|
|
|
+ return target.cast(value.floatValue());
|
|
|
+ }
|
|
|
+ if(Double.class.equals(target)) {
|
|
|
+ return target.cast(value.doubleValue());
|
|
|
+ }
|
|
|
+ if(BigDecimal.class.equals(target)) {
|
|
|
+ return target.cast(asBigDecimal(value));
|
|
|
+ }
|
|
|
+
|
|
|
+ if(Byte.class.equals(target)) {
|
|
|
+ return target.cast(value.byteValue());
|
|
|
+ }
|
|
|
+ if(Short.class.equals(target)) {
|
|
|
+ return target.cast(value.shortValue());
|
|
|
+ }
|
|
|
+ if(Integer.class.equals(target)) {
|
|
|
+ return target.cast(value.intValue());
|
|
|
+ }
|
|
|
+ if(Long.class.equals(target)) {
|
|
|
+ return target.cast(value.longValue());
|
|
|
+ }
|
|
|
+ if(BigInteger.class.equals(target)) {
|
|
|
+ return target.cast(BigInteger.valueOf(value.longValue()));
|
|
|
+ }
|
|
|
+
|
|
|
+ return target.cast(value);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static BigDecimal asBigDecimal(Number value) {
|
|
|
+ if(value instanceof BigInteger) {
|
|
|
+ return new BigDecimal(((BigInteger) value));
|
|
|
+ }
|
|
|
+ if(value instanceof Float || value instanceof Double) {
|
|
|
+ return new BigDecimal(value.doubleValue());
|
|
|
+ }
|
|
|
+ return new BigDecimal(value.longValue());
|
|
|
+ }
|
|
|
+
|
|
|
+ public NumberPair widen() {
|
|
|
+ Class<? extends Number> target = wider(a.getClass(), b.getClass());
|
|
|
+ return new NumberPair(cast(target, a), cast(target, b));
|
|
|
+ }
|
|
|
+
|
|
|
+ public Number add() {
|
|
|
+ Class<? extends Number> target = wider(a.getClass(), b.getClass());
|
|
|
+
|
|
|
+ if(Float.class.equals(target)) {
|
|
|
+ return a.floatValue() + b.floatValue();
|
|
|
+ }
|
|
|
+ if(Double.class.equals(target)) {
|
|
|
+ return a.doubleValue() + b.doubleValue();
|
|
|
+ }
|
|
|
+ if(BigDecimal.class.equals(target)) {
|
|
|
+ return asBigDecimal(a).add(asBigDecimal(b));
|
|
|
+ }
|
|
|
+ if(Byte.class.equals(target)) {
|
|
|
+ return a.byteValue() + b.byteValue();
|
|
|
+ }
|
|
|
+ if(Short.class.equals(target)) {
|
|
|
+ return a.shortValue() + b.shortValue();
|
|
|
+ }
|
|
|
+ if(Integer.class.equals(target)) {
|
|
|
+ return a.intValue() + b.intValue();
|
|
|
+ }
|
|
|
+ if(Long.class.equals(target)) {
|
|
|
+ return a.longValue() + b.longValue();
|
|
|
+ }
|
|
|
+ if(BigInteger.class.equals(target)) {
|
|
|
+ return BigInteger.valueOf(a.longValue()).add(BigInteger.valueOf(b.longValue()));
|
|
|
+ }
|
|
|
+
|
|
|
+ throw new UnsupportedOperationException("Can't add " + a.getClass() + " & " + b.getClass());
|
|
|
+ }
|
|
|
+
|
|
|
+ public Number subtract() {
|
|
|
+ Class<? extends Number> target = wider(a.getClass(), b.getClass());
|
|
|
+
|
|
|
+ if(Float.class.equals(target)) {
|
|
|
+ return a.floatValue() - b.floatValue();
|
|
|
+ }
|
|
|
+ if(Double.class.equals(target)) {
|
|
|
+ return a.doubleValue() - b.doubleValue();
|
|
|
+ }
|
|
|
+ if(BigDecimal.class.equals(target)) {
|
|
|
+ return asBigDecimal(a).subtract(asBigDecimal(b));
|
|
|
+ }
|
|
|
+ if(Byte.class.equals(target)) {
|
|
|
+ return a.byteValue() - b.byteValue();
|
|
|
+ }
|
|
|
+ if(Short.class.equals(target)) {
|
|
|
+ return a.shortValue() - b.shortValue();
|
|
|
+ }
|
|
|
+ if(Integer.class.equals(target)) {
|
|
|
+ return a.intValue() - b.intValue();
|
|
|
+ }
|
|
|
+ if(Long.class.equals(target)) {
|
|
|
+ return a.longValue() - b.longValue();
|
|
|
+ }
|
|
|
+ if(BigInteger.class.equals(target)) {
|
|
|
+ return BigInteger.valueOf(a.longValue()).subtract(BigInteger.valueOf(b.longValue()));
|
|
|
+ }
|
|
|
+
|
|
|
+ throw new UnsupportedOperationException("Can't subtract " + a.getClass() + " & " + b.getClass());
|
|
|
+ }
|
|
|
+
|
|
|
+ public Number multiply() {
|
|
|
+ Class<? extends Number> target = wider(a.getClass(), b.getClass());
|
|
|
+
|
|
|
+ if(Float.class.equals(target)) {
|
|
|
+ return a.floatValue() * b.floatValue();
|
|
|
+ }
|
|
|
+ if(Double.class.equals(target)) {
|
|
|
+ return a.doubleValue() * b.doubleValue();
|
|
|
+ }
|
|
|
+ if(BigDecimal.class.equals(target)) {
|
|
|
+ return asBigDecimal(a).multiply(asBigDecimal(b));
|
|
|
+ }
|
|
|
+ if(Byte.class.equals(target)) {
|
|
|
+ return a.byteValue() * b.byteValue();
|
|
|
+ }
|
|
|
+ if(Short.class.equals(target)) {
|
|
|
+ return a.shortValue() * b.shortValue();
|
|
|
+ }
|
|
|
+ if(Integer.class.equals(target)) {
|
|
|
+ return a.intValue() * b.intValue();
|
|
|
+ }
|
|
|
+ if(Long.class.equals(target)) {
|
|
|
+ return a.longValue() * b.longValue();
|
|
|
+ }
|
|
|
+ if(BigInteger.class.equals(target)) {
|
|
|
+ return BigInteger.valueOf(a.longValue()).multiply(BigInteger.valueOf(b.longValue()));
|
|
|
+ }
|
|
|
+
|
|
|
+ throw new UnsupportedOperationException("Can't multiply " + a.getClass() + " & " + b.getClass());
|
|
|
+ }
|
|
|
+
|
|
|
+ public Number divide() {
|
|
|
+ Class<? extends Number> target = wider(a.getClass(), b.getClass());
|
|
|
+
|
|
|
+ if(Float.class.equals(target)) {
|
|
|
+ return a.floatValue() / b.floatValue();
|
|
|
+ }
|
|
|
+ if(Double.class.equals(target)) {
|
|
|
+ return a.doubleValue() / b.doubleValue();
|
|
|
+ }
|
|
|
+ if(BigDecimal.class.equals(target)) {
|
|
|
+ return asBigDecimal(a).divide(asBigDecimal(b));
|
|
|
+ }
|
|
|
+ if(Byte.class.equals(target)) {
|
|
|
+ return a.byteValue() / b.byteValue();
|
|
|
+ }
|
|
|
+ if(Short.class.equals(target)) {
|
|
|
+ return a.shortValue() / b.shortValue();
|
|
|
+ }
|
|
|
+ if(Integer.class.equals(target)) {
|
|
|
+ return a.intValue() / b.intValue();
|
|
|
+ }
|
|
|
+ if(Long.class.equals(target)) {
|
|
|
+ return a.longValue() / b.longValue();
|
|
|
+ }
|
|
|
+ if(BigInteger.class.equals(target)) {
|
|
|
+ return BigInteger.valueOf(a.longValue()).divide(BigInteger.valueOf(b.longValue()));
|
|
|
+ }
|
|
|
+
|
|
|
+ throw new UnsupportedOperationException("Can't add " + a.getClass() + " & " + b.getClass());
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+}
|