/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.facebook.presto.spi.type; import com.google.common.primitives.Bytes; import io.airlift.slice.Slice; import io.airlift.slice.Slices; import org.testng.annotations.Test; import java.math.BigInteger; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.IntBuffer; import java.util.Collections; import static com.facebook.presto.spi.type.Decimals.MAX_DECIMAL_UNSCALED_VALUE; import static com.facebook.presto.spi.type.Decimals.MIN_DECIMAL_UNSCALED_VALUE; import static com.facebook.presto.spi.type.Decimals.bigIntegerTenToNth; import static com.facebook.presto.spi.type.Decimals.decodeUnscaledValue; import static com.facebook.presto.spi.type.UnscaledDecimal128Arithmetic.add; import static com.facebook.presto.spi.type.UnscaledDecimal128Arithmetic.addWithOverflow; import static com.facebook.presto.spi.type.UnscaledDecimal128Arithmetic.compare; import static com.facebook.presto.spi.type.UnscaledDecimal128Arithmetic.divide; import static com.facebook.presto.spi.type.UnscaledDecimal128Arithmetic.hash; import static com.facebook.presto.spi.type.UnscaledDecimal128Arithmetic.isNegative; import static com.facebook.presto.spi.type.UnscaledDecimal128Arithmetic.multiply; import static com.facebook.presto.spi.type.UnscaledDecimal128Arithmetic.multiply256; import static com.facebook.presto.spi.type.UnscaledDecimal128Arithmetic.overflows; import static com.facebook.presto.spi.type.UnscaledDecimal128Arithmetic.rescale; import static com.facebook.presto.spi.type.UnscaledDecimal128Arithmetic.shiftLeft; import static com.facebook.presto.spi.type.UnscaledDecimal128Arithmetic.shiftLeftDestructive; import static com.facebook.presto.spi.type.UnscaledDecimal128Arithmetic.shiftLeftMultiPrecision; import static com.facebook.presto.spi.type.UnscaledDecimal128Arithmetic.shiftRight; import static com.facebook.presto.spi.type.UnscaledDecimal128Arithmetic.shiftRightArray8; import static com.facebook.presto.spi.type.UnscaledDecimal128Arithmetic.shiftRightMultiPrecision; import static com.facebook.presto.spi.type.UnscaledDecimal128Arithmetic.toUnscaledString; import static com.facebook.presto.spi.type.UnscaledDecimal128Arithmetic.unscaledDecimal; import static com.facebook.presto.spi.type.UnscaledDecimal128Arithmetic.unscaledDecimalToBigInteger; import static com.facebook.presto.spi.type.UnscaledDecimal128Arithmetic.unscaledDecimalToUnscaledLong; import static io.airlift.slice.SizeOf.SIZE_OF_LONG; import static io.airlift.slice.Slices.wrappedIntArray; import static io.airlift.slice.Slices.wrappedLongArray; import static java.lang.String.format; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotEquals; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; public class TestUnscaledDecimal128Arithmetic { private static final Slice MAX_DECIMAL = unscaledDecimal(MAX_DECIMAL_UNSCALED_VALUE); private static final Slice MIN_DECIMAL = unscaledDecimal(MIN_DECIMAL_UNSCALED_VALUE); private static final BigInteger TWO = BigInteger.valueOf(2); @Test public void testUnscaledBigIntegerToDecimal() { assertConvertsUnscaledBigIntegerToDecimal(MAX_DECIMAL_UNSCALED_VALUE); assertConvertsUnscaledBigIntegerToDecimal(MIN_DECIMAL_UNSCALED_VALUE); assertConvertsUnscaledBigIntegerToDecimal(BigInteger.ZERO); assertConvertsUnscaledBigIntegerToDecimal(BigInteger.ONE); assertConvertsUnscaledBigIntegerToDecimal(BigInteger.ONE.negate()); } @Test public void testUnscaledBigIntegerToDecimalOverflow() { assertUnscaledBigIntegerToDecimalOverflows(MAX_DECIMAL_UNSCALED_VALUE.add(BigInteger.ONE)); assertUnscaledBigIntegerToDecimalOverflows(MAX_DECIMAL_UNSCALED_VALUE.setBit(95)); assertUnscaledBigIntegerToDecimalOverflows(MAX_DECIMAL_UNSCALED_VALUE.setBit(127)); assertUnscaledBigIntegerToDecimalOverflows(MIN_DECIMAL_UNSCALED_VALUE.subtract(BigInteger.ONE)); } @Test public void testUnscaledLongToDecimal() { assertConvertsUnscaledLongToDecimal(0); assertConvertsUnscaledLongToDecimal(1); assertConvertsUnscaledLongToDecimal(-1); assertConvertsUnscaledLongToDecimal(Long.MAX_VALUE); assertConvertsUnscaledLongToDecimal(Long.MIN_VALUE); } @Test public void testDecimalToUnscaledLongOverflow() { assertDecimalToUnscaledLongOverflows(BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE)); assertDecimalToUnscaledLongOverflows(BigInteger.valueOf(Long.MIN_VALUE).subtract(BigInteger.ONE)); assertDecimalToUnscaledLongOverflows(MAX_DECIMAL_UNSCALED_VALUE); assertDecimalToUnscaledLongOverflows(MIN_DECIMAL_UNSCALED_VALUE); } @Test public void testRescale() { assertEquals(rescale(unscaledDecimal(10), 0), unscaledDecimal(10L)); assertEquals(rescale(unscaledDecimal(10), -20), unscaledDecimal(0L)); assertEquals(rescale(unscaledDecimal(15), -1), unscaledDecimal(2)); assertEquals(rescale(unscaledDecimal(1050), -3), unscaledDecimal(1)); assertEquals(rescale(unscaledDecimal(15), 1), unscaledDecimal(150)); assertEquals(rescale(unscaledDecimal(-14), -1), unscaledDecimal(-1)); assertEquals(rescale(unscaledDecimal(-14), 1), unscaledDecimal(-140)); assertEquals(rescale(unscaledDecimal(0), 1), unscaledDecimal(0)); assertEquals(rescale(unscaledDecimal(5), -1), unscaledDecimal(1)); assertEquals(rescale(unscaledDecimal(10), 10), unscaledDecimal(100000000000L)); assertEquals(rescale(unscaledDecimal("150000000000000000000"), -20), unscaledDecimal(2)); assertEquals(rescale(unscaledDecimal("-140000000000000000000"), -20), unscaledDecimal(-1)); assertEquals(rescale(unscaledDecimal("50000000000000000000"), -20), unscaledDecimal(1)); assertEquals(rescale(unscaledDecimal("150500000000000000000"), -18), unscaledDecimal(151)); assertEquals(rescale(unscaledDecimal("-140000000000000000000"), -18), unscaledDecimal(-140)); assertEquals(rescale(unscaledDecimal(BigInteger.ONE.shiftLeft(63)), -18), unscaledDecimal(9L)); assertEquals(rescale(unscaledDecimal(BigInteger.ONE.shiftLeft(62)), -18), unscaledDecimal(5L)); assertEquals(rescale(unscaledDecimal(BigInteger.ONE.shiftLeft(62)), -19), unscaledDecimal(0L)); assertEquals(rescale(MAX_DECIMAL, -1), unscaledDecimal(MAX_DECIMAL_UNSCALED_VALUE.divide(BigInteger.TEN).add(BigInteger.ONE))); assertEquals(rescale(MIN_DECIMAL, -10), unscaledDecimal(MIN_DECIMAL_UNSCALED_VALUE.divide(BigInteger.valueOf(10000000000L)).subtract(BigInteger.ONE))); assertEquals(rescale(unscaledDecimal(1), 37), unscaledDecimal("10000000000000000000000000000000000000")); assertEquals(rescale(unscaledDecimal(-1), 37), unscaledDecimal("-10000000000000000000000000000000000000")); assertEquals(rescale(unscaledDecimal("10000000000000000000000000000000000000"), -37), unscaledDecimal(1)); } @Test public void testRescaleOverflows() { assertRescaleOverflows(unscaledDecimal(1), 38); } @Test public void testAdd() { assertEquals(add(unscaledDecimal(0), unscaledDecimal(0)), unscaledDecimal(0)); assertEquals(add(unscaledDecimal(1), unscaledDecimal(0)), unscaledDecimal(1)); assertEquals(add(unscaledDecimal(1), unscaledDecimal(1)), unscaledDecimal(2)); assertEquals(add(unscaledDecimal(1L << 32), unscaledDecimal(0)), unscaledDecimal(1L << 32)); assertEquals(add(unscaledDecimal(1L << 31), unscaledDecimal(1L << 31)), unscaledDecimal(1L << 32)); assertEquals(add(unscaledDecimal(1L << 32), unscaledDecimal(1L << 33)), unscaledDecimal((1L << 32) + (1L << 33))); } @Test public void testAddReturnOverflow() { assertAddReturnOverflow(TWO, TWO); assertAddReturnOverflow(MAX_DECIMAL_UNSCALED_VALUE, MAX_DECIMAL_UNSCALED_VALUE); assertAddReturnOverflow(MAX_DECIMAL_UNSCALED_VALUE.negate(), MAX_DECIMAL_UNSCALED_VALUE); assertAddReturnOverflow(MAX_DECIMAL_UNSCALED_VALUE, MAX_DECIMAL_UNSCALED_VALUE.negate()); assertAddReturnOverflow(MAX_DECIMAL_UNSCALED_VALUE.negate(), MAX_DECIMAL_UNSCALED_VALUE.negate()); } @Test public void testMultiply() { assertEquals(multiply(unscaledDecimal(0), MAX_DECIMAL), unscaledDecimal(0)); assertEquals(multiply(unscaledDecimal(1), MAX_DECIMAL), MAX_DECIMAL); assertEquals(multiply(unscaledDecimal(1), MIN_DECIMAL), MIN_DECIMAL); assertEquals(multiply(unscaledDecimal(-1), MAX_DECIMAL), MIN_DECIMAL); assertEquals(multiply(unscaledDecimal(-1), MIN_DECIMAL), MAX_DECIMAL); assertEquals(multiply(wrappedIntArray(0xFFFFFFFF, 0xFFFFFFFF, 0, 0), wrappedIntArray(0xFFFFFFFF, 0x00FFFFFF, 0, 0)), wrappedLongArray(0xff00000000000001L, 0xfffffffffffffeL)); assertEquals(multiply(wrappedLongArray(0xFFFFFF0096BFB800L, 0), wrappedLongArray(0x39003539D9A51600L, 0)), wrappedLongArray(0x1CDBB17E11D00000L, 0x39003500FB00AB76L)); assertEquals(multiply(unscaledDecimal(Integer.MAX_VALUE), unscaledDecimal(Integer.MIN_VALUE)), unscaledDecimal((long) Integer.MAX_VALUE * Integer.MIN_VALUE)); assertEquals(multiply(unscaledDecimal("99999999999999"), unscaledDecimal("-1000000000000000000000000")), unscaledDecimal("-99999999999999000000000000000000000000")); assertEquals(multiply(unscaledDecimal("12380837221737387489365741632769922889"), unscaledDecimal("3")), unscaledDecimal("37142511665212162468097224898309768667")); } @Test public void testMultiply256() throws Exception { assertMultiply256(MAX_DECIMAL, MAX_DECIMAL, wrappedLongArray(0xECEBBB8000000001L, 0xE0FF0CA0BC87870BL, 0x0764B4ABE8652978L, 0x161BCCA7119915B5L)); assertMultiply256(MIN_DECIMAL, MIN_DECIMAL, wrappedLongArray(0xECEBBB8000000001L, 0xE0FF0CA0BC87870BL, 0x0764B4ABE8652978L, 0x161BCCA7119915B5L)); assertMultiply256(wrappedLongArray(0xFFFFFFFFFFFFFFFFL, 0x0FFFFFFFFFFFFFFFL), wrappedLongArray(0xFFFFFFFFFFFFFFFFL, 0x0FFFFFFFFFFFFFFFL), wrappedLongArray(0x0000000000000001L, 0xE000000000000000L, 0xFFFFFFFFFFFFFFFFL, 0x00FFFFFFFFFFFFFFL)); assertMultiply256(wrappedLongArray(0x1234567890ABCDEFL, 0x0EDCBA0987654321L), wrappedLongArray(0xFEDCBA0987654321L, 0x1234567890ABCDEL), wrappedLongArray(0xC24A442FE55618CFL, 0xAA71A60D0DA49DDAL, 0x7C163D5A13DF8695L, 0x0010E8EEF9BD1294L)); } private static void assertMultiply256(Slice left, Slice right, Slice expected) { Slice actual = Slices.allocate(Long.BYTES * 4); multiply256(left, right, actual); assertEquals(actual, expected); } @Test public void testMultiplyByInt() { assertEquals(multiply(unscaledDecimal(0), 1), unscaledDecimal(0)); assertEquals(multiply(unscaledDecimal(2), Integer.MAX_VALUE), unscaledDecimal(2L * Integer.MAX_VALUE)); assertEquals(multiply(unscaledDecimal(Integer.MAX_VALUE), -3), unscaledDecimal(-3L * Integer.MAX_VALUE)); assertEquals(multiply(unscaledDecimal(Integer.MIN_VALUE), -3), unscaledDecimal(-3L * Integer.MIN_VALUE)); assertEquals(multiply(unscaledDecimal(TWO.pow(100).subtract(BigInteger.ONE)), 2), unscaledDecimal(TWO.pow(101).subtract(TWO))); } @Test public void testMultiplyOverflow() { assertMultiplyOverflows(unscaledDecimal("99999999999999"), unscaledDecimal("-10000000000000000000000000")); assertMultiplyOverflows(MAX_DECIMAL, unscaledDecimal("10")); } @Test public void testShiftRight() { assertShiftRight(unscaledDecimal(0), 0, true, unscaledDecimal(0)); assertShiftRight(unscaledDecimal(0), 33, true, unscaledDecimal(0)); assertShiftRight(unscaledDecimal(1), 1, true, unscaledDecimal(1)); assertShiftRight(unscaledDecimal(-4), 1, true, unscaledDecimal(-2)); assertShiftRight(unscaledDecimal(1L << 32), 32, true, unscaledDecimal(1)); assertShiftRight(unscaledDecimal(1L << 31), 32, true, unscaledDecimal(1)); assertShiftRight(unscaledDecimal(1L << 31), 32, false, unscaledDecimal(0)); assertShiftRight(unscaledDecimal(3L << 33), 34, true, unscaledDecimal(2)); assertShiftRight(unscaledDecimal(3L << 33), 34, false, unscaledDecimal(1)); assertShiftRight(unscaledDecimal(BigInteger.valueOf(0x7FFFFFFFFFFFFFFFL).setBit(63).setBit(64)), 1, true, unscaledDecimal(BigInteger.ONE.shiftLeft(64))); assertShiftRight(MAX_DECIMAL, 1, true, unscaledDecimal(MAX_DECIMAL_UNSCALED_VALUE.shiftRight(1).add(BigInteger.ONE))); assertShiftRight(MIN_DECIMAL, 1, true, unscaledDecimal(MAX_DECIMAL_UNSCALED_VALUE.shiftRight(1).add(BigInteger.ONE).negate())); assertShiftRight(MAX_DECIMAL, 66, true, unscaledDecimal(MAX_DECIMAL_UNSCALED_VALUE.shiftRight(66).add(BigInteger.ONE))); } @Test public void testShiftRightArray8() { assertShiftRightArray8(TWO.pow(1), 0); assertShiftRightArray8(TWO.pow(1), 1); assertShiftRightArray8(TWO.pow(1), 10); assertShiftRightArray8(TWO.pow(15).add(TWO.pow(3)), 2); assertShiftRightArray8(TWO.pow(15).add(TWO.pow(3)), 10); assertShiftRightArray8(TWO.pow(15).add(TWO.pow(3)), 20); assertShiftRightArray8(TWO.pow(70), 30); assertShiftRightArray8(TWO.pow(70).subtract(TWO.pow(1)), 30, true); assertShiftRightArray8(TWO.pow(70), 32); assertShiftRightArray8(TWO.pow(70).subtract(TWO.pow(1)), 32, true); assertShiftRightArray8(TWO.pow(120), 70); assertShiftRightArray8(TWO.pow(120).subtract(TWO.pow(1)), 70, true); assertShiftRightArray8(TWO.pow(120), 96); assertShiftRightArray8(TWO.pow(120).subtract(TWO.pow(1)), 96, true); assertShiftRightArray8(MAX_DECIMAL_UNSCALED_VALUE, 20, true); assertShiftRightArray8(MAX_DECIMAL_UNSCALED_VALUE.multiply(MAX_DECIMAL_UNSCALED_VALUE), 130); assertShiftRightArray8(TWO.pow(256).subtract(BigInteger.ONE), 130, true); assertShiftRightArray8Overflow(TWO.pow(156), 1); assertShiftRightArray8Overflow(MAX_DECIMAL_UNSCALED_VALUE.multiply(MAX_DECIMAL_UNSCALED_VALUE), 20); assertShiftRightArray8Overflow(TWO.pow(256).subtract(BigInteger.ONE), 129); } @Test public void testDivide() { // simple cases assertDivideAllSigns("0", "10"); assertDivideAllSigns("5", "10"); assertDivideAllSigns("50", "100"); assertDivideAllSigns("99", "10"); assertDivideAllSigns("95", "10"); assertDivideAllSigns("91", "10"); assertDivideAllSigns("1000000000000000000000000", "10"); assertDivideAllSigns("1000000000000000000000000", "3"); assertDivideAllSigns("1000000000000000000000000", "9"); assertDivideAllSigns("1000000000000000000000000", "100000000000000000000000"); assertDivideAllSigns("1000000000000000000000000", "333333333333333333333333"); assertDivideAllSigns("1000000000000000000000000", "111111111111111111111111"); // dividend < divisor assertDivideAllSigns(new int[] {4, 3, 2, 0}, new int[] {4, 3, 2, 1}); assertDivideAllSigns(new int[] {4, 3, 0, 0}, new int[] {4, 3, 2, 0}); assertDivideAllSigns(new int[] {4, 0, 0, 0}, new int[] {4, 3, 0, 0}); assertDivideAllSigns(new int[] {0, 0, 0, 0}, new int[] {4, 0, 0, 0}); // different lengths assertDivideAllSigns(new int[] {1423957378, 1765820914, 0xFFFFFFFF, 0}, new int[] {4, 0x0000FFFF, 0, 0}); assertDivideAllSigns(new int[] {1423957378, 1765820914, 0xFFFFFFFF, 0}, new int[] {2042457708, 0, 0, 0}); assertDivideAllSigns(new int[] {1423957378, -925263858, 0, 0}, new int[] {2042457708, 0, 0, 0}); assertDivideAllSigns(new int[] {0xFFFFFFFF, 0, 0, 0}, new int[] {2042457708, 0, 0, 0}); // single int divisor assertDivideAllSigns(new int[] {1423957378, -1444436990, -925263858, 1106345725}, new int[] {2042457708, 0, 0, 0}); assertDivideAllSigns(new int[] {0, 0xF7000000, 0, 0x39000000}, new int[] {-1765820914, 0, 0, 0}); // normalization scale = 1 assertDivideAllSigns(new int[] {0x0FF00210, 0xF7001230, 0xFB00AC00, 0x39003500}, new int[] {-1765820914, 2042457708, 0xFFFFFFFF, 0}); assertDivideAllSigns(new int[] {0x0FF00210, 0xF7001230, 0xFB00AC00, 0x39003500}, new int[] {-1765820914, 0xFFFFFF00, 0, 0}); assertDivideAllSigns(new int[] {0x0FF00210, 0xF7001230, 0xFB00AC00, 0x39003500}, new int[] {-1765820914, 0xFF000000, 0, 0}); // normalization scale > 1 assertDivideAllSigns(new int[] {0x0FF00210, 0xF7001230, 0xFB00AC00, 0x39003500}, new int[] {-1765820914, 2042457708, 0xFFFFFFFF, 0x7FFFFFFF}); assertDivideAllSigns(new int[] {0x0FF00210, 0xF7001230, 0xFB00AC00, 0x39003500}, new int[] {-1765820914, 2042457708, 0x4FFFFFFF, 0}); assertDivideAllSigns(new int[] {0x0FF00210, 0xF7001230, 0xFB00AC00, 0x39003500}, new int[] {-1765820914, 2042457708, 0x0000FFFF, 0}); // normalization scale signed overflow assertDivideAllSigns(new int[] {1, 1, 1, 0x7FFFFFFF}, new int[] {0xFFFFFFFF, 1, 0, 0}); // u2 = v1 assertDivideAllSigns(new int[] {0, 0x8FFFFFFF, 0x8FFFFFFF, 0}, new int[] {0xFFFFFFFF, 0x8FFFFFFF, 0, 0}); // qhat is greater than q by 1 assertDivideAllSigns(new int[] {1, 1, 0xFFFFFFFF, 0}, new int[] {0xFFFFFFFF, 0x7FFFFFFF, 0, 0}); // qhat is greater than q by 2 assertDivideAllSigns(new int[] {1, 1, 0xFFFFFFFF, 0}, new int[] {0xFFFFFFFF, 0x7FFFFFFF, 0, 0}); // overflow after multiplyAndSubtract assertDivideAllSigns(new int[] {0x00000003, 0x00000000, 0x80000000, 0}, new int[] {0x00000001, 0x00000000, 0x20000000, 0}); assertDivideAllSigns(new int[] {0x00000003, 0x00000000, 0x00008000, 0}, new int[] {0x00000001, 0x00000000, 0x00002000, 0}); assertDivideAllSigns(new int[] {0, 0, 0x00008000, 0x00007fff}, new int[] {1, 0, 0x00008000, 0}); // test cases from http://www.hackersdelight.org/hdcodetxt/divmnu64.c.txt // license: http://www.hackersdelight.org/permissions.htm assertDivideAllSigns(new int[] {3, 0, 0, 0}, new int[] {2, 0, 0, 0}); assertDivideAllSigns(new int[] {3, 0, 0, 0}, new int[] {3, 0, 0, 0}); assertDivideAllSigns(new int[] {3, 0, 0, 0}, new int[] {4, 0, 0, 0}); assertDivideAllSigns(new int[] {3, 0, 0, 0}, new int[] {0xffffffff, 0, 0, 0}); assertDivideAllSigns(new int[] {0xffffffff, 0, 0, 0}, new int[] {1, 0, 0, 0}); assertDivideAllSigns(new int[] {0xffffffff, 0, 0, 0}, new int[] {0xffffffff, 0, 0, 0}); assertDivideAllSigns(new int[] {0xffffffff, 0, 0, 0}, new int[] {3, 0, 0, 0}); assertDivideAllSigns(new int[] {0xffffffff, 0xffffffff, 0, 0}, new int[] {1, 0, 0, 0}); assertDivideAllSigns(new int[] {0xffffffff, 0xffffffff, 0, 0}, new int[] {0xffffffff, 0, 0, 0}); assertDivideAllSigns(new int[] {0xffffffff, 0xfffffffe, 0, 0}, new int[] {0xffffffff, 0, 0, 0}); assertDivideAllSigns(new int[] {0x00005678, 0x00001234, 0, 0}, new int[] {0x00009abc, 0, 0, 0}); assertDivideAllSigns(new int[] {0, 0, 0, 0}, new int[] {0, 1, 0, 0}); assertDivideAllSigns(new int[] {0, 7, 0, 0}, new int[] {0, 3, 0, 0}); assertDivideAllSigns(new int[] {5, 7, 0, 0}, new int[] {0, 3, 0, 0}); assertDivideAllSigns(new int[] {0, 6, 0, 0}, new int[] {0, 2, 0, 0}); assertDivideAllSigns(new int[] {0x80000000, 0, 0, 0}, new int[] {0x40000001, 0, 0, 0}); assertDivideAllSigns(new int[] {0x00000000, 0x80000000, 0, 0}, new int[] {0x40000001, 0, 0, 0}); assertDivideAllSigns(new int[] {0x00000000, 0x80000000, 0, 0}, new int[] {0x00000001, 0x40000000, 0, 0}); assertDivideAllSigns(new int[] {0x0000789a, 0x0000bcde, 0, 0}, new int[] {0x0000789a, 0x0000bcde, 0, 0}); assertDivideAllSigns(new int[] {0x0000789b, 0x0000bcde, 0, 0}, new int[] {0x0000789a, 0x0000bcde, 0, 0}); assertDivideAllSigns(new int[] {0x00007899, 0x0000bcde, 0, 0}, new int[] {0x0000789a, 0x0000bcde, 0, 0}); assertDivideAllSigns(new int[] {0x0000ffff, 0x0000ffff, 0, 0}, new int[] {0x0000ffff, 0x0000ffff, 0, 0}); assertDivideAllSigns(new int[] {0x0000ffff, 0x0000ffff, 0, 0}, new int[] {0x00000000, 0x0000ffff, 0, 0}); assertDivideAllSigns(new int[] {0x000089ab, 0x00004567, 0x00000123, 0}, new int[] {0x00000000, 0x00000001, 0, 0}); assertDivideAllSigns(new int[] {0x000089ab, 0x00004567, 0x00000123, 0}, new int[] {0x00000000, 0x00000001, 0, 0}); assertDivideAllSigns(new int[] {0x00000000, 0x0000fffe, 0x00008000, 0}, new int[] {0x0000ffff, 0x00008000, 0, 0}); assertDivideAllSigns(new int[] {0x00000003, 0x00000000, 0x80000000, 0}, new int[] {0x00000001, 0x00000000, 0x20000000, 0}); assertDivideAllSigns(new int[] {0x00000003, 0x00000000, 0x00008000, 0}, new int[] {0x00000001, 0x00000000, 0x00002000, 0}); assertDivideAllSigns(new int[] {0, 0, 0x00008000, 0x00007fff}, new int[] {1, 0, 0x00008000, 0}); assertDivideAllSigns(new int[] {0, 0x0000fffe, 0, 0x00008000}, new int[] {0x0000ffff, 0, 0x00008000, 0}); assertDivideAllSigns(new int[] {0, 0xfffffffe, 0, 0x80000000}, new int[] {0x0000ffff, 0, 0x80000000, 0}); assertDivideAllSigns(new int[] {0, 0xfffffffe, 0, 0x80000000}, new int[] {0xffffffff, 0, 0x80000000, 0}); // with rescale assertDivideAllSigns("100000000000000000000000", 10, "111111111111111111111111", 10); assertDivideAllSigns("100000000000000000000000", 10, "111111111111", 22); assertDivideAllSigns("99999999999999999999999999999999999999", 37, "99999999999999999999999999999999999999", 37); assertDivideAllSigns("99999999999999999999999999999999999999", 2, "99999999999999999999999999999999999999", 1); assertDivideAllSigns("99999999999999999999999999999999999999", 37, "9", 37); assertDivideAllSigns("99999999999999999999999999999999999999", 37, "1", 37); assertDivideAllSigns("11111111111111111111111111111111111111", 37, "2", 37); assertDivideAllSigns("11111111111111111111111111111111111111", 37, "2", 1); assertDivideAllSigns("97764425639372288753711864842425458618", 36, "32039006229599111733094986468789901155", 0); assertDivideAllSigns("34354576602352622842481633786816220283", 0, "31137583115118564930544829855652258045", 0); assertDivideAllSigns("96690614752287690630596513604374991473", 0, "10039352042372909488692220528497751229", 0); assertDivideAllSigns("87568357716090115374029040878755891076", 0, "46106713604991337798209343815577148589", 0); } @Test public void testOverflows() { assertTrue(overflows(unscaledDecimal("100"), 2)); assertTrue(overflows(unscaledDecimal("-100"), 2)); assertFalse(overflows(unscaledDecimal("99"), 2)); assertFalse(overflows(unscaledDecimal("-99"), 2)); } @Test public void testCompare() { assertCompare(unscaledDecimal(0), unscaledDecimal(0), 0); assertCompare(negate(unscaledDecimal(0)), unscaledDecimal(0), 0); assertCompare(unscaledDecimal(0), negate(unscaledDecimal(0)), 0); assertCompare(unscaledDecimal(0), unscaledDecimal(10), -1); assertCompare(unscaledDecimal(10), unscaledDecimal(0), 1); assertCompare(negate(unscaledDecimal(0)), unscaledDecimal(10), -1); assertCompare(unscaledDecimal(10), negate(unscaledDecimal(0)), 1); assertCompare(negate(unscaledDecimal(0)), MAX_DECIMAL, -1); assertCompare(MAX_DECIMAL, negate(unscaledDecimal(0)), 1); assertCompare(unscaledDecimal(-10), unscaledDecimal(-11), 1); assertCompare(unscaledDecimal(-11), unscaledDecimal(-11), 0); assertCompare(unscaledDecimal(-12), unscaledDecimal(-11), -1); assertCompare(unscaledDecimal(10), unscaledDecimal(11), -1); assertCompare(unscaledDecimal(11), unscaledDecimal(11), 0); assertCompare(unscaledDecimal(12), unscaledDecimal(11), 1); } @Test public void testNegate() { assertEquals(negate(negate(MIN_DECIMAL)), MIN_DECIMAL); assertEquals(negate(MIN_DECIMAL), MAX_DECIMAL); assertEquals(negate(MIN_DECIMAL), MAX_DECIMAL); assertEquals(negate(unscaledDecimal(1)), unscaledDecimal(-1)); assertEquals(negate(unscaledDecimal(-1)), unscaledDecimal(1)); assertEquals(negate(negate(unscaledDecimal(0))), unscaledDecimal(0)); } @Test public void testIsNegative() { assertEquals(isNegative(MIN_DECIMAL), true); assertEquals(isNegative(MAX_DECIMAL), false); assertEquals(isNegative(unscaledDecimal(0)), false); } @Test public void testHash() { assertEquals(hash(unscaledDecimal(0)), hash(negate(unscaledDecimal(0)))); assertNotEquals(hash(unscaledDecimal(0)), unscaledDecimal(1)); } @Test public void testToString() { assertEquals(toUnscaledString(unscaledDecimal(0)), "0"); assertEquals(toUnscaledString(negate(unscaledDecimal(0))), "0"); assertEquals(toUnscaledString(unscaledDecimal(1)), "1"); assertEquals(toUnscaledString(unscaledDecimal(-1)), "-1"); assertEquals(toUnscaledString(unscaledDecimal(MAX_DECIMAL)), MAX_DECIMAL_UNSCALED_VALUE.toString()); assertEquals(toUnscaledString(unscaledDecimal(MIN_DECIMAL)), MIN_DECIMAL_UNSCALED_VALUE.toString()); assertEquals(toUnscaledString(unscaledDecimal("1000000000000000000000000000000000000")), "1000000000000000000000000000000000000"); assertEquals(toUnscaledString(unscaledDecimal("-1000000000002000000000000300000000000")), "-1000000000002000000000000300000000000"); } @Test public void testShiftLeftMultiPrecision() throws Exception { assertEquals(shiftLeftMultiPrecision( new int[] {0b10100001010001011010000101000101, 0b01010110100101101011010101010101, 0b01010010111110001111100010101010, 0b11111111000000011010101010101011, 0b00000000000000000000000000000000}, 4, 0), new int[] {0b10100001010001011010000101000101, 0b01010110100101101011010101010101, 0b01010010111110001111100010101010, 0b11111111000000011010101010101011, 0b00000000000000000000000000000000}); assertEquals(shiftLeftMultiPrecision( new int[] {0b10100001010001011010000101000101, 0b01010110100101101011010101010101, 0b01010010111110001111100010101010, 0b11111111000000011010101010101011, 0b00000000000000000000000000000000}, 5, 1), new int[] {0b01000010100010110100001010001010, 0b10101101001011010110101010101011, 0b10100101111100011111000101010100, 0b11111110000000110101010101010110, 0b00000000000000000000000000000001}); assertEquals(shiftLeftMultiPrecision( new int[] {0b10100001010001011010000101000101, 0b01010110100101101011010101010101, 0b01010010111110001111100010101010, 0b11111111000000011010101010101011, 0b00000000000000000000000000000000}, 5, 31), new int[] {0b10000000000000000000000000000000, 0b11010000101000101101000010100010, 0b00101011010010110101101010101010, 0b10101001011111000111110001010101, 0b1111111100000001101010101010101}); assertEquals(shiftLeftMultiPrecision( new int[] {0b10100001010001011010000101000101, 0b01010110100101101011010101010101, 0b01010010111110001111100010101010, 0b11111111000000011010101010101011, 0b00000000000000000000000000000000}, 5, 32), new int[] {0b00000000000000000000000000000000, 0b10100001010001011010000101000101, 0b01010110100101101011010101010101, 0b01010010111110001111100010101010, 0b11111111000000011010101010101011}); assertEquals(shiftLeftMultiPrecision( new int[] {0b10100001010001011010000101000101, 0b01010110100101101011010101010101, 0b01010010111110001111100010101010, 0b11111111000000011010101010101011, 0b00000000000000000000000000000000, 0b00000000000000000000000000000000}, 6, 33), new int[] {0b00000000000000000000000000000000, 0b01000010100010110100001010001010, 0b10101101001011010110101010101011, 0b10100101111100011111000101010100, 0b11111110000000110101010101010110, 0b00000000000000000000000000000001}); assertEquals(shiftLeftMultiPrecision( new int[] {0b10100001010001011010000101000101, 0b01010110100101101011010101010101, 0b01010010111110001111100010101010, 0b11111111000000011010101010101011, 0b00000000000000000000000000000000, 0b00000000000000000000000000000000}, 6, 37), new int[] {0b00000000000000000000000000000000, 0b00101000101101000010100010100000, 0b11010010110101101010101010110100, 0b01011111000111110001010101001010, 0b11100000001101010101010101101010, 0b00000000000000000000000000011111}); assertEquals(shiftLeftMultiPrecision( new int[] {0b10100001010001011010000101000101, 0b01010110100101101011010101010101, 0b01010010111110001111100010101010, 0b11111111000000011010101010101011, 0b00000000000000000000000000000000, 0b00000000000000000000000000000000}, 6, 64), new int[] {0b00000000000000000000000000000000, 0b00000000000000000000000000000000, 0b10100001010001011010000101000101, 0b01010110100101101011010101010101, 0b01010010111110001111100010101010, 0b11111111000000011010101010101011}); } @Test public void testShiftRightMultiPrecision() throws Exception { assertEquals(shiftRightMultiPrecision( new int[] {0b10100001010001011010000101000101, 0b01010110100101101011010101010101, 0b01010010111110001111100010101010, 0b11111111000000011010101010101011, 0b00000000000000000000000000000000}, 4, 0), new int[] {0b10100001010001011010000101000101, 0b01010110100101101011010101010101, 0b01010010111110001111100010101010, 0b11111111000000011010101010101011, 0b00000000000000000000000000000000}); assertEquals(shiftRightMultiPrecision( new int[] {0b00000000000000000000000000000000, 0b10100001010001011010000101000101, 0b01010110100101101011010101010101, 0b01010010111110001111100010101010, 0b11111111000000011010101010101011}, 5, 1), new int[] {0b10000000000000000000000000000000, 0b11010000101000101101000010100010, 0b00101011010010110101101010101010, 0b10101001011111000111110001010101, 0b1111111100000001101010101010101}); assertEquals(shiftRightMultiPrecision( new int[] {0b00000000000000000000000000000000, 0b10100001010001011010000101000101, 0b01010110100101101011010101010101, 0b01010010111110001111100010101010, 0b11111111000000011010101010101011}, 5, 32), new int[] {0b10100001010001011010000101000101, 0b01010110100101101011010101010101, 0b01010010111110001111100010101010, 0b11111111000000011010101010101011, 0b00000000000000000000000000000000}); assertEquals(shiftRightMultiPrecision( new int[] {0b00000000000000000000000000000000, 0b00000000000000000000000000000000, 0b10100001010001011010000101000101, 0b01010110100101101011010101010101, 0b01010010111110001111100010101010, 0b11111111000000011010101010101011}, 6, 33), new int[] {0b10000000000000000000000000000000, 0b11010000101000101101000010100010, 0b00101011010010110101101010101010, 0b10101001011111000111110001010101, 0b01111111100000001101010101010101, 0b00000000000000000000000000000000}); assertEquals(shiftRightMultiPrecision( new int[] {0b00000000000000000000000000000000, 0b00000000000000000000000000000000, 0b10100001010001011010000101000101, 0b01010110100101101011010101010101, 0b01010010111110001111100010101010, 0b11111111000000011010101010101011}, 6, 37), new int[] {0b00101000000000000000000000000000, 0b10101101000010100010110100001010, 0b01010010101101001011010110101010, 0b01011010100101111100011111000101, 0b00000111111110000000110101010101, 0b00000000000000000000000000000000}); assertEquals(shiftRightMultiPrecision( new int[] {0b00000000000000000000000000000000, 0b00000000000000000000000000000000, 0b10100001010001011010000101000101, 0b01010110100101101011010101010101, 0b01010010111110001111100010101010, 0b11111111000000011010101010101011}, 6, 64), new int[] {0b10100001010001011010000101000101, 0b01010110100101101011010101010101, 0b01010010111110001111100010101010, 0b11111111000000011010101010101011, 0b00000000000000000000000000000000, 0b00000000000000000000000000000000}); } @Test public void testShiftLeftCompareToBigInteger() { assertShiftLeft(new BigInteger("446319580078125"), 19); assertShiftLeft(TWO.pow(1), 10); assertShiftLeft(TWO.pow(5).add(TWO.pow(1)), 10); assertShiftLeft(TWO.pow(1), 100); assertShiftLeft(TWO.pow(5).add(TWO.pow(1)), 100); assertShiftLeft(TWO.pow(70), 30); assertShiftLeft(TWO.pow(70).add(TWO.pow(1)), 30); assertShiftLeft(TWO.pow(106), 20); assertShiftLeft(TWO.pow(106).add(TWO.pow(1)), 20); assertShiftLeftOverflow(TWO.pow(2), 127); assertShiftLeftOverflow(TWO.pow(64), 64); assertShiftLeftOverflow(TWO.pow(100), 28); } @Test public void testShiftLeft() throws Exception { assertEquals(shiftLeft(wrappedLongArray(0x1234567890ABCDEFL, 0xEFDCBA0987654321L), 0), wrappedLongArray(0x1234567890ABCDEFL, 0xEFDCBA0987654321L)); assertEquals(shiftLeft(wrappedLongArray(0x1234567890ABCDEFL, 0xEFDCBA0987654321L), 1), wrappedLongArray(0x2468ACF121579BDEL, 0xDFB974130ECA8642L)); assertEquals(shiftLeft(wrappedLongArray(0x1234567890ABCDEFL, 0x00DCBA0987654321L), 8), wrappedLongArray(0x34567890ABCDEF00L, 0xDCBA098765432112L)); assertEquals(shiftLeft(wrappedLongArray(0x1234567890ABCDEFL, 0x0000BA0987654321L), 16), wrappedLongArray(0x567890ABCDEF0000L, 0xBA09876543211234L)); assertEquals(shiftLeft(wrappedLongArray(0x1234567890ABCDEFL, 0x0000000087654321L), 32), wrappedLongArray(0x90ABCDEF00000000L, 0x8765432112345678L)); assertEquals(shiftLeft(wrappedLongArray(0x1234567890ABCDEFL, 0L), 64), wrappedLongArray(0x0000000000000000L, 0x1234567890ABCDEFL)); assertEquals(shiftLeft(wrappedLongArray(0x0034567890ABCDEFL, 0L), 64 + 8), wrappedLongArray(0x0000000000000000L, 0x34567890ABCDEF00L)); assertEquals(shiftLeft(wrappedLongArray(0x000000000000CDEFL, 0L), 64 + 48), wrappedLongArray(0x0000000000000000L, 0xCDEF000000000000L)); assertEquals(shiftLeft(wrappedLongArray(0x1L, 0L), 64 + 63), wrappedLongArray(0x0000000000000000L, 0x8000000000000000L)); } private void assertAddReturnOverflow(BigInteger left, BigInteger right) { Slice result = unscaledDecimal(); long overflow = addWithOverflow(unscaledDecimal(left), unscaledDecimal(right), result); BigInteger actual = unscaledDecimalToBigInteger(result); BigInteger expected = left.add(right).remainder(TWO.pow(UnscaledDecimal128Arithmetic.UNSCALED_DECIMAL_128_SLICE_LENGTH * 8 - 1)); BigInteger expectedOverflow = left.add(right).divide(TWO.pow(UnscaledDecimal128Arithmetic.UNSCALED_DECIMAL_128_SLICE_LENGTH * 8 - 1)); assertEquals(actual, expected); assertEquals(overflow, expectedOverflow.longValueExact()); } private static void assertUnscaledBigIntegerToDecimalOverflows(BigInteger value) { try { unscaledDecimal(value); fail(); } catch (ArithmeticException ignored) { } } private static void assertDecimalToUnscaledLongOverflows(BigInteger value) { Slice decimal = unscaledDecimal(value); try { unscaledDecimalToUnscaledLong(decimal); fail(); } catch (ArithmeticException ignored) { } } private static void assertMultiplyOverflows(Slice left, Slice right) { try { multiply(left, right); fail(); } catch (ArithmeticException ignored) { } } private static void assertRescaleOverflows(Slice decimal, int rescaleFactor) { try { rescale(decimal, rescaleFactor); fail(); } catch (ArithmeticException ignored) { } } private static void assertCompare(Slice left, Slice right, int expectedResult) { assertEquals(compare(left, right), expectedResult); assertEquals(compare(left.getLong(0), left.getLong(SIZE_OF_LONG), right.getLong(0), right.getLong(SIZE_OF_LONG)), expectedResult); } private static void assertConvertsUnscaledBigIntegerToDecimal(BigInteger value) { assertEquals(unscaledDecimalToBigInteger(unscaledDecimal(value)), value); } private static void assertConvertsUnscaledLongToDecimal(long value) { assertEquals(unscaledDecimalToUnscaledLong(unscaledDecimal(value)), value); assertEquals(unscaledDecimal(value), unscaledDecimal(BigInteger.valueOf(value))); } private static void assertShiftRight(Slice decimal, int rightShifts, boolean roundUp, Slice expectedResult) { Slice result = unscaledDecimal(); shiftRight(decimal, rightShifts, roundUp, result); assertEquals(result, expectedResult); } private static void assertDivideAllSigns(int[] dividend, int[] divisor) { assertDivideAllSigns(Slices.wrappedIntArray(dividend), 0, Slices.wrappedIntArray(divisor), 0); } private void assertShiftRightArray8Overflow(BigInteger value, int rightShifts) { try { assertShiftRightArray8(value, rightShifts); fail(); } catch (ArithmeticException ignored) { } } private void assertShiftRightArray8(BigInteger value, int rightShifts) { assertShiftRightArray8(value, rightShifts, false); } private void assertShiftRightArray8(BigInteger value, int rightShifts, boolean roundUp) { BigInteger expectedResult = value.shiftRight(rightShifts); if (roundUp) { expectedResult = expectedResult.add(BigInteger.ONE); } int[] ints = toInt8Array(value); Slice result = unscaledDecimal(); shiftRightArray8(ints, rightShifts, result); assertEquals(decodeUnscaledValue(result), expectedResult); } private void assertShiftLeftOverflow(BigInteger value, int leftShifts) { try { assertShiftLeft(value, leftShifts); fail(); } catch (ArithmeticException ignored) { } } private void assertShiftLeft(BigInteger value, int leftShifts) { Slice decimal = unscaledDecimal(value); BigInteger expectedResult = value.multiply(TWO.pow(leftShifts)); shiftLeftDestructive(decimal, leftShifts); assertEquals(decodeUnscaledValue(decimal), expectedResult); } private static void assertDivideAllSigns(String dividend, String divisor) { assertDivideAllSigns(dividend, 0, divisor, 0); } private static void assertDivideAllSigns(String dividend, int dividendRescaleFactor, String divisor, int divisorRescaleFactor) { assertDivideAllSigns(unscaledDecimal(dividend), dividendRescaleFactor, unscaledDecimal(divisor), divisorRescaleFactor); } private static void assertDivideAllSigns(Slice dividend, int dividendRescaleFactor, Slice divisor, int divisorRescaleFactor) { assertDivide(dividend, dividendRescaleFactor, divisor, divisorRescaleFactor); assertDivide(dividend, dividendRescaleFactor, negate(divisor), divisorRescaleFactor); assertDivide(negate(dividend), dividendRescaleFactor, divisor, divisorRescaleFactor); assertDivide(negate(dividend), dividendRescaleFactor, negate(divisor), divisorRescaleFactor); } private static void assertDivide(Slice dividend, int dividendRescaleFactor, Slice divisor, int divisorRescaleFactor) { BigInteger dividendBigInteger = decodeUnscaledValue(dividend); BigInteger divisorBigInteger = decodeUnscaledValue(divisor); BigInteger rescaledDividend = dividendBigInteger.multiply(bigIntegerTenToNth(dividendRescaleFactor)); BigInteger rescaledDivisor = divisorBigInteger.multiply(bigIntegerTenToNth(divisorRescaleFactor)); BigInteger[] expectedQuotientAndRemainder = rescaledDividend.divideAndRemainder(rescaledDivisor); BigInteger expectedQuotient = expectedQuotientAndRemainder[0]; BigInteger expectedRemainder = expectedQuotientAndRemainder[1]; boolean overflowIsExpected = expectedQuotient.abs().compareTo(bigIntegerTenToNth(38)) >= 0 || expectedRemainder.abs().compareTo(bigIntegerTenToNth(38)) >= 0; Slice quotient = unscaledDecimal(); Slice remainder = unscaledDecimal(); try { divide(dividend, dividendRescaleFactor, divisor, divisorRescaleFactor, quotient, remainder); if (overflowIsExpected) { fail("overflow is expected"); } } catch (ArithmeticException e) { if (!overflowIsExpected) { fail("overflow wasn't expected"); } else { return; } } BigInteger actualQuotient = decodeUnscaledValue(quotient); BigInteger actualRemainder = decodeUnscaledValue(remainder); if (expectedQuotient.equals(actualQuotient) && expectedRemainder.equals(actualRemainder)) { return; } fail(format("%s / %s ([%s * 2^%d] / [%s * 2^%d]) Expected: %s(%s). Actual: %s(%s)", rescaledDividend, rescaledDivisor, dividendBigInteger, dividendRescaleFactor, divisorBigInteger, divisorRescaleFactor, expectedQuotient, expectedRemainder, actualQuotient, actualRemainder)); } private static Slice negate(Slice slice) { Slice copy = unscaledDecimal(slice); UnscaledDecimal128Arithmetic.negate(copy); return copy; } private static int[] toInt8Array(BigInteger value) { byte[] bigIntegerBytes = value.toByteArray(); Collections.reverse(Bytes.asList(bigIntegerBytes)); byte[] bytes = new byte[8 * 4 + 1]; System.arraycopy(bigIntegerBytes, 0, bytes, 0, bigIntegerBytes.length); return toInt8Array(bytes); } private static int[] toInt8Array(byte[] bytes) { Slice slice = Slices.wrappedBuffer(bytes); int[] ints = new int[8]; for (int i = 0; i < ints.length; i++) { ints[i] = slice.getInt(i * Integer.SIZE / Byte.SIZE); } return ints; } private static BigInteger toBigInteger(int[] data) { byte[] array = new byte[data.length * 4]; ByteBuffer byteBuffer = ByteBuffer.wrap(array); byteBuffer.order(ByteOrder.LITTLE_ENDIAN); IntBuffer intBuffer = byteBuffer.asIntBuffer(); intBuffer.put(data); Collections.reverse(Bytes.asList(array)); array[0] &= ~(1 << 7); return new BigInteger((array[0] & (1 << 7)) > 0 ? -1 : 1, array); } }