diff --git a/src/main/java/me/mrletsplay/shareclientcore/document/Util.java b/src/main/java/me/mrletsplay/shareclientcore/document/Util.java index 4c92a70..01d8e5e 100644 --- a/src/main/java/me/mrletsplay/shareclientcore/document/Util.java +++ b/src/main/java/me/mrletsplay/shareclientcore/document/Util.java @@ -44,7 +44,27 @@ public class Util { return null; } - public static void getIncrement(Identifier[] i1, Identifier[] i2, int offset) { + public static int[] getIncremented(Identifier[] i1, Identifier[] i2, int offset) { + // TODO: can potentially be optimized by just calculating the index of the first non-zero digit (e.g. don't return array and discard it -> just return the index) + int[] delta = subtract(i2, i1, offset); + int firstNonZero = 0; + for(int i = 0; i < delta.length; i++) { + if(delta[i] != 0) { + firstNonZero = i; + break; + } + } + + // Because of math, (firstNonZero + 1) >= i1.length (I think) + // then make the array 1 longer so we have space for the increment (which is one order of magnitude smaller) + int[] incremented = new int[firstNonZero + 2]; + for(int i = 0; i < incremented.length; i++) { + incremented[i] = i <= i1.length ? i1[i].digit() : 0; + } + + add1AtIndex(incremented, firstNonZero + 1); // last digit might be zero, which is ambigious, inc again + if(incremented[incremented.length - 1] == 0) add1AtIndex(incremented, firstNonZero + 1); + return incremented; } /** @@ -67,7 +87,26 @@ public class Util { } } + if(carry == 1) throw new RuntimeException("Can't subtract numbers (are you sure a > b?)"); + return diff; } + public static void add1AtIndex(int[] a, int index) { + // Technically, some of the overflow preventions are probably overkill since the biggest carry we can get is 1 (we're only adding a 1 somewhere after all) + + int carry = 0; + for(int i = a.length - 1; i >= 0; i--) { + int dA = a[i]; + int dB = (index == i ? 1 : 0) + carry; + + carry = dB - (BASE - dA) + 1; // Calculate carry and be overflow-safe + if(carry < 0) carry = 0; + + a[i] = carry != 0 ? -BASE + dA + dB : dA + dB; // Calculate sum of digits and be overflow-safe + } + + if(carry == 1) throw new RuntimeException("Can't add numbers: Too large"); + } + } diff --git a/src/test/java/me/mrletsplay/shareclientcore/DecimalTest.java b/src/test/java/me/mrletsplay/shareclientcore/DecimalTest.java index ddccb31..4bcd748 100644 --- a/src/test/java/me/mrletsplay/shareclientcore/DecimalTest.java +++ b/src/test/java/me/mrletsplay/shareclientcore/DecimalTest.java @@ -1,6 +1,7 @@ package me.mrletsplay.shareclientcore; import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.Test; @@ -13,21 +14,56 @@ public class DecimalTest { public void testSimpleSubtraction() { Identifier[] a = new Identifier[] { new Identifier(1, 0), new Identifier(2, 0), new Identifier(3, 0) }; Identifier[] b = new Identifier[] { new Identifier(1, 0), new Identifier(1, 0), new Identifier(3, 0) }; - assertArrayEquals(Util.subtract(a, b, 0), new int[] { 0, 1, 0 }); + assertArrayEquals(new int[] { 0, 1, 0 }, Util.subtract(a, b, 0)); } @Test public void testCarrySubtraction() { Identifier[] a = new Identifier[] { new Identifier(2, 0), new Identifier(0, 0), new Identifier(4, 0) }; Identifier[] b = new Identifier[] { new Identifier(1, 0), new Identifier(0, 0), new Identifier(5, 0) }; - assertArrayEquals(Util.subtract(a, b, 0), new int[] { 0, 9, 9 }); + assertArrayEquals(new int[] { 0, Util.BASE - 1, Util.BASE - 1 }, Util.subtract(a, b, 0)); } @Test public void testOffsetSubtraction() { Identifier[] a = new Identifier[] { new Identifier(1, 0), new Identifier(0, 0), new Identifier(5, 0) }; Identifier[] b = new Identifier[] { new Identifier(1, 0), new Identifier(0, 0), new Identifier(4, 0) }; - assertArrayEquals(Util.subtract(a, b, 1), new int[] { 0, 1 }); + assertArrayEquals(new int[] { 0, 1 }, Util.subtract(a, b, 1)); + } + + @Test + public void testReversedInputFails() { + Identifier[] a = new Identifier[] { new Identifier(1, 0), new Identifier(1, 0), new Identifier(3, 0) }; + Identifier[] b = new Identifier[] { new Identifier(1, 0), new Identifier(2, 0), new Identifier(3, 0) }; + assertThrows(RuntimeException.class, () -> Util.subtract(a, b, 0)); + } + + @Test + public void testSimpleAdd1() { + int[] a = new int[] { 0, 0 }; + Util.add1AtIndex(a, 1); + assertArrayEquals(new int[] { 0, 1 }, a); + } + + @Test + public void testCarryAdd1() { + int[] a = new int[] { 0, Util.BASE - 1 }; + Util.add1AtIndex(a, 1); + assertArrayEquals(new int[] { 1, 0 }, a); + } + + @Test + public void testCarryAdd1_2() { + int[] a = new int[] { 0, Util.BASE - 1, Util.BASE - 1 }; + Util.add1AtIndex(a, 2); + assertArrayEquals(new int[] { 1, 0, 0 }, a); + } + + @Test + public void testCarryAdd1_3() { + int[] a = new int[] { 0, Util.BASE - 1, Util.BASE - 1 }; + Util.add1AtIndex(a, 1); + assertArrayEquals(new int[] { 1, 0, Util.BASE - 1 }, a); } }