diff --git common/src/test/org/apache/hadoop/hive/common/type/TestDecimalV2.java common/src/test/org/apache/hadoop/hive/common/type/TestDecimalV2.java new file mode 100644 index 0000000..a9df787 --- /dev/null +++ common/src/test/org/apache/hadoop/hive/common/type/TestDecimalV2.java @@ -0,0 +1,128 @@ +package org.apache.hadoop.hive.common.type; + +import junit.framework.Assert; +import org.junit.Test; + +public class TestDecimalV2 { + @Test + public void testToString() { + // Positive + Assert.assertEquals("1", new DecimalV2("1", 10, 2).toString()); + Assert.assertEquals("1", new DecimalV2("1", 2, 0).toString()); + Assert.assertEquals("1.23", new DecimalV2("1.23", 10, 2).toString()); + + // Negative + Assert.assertEquals("-1.23", new DecimalV2("-1.23", 10, 2).toString()); + + // Between 0 and 1 + Assert.assertEquals("0.5", new DecimalV2("0.50", 10, 2).toString()); + Assert.assertEquals("0.005", new DecimalV2("0.0050", 10, 3).toString()); + + // Cut scale + Assert.assertEquals("1.23", new DecimalV2("1.234", 10, 2).toString()); + + // Very long positive + Assert.assertEquals("9999999999999999999999999999.9999999999", + new DecimalV2("9999999999999999999999999999.9999999999", 38, 10).toString()); + + // Very long negative + Assert.assertEquals("-9999999999999999999999999999.9999999999", + new DecimalV2("-9999999999999999999999999999.9999999999", 38, 10).toString()); + } + + @Test + public void testAdd() { + // Positive + positive + DecimalV2 result = new DecimalV2(); + result.add(new DecimalV2("1.2", 10, 2), new DecimalV2("1.23", 10, 2)); + Assert.assertEquals(new DecimalV2("2.43", 10, 2), result); + + // Negative + positive = positive + result.add(new DecimalV2("-1.2", 10, 2), new DecimalV2("1.23", 10, 2)); + Assert.assertEquals(new DecimalV2("0.03", 10, 2), result); + + // Negative + positive = zero + result.add(new DecimalV2("-1.23", 10, 2), new DecimalV2("1.23", 10, 2)); + Assert.assertEquals(new DecimalV2("0.00", 10, 2), result); + + // Negative + positive = negative + result.add(new DecimalV2("-1.23", 10, 2), new DecimalV2("1.2", 10, 2)); + Assert.assertEquals(new DecimalV2("-0.03", 10, 2), result); + + // Different precision and scale + result.add(new DecimalV2("1.2", 11, 3), new DecimalV2("1.2", 10, 2)); + Assert.assertEquals(new DecimalV2("2.4", 11, 3), result); + + result.add(new DecimalV2("1.2", 10, 2), new DecimalV2("1.2", 11, 3)); + Assert.assertEquals(new DecimalV2("2.4", 11, 3), result); + } + + @Test + public void testSubtract() { + // Positive - negative + DecimalV2 result = new DecimalV2(); + result.subtract(new DecimalV2("1.2", 10, 2), new DecimalV2("-1.23", 10, 2)); + Assert.assertEquals(new DecimalV2("2.43", 10, 2), result); + + // Positive - negative = positive + result.subtract(new DecimalV2("1.23", 10, 2), new DecimalV2("1.2", 10, 2)); + Assert.assertEquals(new DecimalV2("0.03", 10, 2), result); + + // Positive - negative = zero + result.subtract(new DecimalV2("1.23", 10, 2), new DecimalV2("1.23", 10, 2)); + Assert.assertEquals(new DecimalV2("0.00", 10, 2), result); + + // Positive - negative = negative + result.subtract(new DecimalV2("1.2", 10, 2), new DecimalV2("1.23", 10, 2)); + Assert.assertEquals(new DecimalV2("-0.03", 10, 2), result); + + // Different precision and scale + result.subtract(new DecimalV2("2.76", 11, 3), new DecimalV2("2.76", 10, 2)); + Assert.assertEquals(new DecimalV2("0", 11, 3), result); + + result.subtract(new DecimalV2("2.76", 10, 2), new DecimalV2("2.76", 11, 3)); + Assert.assertEquals(new DecimalV2("0", 11, 3), result); + } + + @Test + public void testMultiply() { + DecimalV2 result = new DecimalV2(); + result.multiply(new DecimalV2("1.2", 10, 2), new DecimalV2("2.3", 10, 2)); + Assert.assertEquals(new DecimalV2("2.76", 10, 2), result); + + result.multiply(new DecimalV2("1.2", 10, 2), new DecimalV2("0", 10, 2)); + Assert.assertEquals(new DecimalV2("0", 10, 2), result); + + result.multiply(new DecimalV2("1.2", 10, 2), new DecimalV2("-2.3", 10, 2)); + Assert.assertEquals(new DecimalV2("-2.76", 10, 2), result); + + result.multiply(new DecimalV2("-1.2", 10, 2), new DecimalV2("2.3", 10, 2)); + Assert.assertEquals(new DecimalV2("-2.76", 10, 2), result); + + result.multiply(new DecimalV2("-1.2", 10, 2), new DecimalV2("-2.3", 10, 2)); + Assert.assertEquals(new DecimalV2("2.76", 10, 2), result); + } + + @Test + public void testDivide() { + DecimalV2 result = new DecimalV2(); + result.divide(new DecimalV2("1", 2, 0), new DecimalV2("3", 2, 0)); + Assert.assertEquals(new DecimalV2("0.333333", 10, 6), result); + + result.divide(new DecimalV2("1", 2, 0), new DecimalV2("10", 2, 0)); + Assert.assertEquals(new DecimalV2("0.1", 10, 6), result); + + result.divide(new DecimalV2("10", 2, 0), new DecimalV2("1", 2, 0)); + Assert.assertEquals(new DecimalV2("10", 10, 6), result); + Assert.assertEquals(new Int128V2("10000000"), result.getUnscaled()); + Assert.assertEquals(8, result.getPrecision()); + Assert.assertEquals(6, result.getScale()); + } + + @Test + public void testRemainder() { + DecimalV2 result = new DecimalV2(); + result.remainder(new DecimalV2("4", 10, 2), new DecimalV2("3", 10, 2)); + Assert.assertEquals(new DecimalV2("1", 10, 2), result); + } +} diff --git common/src/test/org/apache/hadoop/hive/common/type/TestHiveDecimalV2.java common/src/test/org/apache/hadoop/hive/common/type/TestHiveDecimalV2.java new file mode 100644 index 0000000..cf30570 --- /dev/null +++ common/src/test/org/apache/hadoop/hive/common/type/TestHiveDecimalV2.java @@ -0,0 +1,203 @@ +package org.apache.hadoop.hive.common.type; + +import com.google.code.tempusfugit.concurrency.ConcurrentRule; +import com.google.code.tempusfugit.concurrency.RepeatingRule; +import com.google.code.tempusfugit.concurrency.annotations.Concurrent; +import com.google.code.tempusfugit.concurrency.annotations.Repeating; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; + +import java.math.BigDecimal; +import java.math.BigInteger; + +/** + * Check whether HiveDecimalV2 works just same as HiveDecimalV2. + */ +public class TestHiveDecimalV2 { + @Rule public ConcurrentRule concurrentRule = new ConcurrentRule(); + @Rule public RepeatingRule repeatingRule = new RepeatingRule(); + + @Test + @Concurrent(count=4) + @Repeating(repetition=100) + public void testPrecisionScaleEnforcement() { + String decStr = "1786135888657847525803324040144343378.09799306448796128931113691624"; + HiveDecimalV2 dec = HiveDecimalV2.create(decStr); + Assert.assertEquals("1786135888657847525803324040144343378.1", dec.toString()); + Assert.assertTrue("Decimal precision should not go above maximum", + dec.precision() <= HiveDecimal.MAX_PRECISION); + Assert.assertTrue("Decimal scale should not go above maximum", dec.scale() <= HiveDecimal.MAX_SCALE); + + decStr = "57847525803324040144343378.09799306448796128931113691624"; + HiveDecimalV2 bd = HiveDecimalV2.create(decStr); + HiveDecimalV2 bd1 = HiveDecimalV2.enforcePrecisionScale(bd, 20, 5); + Assert.assertNull(bd1); + bd1 = HiveDecimalV2.enforcePrecisionScale(bd, 35, 5); + Assert.assertEquals("57847525803324040144343378.09799", bd1.toString()); + bd1 = HiveDecimalV2.enforcePrecisionScale(bd, 45, 20); + Assert.assertNull(bd1); + + dec = HiveDecimalV2.create(new BigDecimal(decStr), false); + Assert.assertNull(dec); + + dec = HiveDecimalV2.create("-1786135888657847525803324040144343378.09799306448796128931113691624"); + Assert.assertEquals("-1786135888657847525803324040144343378.1", dec.toString()); + + dec = HiveDecimalV2.create("005.34000"); + Assert.assertEquals(dec.precision(), 3); + Assert.assertEquals(dec.scale(), 2); + + dec = HiveDecimalV2.create("178613588865784752580332404014434337809799306448796128931113691624"); + Assert.assertNull(dec); + + // Rounding numbers that increase int digits + Assert.assertEquals("10", + HiveDecimalV2.enforcePrecisionScale(HiveDecimalV2.create("9.5"), 2, 0).toString()); + Assert.assertNull(HiveDecimalV2.enforcePrecisionScale(HiveDecimalV2.create("9.5"), 1, 0)); + Assert.assertEquals("9", + HiveDecimalV2.enforcePrecisionScale(HiveDecimalV2.create("9.4"), 1, 0).toString()); + } + + @Test + @Concurrent(count=4) + @Repeating(repetition=100) + public void testTrailingZeroRemovalAfterEnforcement() { + String decStr = "8.090000000000000000000000000000000000000123456"; + HiveDecimalV2 dec = HiveDecimalV2.create(decStr); + Assert.assertEquals("8.09", dec.toString()); + } + + @Test + @Concurrent(count=4) + @Repeating(repetition=100) + public void testMultiply() { + HiveDecimalV2 dec1 = HiveDecimalV2.create("0.00001786135888657847525803"); + HiveDecimalV2 dec2 = HiveDecimalV2.create("3.0000123456789"); + Assert.assertNull(dec1.multiply(dec2)); + + dec1 = HiveDecimalV2.create("178613588865784752580323232232323444.4"); + dec2 = HiveDecimalV2.create("178613588865784752580302323232.3"); + Assert.assertNull(dec1.multiply(dec2)); + + dec1 = HiveDecimalV2.create("47.324"); + dec2 = HiveDecimalV2.create("9232.309"); + Assert.assertEquals("436909.791116", dec1.multiply(dec2).toString()); + + dec1 = HiveDecimalV2.create("3.140"); + dec2 = HiveDecimalV2.create("1.00"); + Assert.assertEquals("3.14", dec1.multiply(dec2).toString()); + + dec1 = HiveDecimalV2.create("43.010"); + dec2 = HiveDecimalV2.create("2"); + Assert.assertEquals("86.02", dec1.multiply(dec2).toString()); + } + + @Test + @Concurrent(count=4) + @Repeating(repetition=100) + public void testPow() { + HiveDecimalV2 dec = HiveDecimalV2.create("3.00001415926"); + Assert.assertEquals(dec.pow(2), dec.multiply(dec)); + + HiveDecimalV2 dec1 = HiveDecimalV2.create("0.000017861358882"); + dec1 = dec1.pow(3); + Assert.assertNull(dec1); + + dec1 = HiveDecimalV2.create("3.140"); + Assert.assertEquals("9.8596", dec1.pow(2).toString()); + } + + @Test + @Concurrent(count=4) + @Repeating(repetition=100) + public void testDivide() { + HiveDecimalV2 dec1 = HiveDecimalV2.create("3.14"); + HiveDecimalV2 dec2 = HiveDecimalV2.create("3"); + Assert.assertNotNull(dec1.divide(dec2)); + + dec1 = HiveDecimalV2.create("15"); + dec2 = HiveDecimalV2.create("5"); + Assert.assertEquals("3", dec1.divide(dec2).toString()); + + dec1 = HiveDecimalV2.create("3.140"); + dec2 = HiveDecimalV2.create("1.00"); + Assert.assertEquals("3.14", dec1.divide(dec2).toString()); + } + + @Test + @Concurrent(count=4) + @Repeating(repetition=100) + public void testPlus() { + HiveDecimalV2 dec1 = HiveDecimalV2.create("99999999999999999999999999999999999"); + HiveDecimalV2 dec2 = HiveDecimalV2.create("1"); + Assert.assertNotNull(dec1.add(dec2)); + + dec1 = HiveDecimalV2.create("3.140"); + dec2 = HiveDecimalV2.create("1.00"); + Assert.assertEquals("4.14", dec1.add(dec2).toString()); + } + + + @Test + @Concurrent(count=4) + @Repeating(repetition=100) + public void testSubtract() { + HiveDecimalV2 dec1 = HiveDecimalV2.create("3.140"); + HiveDecimalV2 dec2 = HiveDecimalV2.create("1.00"); + Assert.assertEquals("2.14", dec1.subtract(dec2).toString()); + } + + @Test + @Concurrent(count=4) + @Repeating(repetition=100) + public void testPosMod() { + HiveDecimalV2 hd1 = HiveDecimalV2.create("-100.91"); + HiveDecimalV2 hd2 = HiveDecimalV2.create("9.8"); + HiveDecimalV2 dec = hd1.remainder(hd2).add(hd2).remainder(hd2); + Assert.assertEquals("6.89", dec.toString()); + } + + @Test + @Concurrent(count=4) + @Repeating(repetition=100) + public void testHashCode() { + Assert.assertEquals(HiveDecimalV2.create("9").hashCode(), HiveDecimalV2.create("9.00").hashCode()); + Assert.assertEquals(HiveDecimalV2.create("0").hashCode(), HiveDecimalV2.create("0.00").hashCode()); + } + + @Test + @Concurrent(count=4) + @Repeating(repetition=100) + public void testException() { + HiveDecimalV2 dec = HiveDecimalV2.create("3.1415.926"); + Assert.assertNull(dec); + dec = HiveDecimalV2.create("3abc43"); + Assert.assertNull(dec); + } + + @Test + @Concurrent(count=4) + @Repeating(repetition=100) + public void testBinaryConversion() { + testBinaryConversion("0.00"); + testBinaryConversion("-12.25"); + testBinaryConversion("234.79"); + } + + private void testBinaryConversion(String num) { + HiveDecimalV2 dec = HiveDecimalV2.create(num); + int scale = 2; + byte[] d = dec.setScale(2).unscaledValue().toByteArray(); + Assert.assertEquals(dec, HiveDecimalV2.create(new BigInteger(d), scale)); + int prec = 5; + int len = (int) + Math.ceil((Math.log(Math.pow(10, prec) - 1) / Math.log(2) + 1) / 8); + byte[] res = new byte[len]; + if ( dec.signum() == -1) + for (int i = 0; i < len; i++) + res[i] |= 0xFF; + System.arraycopy(d, 0, res, len-d.length, d.length); // Padding leading zeros. + Assert.assertEquals(dec, HiveDecimalV2.create(new BigInteger(res), scale)); + } +} diff --git common/src/test/org/apache/hadoop/hive/common/type/TestInt128V2.java common/src/test/org/apache/hadoop/hive/common/type/TestInt128V2.java new file mode 100644 index 0000000..99fe90e --- /dev/null +++ common/src/test/org/apache/hadoop/hive/common/type/TestInt128V2.java @@ -0,0 +1,243 @@ +package org.apache.hadoop.hive.common.type; + +import junit.framework.Assert; +import org.junit.Test; + +public class TestInt128V2 { + @Test + public void testAdd() { + // UnsignedInt128 + int + Int128V2 left = new Int128V2("1"); + Int128V2 result = new Int128V2(); + result.add(left, 2); + Assert.assertEquals(new Int128V2("3"), result); + + // UnsignedInt128 + UnsignedInt128 + left = new Int128V2("1"); + result.add(left, new Int128V2("2")); + Assert.assertEquals(new Int128V2("3"), result); + + // Carry + left = new Int128V2("9"); + result.add(left, 1); + Assert.assertEquals(new Int128V2("10"), result); + + // Very large + left = new Int128V2("99999999999999999999999999999999999998"); + result.add(left, 1); + Assert.assertEquals(new Int128V2("99999999999999999999999999999999999999"), result); + + // Very large carry + left = new Int128V2("89999999999999999999999999999999999999"); + result.add(left, 1); + Assert.assertEquals(new Int128V2("90000000000000000000000000000000000000"), result); + + // Overflow + try { + left = new Int128V2("99999999999999999999999999999999999999"); + result.add(left, new Int128V2("99999999999999999999999999999999999999")); + Assert.fail("Should not reach here"); + } catch (IllegalArgumentException ex) { + // Do nothing + } + + // -1 + 2 = 1 + left = new Int128V2("-1"); + result.add(left, new Int128V2("2")); + Assert.assertEquals(new Int128V2("1"), result); + + // -2 + 1 = -1 + left = new Int128V2("-2"); + result.add(left, new Int128V2("1")); + Assert.assertEquals(new Int128V2("-1"), result); + + // 1 + -2 = -1 + left = new Int128V2("1"); + result.add(left, new Int128V2("-2")); + Assert.assertEquals(new Int128V2("-1"), result); + + // 2 + -1 = 1 + left = new Int128V2("2"); + result.add(left, new Int128V2("-1")); + Assert.assertEquals(new Int128V2("1"), result); + } + + @Test + public void testSubtract() { + // UnsignedInt128 - UnsignedInt128 + Int128V2 left = new Int128V2("3"); + Int128V2 result = new Int128V2(); + result.subtract(left, new Int128V2("2")); + Assert.assertEquals(new Int128V2("1"), result); + + // Borrow + left = new Int128V2("10"); + result.subtract(left, new Int128V2("1")); + Assert.assertEquals(new Int128V2("9"), result); + + // Very large + left = new Int128V2("99999999999999999999999999999999999999"); + result.subtract(left, new Int128V2("1")); + Assert.assertEquals(new Int128V2("99999999999999999999999999999999999998"), result); + + // Very large borrow + left = new Int128V2("90000000000000000000000000000000000000"); + result.subtract(left, new Int128V2("1")); + Assert.assertEquals(new Int128V2("89999999999999999999999999999999999999"), result); + + // 1 - 2 = -1 + left = new Int128V2("1"); + result.subtract(left, new Int128V2("2")); + Assert.assertEquals(new Int128V2("-1"), result); + + // 2 - 1 = 1 + left = new Int128V2("2"); + result.subtract(left, new Int128V2("1")); + Assert.assertEquals(new Int128V2("1"), result); + + // -1 - -2 = 1 + left = new Int128V2("-1"); + result.subtract(left, new Int128V2("-2")); + Assert.assertEquals(new Int128V2("1"), result); + + // -2 - -1 = -1 + left = new Int128V2("-2"); + result.subtract(left, new Int128V2("-1")); + Assert.assertEquals(new Int128V2("-1"), result); + } + + @Test + public void testNegate() { + Int128V2 left = new Int128V2("0"); + Int128V2 result = new Int128V2(); + result.negate(left); + Assert.assertEquals(new Int128V2("0"), result); + + left = new Int128V2("1"); + result.negate(left); + Assert.assertEquals(new Int128V2("-1"), result); + + left = new Int128V2("-1"); + result.negate(left); + Assert.assertEquals(new Int128V2("1"), result); + } + + @Test + public void testToString() { + // Smallest + Assert.assertEquals("0", new Int128V2("0").toString()); + + // Simple + Assert.assertEquals("12345", new Int128V2("12345").toString()); + + // Various + Assert.assertEquals("12345678901234567890123456789012345678", + new Int128V2("12345678901234567890123456789012345678").toString()); + + // Largest + Assert.assertEquals("99999999999999999999999999999999999999", + new Int128V2("99999999999999999999999999999999999999").toString()); + + // Negative simple + Assert.assertEquals("-12345", new Int128V2("-12345").toString()); + + // Negative various + Assert.assertEquals("-12345678901234567890123456789012345678", + new Int128V2("-12345678901234567890123456789012345678").toString()); + + // Negative largest + Assert.assertEquals("-99999999999999999999999999999999999999", + new Int128V2("-99999999999999999999999999999999999999").toString()); + } + + @Test + public void testDivideByTen() { + Int128V2 left = new Int128V2("1"); + Int128V2 result = new Int128V2(); + Int128V2 remainder = new Int128V2(); + result.divideByTen(left, remainder); + Assert.assertEquals(new Int128V2("0"), result); + Assert.assertEquals(1, remainder.i4); + + left = new Int128V2("10"); + result.divideByTen(left, remainder); + Assert.assertEquals(new Int128V2("1"), result); + Assert.assertEquals(0, remainder.i4); + + left = new Int128V2("20"); + result.divideByTen(left, remainder); + Assert.assertEquals(new Int128V2("2"), result); + Assert.assertEquals(0, remainder.i4); + + left = new Int128V2("12"); + result.divideByTen(left, remainder); + Assert.assertEquals(new Int128V2("1"), result); + Assert.assertEquals(2, remainder.i4); + + left = new Int128V2("99999999999999999999999999999999999999"); + result.divideByTen(left, remainder); + Assert.assertEquals(new Int128V2("9999999999999999999999999999999999999"), result); + Assert.assertEquals(9, remainder.i4); + } + + @Test + public void testMultiply() { + Int128V2 left = new Int128V2("3"); + Int128V2 result = new Int128V2(); + result.multiply(left, new Int128V2("5")); + Assert.assertEquals(new Int128V2("15"), result); + + left = new Int128V2("9"); + result.multiply(left, new Int128V2("9")); + Assert.assertEquals(new Int128V2("81"), result); + + left = new Int128V2("0"); + result.multiply(left, new Int128V2("0")); + Assert.assertEquals(new Int128V2("0"), result); + } + + @Test + public void testDivide() { + Int128V2 left = new Int128V2("16"); + Int128V2 result = new Int128V2(); + Int128V2 remainder = new Int128V2(); + result.divide(left, new Int128V2("5"), remainder); + Assert.assertEquals(new Int128V2("3"), result); + Assert.assertEquals(new Int128V2("1"), remainder); + + left = new Int128V2("83"); + result.divide(left, new Int128V2("9"), remainder); + Assert.assertEquals(new Int128V2("9"), result); + Assert.assertEquals(new Int128V2("2"), remainder); + + left = new Int128V2("7"); + result.divide(left, new Int128V2("7"), remainder); + Assert.assertEquals(new Int128V2("1"), result); + Assert.assertEquals(new Int128V2("0"), remainder); + + left = new Int128V2("0"); + result.divide(left, new Int128V2("1"), remainder); + Assert.assertEquals(new Int128V2("0"), result); + Assert.assertEquals(new Int128V2("0"), remainder); + + left = new Int128V2("13"); + result.divide(left, new Int128V2("3"), remainder); + Assert.assertEquals(new Int128V2(String.valueOf(13 / 3)), result); + Assert.assertEquals(new Int128V2(String.valueOf(13 % 3)), remainder); + + left = new Int128V2("-13"); + result.divide(left, new Int128V2("-3"), remainder); + Assert.assertEquals(new Int128V2(String.valueOf(-13 / -3)), result); + Assert.assertEquals(new Int128V2(String.valueOf(-13 % -3)), remainder); + + left = new Int128V2("-13"); + result.divide(left, new Int128V2("3"), remainder); + Assert.assertEquals(new Int128V2(String.valueOf(-13 / 3)), result); + Assert.assertEquals(new Int128V2(String.valueOf(-13 % 3)), remainder); + + left = new Int128V2("13"); + result.divide(left, new Int128V2("-3"), remainder); + Assert.assertEquals(new Int128V2(String.valueOf(13 / -3)), result); + Assert.assertEquals(new Int128V2(String.valueOf(13 % -3)), remainder); + } +} diff --git itests/hive-jmh/src/main/java/org/apache/hive/benchmark/vectorization/AbstractExpression.java itests/hive-jmh/src/main/java/org/apache/hive/benchmark/vectorization/AbstractExpression.java index 94af3e0..8abb24f 100644 --- itests/hive-jmh/src/main/java/org/apache/hive/benchmark/vectorization/AbstractExpression.java +++ itests/hive-jmh/src/main/java/org/apache/hive/benchmark/vectorization/AbstractExpression.java @@ -13,10 +13,10 @@ */ package org.apache.hive.benchmark.vectorization; -import org.apache.hadoop.hive.ql.exec.vector.ColumnVector; -import org.apache.hadoop.hive.ql.exec.vector.DoubleColumnVector; -import org.apache.hadoop.hive.ql.exec.vector.LongColumnVector; -import org.apache.hadoop.hive.ql.exec.vector.VectorizedRowBatch; +import org.apache.hadoop.hive.common.type.DecimalV2; +import org.apache.hadoop.hive.common.type.HiveDecimal; +import org.apache.hadoop.hive.common.type.HiveDecimalV2; +import org.apache.hadoop.hive.ql.exec.vector.*; import org.apache.hadoop.hive.ql.exec.vector.expressions.VectorExpression; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; @@ -37,7 +37,9 @@ @State(Scope.Thread) @OutputTimeUnit(TimeUnit.NANOSECONDS) public abstract class AbstractExpression { - private static final int DEFAULT_ITER_TIME = 1000000; + private static final int DEFAULT_ITER_TIME = 100000; + protected static final int DECIMAL_BATCH_SIZE = 128; + protected VectorExpression expression; protected VectorizedRowBatch rowBatch; @@ -63,6 +65,32 @@ public void bench() { } } + protected DecimalColumnVector getDecimalColumnVector(int precision, int scale) { + DecimalColumnVector columnVector = new DecimalColumnVector(DECIMAL_BATCH_SIZE, precision, scale); + Random random = new Random(); + for (int i = 0; i != DECIMAL_BATCH_SIZE; i++) { + if (precision - scale >= 19) { + columnVector.set(i, HiveDecimal.create(Long.toString(random.nextLong()))); + } else { + columnVector.set(i, HiveDecimal.create(Integer.toString(random.nextInt()))); + } + } + return columnVector; + } + + protected DecimalV2ColumnVector getDecimalV2ColumnVector(int precision, int scale) { + DecimalV2ColumnVector columnVector = new DecimalV2ColumnVector(DECIMAL_BATCH_SIZE, precision, scale); + Random random = new Random(); + for (int i = 0; i != DECIMAL_BATCH_SIZE; i++) { + if (precision - scale >= 19) { + columnVector.set(new DecimalV2(Long.toString(random.nextLong())), i); + } else { + columnVector.set(new DecimalV2(Integer.toString(random.nextInt())), i); + } + } + return columnVector; + } + protected LongColumnVector getLongColumnVector() { LongColumnVector columnVector = new LongColumnVector(VectorizedRowBatch.DEFAULT_SIZE); Random random = new Random(); diff --git itests/hive-jmh/src/main/java/org/apache/hive/benchmark/vectorization/VectorizedArithmeticBench.java itests/hive-jmh/src/main/java/org/apache/hive/benchmark/vectorization/VectorizedArithmeticBench.java index b6e2fec..1cb1e74 100644 --- itests/hive-jmh/src/main/java/org/apache/hive/benchmark/vectorization/VectorizedArithmeticBench.java +++ itests/hive-jmh/src/main/java/org/apache/hive/benchmark/vectorization/VectorizedArithmeticBench.java @@ -13,12 +13,15 @@ */ package org.apache.hive.benchmark.vectorization; +import org.apache.hadoop.hive.ql.exec.vector.DecimalColumnVector; +import org.apache.hadoop.hive.ql.exec.vector.DecimalV2ColumnVector; import org.apache.hadoop.hive.ql.exec.vector.DoubleColumnVector; import org.apache.hadoop.hive.ql.exec.vector.LongColumnVector; +import org.apache.hadoop.hive.ql.exec.vector.expressions.DecimalV2ColAddDecimalColumn; +import org.apache.hadoop.hive.ql.exec.vector.expressions.DecimalV2ColDivideDecimalColumn; +import org.apache.hadoop.hive.ql.exec.vector.expressions.DecimalV2ColMultiplyDecimalColumn; import org.apache.hadoop.hive.ql.exec.vector.expressions.LongColDivideLongColumn; -import org.apache.hadoop.hive.ql.exec.vector.expressions.gen.DoubleColAddDoubleColumn; -import org.apache.hadoop.hive.ql.exec.vector.expressions.gen.DoubleColDivideDoubleColumn; -import org.apache.hadoop.hive.ql.exec.vector.expressions.gen.LongColAddLongColumn; +import org.apache.hadoop.hive.ql.exec.vector.expressions.gen.*; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.runner.Runner; @@ -104,6 +107,70 @@ public void setup() { } } + public static class DecimalColAddDecimalColColumnBench extends AbstractExpression { + @Override + public void setup() { + int precision = 30; + int scale = 2; + rowBatch = buildRowBatch(new DecimalColumnVector(DECIMAL_BATCH_SIZE, precision, scale), 2, + getDecimalColumnVector(precision, scale), getDecimalColumnVector(precision, scale)); + rowBatch.size = DECIMAL_BATCH_SIZE; + expression = new DecimalColAddDecimalColumn(0, 1, 2); + } + } + + public static class DecimalV2ColAddDecimalColColumnBench extends AbstractExpression { + @Override + public void setup() { + int precision = 30; + int scale = 2; + rowBatch = buildRowBatch(new DecimalV2ColumnVector(DECIMAL_BATCH_SIZE, precision, scale), 2, + getDecimalV2ColumnVector(precision, scale), getDecimalV2ColumnVector(precision, scale)); + rowBatch.size = DECIMAL_BATCH_SIZE; + expression = new DecimalV2ColAddDecimalColumn(0, 1, 2); + } + } + + public static class DecimalColDivideDecimalColColumnBench extends AbstractExpression { + @Override + public void setup() { + rowBatch = buildRowBatch(new DecimalColumnVector(DECIMAL_BATCH_SIZE, 10, 2), 2, + getDecimalColumnVector(30, 2), getDecimalColumnVector(30, 2)); + rowBatch.size = DECIMAL_BATCH_SIZE; + expression = new DecimalColDivideDecimalColumn(0, 1, 2); + } + } + + public static class DecimalV2ColDivideDecimalColColumnBench extends AbstractExpression { + @Override + public void setup() { + rowBatch = buildRowBatch(new DecimalV2ColumnVector(DECIMAL_BATCH_SIZE, 10, 2), 2, + getDecimalV2ColumnVector(30, 2), getDecimalV2ColumnVector(30, 2)); + rowBatch.size = DECIMAL_BATCH_SIZE; + expression = new DecimalV2ColDivideDecimalColumn(0, 1, 2); + } + } + + public static class DecimalColMultiplyDecimalColColumnBench extends AbstractExpression { + @Override + public void setup() { + rowBatch = buildRowBatch(new DecimalColumnVector(DECIMAL_BATCH_SIZE, 22, 0), 2, + getDecimalColumnVector(10, 0), getDecimalColumnVector(10, 0)); + rowBatch.size = DECIMAL_BATCH_SIZE; + expression = new DecimalColMultiplyDecimalColumn(0, 1, 2); + } + } + + public static class DecimalV2ColMultiplyDecimalColColumnBench extends AbstractExpression { + @Override + public void setup() { + rowBatch = buildRowBatch(new DecimalV2ColumnVector(DECIMAL_BATCH_SIZE, 22, 0), 2, + getDecimalV2ColumnVector(10, 0), getDecimalV2ColumnVector(10, 0)); + rowBatch.size = DECIMAL_BATCH_SIZE; + expression = new DecimalV2ColMultiplyDecimalColumn(0, 1, 2); + } + } + public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder().include(".*" + VectorizedArithmeticBench.class.getSimpleName() + ".*").build(); diff --git ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ColAddDecimalColumn.java ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ColAddDecimalColumn.java new file mode 100644 index 0000000..a2220fa --- /dev/null +++ ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ColAddDecimalColumn.java @@ -0,0 +1,78 @@ +package org.apache.hadoop.hive.ql.exec.vector.expressions; + +import org.apache.hadoop.hive.ql.exec.vector.DecimalV2ColumnVector; +import org.apache.hadoop.hive.ql.exec.vector.VectorExpressionDescriptor; +import org.apache.hadoop.hive.ql.exec.vector.VectorizedRowBatch; +import org.apache.hadoop.hive.serde2.io.HiveDecimalWritable; + +public class DecimalV2ColAddDecimalColumn extends VectorExpression { + + private static final long serialVersionUID = 1L; + + private int colNum1; + private int colNum2; + private int outputColumn; + + public DecimalV2ColAddDecimalColumn(int colNum1, int colNum2, int outputColumn) { + this.colNum1 = colNum1; + this.colNum2 = colNum2; + this.outputColumn = outputColumn; + this.outputType = "decimal"; + } + + public DecimalV2ColAddDecimalColumn() { + this.outputType = "decimal"; + } + + @Override + public void evaluate(VectorizedRowBatch batch) { + + if (childExpressions != null) { + super.evaluateChildren(batch); + } + + DecimalV2ColumnVector inputColVector1 = (DecimalV2ColumnVector) batch.cols[colNum1]; + DecimalV2ColumnVector inputColVector2 = (DecimalV2ColumnVector) batch.cols[colNum2]; + DecimalV2ColumnVector outputColVector = (DecimalV2ColumnVector) batch.cols[outputColumn]; + outputColVector.add(inputColVector1, inputColVector2); + } + + @Override + public int getOutputColumn() { + return outputColumn; + } + + public int getColNum1() { + return colNum1; + } + + public void setColNum1(int colNum1) { + this.colNum1 = colNum1; + } + + public int getColNum2() { + return colNum2; + } + + public void setColNum2(int colNum2) { + this.colNum2 = colNum2; + } + + public void setOutputColumn(int outputColumn) { + this.outputColumn = outputColumn; + } + + @Override + public VectorExpressionDescriptor.Descriptor getDescriptor() { + return (new VectorExpressionDescriptor.Builder()) + .setMode( + VectorExpressionDescriptor.Mode.PROJECTION) + .setNumArguments(2) + .setArgumentTypes( + VectorExpressionDescriptor.ArgumentType.getType("decimal"), + VectorExpressionDescriptor.ArgumentType.getType("decimal")) + .setInputExpressionTypes( + VectorExpressionDescriptor.InputExpressionType.COLUMN, + VectorExpressionDescriptor.InputExpressionType.COLUMN).build(); + } +} diff --git ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ColDivideDecimalColumn.java ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ColDivideDecimalColumn.java new file mode 100644 index 0000000..3109d89 --- /dev/null +++ ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ColDivideDecimalColumn.java @@ -0,0 +1,78 @@ +package org.apache.hadoop.hive.ql.exec.vector.expressions; + +import org.apache.hadoop.hive.ql.exec.vector.DecimalV2ColumnVector; +import org.apache.hadoop.hive.ql.exec.vector.VectorExpressionDescriptor; +import org.apache.hadoop.hive.ql.exec.vector.VectorizedRowBatch; +import org.apache.hadoop.hive.serde2.io.HiveDecimalWritable; + +public class DecimalV2ColDivideDecimalColumn extends VectorExpression { + + private static final long serialVersionUID = 1L; + + private int colNum1; + private int colNum2; + private int outputColumn; + + public DecimalV2ColDivideDecimalColumn(int colNum1, int colNum2, int outputColumn) { + this.colNum1 = colNum1; + this.colNum2 = colNum2; + this.outputColumn = outputColumn; + this.outputType = "decimal"; + } + + public DecimalV2ColDivideDecimalColumn() { + this.outputType = "decimal"; + } + + @Override + public void evaluate(VectorizedRowBatch batch) { + + if (childExpressions != null) { + super.evaluateChildren(batch); + } + + DecimalV2ColumnVector inputColVector1 = (DecimalV2ColumnVector) batch.cols[colNum1]; + DecimalV2ColumnVector inputColVector2 = (DecimalV2ColumnVector) batch.cols[colNum2]; + DecimalV2ColumnVector outputColVector = (DecimalV2ColumnVector) batch.cols[outputColumn]; + outputColVector.divide(inputColVector1, inputColVector2); + } + + @Override + public int getOutputColumn() { + return outputColumn; + } + + public int getColNum1() { + return colNum1; + } + + public void setColNum1(int colNum1) { + this.colNum1 = colNum1; + } + + public int getColNum2() { + return colNum2; + } + + public void setColNum2(int colNum2) { + this.colNum2 = colNum2; + } + + public void setOutputColumn(int outputColumn) { + this.outputColumn = outputColumn; + } + + @Override + public VectorExpressionDescriptor.Descriptor getDescriptor() { + return (new VectorExpressionDescriptor.Builder()) + .setMode( + VectorExpressionDescriptor.Mode.PROJECTION) + .setNumArguments(2) + .setArgumentTypes( + VectorExpressionDescriptor.ArgumentType.getType("decimal"), + VectorExpressionDescriptor.ArgumentType.getType("decimal")) + .setInputExpressionTypes( + VectorExpressionDescriptor.InputExpressionType.COLUMN, + VectorExpressionDescriptor.InputExpressionType.COLUMN).build(); + } +} diff --git ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ColMultiplyDecimalColumn.java ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ColMultiplyDecimalColumn.java new file mode 100644 index 0000000..343902f --- /dev/null +++ ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ColMultiplyDecimalColumn.java @@ -0,0 +1,78 @@ +package org.apache.hadoop.hive.ql.exec.vector.expressions; + +import org.apache.hadoop.hive.ql.exec.vector.DecimalV2ColumnVector; +import org.apache.hadoop.hive.ql.exec.vector.VectorExpressionDescriptor; +import org.apache.hadoop.hive.ql.exec.vector.VectorizedRowBatch; +import org.apache.hadoop.hive.serde2.io.HiveDecimalWritable; + +public class DecimalV2ColMultiplyDecimalColumn extends VectorExpression { + + private static final long serialVersionUID = 1L; + + private int colNum1; + private int colNum2; + private int outputColumn; + + public DecimalV2ColMultiplyDecimalColumn(int colNum1, int colNum2, int outputColumn) { + this.colNum1 = colNum1; + this.colNum2 = colNum2; + this.outputColumn = outputColumn; + this.outputType = "decimal"; + } + + public DecimalV2ColMultiplyDecimalColumn() { + this.outputType = "decimal"; + } + + @Override + public void evaluate(VectorizedRowBatch batch) { + + if (childExpressions != null) { + super.evaluateChildren(batch); + } + + DecimalV2ColumnVector inputColVector1 = (DecimalV2ColumnVector) batch.cols[colNum1]; + DecimalV2ColumnVector inputColVector2 = (DecimalV2ColumnVector) batch.cols[colNum2]; + DecimalV2ColumnVector outputColVector = (DecimalV2ColumnVector) batch.cols[outputColumn]; + outputColVector.multiply(inputColVector1, inputColVector2); + } + + @Override + public int getOutputColumn() { + return outputColumn; + } + + public int getColNum1() { + return colNum1; + } + + public void setColNum1(int colNum1) { + this.colNum1 = colNum1; + } + + public int getColNum2() { + return colNum2; + } + + public void setColNum2(int colNum2) { + this.colNum2 = colNum2; + } + + public void setOutputColumn(int outputColumn) { + this.outputColumn = outputColumn; + } + + @Override + public VectorExpressionDescriptor.Descriptor getDescriptor() { + return (new VectorExpressionDescriptor.Builder()) + .setMode( + VectorExpressionDescriptor.Mode.PROJECTION) + .setNumArguments(2) + .setArgumentTypes( + VectorExpressionDescriptor.ArgumentType.getType("decimal"), + VectorExpressionDescriptor.ArgumentType.getType("decimal")) + .setInputExpressionTypes( + VectorExpressionDescriptor.InputExpressionType.COLUMN, + VectorExpressionDescriptor.InputExpressionType.COLUMN).build(); + } +} diff --git storage-api/src/java/org/apache/hadoop/hive/common/type/DecimalV2.java storage-api/src/java/org/apache/hadoop/hive/common/type/DecimalV2.java new file mode 100644 index 0000000..6a55370 --- /dev/null +++ storage-api/src/java/org/apache/hadoop/hive/common/type/DecimalV2.java @@ -0,0 +1,495 @@ +package org.apache.hadoop.hive.common.type; + +import org.apache.hadoop.hive.ql.exec.vector.DecimalV2ColumnVector; + +import java.math.BigDecimal; + +/** + * A mutable signed decimal class. It has precision and scale. + * @see DecimalV2ColumnVector + */ +public class DecimalV2 implements Comparable { + public int precision; + public int scale; + public Int128V2 unscaled; + + /* + * Constructors + */ + public DecimalV2() { + unscaled = new Int128V2(); + } + + public DecimalV2(String value, int precision, int scale) { + parse(value, true, precision, scale, true); + } + + public DecimalV2(String value, boolean allowRounding) { + parse(value, false, 0, 0, allowRounding); + } + + public DecimalV2(String value) { + this(value, true); + } + + public void parse(String value, boolean precisionScaleGiven, + int precision, int scale, boolean allowRounding) { + this.unscaled = new Int128V2(); + + // To travel among bytes + int length = value.length(); + byte[] bytes = value.getBytes(); + byte b; + int i = 0; + + // Digit ranges + int from; + int to; + int digits = 0; + int dotIndex = -1; + int digitsAfterDot = 0; + int digitsAfterRounding = 0; + + // Flags + boolean negative = false; + boolean addOne = false; + + // Phase 1. Recognize digit ranges, and flag simple variables + // Sign + if (i < length) { + b = bytes[i]; + if (b == '-') { + negative = true; + i++; + } else if (b == '+') { + i++; + } + } + + // Skip heading zeros + while (i < length && bytes[i] == '0') { + i++; + } + + // The start of meaningful digits + from = i; + + // Integer part + while (i < length && digits < HiveDecimal.MAX_PRECISION && '0' <= (b = bytes[i]) && b <= '9') { + digits++; + i++; + } + + // Dot (optional) + if (i < length && bytes[i] == '.') { + dotIndex = i; + i++; + + // If precision and scale are given + if (precisionScaleGiven) { + // Fraction (optional) + while (i < length && '0' <= (b = bytes[i]) && b <= '9' && digitsAfterDot <= scale) { + digits++; + digitsAfterDot++; + i++; + } + + if (allowRounding) { + // Rounding + if (i < length && '0' <= (b = bytes[i]) && b <= '9' && b >= '5') { + addOne = true; + } + + // Remaining digits + while (i < length && '0' <= (b = bytes[i]) && b <= '9' && digitsAfterDot <= scale) { + digitsAfterRounding++; + i++; + } + } + } + + // If precision and scale are not given + else { + // Fraction (optional) + while (i < length && digits < HiveDecimal.MAX_PRECISION && '0' <= (b = bytes[i]) && b <= '9') { + digits++; + digitsAfterDot++; + i++; + } + + if (allowRounding) { + // Rounding + if (i < length && digits == HiveDecimal.MAX_PRECISION && '0' <= (b = bytes[i]) && b <= '9' && b >= '5') { + addOne = true; + } + + // Remaining digits + while (i < length && '0' <= (b = bytes[i]) && b <= '9') { + digitsAfterRounding++; + i++; + } + } + } + // If there are remaining characters, it's in an illegal format + if (i != length) { + throw new NumberFormatException(); + } + + // Back to rounding position + i -= digitsAfterRounding; + + // Remove trailing zeros + if (i == length) { + while (i > 0 && bytes[i - 1] == '0') { + digitsAfterDot--; + i--; + } + } + } else { + // If there are remaining characters, it's in an illegal format + if (i != length) { + throw new NumberFormatException(); + } + } + + // The end of meaningful digits + to = i; + + // Phase 2. Parse digits + if (precisionScaleGiven) { + this.precision = precision; + this.scale = scale; + } + + // Parse an integer + if (dotIndex == -1) { + // Calculate precision and scale + if (!precisionScaleGiven) { + this.precision = to - from; + this.scale = 0; + } + + // Parse integer digits + for (int j = from; j < to; j++) { + unscaled.parse(bytes[j]); + } + } + + // Parse a floating point + else { + // Calculate precision and scale + if (!precisionScaleGiven) { + this.precision = to - from - 1; + this.scale = digitsAfterDot; + } + + // Parse integer digits + for (int j = from; j < dotIndex; j++) { + unscaled.parse(bytes[j]); + } + + // Parse fraction digits + for (int j = dotIndex + 1; j < to; j++) { + unscaled.parse(bytes[j]); + } + } + + // If it has less fraction digits than it should, then fill zeros + int scaleDiff = this.scale - digitsAfterDot; + if (scaleDiff > 0) { + unscaled.multiplyByTen(unscaled, scaleDiff); + } + + // If it has more fraction digits than it should, then remove them + else if (scaleDiff < 0) { + Int128V2 remainder = new Int128V2(); + for (int j = scaleDiff; j < 0; j++) { + unscaled.divideByTen(unscaled, remainder); + } + } + + // Rounding + if (addOne) { + unscaled.add(unscaled, Int128V2.ONE); + } + + // If it has a negative sign, negate it + if (negative) { + unscaled.negate(unscaled); + } + } + + public void set(DecimalV2 left) { + this.scale = left.scale; + this.precision = left.precision; + this.unscaled.set(left.unscaled); + } + + public DecimalV2 copy() { + DecimalV2 copy = new DecimalV2(); + copy.set(this); + return copy; + } + + /* + * String methods + */ + private void stringifyValue(StringBuilder builder) { + // 0 + if (unscaled.equals(Int128V2.ZERO)) { + builder.append('0'); + return; + } + + // Make it positive + StringBuilder innerBuilder = new StringBuilder(); + Int128V2 unscaledCopy = unscaled.copy(); + int signum = unscaledCopy.getSignum(); + if (signum == -1) { + unscaledCopy.negate(unscaledCopy); + } + + // Digitize from the smallest + boolean digitizedNonZero = false; + Int128V2 remainder = new Int128V2(); + for (int i = 0; i < precision; i++) { + // . + if (scale == i && digitizedNonZero) { + innerBuilder.append('.'); + } + unscaledCopy.divideByTen(unscaledCopy, remainder); + + // Remove trailing zeros + if (!(scale > 0 && i < scale && !digitizedNonZero && remainder.i4 == 0)) { + innerBuilder.append(remainder.i4); + digitizedNonZero = true; + } + + // Remove heading zeros + if (unscaledCopy.equals(Int128V2.ZERO) && i >= scale) { + // 0. + if (i < scale) { + innerBuilder.append(".0"); + } + break; + } + } + + // - + if (signum == -1) { + innerBuilder.append('-'); + } + + // Reverse the digits + innerBuilder.reverse(); + builder.append(innerBuilder); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + stringifyValue(builder); + return builder.toString(); + } + + /* + * Comparison methods + */ + @Override + public boolean equals(Object obj) { + DecimalV2 right = (DecimalV2) obj; + DecimalV2 result = new DecimalV2(); + result.subtract(this, right); + return result.isZero(); + } + + @Override + public int hashCode() { + return unscaled.hashCode() + scale; + } + + @Override + public int compareTo(DecimalV2 right) { + DecimalV2 result = new DecimalV2(); + result.subtract(this, right); + if (result.isZero()) { + return 0; + } else { + return result.getSignum(); + } + } + + public boolean isZero() { + return unscaled.equals(Int128V2.ZERO); + } + + /* + * Arithmetic methods + */ + public void add(DecimalV2 left, DecimalV2 right) { + // Precision and scale + this.precision = Math.max(left.precision, right.precision); + this.scale = Math.max(left.scale, right.scale); + int scaleDiff = left.scale - right.scale; + + // Same scale + if (scaleDiff == 0) { + this.unscaled.add(left.unscaled, right.unscaled); + } + + // Left scale is greater + else if (scaleDiff > 0) { + this.unscaled.set(right.unscaled); + this.unscaled.multiplyByTen(this.unscaled, scaleDiff); + this.unscaled.add(left.unscaled, this.unscaled); + } + + // Right scale is greater + else { + this.unscaled.set(left.unscaled); + this.unscaled.multiplyByTen(this.unscaled, -scaleDiff); + this.unscaled.add(this.unscaled, right.unscaled); + } + } + + public void subtract(DecimalV2 left, DecimalV2 right) { + // Precision and scale + this.precision = Math.max(left.precision, right.precision); + this.scale = Math.max(left.scale, right.scale); + int scaleDiff = left.scale - right.scale; + + // Same scale + if (scaleDiff == 0) { + this.unscaled.subtract(left.unscaled, right.unscaled); + } + + // Left scale is greater + else if (scaleDiff > 0) { + this.unscaled.set(right.unscaled); + this.unscaled.multiplyByTen(this.unscaled, scaleDiff); + this.unscaled.subtract(left.unscaled, this.unscaled); + } + + // Right scale is greater + else { + this.unscaled.set(left.unscaled); + this.unscaled.multiplyByTen(this.unscaled, -scaleDiff); + this.unscaled.subtract(this.unscaled, right.unscaled); + } + } + + public void multiply(DecimalV2 left, DecimalV2 right) { + this.unscaled.multiply(left.unscaled, right.unscaled); + + this.precision = left.precision + right.precision; + this.scale = left.scale + right.scale; + if (this.scale >= HiveDecimal.MAX_PRECISION) { + throw new IllegalArgumentException("Underflow"); + } + } + + // Division is hard to optimize yet + public void divide(DecimalV2 left, DecimalV2 right) { + int p1 = left.precision; + int p2 = right.precision; + int s1 = left.scale; + int s2 = right.scale; + + this.scale = Math.max(6, s1 + p2 + 1); + this.precision = p1 - s1 + s2 + this.scale; + int scaleDiff = this.scale - s1 + s2; + + this.unscaled.multiplyByTen(left.unscaled, scaleDiff); + this.unscaled.divide(this.unscaled, right.unscaled, new Int128V2()); + } + + // Remainder is hard to optimize yet + public void remainder(DecimalV2 left, DecimalV2 right) { + int scaleDiff = left.scale - right.scale; + this.scale = Math.max(left.scale, right.scale); + this.precision = Math.max(left.precision, right.precision); + + Int128V2 remainder = new Int128V2(); + this.unscaled.multiplyByTen(right.unscaled, scaleDiff); + this.unscaled.divide(left.unscaled, this.unscaled, remainder); + this.unscaled.set(remainder); + } + + /* + * Precision and scale methods + */ + public Int128V2 getUnscaled() { + return unscaled; + } + + public int getScale() { + return scale; + } + + public int getPrecision() { + return precision; + } + + public void setScale(int i) { + scale = i; + } + + /* + * Sign methods + */ + public void negate() { + unscaled.negate(unscaled); + } + + public void abs() { + unscaled.abs(unscaled); + } + + public int getSignum() { + return unscaled.getSignum(); + } + + /* + * Type conversions + */ + private Int128V2 integerPart() { + Int128V2 unscaledCopy = unscaled.copy(); + Int128V2 remainder = new Int128V2(); + for (int i = 0; i < scale; i++) { + unscaledCopy.divideByTen(unscaledCopy, remainder); + } + return unscaledCopy; + } + + public long longValue() { + Int128V2 integerPart = integerPart(); + return integerPart.i4 | (integerPart.i3 << 31) | + ((integerPart.i2 & 1L) << 62) | (integerPart.i0 & Long.MIN_VALUE); + } + + public int intValue() { + Int128V2 integerPart = integerPart(); + int value = integerPart.i4 | (integerPart.i0 & Integer.MIN_VALUE); + return value; + } + + public short shortValue() { + return (short) intValue(); + } + + public byte byteValue() { + return (byte) intValue(); + } + + public double doubleValue() { + return Double.valueOf(toString()); + } + + public float floatValue() { + return Float.valueOf(toString()); + } + + public BigDecimal bigDecimalValue() { + return new BigDecimal(toString()); + } +} diff --git storage-api/src/java/org/apache/hadoop/hive/common/type/HiveDecimalV2.java storage-api/src/java/org/apache/hadoop/hive/common/type/HiveDecimalV2.java new file mode 100644 index 0000000..ee190b4 --- /dev/null +++ storage-api/src/java/org/apache/hadoop/hive/common/type/HiveDecimalV2.java @@ -0,0 +1,285 @@ +package org.apache.hadoop.hive.common.type; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.RoundingMode; + +/** + *

{@code HiveDecimal} is a immutable representation of a decimal object.

+ */ +public class HiveDecimalV2 { + private DecimalV2 decimalV2; + + public HiveDecimalV2(String decStr, boolean allowRounding) { + decimalV2 = new DecimalV2(decStr, allowRounding); + } + + public HiveDecimalV2(String decStr) { + decimalV2 = new DecimalV2(decStr); + } + + public HiveDecimalV2(String decStr, int scale) { + decimalV2 = new DecimalV2(decStr); + decimalV2.setScale(scale); + } + + public HiveDecimalV2(DecimalV2 decimalV2) { + this.decimalV2 = decimalV2; + } + + public static HiveDecimalV2 create(String s, int i) { + return new HiveDecimalV2(s, i); + } + + public static HiveDecimalV2 create(String decStr) { + try { + return new HiveDecimalV2(decStr); + } catch (Exception e) { + return null; + } + } + + public static HiveDecimalV2 create(long l) { + try { + return new HiveDecimalV2(Long.toString(l)); + } catch (Exception e) { + return null; + } + } + + public static HiveDecimalV2 create(BigDecimal bigDecimal, boolean allowRounding) { + try { + return new HiveDecimalV2(bigDecimal.toString(), allowRounding); + } catch (Exception e) { + return null; + } + } + + public static HiveDecimalV2 create(BigDecimal bigDecimal) { + try { + return new HiveDecimalV2(bigDecimal.toString()); + } catch (Exception e) { + return null; + } + } + + public static HiveDecimalV2 create(BigInteger bigInteger, int scale) { + return new HiveDecimalV2(bigInteger.toString(), scale); + } + + /* + * Precision and scale methods + */ + public int precision() { + return decimalV2.getPrecision(); + } + + public int scale() { + return decimalV2.getScale(); + } + + public HiveDecimalV2 setScale(int adjustedScale, int rm) { + return null; + } + + public HiveDecimalV2 setScale(int i) { + decimalV2.setScale(i); + return this; + } + + public BigInteger unscaledValue() { + return new BigInteger(decimalV2.getUnscaled().toString()); + } + + /* + * Type conversion methods + */ + public long longValue() { + return decimalV2.longValue(); + } + + public int intValue() { + return decimalV2.intValue(); + } + + public short shortValue() { + return decimalV2.shortValue(); + } + + public byte byteValue() { + return decimalV2.byteValue(); + } + + public double doubleValue() { + return decimalV2.doubleValue(); + } + + public float floatValue() { + return decimalV2.floatValue(); + } + + public BigDecimal bigDecimalValue() { + return decimalV2.bigDecimalValue(); + } + + /* + * Arithmetic methods + */ + public HiveDecimalV2 add(HiveDecimalV2 dec) { + DecimalV2 right = dec.decimalV2; + DecimalV2 result = new DecimalV2(); + result.add(decimalV2, right); + return new HiveDecimalV2(result); + } + + public HiveDecimalV2 subtract(HiveDecimalV2 dec) { + DecimalV2 right = dec.decimalV2; + DecimalV2 result = new DecimalV2(); + result.subtract(decimalV2, right); + return new HiveDecimalV2(result); + } + + public HiveDecimalV2 pow(int n) { + DecimalV2 result = new DecimalV2(); + result.set(decimalV2); + try { + for (int i = 1; i < n; i++) { + result.multiply(result, decimalV2); + } + return new HiveDecimalV2(result); + } catch (Exception e) { + return null; + } + } + + public HiveDecimalV2 remainder(HiveDecimalV2 dec) { + DecimalV2 right = dec.decimalV2; + DecimalV2 result = new DecimalV2(); + result.remainder(decimalV2, right); + return new HiveDecimalV2(result); + } + + public HiveDecimalV2 divide(HiveDecimalV2 dec) { + DecimalV2 right = dec.decimalV2; + DecimalV2 result = new DecimalV2(); + result.divide(decimalV2, right); + return new HiveDecimalV2(result); + } + + public HiveDecimalV2 multiply(HiveDecimalV2 dec) { + DecimalV2 right = dec.decimalV2; + DecimalV2 result = new DecimalV2(); + try { + result.multiply(decimalV2, right); + return new HiveDecimalV2(result); + } catch (Exception e) { + return null; + } + } + + public HiveDecimalV2 scaleByPowerOfTen(int n) { + throw new UnsupportedOperationException(); + } + + /* + * Sign methods + */ + public HiveDecimalV2 abs() { + DecimalV2 copy = decimalV2.copy(); + copy.abs(); + return new HiveDecimalV2(copy); + } + + public HiveDecimalV2 negate() { + DecimalV2 copy = decimalV2.copy(); + copy.negate(); + return new HiveDecimalV2(copy); + } + + public int signum() { + return decimalV2.getUnscaled().getSignum(); + } + + private static BigDecimal trim(BigDecimal d) { + if (d.compareTo(BigDecimal.ZERO) == 0) { + // Special case for 0, because java doesn't strip zeros correctly on that number. + d = BigDecimal.ZERO; + } else { + d = d.stripTrailingZeros(); + if (d.scale() < 0) { + // no negative scale decimals + d = d.setScale(0); + } + } + return d; + } + + private static BigDecimal enforcePrecisionScale(BigDecimal bd, int maxPrecision, int maxScale) { + if (bd == null) { + return null; + } + + /** + * Specially handling the case that bd=0, and we are converting it to a type where precision=scale, + * such as decimal(1, 1). + */ + if (bd.compareTo(BigDecimal.ZERO) == 0 && bd.scale() == 0 && maxPrecision == maxScale) { + return bd.setScale(maxScale); + } + + bd = trim(bd); + + if (bd.scale() > maxScale) { + bd = bd.setScale(maxScale, RoundingMode.HALF_UP); + } + + int maxIntDigits = maxPrecision - maxScale; + int intDigits = bd.precision() - bd.scale(); + if (intDigits > maxIntDigits) { + return null; + } + + return bd; + } + + public static HiveDecimalV2 enforcePrecisionScale(HiveDecimalV2 dec, int maxPrecision, int maxScale) { + if (dec == null) { + return null; + } + + // Minor optimization, avoiding creating new objects. + if (dec.precision() - dec.scale() <= maxPrecision - maxScale && + dec.scale() <= maxScale) { + return dec; + } + + BigDecimal bd = enforcePrecisionScale(new BigDecimal(dec.toString()), maxPrecision, maxScale); + if (bd == null) { + return null; + } + + return create(bd); + } + + /* + * Comparison methods + */ + @Override + public int hashCode() { + return decimalV2.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return decimalV2.equals(((HiveDecimalV2) obj).decimalV2); + } + + public int compareTo(HiveDecimalV2 o) { + return decimalV2.compareTo(o.decimalV2); + } + + @Override + public String toString() { + return decimalV2.toString(); + } +} diff --git storage-api/src/java/org/apache/hadoop/hive/common/type/Int128V2.java storage-api/src/java/org/apache/hadoop/hive/common/type/Int128V2.java new file mode 100644 index 0000000..8856b12 --- /dev/null +++ storage-api/src/java/org/apache/hadoop/hive/common/type/Int128V2.java @@ -0,0 +1,488 @@ +package org.apache.hadoop.hive.common.type; + +/** + * A mutable integer class with 38 digits. + * @see org.apache.hadoop.hive.ql.exec.vector.Int128V2ColumnVector + */ +public class Int128V2 implements Comparable { + public static final Int128V2 ZERO = new Int128V2(); + public static final Int128V2 ONE = new Int128V2("1"); + public static final Int128V2 LOW_32_MASK = new Int128V2(Integer.toString(Integer.MIN_VALUE)); + public static final Int128V2 FIVE_TIMES_2_POWER_124 = new Int128V2("5"); + public static final Int128V2[] POWER_OF_TEN = new Int128V2[38]; + + public static final int MAX_DIGITS = 38; + + // Big endian. The most important digit comes first. i0 is the most important. + public int i0; + public int i1; + public int i2; + public int i3; + public int i4; + + static { + FIVE_TIMES_2_POWER_124.shiftLeft(FIVE_TIMES_2_POWER_124, 124); + POWER_OF_TEN[0] = ONE; + Int128V2 ten = new Int128V2("10"); + for (int i = 0; i < 37; i++) { + POWER_OF_TEN[i + 1] = new Int128V2(); + POWER_OF_TEN[i + 1].multiply(POWER_OF_TEN[i], ten); + } + } + + /* + * Constructors + */ + public Int128V2() { + } + + public Int128V2(String value) { + for (byte b : value.getBytes()) { + parse(b); + } + if (value.charAt(0) == '-') { + negate(this); + } + } + + public void set(Int128V2 value) { + i4 = value.i4; + i3 = value.i3; + i2 = value.i2; + i1 = value.i1; + i0 = value.i0; + } + + public Int128V2 copy() { + Int128V2 copy = new Int128V2(); + copy.set(this); + return copy; + } + + public void parse(byte b) { + if ('0' <= b && b <= '9') { + multiplyByTen(this); + add(this, b - '0'); + } + } + + /* + * String methods + */ + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + stringifyValue(builder); + return builder.toString(); + } + + public void stringifyValue(StringBuilder givenBuilder) { + // Zero + if (this.equals(ZERO)) { + givenBuilder.append('0'); + return; + } + + // Negative + Int128V2 copy = new Int128V2(); + if (getSignum() == 1) { + copy.set(this); + } else { + givenBuilder.append('-'); + copy.negate(this); + } + + // Positive or negated negative + Int128V2 remainder = new Int128V2(); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < MAX_DIGITS; i++) { + copy.divideByTen(copy, remainder); + builder.append(remainder.i4); + if (copy.equals(ZERO)) { + break; + } + } + builder.reverse(); + givenBuilder.append(builder); + } + + /* + * Comparison methods + */ + @Override + public boolean equals(Object obj) { + Int128V2 right = (Int128V2) obj; + return (i4 == right.i4) && + (i3 == right.i3) && (i2 == right.i2) && + (i1 == right.i1) && (i0 == right.i0); + } + + @Override + public int compareTo(Int128V2 right) { + if (i0 < right.i0) { + return -1; + } + if (i0 > right.i0) { + return 1; + } + if (i1 < right.i1) { + return -1; + } + if (i1 > right.i1) { + return 1; + } + if (i2 < right.i2) { + return -1; + } + if (i2 > right.i2) { + return 1; + } + if (i3 < right.i3) { + return -1; + } + if (i3 > right.i3) { + return 1; + } + if (i4 < right.i4) { + return -1; + } + if (i4 > right.i4) { + return 1; + } + return 0; + } + + @Override + public int hashCode() { + return i4 ^ i3 ^ i2 ^ i1 ^ i0; + } + + /* + * Arithmetic methods + */ + void add(Int128V2 left, int right) { + + // Add with carries + this.i4 = left.i4 + right; + this.i3 = left.i3 + (this.i4 >>> 31); + this.i2 = left.i2 + (this.i3 >>> 31); + this.i1 = left.i1 + (this.i2 >>> 31); + this.i0 = left.i0 + (this.i1 >>> 31); + + // Clear carries + this.i4 &= Integer.MAX_VALUE; + this.i3 &= Integer.MAX_VALUE; + this.i2 &= Integer.MAX_VALUE; + this.i1 &= Integer.MAX_VALUE; + + if (this.i0 >= 8) { + throw new IllegalArgumentException("Overflow"); + } + if (this.i0 < -8) { + throw new IllegalArgumentException("Underflow"); + } + } + + public void add(Int128V2 left, Int128V2 right) { + // Add with carries + this.i4 = left.i4 + right.i4; + this.i3 = left.i3 + right.i3 + (this.i4 >>> 31); + this.i2 = left.i2 + right.i2 + (this.i3 >>> 31); + this.i1 = left.i1 + right.i1 + (this.i2 >>> 31); + this.i0 = left.i0 + right.i0 + (this.i1 >>> 31); + + // Clear carries + this.i4 &= Integer.MAX_VALUE; + this.i3 &= Integer.MAX_VALUE; + this.i2 &= Integer.MAX_VALUE; + this.i1 &= Integer.MAX_VALUE; + + if (this.i0 >= 8) { + throw new IllegalArgumentException("Overflow"); + } + if (this.i0 < -8) { + throw new IllegalArgumentException("Underflow"); + } + } + + public void subtract(Int128V2 left, Int128V2 right) { + // Add with borrows + this.i4 = left.i4 - right.i4; + this.i3 = left.i3 - right.i3 - (this.i4 >>> 31); + this.i2 = left.i2 - right.i2 - (this.i3 >>> 31); + this.i1 = left.i1 - right.i1 - (this.i2 >>> 31); + this.i0 = left.i0 - right.i0 - (this.i1 >>> 31); + + // Clear borrows + this.i4 &= Integer.MAX_VALUE; + this.i3 &= Integer.MAX_VALUE; + this.i2 &= Integer.MAX_VALUE; + this.i1 &= Integer.MAX_VALUE; + + if (this.i0 >= 8) { + throw new IllegalArgumentException("Overflow"); + } + if (this.i0 < -8) { + throw new IllegalArgumentException("Underflow"); + } + } + + public void multiply(Int128V2 left, Int128V2 right) { + // Make all positive + int leftSignum = left.getSignum(); + int rightSignum = right.getSignum(); + + if (this == left) { + left = left.copy(); + } + if (leftSignum == -1) { + left.negate(left); + } + + right = right.copy(); + if (rightSignum == -1) { + right.negate(right); + } + + // Do a multiplication with shifts and adds + this.set(ZERO); + + Int128V2 high = new Int128V2(); + Int128V2 low = new Int128V2(); + + multiply(left.i4, left.i4, left.i4, left.i4, left.i4, + right.i0, right.i1, right.i2, right.i3, right.i4, high, low); + + multiply(left.i3, left.i3, left.i3, left.i3, 0, + right.i1, right.i2, right.i3, right.i4, 0, high, low); + + multiply(left.i2, left.i2, left.i2, 0, 0, + right.i2, right.i3, right.i4, 0, 0, high, low); + + multiply(left.i1, left.i1, 0, 0, 0, + right.i3, right.i4, 0, 0, 0, high, low); + + multiply(left.i0, 0, 0, 0, 0, + right.i4, 0, 0, 0, 0, high, low); + + // Handle signs + if (leftSignum != rightSignum) { + this.negate(this); + } + if (leftSignum == -1) { + left.negate(left); + } + } + + private void multiply(int l4, int l3, int l2, int l1, int l0, int r4, int r3, int r2, int r1, int r0, + Int128V2 high, Int128V2 low) { + low.i0 = ((l4 * r4) & 0x7fffffff); + + long result = (long) l3 * r3; + high.i0 = (int) (result >> 31); + low.i1 = (int) (result & 0x7fffffff); + + result = (long) l2 * r2; + high.i1 = (int) (result >> 31); + low.i2 = (int) (result & 0x7fffffff); + + result = (long) l1 * r1; + high.i2 = (int) (result >> 31); + low.i3 = (int) (result & 0x7fffffff); + + result = (long) l0 * r0; + high.i3 = (int) (result >> 31); + low.i4 = (int) (result & 0x7fffffff); + + high.i4 = 0; + + this.add(this, high); + this.add(this, low); + } + + public void multiplyByTen(Int128V2 left) { + // x * 10 = x * 8 + x * 2 + Int128V2 toAdd = new Int128V2(); + toAdd.shiftLeft(left, 3); + this.shiftLeft(left, 1); + this.add(left, toAdd); + } + + public void multiplyByTen(Int128V2 left, int times) { + multiply(POWER_OF_TEN[times], left); + } + + public void divide(Int128V2 left, Int128V2 right, Int128V2 remainder) { + // Handle divide by zero + if (right.equals(ZERO)) { + throw new ArithmeticException("divide by zero"); + } + + // Make all positive + int leftSignum = left.getSignum(); + if (leftSignum == 1) { + remainder.set(left); + } else { + remainder.negate(left); + } + + int rightSignum = right.getSignum(); + Int128V2 rightCopy = new Int128V2(); + if (rightSignum == 1) { + rightCopy.set(right); + } else { + rightCopy.negate(right); + } + + // Configure remainder and right + this.set(ZERO); + int i = 0; + while (rightCopy.compareTo(remainder) < 0) { + rightCopy.shiftLeft(rightCopy, 1); + i++; + } + + // Subtract right from remainder and mark on left for each bit + while (i >= 0) { + i--; + this.shiftLeft(this, 1); + if (remainder.compareTo(rightCopy) >= 0) { + remainder.subtract(remainder, rightCopy); + this.i4 |= 1; + } + rightCopy.shiftRight(rightCopy, 1); + } + + // Handle signs + if (leftSignum * rightSignum == -1) { + this.negate(this); + } + + if (leftSignum == -1) { + remainder.negate(remainder); + } + } + + public void divideByTen(Int128V2 left, Int128V2 remainder) { + // Make all positive + int leftSignum = left.getSignum(); + if (leftSignum == 1) { + remainder.set(left); + } else { + remainder.negate(left); + } + + Int128V2 copy = FIVE_TIMES_2_POWER_124.copy(); + this.set(ZERO); + for (int i = 0; i < 124; i++) { + this.shiftLeft(this, 1); + if (remainder.compareTo(copy) >= 0) { + remainder.subtract(remainder, copy); + this.i4 |= 1; + } + copy.shiftRight(copy, 1); + } + + if (leftSignum == -1) { + this.negate(this); + remainder.negate(remainder); + } + } + + public void divideByTen(Int128V2 left, Int128V2 remainder, int times) { + for (int i = 0; i < times; i++) { + divideByTen(left, remainder); + } + } + + /* + * Bitwise methods + */ + public void shiftLeft(Int128V2 left, int right) { + if (right > 31) { + shiftLeft(left, 31); + shiftLeft(left, right - 31); + return; + } + this.i0 = ((left.i0 << right) | (left.i1 >>> (31 - right))) & Integer.MAX_VALUE; + this.i1 = ((left.i1 << right) | (left.i2 >>> (31 - right))) & Integer.MAX_VALUE; + this.i2 = ((left.i2 << right) | (left.i3 >>> (31 - right))) & Integer.MAX_VALUE; + this.i3 = ((left.i3 << right) | (left.i4 >>> (31 - right))) & Integer.MAX_VALUE; + this.i4 = (left.i4 << right) & Integer.MAX_VALUE; + + if (this.i0 >= 8) { + throw new IllegalArgumentException("Overflow"); + } + if (this.i0 < -8) { + throw new IllegalArgumentException("Underflow"); + } + } + + private void shiftLeftIgnoreOverflow(Int128V2 left, int right) { + if (right > 31) { + shiftLeftIgnoreOverflow(left, 31); + shiftLeftIgnoreOverflow(left, right - 31); + return; + } + this.i0 = ((left.i0 << right) | (left.i1 >>> (31 - right))) & Integer.MAX_VALUE; + this.i1 = ((left.i1 << right) | (left.i2 >>> (31 - right))) & Integer.MAX_VALUE; + this.i2 = ((left.i2 << right) | (left.i3 >>> (31 - right))) & Integer.MAX_VALUE; + this.i3 = ((left.i3 << right) | (left.i4 >>> (31 - right))) & Integer.MAX_VALUE; + this.i4 = (left.i4 << right) & Integer.MAX_VALUE; + } + + public void shiftRight(Int128V2 left, int right) { + if (right > 31) { + shiftRight(left, 31); + shiftRight(left, right - 31); + return; + } + this.i4 = ((left.i4 >>> right) | (left.i3 << (31 - right))) & Integer.MAX_VALUE; + this.i3 = ((left.i3 >>> right) | (left.i2 << (31 - right))) & Integer.MAX_VALUE; + this.i2 = ((left.i2 >>> right) | (left.i1 << (31 - right))) & Integer.MAX_VALUE; + this.i1 = ((left.i1 >>> right) | (left.i0 << (31 - right))) & Integer.MAX_VALUE; + this.i0 = (left.i0 >>> right) & Integer.MAX_VALUE; + + if (this.i0 >= 8) { + throw new IllegalArgumentException("Overflow"); + } + if (this.i0 < -8) { + throw new IllegalArgumentException("Underflow"); + } + } + + /* + * Sign methods + */ + public void negate(Int128V2 left) { + this.i4 = -left.i4; + this.i3 = -left.i3 - (this.i4 >>> 31); + this.i2 = -left.i2 - (this.i3 >>> 31); + this.i1 = -left.i1 - (this.i2 >>> 31); + this.i0 = -left.i0 - (this.i1 >>> 31); + + this.i4 &= Integer.MAX_VALUE; + this.i3 &= Integer.MAX_VALUE; + this.i2 &= Integer.MAX_VALUE; + this.i1 &= Integer.MAX_VALUE; + + if (this.i0 >= 8) { + throw new IllegalArgumentException("Overflow"); + } + if (this.i0 < -8) { + throw new IllegalArgumentException("Underflow"); + } + } + + public int getSignum() { + return (i0 >> 3) | 1; + } + + public void abs(Int128V2 left) { + if (left.getSignum() == -1) { + negate(left); + } else { + set(left); + } + } +} diff --git storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/DecimalV2ColumnVector.java storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/DecimalV2ColumnVector.java new file mode 100644 index 0000000..8b85acb --- /dev/null +++ storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/DecimalV2ColumnVector.java @@ -0,0 +1,365 @@ +package org.apache.hadoop.hive.ql.exec.vector; + +import org.apache.hadoop.hive.common.type.DecimalV2; +import org.apache.hadoop.hive.common.type.HiveDecimal; +import org.apache.hadoop.hive.common.type.Int128V2; + +import java.math.BigDecimal; + +/** + * A vector of {@code DecimalV2}. + * @see DecimalV2 + */ + +public class DecimalV2ColumnVector extends ColumnVector { + public int precision; + public int scale; + + public Int128V2ColumnVector unscaledVector; + + /* + * Constructors + */ + public DecimalV2ColumnVector() { + this(VectorizedRowBatch.DEFAULT_SIZE); + } + + public DecimalV2ColumnVector(int len) { + super(len); + unscaledVector = new Int128V2ColumnVector(len); + } + + public DecimalV2ColumnVector(int precision, int scale) { + this(VectorizedRowBatch.DEFAULT_SIZE, precision, scale); + } + + public DecimalV2ColumnVector(int len, int precision, int scale) { + super(len); + + this.precision = precision; + this.scale = scale; + + unscaledVector = new Int128V2ColumnVector(len); + } + + @Override + public void flatten(boolean selectedInUse, int[] sel, int size) { + + } + + @Override + public void setElement(int outElementNum, int inputElementNum, ColumnVector inputVector) { + + } + + public void stringifyValue(StringBuilder builder, int row) { + // 0 + if (isZero(row)) { + builder.append('0'); + return; + } + + // Make it positive + StringBuilder innerBuilder = new StringBuilder(); + Int128V2 unscaledCopy = new Int128V2(); + unscaledVector.get(unscaledCopy, row); + int signum = unscaledCopy.getSignum(); + if (signum == -1) { + unscaledCopy.negate(unscaledCopy); + } + + // Digitize from the smallest + boolean digitizedNonZero = false; + Int128V2 remainder = new Int128V2(); + for (int i = 0; i < precision; i++) { + // . + if (scale == i && digitizedNonZero) { + innerBuilder.append('.'); + } + unscaledCopy.divideByTen(unscaledCopy, remainder); + + // Remove trailing zeros + if (!(scale > 0 && i < scale && !digitizedNonZero && remainder.i4 == 0)) { + innerBuilder.append(remainder.i4); + digitizedNonZero = true; + } + + // Remove heading zeros + if (unscaledCopy.equals(Int128V2.ZERO) && i >= scale) { + // 0. + if (i < scale) { + innerBuilder.append(".0"); + } + break; + } + } + + // - + if (signum == -1) { + innerBuilder.append('-'); + } + + // Reverse the digits + innerBuilder.reverse(); + builder.append(innerBuilder); + } + + public String toString(int i) { + StringBuilder builder = new StringBuilder(); + stringifyValue(builder, i); + return builder.toString(); + } + + private void parse(String value, boolean precisionScaleGiven, + int precision, int scale, boolean allowRounding, int i) { + DecimalV2 decimalV2 = new DecimalV2(); + decimalV2.parse(value, precisionScaleGiven, precision, scale, allowRounding); + this.set(decimalV2, i); + } + + public void set(DecimalV2ColumnVector left) { + int scaleDiff = this.scale - left.scale; + this.scale = left.scale; + this.precision = left.precision; + if (scaleDiff > 0) { + this.unscaledVector.multiplyByTen(left.unscaledVector, scaleDiff); + } else { + this.unscaledVector.divideByTen(left.unscaledVector, + new Int128V2ColumnVector(unscaledVector.i4.length), -scaleDiff); + } + } + + public void set(DecimalV2 left, int i) { + left = left.copy(); + int scaleDiff = this.scale - left.scale; + if (scaleDiff > 0) { + left.unscaled.multiplyByTen(left.unscaled, scaleDiff); + } else { + left.unscaled.divideByTen(left.unscaled, new Int128V2(), -scaleDiff); + } + this.unscaledVector.i4[i] = left.unscaled.i4; + this.unscaledVector.i3[i] = left.unscaled.i3; + this.unscaledVector.i2[i] = left.unscaled.i2; + this.unscaledVector.i1[i] = left.unscaled.i1; + this.unscaledVector.i0[i] = left.unscaled.i0; + } + + public DecimalV2ColumnVector copy() { + DecimalV2ColumnVector copy = new DecimalV2ColumnVector(); + copy.set(this); + return copy; + } + + /* + * Comparison methods + */ + public boolean equals(DecimalV2 right, int i) { + DecimalV2 result = new DecimalV2(); + this.get(result, i); + result.subtract(result, right); + return result.isZero(); + } + + public DecimalV2 get(DecimalV2 result, int i) { + result.precision = precision; + result.scale = scale; + + result.unscaled.i4 = unscaledVector.i4[i]; + result.unscaled.i3 = unscaledVector.i3[i]; + result.unscaled.i2 = unscaledVector.i2[i]; + result.unscaled.i1 = unscaledVector.i1[i]; + result.unscaled.i0 = unscaledVector.i0[i]; + return result; + } + + public int compareTo(DecimalV2ColumnVector rightVector, int i) { + DecimalV2 left = new DecimalV2(); + DecimalV2 result = new DecimalV2(); + this.get(left, i); + rightVector.get(result, i); + result.subtract(left, result); + if (result.isZero()) { + return 0; + } else { + return result.getSignum(); + } + } + + public boolean isZero(int i) { + return unscaledVector.i4[i] == 0 && unscaledVector.i3[i] == 0 && + unscaledVector.i2[i] == 0 && unscaledVector.i1[i] == 0 && unscaledVector.i0[i] == 0; + } + + /* + * Arithmetic methods + */ + public void add(DecimalV2ColumnVector left, DecimalV2ColumnVector right) { + // Precision and scale + this.precision = Math.max(left.precision, right.precision); + this.scale = Math.max(left.scale, right.scale); + int scaleDiff = left.scale - right.scale; + + // Same scale + if (scaleDiff == 0) { + this.unscaledVector.add(left.unscaledVector, right.unscaledVector); + } + + // Left scale is greater + else if (scaleDiff > 0) { + this.unscaledVector.set(right.unscaledVector); + this.unscaledVector.multiplyByTen(this.unscaledVector, scaleDiff); + this.unscaledVector.add(left.unscaledVector, this.unscaledVector); + } + + // Right scale is greater + else { + this.unscaledVector.set(left.unscaledVector); + this.unscaledVector.multiplyByTen(this.unscaledVector, -scaleDiff); + this.unscaledVector.add(this.unscaledVector, right.unscaledVector); + } + } + + public void subtract(DecimalV2ColumnVector left, DecimalV2ColumnVector right) { + // Precision and scale + this.precision = Math.max(left.precision, right.precision); + this.scale = Math.max(left.scale, right.scale); + int scaleDiff = left.scale - right.scale; + + // Same scale + if (scaleDiff == 0) { + this.unscaledVector.subtract(left.unscaledVector, right.unscaledVector); + } + + // Left scale is greater + else if (scaleDiff > 0) { + this.unscaledVector.set(right.unscaledVector); + this.unscaledVector.multiplyByTen(this.unscaledVector, scaleDiff); + this.unscaledVector.subtract(left.unscaledVector, this.unscaledVector); + } + + // Right scale is greater + else { + this.unscaledVector.set(left.unscaledVector); + this.unscaledVector.multiplyByTen(this.unscaledVector, -scaleDiff); + this.unscaledVector.subtract(this.unscaledVector, right.unscaledVector); + } + } + + public void multiply(DecimalV2ColumnVector left, DecimalV2ColumnVector right) { + this.unscaledVector.multiply(left.unscaledVector, right.unscaledVector); + + this.precision = left.precision + right.precision; + this.scale = left.scale + right.scale; + if (this.scale >= HiveDecimal.MAX_PRECISION) { + throw new IllegalArgumentException("Underflow"); + } + } + + // Division is hard to optimize yet + public void divide(DecimalV2ColumnVector left, DecimalV2ColumnVector right) { + int p1 = left.precision; + int p2 = right.precision; + int s1 = left.scale; + int s2 = right.scale; + + this.scale = Math.max(6, s1 + p2 + 1); + this.precision = p1 - s1 + s2 + this.scale; + int scaleDiff = this.scale - s1 + s2; + + this.unscaledVector.multiplyByTen(left.unscaledVector, scaleDiff); + this.unscaledVector.divide(this.unscaledVector, right.unscaledVector, + new Int128V2ColumnVector(unscaledVector.i4.length)); + } + + // Remainder is hard to optimize yet + public void remainder(DecimalV2ColumnVector left, DecimalV2ColumnVector right) { + int scaleDiff = left.scale - right.scale; + this.scale = Math.max(left.scale, right.scale); + this.precision = Math.max(left.precision, right.precision); + + Int128V2ColumnVector remainder = new Int128V2ColumnVector(unscaledVector.i4.length); + this.unscaledVector.multiplyByTen(right.unscaledVector, scaleDiff); + this.unscaledVector.divide(left.unscaledVector, this.unscaledVector, remainder); + this.unscaledVector.set(remainder); + } + + /* + * Precision and scale methods + */ + public Int128V2ColumnVector getUnscaled() { + return unscaledVector; + } + + public int getScale() { + return scale; + } + + public int getPrecision() { + return precision; + } + + public void setScale(int i) { + scale = i; + } + + /* + * Sign methods + */ + public void negate() { + unscaledVector.negate(unscaledVector); + } + + public void abs() { + unscaledVector.abs(unscaledVector); + } + + public int getSignum(int i) { + return unscaledVector.getSignum(i); + } + + /* + * Type conversions + */ + private Int128V2 integerPart(int row) { + Int128V2 unscaled = new Int128V2(); + unscaledVector.get(unscaled, row); + Int128V2 unscaledCopy = unscaled.copy(); + Int128V2 remainder = new Int128V2(); + for (int i = 0; i < scale; i++) { + unscaledCopy.divideByTen(unscaledCopy, remainder); + } + return unscaledCopy; + } + + public long longValue(int i) { + Int128V2 integerPart = integerPart(i); + return integerPart.i4 | (integerPart.i3 << 31) | + ((integerPart.i2 & 1L) << 62) | (integerPart.i0 & Long.MIN_VALUE); + } + + public int intValue(int i) { + Int128V2 integerPart = integerPart(i); + int value = integerPart.i4 | (integerPart.i0 & Integer.MIN_VALUE); + return value; + } + + public short shortValue(int i) { + return (short) intValue(i); + } + + public byte byteValue(int i) { + return (byte) intValue(i); + } + + public double doubleValue(int i) { + return Double.valueOf(toString(i)); + } + + public float floatValue(int i) { + return Float.valueOf(toString(i)); + } + + public BigDecimal bigDecimalValue(int i) { + return new BigDecimal(toString(i)); + } +} diff --git storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/Int128V2ColumnVector.java storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/Int128V2ColumnVector.java new file mode 100644 index 0000000..1784a44 --- /dev/null +++ storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/Int128V2ColumnVector.java @@ -0,0 +1,862 @@ +package org.apache.hadoop.hive.ql.exec.vector; + +import org.apache.hadoop.hive.common.type.Int128V2; + +import java.math.BigInteger; +import java.util.Arrays; + +/** + * A vector of {@code Int128V2}. + * @see Int128V2 + */ + +public class Int128V2ColumnVector extends ColumnVector { + public int length; + + // Big endian. The most important digit comes first. i0 is the most important. + public int[] i0; + public int[] i1; + public int[] i2; + public int[] i3; + public int[] i4; + + private Int128V2ColumnVector highCol; + private Int128V2ColumnVector lowCol; + + private Int128V2ColumnVector unsignedLeft; + private Int128V2ColumnVector unsignedRight; + + public Int128V2ColumnVector() { + this(VectorizedRowBatch.DEFAULT_SIZE); + } + + public Int128V2ColumnVector(int len) { + super(len); + length = len; + i4 = new int[len]; + i3 = new int[len]; + i2 = new int[len]; + i1 = new int[len]; + i0 = new int[len]; + } + + @Override + public void flatten(boolean selectedInUse, int[] sel, int size) { + flattenPush(); + if (isRepeating) { + isRepeating = false; + int i4Val = i4[0]; + int i3Val = i3[0]; + int i2Val = i2[0]; + int i1Val = i1[0]; + int i0Val = i0[0]; + if (selectedInUse) { + for (int j = 0; j < size; j++) { + int i = sel[j]; + i4[i] = i4Val; + i3[i] = i3Val; + i2[i] = i2Val; + i1[i] = i1Val; + i0[i] = i0Val; + } + } else { + Arrays.fill(i4, 0, size, i4Val); + Arrays.fill(i3, 0, size, i3Val); + Arrays.fill(i2, 0, size, i2Val); + Arrays.fill(i1, 0, size, i1Val); + Arrays.fill(i0, 0, size, i0Val); + } + flattenRepeatingNulls(selectedInUse, sel, size); + } + flattenNoNulls(selectedInUse, sel, size); + } + + @Override + public void setElement(int outElementNum, int inputElementNum, ColumnVector inputVector) { + if (inputVector.isRepeating) { + inputElementNum = 0; + } + if (inputVector.noNulls || !inputVector.isNull[inputElementNum]) { + isNull[outElementNum] = false; + i4[outElementNum] = + ((Int128V2ColumnVector) inputVector).i4[inputElementNum]; + i3[outElementNum] = + ((Int128V2ColumnVector) inputVector).i3[inputElementNum]; + i2[outElementNum] = + ((Int128V2ColumnVector) inputVector).i2[inputElementNum]; + i1[outElementNum] = + ((Int128V2ColumnVector) inputVector).i1[inputElementNum]; + i0[outElementNum] = + ((Int128V2ColumnVector) inputVector).i0[inputElementNum]; + } else { + isNull[outElementNum] = true; + noNulls = false; + } + } + + public void set(Int128V2ColumnVector value) { + System.arraycopy(value.i4, 0, this.i4, 0, length); + System.arraycopy(value.i3, 0, this.i3, 0, length); + System.arraycopy(value.i2, 0, this.i2, 0, length); + System.arraycopy(value.i1, 0, this.i1, 0, length); + System.arraycopy(value.i0, 0, this.i0, 0, length); + } + + public void set(Int128V2 value) { + Arrays.fill(this.i4, value.i4); + Arrays.fill(this.i3, value.i3); + Arrays.fill(this.i2, value.i2); + Arrays.fill(this.i1, value.i1); + Arrays.fill(this.i0, value.i0); + } + + public void set(Int128V2 value, int i) { + this.i4[i] = value.i4; + this.i3[i] = value.i3; + this.i2[i] = value.i2; + this.i1[i] = value.i1; + this.i0[i] = value.i0; + } + + public void set(Int128V2ColumnVector value, int i) { + this.i4[i] = value.i4[i]; + this.i3[i] = value.i3[i]; + this.i2[i] = value.i2[i]; + this.i1[i] = value.i1[i]; + this.i0[i] = value.i0[i]; + } + + public Int128V2ColumnVector copy() { + Int128V2ColumnVector copy = new Int128V2ColumnVector(length); + copy.set(this); + return copy; + } + + void parse(byte b) { + if ('0' <= b && b <= '9') { + multiplyByTen(this); + add(this, b - '0'); + } + } + + @Override + public void stringifyValue(StringBuilder givenBuilder, int i) { + // Zero + if (this.equals(Int128V2.ZERO, i)) { + givenBuilder.append('0'); + return; + } + + // Negative + Int128V2 copy = new Int128V2(); + if (getSignum(i) == 1) { + this.get(copy, i); + } else { + givenBuilder.append('-'); + this.get(copy, i); + copy.negate(copy); + } + + // Positive or negated negative + Int128V2 remainder = new Int128V2(); + StringBuilder builder = new StringBuilder(); + for (int j = 0; j < Int128V2.MAX_DIGITS; j++) { + copy.divideByTen(copy, remainder); + builder.append(remainder.i4); + if (copy.equals(Int128V2.ZERO)) { + break; + } + } + builder.reverse(); + givenBuilder.append(builder); + } + + public void get(Int128V2 int128V2, int i) { + int128V2.i4 = i4[i]; + int128V2.i3 = i3[i]; + int128V2.i2 = i2[i]; + int128V2.i1 = i1[i]; + int128V2.i0 = i0[i]; + } + + /* + * Comparison methods + */ + public boolean equals(Int128V2ColumnVector right, int i) { + return (i4[i] == right.i4[i]) && + (i3[i] == right.i3[i]) && (i2[i] == right.i2[i]) && + (i1[i] == right.i1[i]) && (i0[i] == right.i0[i]); + } + + public boolean equals(Int128V2 right, int i) { + return (i4[i] == right.i4) && + (i3[i] == right.i3) && (i2[i] == right.i2) && + (i1[i] == right.i1) && (i0[i] == right.i0); + } + + public int compareTo(Int128V2ColumnVector right, int i) { + if (i0[i] < right.i0[i]) { + return -1; + } + if (i0[i] > right.i0[i]) { + return 1; + } + if (i1[i] < right.i1[i]) { + return -1; + } + if (i1[i] > right.i1[i]) { + return 1; + } + if (i2[i] < right.i2[i]) { + return -1; + } + if (i2[i] > right.i2[i]) { + return 1; + } + if (i3[i] < right.i3[i]) { + return -1; + } + if (i3[i] > right.i3[i]) { + return 1; + } + if (i4[i] < right.i4[i]) { + return -1; + } + if (i4[i] > right.i4[i]) { + return 1; + } + return 0; + } + + /** + * Vectorized addition + * @param left + * @param right + */ + void add(Int128V2ColumnVector left, int right) { + + // Add with carries + for (int i = 0; i < length; i++) { + this.i4[i] = left.i4[i] + right; + } + for (int i = 0; i < length; i++) { + this.i3[i] = left.i3[i] + (this.i4[i] >>> 31); + } + for (int i = 0; i < length; i++) { + this.i2[i] = left.i2[i] + (this.i3[i] >>> 31); + } + for (int i = 0; i < length; i++) { + this.i1[i] = left.i1[i] + (this.i2[i] >>> 31); + } + for (int i = 0; i < length; i++) { + this.i0[i] = left.i0[i] + (this.i1[i] >>> 31); + } + + // Clear carries + for (int i = 0; i < length; i++) { + this.i4[i] &= Integer.MAX_VALUE; + this.i3[i] &= Integer.MAX_VALUE; + this.i2[i] &= Integer.MAX_VALUE; + this.i1[i] &= Integer.MAX_VALUE; + } + + // Overflow + for (int i = 0; i < length; i++) { + int value = this.i0[i]; + this.isNull[i] |= (value >= 8 || value < -8); + } + } + + /** + * Vectorized addition + * @param left + * @param right + */ + public void add(Int128V2ColumnVector left, Int128V2ColumnVector right) { + // Add with carries + for (int i = 0; i < length; i++) { + this.i4[i] = left.i4[i] + right.i4[i]; + } + for (int i = 0; i < length; i++) { + this.i3[i] = left.i3[i] + right.i3[i] + (this.i4[i] >>> 31); + } + for (int i = 0; i < length; i++) { + this.i2[i] = left.i2[i] + right.i2[i] + (this.i3[i] >>> 31); + } + for (int i = 0; i < length; i++) { + this.i1[i] = left.i1[i] + right.i1[i] + (this.i2[i] >>> 31); + } + for (int i = 0; i < length; i++) { + this.i0[i] = left.i0[i] + right.i0[i] + (this.i1[i] >>> 31); + } + + // Clear carries + for (int i = 0; i < length; i++) { + this.i4[i] &= Integer.MAX_VALUE; + this.i3[i] &= Integer.MAX_VALUE; + this.i2[i] &= Integer.MAX_VALUE; + this.i1[i] &= Integer.MAX_VALUE; + } + + // Overflow + for (int i = 0; i < length; i++) { + int value = this.i0[i]; + this.isNull[i] |= value >= 8 || value < -8; + } + } + + private void add(Int128V2ColumnVector left, Int128V2ColumnVector right, int i) { + // Add with carries + this.i4[i] = left.i4[i] + right.i4[i]; + this.i3[i] = left.i3[i] + right.i3[i] + (this.i4[i] >>> 31); + this.i2[i] = left.i2[i] + right.i2[i] + (this.i3[i] >>> 31); + this.i1[i] = left.i1[i] + right.i1[i] + (this.i2[i] >>> 31); + this.i0[i] = left.i0[i] + right.i0[i] + (this.i1[i] >>> 31); + + // Clear carries + this.i4[i] &= Integer.MAX_VALUE; + this.i3[i] &= Integer.MAX_VALUE; + this.i2[i] &= Integer.MAX_VALUE; + this.i1[i] &= Integer.MAX_VALUE; + + // Overflow + int value = this.i0[i]; + if (value >= 8 || value < -8) { + this.isNull[i] = true; + } + } + + private void add(Int128V2ColumnVector left, Int128V2 right, int i) { + // Add with carries + this.i4[i] = left.i4[i] + right.i4; + this.i3[i] = left.i3[i] + right.i3 + (this.i4[i] >>> 31); + this.i2[i] = left.i2[i] + right.i2 + (this.i3[i] >>> 31); + this.i1[i] = left.i1[i] + right.i1 + (this.i2[i] >>> 31); + this.i0[i] = left.i0[i] + right.i0 + (this.i1[i] >>> 31); + + // Clear carries + this.i4[i] &= Integer.MAX_VALUE; + this.i3[i] &= Integer.MAX_VALUE; + this.i2[i] &= Integer.MAX_VALUE; + this.i1[i] &= Integer.MAX_VALUE; + + // Overflow + int value = this.i0[i]; + if (value >= 8 || value < -8) { + this.isNull[i] = true; + } + } + + /** + * Vectorized subtraction + * @param left + * @param right + */ + public void subtract(Int128V2ColumnVector left, Int128V2ColumnVector right) { + // Add with carries + for (int i = 0; i < length; i++) { + this.i4[i] = left.i4[i] - right.i4[i]; + } + for (int i = 0; i < length; i++) { + this.i3[i] = left.i3[i] - right.i3[i] - (this.i4[i] >>> 31); + } + for (int i = 0; i < length; i++) { + this.i2[i] = left.i2[i] - right.i2[i] - (this.i3[i] >>> 31); + } + for (int i = 0; i < length; i++) { + this.i1[i] = left.i1[i] - right.i1[i] - (this.i2[i] >>> 31); + } + for (int i = 0; i < length; i++) { + this.i0[i] = left.i0[i] - right.i0[i] - (this.i1[i] >>> 31); + } + + // Clear carries + for (int i = 0; i < length; i++) { + this.i4[i] &= Integer.MAX_VALUE; + this.i3[i] &= Integer.MAX_VALUE; + this.i2[i] &= Integer.MAX_VALUE; + this.i1[i] &= Integer.MAX_VALUE; + } + + // Overflow + for (int i = 0; i < length; i++) { + int value = this.i0[i]; + this.isNull[i] |= (value >= 8 || value < -8); + } + } + + private void subtract(Int128V2ColumnVector left, Int128V2ColumnVector right, int i) { + // Add with carries + this.i4[i] = left.i4[i] - right.i4[i]; + this.i3[i] = left.i3[i] - right.i3[i] - (this.i4[i] >>> 31); + this.i2[i] = left.i2[i] - right.i2[i] - (this.i3[i] >>> 31); + this.i1[i] = left.i1[i] - right.i1[i] - (this.i2[i] >>> 31); + this.i0[i] = left.i0[i] - right.i0[i] - (this.i1[i] >>> 31); + + // Clear carries + this.i4[i] &= Integer.MAX_VALUE; + this.i3[i] &= Integer.MAX_VALUE; + this.i2[i] &= Integer.MAX_VALUE; + this.i1[i] &= Integer.MAX_VALUE; + + // Overflow + int value = this.i0[i]; + this.isNull[i] |= (value >= 8 || value < -8); + } + + /** + * Vectorized multiplication + * @param left + * @param right + */ + public void multiply(Int128V2ColumnVector left, Int128V2ColumnVector right) { + + if (highCol == null || highCol.i4.length < length) { + unsignedLeft = new Int128V2ColumnVector(length); + unsignedRight = new Int128V2ColumnVector(length); + + highCol = new Int128V2ColumnVector(length); + lowCol = new Int128V2ColumnVector(length); + } + + unsignedLeft.abs(left); + unsignedRight.abs(right); + + this.set(Int128V2.ZERO); + + multiplyAndAdd(unsignedLeft.i4, unsignedLeft.i4, unsignedLeft.i4, unsignedLeft.i4, unsignedLeft.i4, + unsignedRight.i0, unsignedRight.i1, unsignedRight.i2, unsignedRight.i3, unsignedRight.i4); + + multiplyAndAdd(unsignedLeft.i3, unsignedLeft.i3, unsignedLeft.i3, unsignedLeft.i3, null, + unsignedRight.i1, unsignedRight.i2, unsignedRight.i3, unsignedRight.i4, null); + + multiplyAndAdd(unsignedLeft.i2, unsignedLeft.i2, unsignedLeft.i2, null, null, + unsignedRight.i2, unsignedRight.i3, unsignedRight.i4, null, null); + + multiplyAndAdd(unsignedLeft.i1, unsignedLeft.i1, null, null, null, + unsignedRight.i3, unsignedRight.i4, null, null, null); + + multiplyAndAdd(unsignedLeft.i0, null, null, null, null, + unsignedRight.i4, null, null, null, null); + + // Handle signs + for (int i = 0; i < length; i++) { + if (((left.i0[i] ^ right.i0[i]) & 4) == 4) { + this.negate(this, i); + } + } + } + + private void multiplyAndAdd(int[] l4, int[] l3, int[] l2, int[] l1, int[] l0, + int[] r4, int[] r3, int[] r2, int[] r1, int[] r0) { + if (l4 != null && r4 != null) { + for (int i = 0; i < length; i++) { + lowCol.i0[i] = (l4[i] * r4[i]) & 0x7fffffff; + } + } else { + for (int i = 0; i < length; i++) { + lowCol.i0[i] = 0; + } + } + + multiply(l3, r3, highCol.i0, lowCol.i1); + multiply(l2, r2, highCol.i1, lowCol.i2); + multiply(l1, r1, highCol.i2, lowCol.i3); + multiply(l0, r0, highCol.i3, lowCol.i4); + + for (int i = 0; i < length; i++) { + highCol.i4[i] = 0; + } + + this.add(this, highCol); + this.add(this, lowCol); + } + + private void multiply(int[] left, int[] right, int[] high, int[] low) { + if (left != null && right != null) { + for (int i = 0; i < length; i++) { + long result = (long) left[i] * right[i]; + high[i] = (int) (result >> 31); + low[i] = (int) (result & 0x7fffffff); + } + } else { + for (int i = 0; i < length; i++) { + high[i] = 0; + low[i] = 0; + } + } + } + + public void multiply(Int128V2 left, Int128V2ColumnVector right) { + Int128V2 unsignedLeft = new Int128V2(); + + if (highCol == null || highCol.i4.length < length) { + unsignedRight = new Int128V2ColumnVector(length); + + highCol = new Int128V2ColumnVector(length); + lowCol = new Int128V2ColumnVector(length); + } + + unsignedLeft.abs(left); + unsignedRight.abs(right); + + this.set(Int128V2.ZERO); + + multiplyAndAdd(unsignedLeft.i4, unsignedLeft.i4, unsignedLeft.i4, unsignedLeft.i4, unsignedLeft.i4, + unsignedRight.i0, unsignedRight.i1, unsignedRight.i2, unsignedRight.i3, unsignedRight.i4); + + multiplyAndAdd(unsignedLeft.i3, unsignedLeft.i3, unsignedLeft.i3, unsignedLeft.i3, 0, + unsignedRight.i1, unsignedRight.i2, unsignedRight.i3, unsignedRight.i4, null); + + multiplyAndAdd(unsignedLeft.i2, unsignedLeft.i2, unsignedLeft.i2, 0, 0, + unsignedRight.i2, unsignedRight.i3, unsignedRight.i4, null, null); + + multiplyAndAdd(unsignedLeft.i1, unsignedLeft.i1, 0, 0, 0, + unsignedRight.i3, unsignedRight.i4, null, null, null); + + multiplyAndAdd(unsignedLeft.i0, 0, 0, 0, 0, + unsignedRight.i4, null, null, null, null); + + // Handle signs + for (int i = 0; i < length; i++) { + if (((left.i0 ^ right.i0[i]) & 4) == 4) { + this.negate(this, i); + } + } + } + + private void multiplyAndAdd(int l4, int l3, int l2, int l1, int l0, + int[] r4, int[] r3, int[] r2, int[] r1, int[] r0) { + if (r4 != null) { + for (int i = 0; i < length; i++) { + lowCol.i0[i] = (l4 * r4[i]) & 0x7fffffff; + } + } else { + for (int i = 0; i < length; i++) { + lowCol.i0[i] = 0; + } + } + + multiply(l3, r3, highCol.i0, lowCol.i1); + multiply(l2, r2, highCol.i1, lowCol.i2); + multiply(l1, r1, highCol.i2, lowCol.i3); + multiply(l0, r0, highCol.i3, lowCol.i4); + + for (int i = 0; i < length; i++) { + highCol.i4[i] = 0; + } + + this.add(this, highCol); + this.add(this, lowCol); + } + + private void multiply(int left, int[] right, int[] high, int[] low) { + if (right != null) { + for (int i = 0; i < length; i++) { + long result = (long) left * right[i]; + high[i] = (int) (result >> 31); + low[i] = (int) (result & 0x7fffffff); + } + } else { + for (int i = 0; i < length; i++) { + high[i] = 0; + low[i] = 0; + } + } + } + + private void multiplyBoolean(Int128V2ColumnVector left, Int128V2ColumnVector right) { + for (int i = 0; i < length; i++) { + this.i4[i] = left.i4[i] * right.i4[i]; + this.i3[i] = left.i3[i] * right.i3[i]; + this.i2[i] = left.i2[i] * right.i2[i]; + this.i1[i] = left.i1[i] * right.i1[i]; + this.i0[i] = left.i0[i] * right.i0[i]; + } + } + + public void multiplyByTen(Int128V2ColumnVector left) { + // x * 10 = x * 8 + x * 2 + Int128V2ColumnVector toAdd = new Int128V2ColumnVector(length); + toAdd.shiftLeft(left, 3); + this.shiftLeft(left, 1); + this.add(left, toAdd); + } + + public void multiplyByTen(Int128V2ColumnVector left, int times) { + multiply(Int128V2.POWER_OF_TEN[times], left); + } + + /** + * @see Int128V2#divide(Int128V2, Int128V2, Int128V2) + * @param leftVector + * @param rightVector + * @param remainderVector + */ + public void divide(Int128V2ColumnVector leftVector, Int128V2ColumnVector rightVector, + Int128V2ColumnVector remainderVector) { + if (this == leftVector) { + leftVector = leftVector.copy(); + } + if (this == rightVector) { + rightVector = rightVector.copy(); + } + + for (int i = 0; i < length; i++) { + if (rightVector.equals(Int128V2.ZERO, i)) { + } else { + if (leftVector.getSignum(i) == -1) { + leftVector.negate(leftVector, i); + if (rightVector.getSignum(i) == -1) { + rightVector.negate(rightVector, i); + + BigInteger left = leftVector.getBigInteger(i); + BigInteger right = rightVector.getBigInteger(i); + BigInteger result = left.divide(right); + this.setBigInteger(i, result); + + rightVector.negate(rightVector, i); + } else { + BigInteger left = leftVector.getBigInteger(i); + BigInteger right = rightVector.getBigInteger(i); + BigInteger result = left.divide(right); + this.setBigInteger(i, result); + this.negate(this, i); + } + leftVector.negate(leftVector, i); + } else { + if (rightVector.getSignum(i) == -1) { + rightVector.negate(rightVector, i); + + BigInteger left = leftVector.getBigInteger(i); + BigInteger right = rightVector.getBigInteger(i); + BigInteger result = left.divide(right); + this.setBigInteger(i, result); + this.negate(this, i); + + rightVector.negate(rightVector, i); + } else { + BigInteger left = leftVector.getBigInteger(i); + BigInteger right = rightVector.getBigInteger(i); + BigInteger result = left.divide(right); + this.setBigInteger(i, result); + } + } + } + } + } + + private void setBigInteger(int i, BigInteger bigInteger) { + byte[] b = new byte[16]; + byte[] byteArray = bigInteger.toByteArray(); + int len = byteArray.length; + for (int j = 0; j < len; j++) { + b[j + (16 - len)] = byteArray[j]; + } + + final int castMask = 0xff; + this.i4[i] = (((b[12] & castMask) << 24) | ((b[13] & castMask) << 16) | + ((b[14] & castMask) << 8) | b[15] ) & Integer.MAX_VALUE; + this.i3[i] = (((b[ 8] & castMask) << 25) | ((b[ 9] & castMask) << 17) | + ((b[10] & castMask) << 9) | ((b[11] & castMask) << 1) | ((b[12] & castMask) >>> 7)) & Integer.MAX_VALUE; + this.i2[i] = (((b[ 4] & castMask) << 26) | ((b[ 5] & castMask) << 18) | + ((b[ 6] & castMask) << 10) | ((b[ 7] & castMask) << 2) | ((b[ 8] & castMask) >>> 6)) & Integer.MAX_VALUE; + this.i1[i] = (((b[ 0] & castMask) << 27) | ((b[ 1] & castMask) << 19) | + ((b[ 2] & castMask) << 11) | ((b[ 3] & castMask) << 3) | ((b[ 4] & castMask) >>> 5)) & Integer.MAX_VALUE; + this.i0[i] = (((b[ 0] & castMask) >>> 4)); + } + + private BigInteger getBigInteger(int i) { + byte[] b = new byte[16]; + + b[15] = (byte) (this.i4[i]); + b[14] = (byte) (this.i4[i] >>> 8); + b[13] = (byte) (this.i4[i] >>> 16); + b[12] = (byte) ((this.i3[i] << 7) | ((this.i4[i] & Integer.MAX_VALUE) >>> 24)); + + b[11] = (byte) (this.i3[i] >>> 1); + b[10] = (byte) (this.i3[i] >>> 9); + b[ 9] = (byte) (this.i3[i] >>> 17); + b[ 8] = (byte) ((this.i2[i] << 6) | ((this.i3[i] & Integer.MAX_VALUE) >>> 25)); + + b[ 7] = (byte) (this.i2[i] >>> 2); + b[ 6] = (byte) (this.i2[i] >>> 10); + b[ 5] = (byte) (this.i2[i] >>> 18); + b[ 4] = (byte) ((this.i1[i] << 5) | ((this.i2[i] & Integer.MAX_VALUE) >>> 26)); + + b[ 3] = (byte) (this.i1[i] >>> 3); + b[ 2] = (byte) (this.i1[i] >>> 11); + b[ 1] = (byte) (this.i1[i] >>> 19); + b[ 0] = (byte) ((this.i0[i] << 4) | ((this.i1[i] & Integer.MAX_VALUE) >>> 27)); + + return new BigInteger(b); + } + + public void divideByTen(Int128V2ColumnVector left, Int128V2ColumnVector remainder, int times) { + for (int i = 0; i < times; i++) { + divideByTen(left, remainder); + } + } + + public void divideByTen(Int128V2ColumnVector left, Int128V2ColumnVector remainder) { + remainder.set(left); + Int128V2ColumnVector copy = new Int128V2ColumnVector(length); + copy.set(Int128V2.FIVE_TIMES_2_POWER_124); + this.set(Int128V2.ZERO); + for (int i = 0; i < 124; i++) { + this.shiftLeft(this, 1); + for (int j = 0; j < length; j++) { + if (remainder.compareTo(copy, j) >= 0) { + remainder.subtract(remainder, copy, j); + this.i4[j] |= 1; + } + } + copy.shiftRight(copy, 1); + } + } + + /* + * Bitwise methods + */ + private void shiftLeft(Int128V2ColumnVector left, int right) { + if (right > 31) { + shiftLeft(left, 31); + shiftLeft(left, right - 31); + return; + } + + // Shift + for (int i = 0; i < length; i++) { + this.i0[i] = ((left.i0[i] << right) | (left.i1[i] >>> (31 - right))) & Integer.MAX_VALUE; + } + for (int i = 0; i < length; i++) { + this.i1[i] = ((left.i1[i] << right) | (left.i2[i] >>> (31 - right))) & Integer.MAX_VALUE; + } + for (int i = 0; i < length; i++) { + this.i2[i] = ((left.i2[i] << right) | (left.i3[i] >>> (31 - right))) & Integer.MAX_VALUE; + } + for (int i = 0; i < length; i++) { + this.i3[i] = ((left.i3[i] << right) | (left.i4[i] >>> (31 - right))) & Integer.MAX_VALUE; + } + for (int i = 0; i < length; i++) { + this.i4[i] = (left.i4[i] << right) & Integer.MAX_VALUE; + } + + // Overflow + for (int i = 0; i < length; i++) { + int value = this.i0[i]; + this.isNull[i] |= value >= 8 || value < -8; + } + } + + private void shiftLeftIgnoreOverflow(Int128V2ColumnVector left, int right) { + if (right > 31) { + shiftLeftIgnoreOverflow(left, 31); + shiftLeftIgnoreOverflow(left, right - 31); + return; + } + for (int i = 0; i < length; i++) { + this.i0[i] = ((left.i0[i] << right) | (left.i1[i] >>> (31 - right))) & Integer.MAX_VALUE; + } + for (int i = 0; i < length; i++) { + this.i1[i] = ((left.i1[i] << right) | (left.i2[i] >>> (31 - right))) & Integer.MAX_VALUE; + } + for (int i = 0; i < length; i++) { + this.i2[i] = ((left.i2[i] << right) | (left.i3[i] >>> (31 - right))) & Integer.MAX_VALUE; + } + for (int i = 0; i < length; i++) { + this.i3[i] = ((left.i3[i] << right) | (left.i4[i] >>> (31 - right))) & Integer.MAX_VALUE; + } + for (int i = 0; i < length; i++) { + this.i4[i] = (left.i4[i] << right) & Integer.MAX_VALUE; + } + } + + private void shiftRight(Int128V2ColumnVector left, int right) { + if (right > 31) { + shiftRight(left, 31); + shiftRight(left, right - 31); + return; + } + for (int i = 0; i < length; i++) { + this.i4[i] = ((left.i4[i] >>> right) | (left.i3[i] << (31 - right))) & Integer.MAX_VALUE; + } + for (int i = 0; i < length; i++) { + this.i3[i] = ((left.i3[i] >>> right) | (left.i2[i] << (31 - right))) & Integer.MAX_VALUE; + } + for (int i = 0; i < length; i++) { + this.i2[i] = ((left.i2[i] >>> right) | (left.i1[i] << (31 - right))) & Integer.MAX_VALUE; + } + for (int i = 0; i < length; i++) { + this.i1[i] = ((left.i1[i] >>> right) | (left.i0[i] << (31 - right))) & Integer.MAX_VALUE; + } + for (int i = 0; i < length; i++) { + this.i0[i] = (left.i0[i] >>> right) & Integer.MAX_VALUE; + } + + for (int i = 0; i < length; i++) { + this.isNull[i] |= (this.i0[i] >= 8 || this.i0[i] < -8); + } + } + + /* + * Sign methods + */ + public void negate(Int128V2ColumnVector left) { + for (int i = 0; i < length; i++) { + this.i4[i] = -left.i4[i]; + } + for (int i = 0; i < length; i++) { + this.i3[i] = -left.i3[i] - (this.i4[i] >>> 31); + } + for (int i = 0; i < length; i++) { + this.i2[i] = -left.i2[i] - (this.i3[i] >>> 31); + } + for (int i = 0; i < length; i++) { + this.i1[i] = -left.i1[i] - (this.i2[i] >>> 31); + } + for (int i = 0; i < length; i++) { + this.i0[i] = -left.i0[i] - (this.i1[i] >>> 31); + } + + for (int i = 0; i < length; i++) { + this.i4[i] &= Integer.MAX_VALUE; + this.i3[i] &= Integer.MAX_VALUE; + this.i2[i] &= Integer.MAX_VALUE; + this.i1[i] &= Integer.MAX_VALUE; + } + + for (int i = 0; i < length; i++) { + this.isNull[i] |= (this.i0[i] >= 8 || this.i0[i] < -8); + } + } + + public void negate(Int128V2ColumnVector left, int i) { + this.i4[i] = -left.i4[i]; + this.i3[i] = -left.i3[i] - (this.i4[i] >>> 31); + this.i2[i] = -left.i2[i] - (this.i3[i] >>> 31); + this.i1[i] = -left.i1[i] - (this.i2[i] >>> 31); + this.i0[i] = -left.i0[i] - (this.i1[i] >>> 31); + + this.i4[i] &= Integer.MAX_VALUE; + this.i3[i] &= Integer.MAX_VALUE; + this.i2[i] &= Integer.MAX_VALUE; + this.i1[i] &= Integer.MAX_VALUE; + + this.isNull[i] |= (this.i0[i] >= 8 || this.i0[i] < -8); + } + + public int getSignum(int i) { + return (i0[i] >> 3) | 1; + } + + public void abs(Int128V2ColumnVector left) { + for (int i = 0; i < length; i++) { + if (left.getSignum(i) == -1) { + negate(left, i); + } else { + set(left, i); + } + } + } +} diff --git storage-api/src/test/org/apache/hadoop/hive/ql/exec/vector/TestDecimalV2ColumnVector.java storage-api/src/test/org/apache/hadoop/hive/ql/exec/vector/TestDecimalV2ColumnVector.java new file mode 100644 index 0000000..acbd9a2 --- /dev/null +++ storage-api/src/test/org/apache/hadoop/hive/ql/exec/vector/TestDecimalV2ColumnVector.java @@ -0,0 +1,153 @@ +package org.apache.hadoop.hive.ql.exec.vector; + +import junit.framework.Assert; +import org.apache.hadoop.hive.common.type.DecimalV2; +import org.apache.hadoop.hive.common.type.Int128V2; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class TestDecimalV2ColumnVector { + @Test + public void testToString() { + // Short decimal + DecimalV2ColumnVector vector1 = new DecimalV2ColumnVector(10, 2); + vector1.set(new DecimalV2("1", 10, 2), 0); + vector1.set(new DecimalV2("1", 2, 0), 1); + vector1.set(new DecimalV2("1.23", 10, 2), 2); + vector1.set(new DecimalV2("-1.23", 10, 2), 3); + vector1.set(new DecimalV2("0.5", 10, 2), 4); + vector1.set(new DecimalV2("0.05", 10, 2), 5); + vector1.set(new DecimalV2("1.234", 10, 2), 6); + + assertEquals("1", vector1.toString(0)); + assertEquals("1", vector1.toString(1)); + assertEquals("1.23", vector1.toString(2)); + assertEquals("-1.23", vector1.toString(3)); + assertEquals("0.5", vector1.toString(4)); + assertEquals("0.05", vector1.toString(5)); + assertEquals("1.23", vector1.toString(6)); + + // Long decimal + DecimalV2ColumnVector vector2 = new DecimalV2ColumnVector(38, 10); + vector2.set(new DecimalV2("9999999999999999999999999999.9999999999", 38, 10), 0); + vector2.set(new DecimalV2("-9999999999999999999999999999.9999999999", 38, 10), 1); + + assertEquals("9999999999999999999999999999.9999999999", vector2.toString(0)); + assertEquals("-9999999999999999999999999999.9999999999", vector2.toString(1)); + } + + @Test + public void testAdd() { + DecimalV2ColumnVector left = new DecimalV2ColumnVector(10, 2); + left.set(new DecimalV2("1.2", 10, 2), 0); + left.set(new DecimalV2("-1.2", 10, 2), 1); + left.set(new DecimalV2("-1.23", 10, 2), 2); + left.set(new DecimalV2("-1.23", 10, 2), 3); + left.set(new DecimalV2("1.2", 11, 3), 4); + left.set(new DecimalV2("1.2", 10, 2), 5); + + DecimalV2ColumnVector right = new DecimalV2ColumnVector(10, 2); + right.set(new DecimalV2("1.23", 10, 2), 0); + right.set(new DecimalV2("1.23", 10, 2), 1); + right.set(new DecimalV2("1.23", 10, 2), 2); + right.set(new DecimalV2("1.2", 10, 2), 3); + right.set(new DecimalV2("1.2", 10, 2), 4); + right.set(new DecimalV2("1.2", 11, 3), 5); + + DecimalV2ColumnVector result = new DecimalV2ColumnVector(10, 2); + result.add(left, right); + DecimalV2 decimalV2 = new DecimalV2(); + assertEquals(new DecimalV2("2.43", 10, 2), result.get(decimalV2, 0)); + assertEquals(new DecimalV2("0.03", 10, 2), result.get(decimalV2, 1)); + assertEquals(new DecimalV2("0.00", 10, 2), result.get(decimalV2, 2)); + assertEquals(new DecimalV2("-0.03", 10, 2), result.get(decimalV2, 3)); + assertEquals(new DecimalV2("2.4", 11, 3), result.get(decimalV2, 4)); + assertEquals(new DecimalV2("2.4", 11, 3), result.get(decimalV2, 5)); + } + + @Test + public void testSubtract() { + DecimalV2ColumnVector left = new DecimalV2ColumnVector(10, 2); + left.set(new DecimalV2("1.2", 10, 2), 0); + left.set(new DecimalV2("1.23", 10, 2), 1); + left.set(new DecimalV2("1.23", 10, 2), 2); + left.set(new DecimalV2("1.2", 10, 2), 3); + left.set(new DecimalV2("2.76", 11, 3), 4); + left.set(new DecimalV2("2.76", 10, 2), 5); + + DecimalV2ColumnVector right = new DecimalV2ColumnVector(10, 2); + right.set(new DecimalV2("-1.23", 10, 2), 0); + right.set(new DecimalV2("1.2", 10, 2), 1); + right.set(new DecimalV2("1.23", 10, 2), 2); + right.set(new DecimalV2("1.23", 10, 2), 3); + right.set(new DecimalV2("2.76", 10, 2), 4); + right.set(new DecimalV2("2.76", 11, 3), 5); + + DecimalV2ColumnVector result = new DecimalV2ColumnVector(10, 2); + result.subtract(left, right); + DecimalV2 decimalV2 = new DecimalV2(); + assertEquals(new DecimalV2("2.43", 10, 2), result.get(decimalV2, 0)); + assertEquals(new DecimalV2("0.03", 10, 2), result.get(decimalV2, 1)); + assertEquals(new DecimalV2("0.00", 10, 2), result.get(decimalV2, 2)); + assertEquals(new DecimalV2("-0.03", 10, 2), result.get(decimalV2, 3)); + assertEquals(new DecimalV2("0", 11, 3), result.get(decimalV2, 4)); + assertEquals(new DecimalV2("0", 11, 3), result.get(decimalV2, 5)); + } + + @Test + public void testMultiply() { + DecimalV2ColumnVector left = new DecimalV2ColumnVector(10, 2); + left.set(new DecimalV2("1.2", 10, 2), 0); + left.set(new DecimalV2("1.2", 10, 2), 1); + left.set(new DecimalV2("1.2", 10, 2), 2); + left.set(new DecimalV2("-1.2", 10, 2), 3); + left.set(new DecimalV2("-1.2", 11, 3), 4); + + DecimalV2ColumnVector right = new DecimalV2ColumnVector(10, 2); + right.set(new DecimalV2("2.3", 10, 2), 0); + right.set(new DecimalV2("0", 10, 2), 1); + right.set(new DecimalV2("-2.3", 10, 2), 2); + right.set(new DecimalV2("2.3", 10, 2), 3); + right.set(new DecimalV2("-2.3", 10, 2), 4); + + DecimalV2ColumnVector result = new DecimalV2ColumnVector(10, 2); + result.multiply(left, right); + DecimalV2 decimalV2 = new DecimalV2(); + assertEquals(new DecimalV2("2.76", 10, 2), result.get(decimalV2, 0)); + assertEquals(new DecimalV2("0", 10, 2), result.get(decimalV2, 1)); + assertEquals(new DecimalV2("-2.76", 10, 2), result.get(decimalV2, 2)); + assertEquals(new DecimalV2("-2.76", 10, 2), result.get(decimalV2, 3)); + assertEquals(new DecimalV2("2.76", 11, 3), result.get(decimalV2, 4)); + } + + @Test + public void testDivide() { + DecimalV2ColumnVector left = new DecimalV2ColumnVector(10, 2); + left.set(new DecimalV2("1", 10, 2), 0); + left.set(new DecimalV2("1", 10, 2), 1); + left.set(new DecimalV2("-10", 10, 2), 2); + left.set(new DecimalV2("-1.5", 10, 2), 3); + + DecimalV2ColumnVector right = new DecimalV2ColumnVector(10, 2); + right.set(new DecimalV2("3", 10, 2), 0); + right.set(new DecimalV2("-10", 10, 2), 1); + right.set(new DecimalV2("-1", 10, 2), 2); + right.set(new DecimalV2("0.5", 10, 2), 3); + + DecimalV2ColumnVector result = new DecimalV2ColumnVector(10, 2); + result.divide(left, right); + DecimalV2 decimalV2 = new DecimalV2(); + assertEquals(new DecimalV2("0.3333333333333", 20, 14), result.get(decimalV2, 0)); + assertEquals(new DecimalV2("-0.1", 20, 14), result.get(decimalV2, 1)); + assertEquals(new DecimalV2("10", 20, 14), result.get(decimalV2, 2)); + assertEquals(new DecimalV2("-3", 20, 14), result.get(decimalV2, 3)); + } + + @Test + public void testRemainder() { + DecimalV2 result = new DecimalV2(); + result.remainder(new DecimalV2("4", 10, 2), new DecimalV2("3", 10, 2)); + Assert.assertEquals(new DecimalV2("1", 10, 2), result); + } +} \ No newline at end of file