From 59229b912114de6347bac8b238926dc6bc83936e Mon Sep 17 00:00:00 2001 From: holt666 Date: Thu, 24 Jun 2021 20:55:56 +0800 Subject: [PATCH] #54 step-1(Define Sub-units of Xdager) --- src/main/java/io/xdag/core/XAmount.java | 130 ++++++++++++++++++++ src/main/java/io/xdag/core/XUnit.java | 37 ++++++ src/main/java/io/xdag/utils/BasicUtils.java | 8 ++ src/test/java/io/xdag/core/XAmountTest.java | 124 +++++++++++++++++++ 4 files changed, 299 insertions(+) create mode 100644 src/main/java/io/xdag/core/XAmount.java create mode 100644 src/main/java/io/xdag/core/XUnit.java create mode 100644 src/test/java/io/xdag/core/XAmountTest.java diff --git a/src/main/java/io/xdag/core/XAmount.java b/src/main/java/io/xdag/core/XAmount.java new file mode 100644 index 00000000..f4eda096 --- /dev/null +++ b/src/main/java/io/xdag/core/XAmount.java @@ -0,0 +1,130 @@ +package io.xdag.core; + +import io.xdag.utils.BasicUtils; + +import java.math.BigDecimal; + +import static java.math.RoundingMode.FLOOR; +import static java.math.RoundingMode.HALF_UP; + +public class XAmount implements Comparable { + + public static final XAmount ZERO = new XAmount(0); + public static final XAmount ONE = new XAmount(1); + public static final XAmount TEN = new XAmount(10); + + private final long nano; + + private XAmount(long nano) { + this.nano = nano; + } + + public static XAmount of(long n) { + return new XAmount(n); + } + + public static XAmount of(String n) { + return new XAmount(Long.parseLong(n)); + } + + public static XAmount of(long n, XUnit unit) throws ArithmeticException { + return new XAmount(Math.multiplyExact(n, unit.factor)); + } + + public static XAmount of(BigDecimal d, XUnit unit) { + return new XAmount(d.movePointRight(unit.exp).setScale(0, FLOOR).longValueExact()); + } + + public BigDecimal toDecimal(int scale, XUnit unit) { + BigDecimal nano = BigDecimal.valueOf(this.nano); + return nano.movePointLeft(unit.exp).setScale(scale, FLOOR); + } + + /** + * Of Xdag Amount from C + */ + public static XAmount ofXAmount(long n) { + BigDecimal d = BasicUtils.amount2xdagNew(n) ; + return new XAmount(d.movePointRight(9).setScale(0, HALF_UP).longValueExact()); + } + + /** + * To Xdag Amount from C + */ + public long toXAmount() { + return BasicUtils.xdag2amount(toDecimal(9, XUnit.XDAG).doubleValue()); + } + + @Override + public int compareTo(XAmount other) { + return this.lessThan(other) ? -1 : (this.greaterThan(other) ? 1 : 0); + } + + @Override + public int hashCode() { + return Long.hashCode(nano); + } + + @Override + public boolean equals(Object other) { + return other instanceof XAmount && ((XAmount) other).nano == nano; + } + + @Override + public String toString() { + return String.valueOf(nano); + } + + public boolean greaterThan(XAmount other) { + return nano > other.nano; + } + + public boolean greaterThanOrEqual(XAmount other) { + return nano >= other.nano; + } + + public boolean isPositive() { + return greaterThan(ZERO); + } + + public boolean isNotNegative() { + return greaterThanOrEqual(ZERO); + } + + public boolean lessThan(XAmount other) { + return nano < other.nano; + } + + public boolean lessThanOrEqual(XAmount other) { + return nano <= other.nano; + } + + public boolean isNegative() { + return lessThan(ZERO); + } + + public boolean isNotPositive() { + return lessThanOrEqual(ZERO); + } + + public XAmount negate() throws ArithmeticException { + return new XAmount(Math.negateExact(this.nano)); + } + + public XAmount add(XAmount a) throws ArithmeticException { + return new XAmount(Math.addExact(this.nano, a.nano)); + } + + public XAmount subtract(XAmount a) throws ArithmeticException { + return new XAmount(Math.subtractExact(this.nano, a.nano)); + } + + public XAmount multiply(long a) throws ArithmeticException { + return new XAmount(Math.multiplyExact(this.nano, a)); + } + + public static XAmount sum(XAmount a, XAmount b) throws ArithmeticException { + return new XAmount(Math.addExact(a.nano, b.nano)); + } + +} \ No newline at end of file diff --git a/src/main/java/io/xdag/core/XUnit.java b/src/main/java/io/xdag/core/XUnit.java new file mode 100644 index 00000000..af3cd964 --- /dev/null +++ b/src/main/java/io/xdag/core/XUnit.java @@ -0,0 +1,37 @@ +package io.xdag.core; + +import static java.util.Arrays.stream; +import java.math.BigInteger; + +public enum XUnit { + + NANO_XDAG(0, "nXDAG"), + + MICRO_XDAG(3, "μXDAG"), + + MILLI_XDAG(6, "mXDAG"), + + XDAG(9, "XDAG"); + + public final int exp; + public final long factor; + public final String symbol; + + XUnit(int exp, String symbol) { + this.exp = exp; + this.factor = BigInteger.TEN.pow(exp).longValueExact(); + this.symbol = symbol; + } + + /** + * Decode the unit from symbol. + * + * @param symbol + * the symbol text + * @return a Unit object if valid; otherwise false + */ + public static XUnit of(String symbol) { + return stream(values()).filter(v -> v.symbol.equals(symbol)).findAny().orElse(null); + } + +} diff --git a/src/main/java/io/xdag/utils/BasicUtils.java b/src/main/java/io/xdag/utils/BasicUtils.java index ab4a4111..7e876287 100644 --- a/src/main/java/io/xdag/utils/BasicUtils.java +++ b/src/main/java/io/xdag/utils/BasicUtils.java @@ -120,6 +120,14 @@ public static double amount2xdag(long xdag) { return bigDecimal.setScale(2, RoundingMode.HALF_UP).doubleValue(); } + public static BigDecimal amount2xdagNew(long xdag) { + if(xdag < 0) throw new XdagOverFlowException(); + long first = xdag >> 32; + long temp = xdag - (first << 32); + double tem = temp / Math.pow(2, 32); + return new BigDecimal(first + tem); + } + public static boolean crc32Verify(byte[] src, int crc) { CRC32 crc32 = new CRC32(); crc32.update(src, 0, 512); diff --git a/src/test/java/io/xdag/core/XAmountTest.java b/src/test/java/io/xdag/core/XAmountTest.java new file mode 100644 index 00000000..1ba463fb --- /dev/null +++ b/src/test/java/io/xdag/core/XAmountTest.java @@ -0,0 +1,124 @@ +package io.xdag.core; + +import org.junit.Test; + +import java.math.BigDecimal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import static io.xdag.core.XUnit.NANO_XDAG; +import static io.xdag.core.XUnit.MICRO_XDAG; +import static io.xdag.core.XUnit.MILLI_XDAG; +import static io.xdag.core.XUnit.XDAG; +import static io.xdag.core.XAmount.ZERO; + +public class XAmountTest { + + @Test + public void testUnits() { + assertEquals(ZERO, ZERO); + assertEquals(XAmount.of(1), XAmount.of(1)); + assertEquals(XAmount.of(1000), XAmount.of(1, MICRO_XDAG)); + assertEquals(XAmount.of(1000, MICRO_XDAG), XAmount.of(1, MILLI_XDAG)); + assertEquals(XAmount.of(1000, MILLI_XDAG), XAmount.of(1, XDAG)); + } + + @Test + public void testFromDecimal() { + assertEquals(ZERO, XAmount.of(BigDecimal.ZERO, XDAG)); + assertEquals(XAmount.of(10, XDAG), XAmount.of(new BigDecimal("10.000"), XDAG)); + } + + @Test + public void testFromSymbol() { + assertEquals(NANO_XDAG, XUnit.of("nXDAG")); + assertEquals(MICRO_XDAG, XUnit.of("μXDAG")); + assertEquals(MILLI_XDAG, XUnit.of("mXDAG")); + assertEquals(XDAG, XUnit.of("XDAG")); + } + + @Test + public void testFromSymbolUnknown() { + assertNull(XUnit.of("???")); + } + + @Test + public void testToDecimal() { + assertEquals(new BigDecimal("0"), ZERO.toDecimal(0, XDAG)); + assertEquals(new BigDecimal("0.000"), ZERO.toDecimal(3, XDAG)); + + XAmount oneXdag = XAmount.of(1, XDAG); + assertEquals(new BigDecimal("1.000"), oneXdag.toDecimal(3, XDAG)); + assertEquals(new BigDecimal("1000.000"), oneXdag.toDecimal(3, MILLI_XDAG)); + } + + @Test + public void testCompareTo() { + assertEquals(ZERO.compareTo(ZERO), 0); + assertEquals(XAmount.of(1000).compareTo(XAmount.of(1, MICRO_XDAG)), 0); + + assertEquals(XAmount.of(10).compareTo(XAmount.of(10)), 0); + assertEquals(XAmount.of(5).compareTo(XAmount.of(10)), -1); + assertEquals(XAmount.of(10).compareTo(XAmount.of(5)), 1); + } + + @Test + public void testHashCode() { + assertEquals(ZERO.hashCode(), XAmount.of(0, XDAG).hashCode()); + assertEquals(XAmount.of(999, XDAG).hashCode(), XAmount.of(999, XDAG).hashCode()); + } + + @Test + public void testGtLtEtc() { + assertTrue(XAmount.of(19, XDAG).isPositive()); + assertTrue(XAmount.of(-9, XDAG).isNegative()); + assertFalse(ZERO.isPositive()); + assertFalse(ZERO.isNegative()); + + assertTrue(ZERO.isNotNegative()); + assertTrue(ZERO.isNotPositive()); + assertFalse(XAmount.of(-9, XDAG).isNotNegative()); + assertFalse(XAmount.of(99, XDAG).isNotPositive()); + + assertTrue(XAmount.of(999, XDAG).greaterThan(XAmount.of(999, MILLI_XDAG))); + assertTrue(XAmount.of(999, XDAG).greaterThanOrEqual(XAmount.of(999, MILLI_XDAG))); + assertFalse(XAmount.of(999, XDAG).lessThan(XAmount.of(999, MILLI_XDAG))); + assertFalse(XAmount.of(999, XDAG).lessThanOrEqual(XAmount.of(999, MILLI_XDAG))); + } + + @Test + public void TestAmount2xdag() { + assertEquals(972.80, XAmount.ofXAmount(4178144185548L).toDecimal(2, XDAG).doubleValue(), 0.0); + + // 3333333334 + assertEquals(51.20, XAmount.ofXAmount(219902325556L).toDecimal(2, XDAG).doubleValue(), 0.0); + + // 400 0000 0000? + assertEquals(1.49, XAmount.ofXAmount(6400000000L).toDecimal(2, XDAG).doubleValue(), 0.0); + + // Xfer:transferred 44796588980 10.430000000 XDAG to the address 0000002f28322e9d817fd94a1357e51a. 10.43 + assertEquals(10.43, XAmount.ofXAmount(44796588980L).toDecimal(2, XDAG).doubleValue(), 0.0); + + // Xfer:transferred 42949672960 10.000000000 XDAG to the address 0000002f28322e9d817fd94a1357e51a. 10.00 + assertEquals(10.00, XAmount.ofXAmount(42949672960L).toDecimal(2, XDAG).doubleValue(), 0.0); + + // Xfer:transferred 4398046511104 1024.000000000 XDAG to the address 0000002f28322e9d817fd94a1357e51a. 1024.00 + assertEquals(1024.00, XAmount.ofXAmount(4398046511104L).toDecimal(2, XDAG).doubleValue(), 0.0); + } + + @Test + public void TestXdag2amount() { + BigDecimal a = BigDecimal.valueOf(972.80); + assertEquals(4178144185549L, XAmount.of(a, XDAG).toXAmount()); + + BigDecimal b = BigDecimal.valueOf(51.20); + assertEquals(219902325556L, XAmount.of(b, XDAG).toXAmount()); + + BigDecimal c = BigDecimal.valueOf(100.00); + assertEquals(429496729600L, XAmount.of(c, XDAG).toXAmount()); + } + +}