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..adcf614 --- /dev/null +++ common/src/test/org/apache/hadoop/hive/common/type/TestDecimalV2.java @@ -0,0 +1,144 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.hive.common.type; + +import junit.framework.Assert; +import org.apache.hadoop.hive.common.type.decimalv2.DecimalV2; +import org.apache.hadoop.hive.common.type.decimalv2.Unscaled; +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 Unscaled("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..727aa78 --- /dev/null +++ common/src/test/org/apache/hadoop/hive/common/type/TestHiveDecimalV2.java @@ -0,0 +1,218 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package 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.apache.hadoop.hive.common.type.decimalv2.HiveDecimalV2; +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/TestUnscaled.java common/src/test/org/apache/hadoop/hive/common/type/TestUnscaled.java new file mode 100644 index 0000000..c53469b --- /dev/null +++ common/src/test/org/apache/hadoop/hive/common/type/TestUnscaled.java @@ -0,0 +1,285 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.hive.common.type; + +import junit.framework.Assert; +import org.apache.hadoop.hive.common.type.decimalv2.Unscaled; +import org.junit.Test; + +public class TestUnscaled { + @Test + public void testAdd() { + // UnsignedInt128 + int + Unscaled left = new Unscaled("1"); + Unscaled result = new Unscaled(); + result.add(left, 2); + Assert.assertEquals(new Unscaled("3"), result); + + // UnsignedInt128 + UnsignedInt128 + left = new Unscaled("1"); + result.add(left, new Unscaled("2")); + Assert.assertEquals(new Unscaled("3"), result); + + // Carry + left = new Unscaled("9"); + result.add(left, 1); + Assert.assertEquals(new Unscaled("10"), result); + + // Very large + left = new Unscaled("99999999999999999999999999999999999998"); + result.add(left, 1); + Assert.assertEquals(new Unscaled("99999999999999999999999999999999999999"), result); + + // Very large carry + left = new Unscaled("89999999999999999999999999999999999999"); + result.add(left, 1); + Assert.assertEquals(new Unscaled("90000000000000000000000000000000000000"), result); + + // Overflow + try { + left = new Unscaled("99999999999999999999999999999999999999"); + result.add(left, new Unscaled("99999999999999999999999999999999999999")); + Assert.fail("Should not reach here"); + } catch (IllegalArgumentException ex) { + // Do nothing + } + + // -1 + 2 = 1 + left = new Unscaled("-1"); + result.add(left, new Unscaled("2")); + Assert.assertEquals(new Unscaled("1"), result); + + // -2 + 1 = -1 + left = new Unscaled("-2"); + result.add(left, new Unscaled("1")); + Assert.assertEquals(new Unscaled("-1"), result); + + // 1 + -2 = -1 + left = new Unscaled("1"); + result.add(left, new Unscaled("-2")); + Assert.assertEquals(new Unscaled("-1"), result); + + // 2 + -1 = 1 + left = new Unscaled("2"); + result.add(left, new Unscaled("-1")); + Assert.assertEquals(new Unscaled("1"), result); + } + + @Test + public void testSubtract() { + // UnsignedInt128 - UnsignedInt128 + Unscaled left = new Unscaled("3"); + Unscaled result = new Unscaled(); + result.subtract(left, new Unscaled("2")); + Assert.assertEquals(new Unscaled("1"), result); + + // Borrow + left = new Unscaled("10"); + result.subtract(left, new Unscaled("1")); + Assert.assertEquals(new Unscaled("9"), result); + + // Very large + left = new Unscaled("99999999999999999999999999999999999999"); + result.subtract(left, new Unscaled("1")); + Assert.assertEquals(new Unscaled("99999999999999999999999999999999999998"), result); + + // Very large borrow + left = new Unscaled("90000000000000000000000000000000000000"); + result.subtract(left, new Unscaled("1")); + Assert.assertEquals(new Unscaled("89999999999999999999999999999999999999"), result); + + // 1 - 2 = -1 + left = new Unscaled("1"); + result.subtract(left, new Unscaled("2")); + Assert.assertEquals(new Unscaled("-1"), result); + + // 2 - 1 = 1 + left = new Unscaled("2"); + result.subtract(left, new Unscaled("1")); + Assert.assertEquals(new Unscaled("1"), result); + + // -1 - -2 = 1 + left = new Unscaled("-1"); + result.subtract(left, new Unscaled("-2")); + Assert.assertEquals(new Unscaled("1"), result); + + // -2 - -1 = -1 + left = new Unscaled("-2"); + result.subtract(left, new Unscaled("-1")); + Assert.assertEquals(new Unscaled("-1"), result); + } + + @Test + public void testNegate() { + Unscaled left = new Unscaled("0"); + Unscaled result = new Unscaled(); + result.negate(left); + Assert.assertEquals(new Unscaled("0"), result); + + left = new Unscaled("1"); + result.negate(left); + Assert.assertEquals(new Unscaled("-1"), result); + + left = new Unscaled("-1"); + result.negate(left); + Assert.assertEquals(new Unscaled("1"), result); + } + + @Test + public void testToString() { + // Smallest + Assert.assertEquals("0", new Unscaled("0").toString()); + + // Simple + Assert.assertEquals("12345", new Unscaled("12345").toString()); + + // Various + Assert.assertEquals("12345678901234567890123456789012345678", + new Unscaled("12345678901234567890123456789012345678").toString()); + + // Largest + Assert.assertEquals("99999999999999999999999999999999999999", + new Unscaled("99999999999999999999999999999999999999").toString()); + + // Negative simple + Assert.assertEquals("-12345", new Unscaled("-12345").toString()); + + // Negative various + Assert.assertEquals("-12345678901234567890123456789012345678", + new Unscaled("-12345678901234567890123456789012345678").toString()); + + // Negative largest + Assert.assertEquals("-99999999999999999999999999999999999999", + new Unscaled("-99999999999999999999999999999999999999").toString()); + } + + @Test + public void testDivideByTen() { + Unscaled left = new Unscaled("1"); + Unscaled result = new Unscaled(); + Unscaled remainder = new Unscaled(); + result.divideByTen(left, remainder); + Assert.assertEquals(new Unscaled("0"), result); + Assert.assertEquals(1, remainder.v4); + + left = new Unscaled("10"); + result.divideByTen(left, remainder); + Assert.assertEquals(new Unscaled("1"), result); + Assert.assertEquals(0, remainder.v4); + + left = new Unscaled("20"); + result.divideByTen(left, remainder); + Assert.assertEquals(new Unscaled("2"), result); + Assert.assertEquals(0, remainder.v4); + + left = new Unscaled("12"); + result.divideByTen(left, remainder); + Assert.assertEquals(new Unscaled("1"), result); + Assert.assertEquals(2, remainder.v4); + + left = new Unscaled("99999999999999999999999999999999999999"); + result.divideByTen(left, remainder); + Assert.assertEquals(new Unscaled("9999999999999999999999999999999999999"), result); + Assert.assertEquals(9, remainder.v4); + } + + @Test + public void testMultiply() { + Unscaled left = new Unscaled("3"); + Unscaled result = new Unscaled(); + result.multiply(left, new Unscaled("5")); + Assert.assertEquals(new Unscaled("15"), result); + + left = new Unscaled("9"); + result.multiply(left, new Unscaled("9")); + Assert.assertEquals(new Unscaled("81"), result); + + left = new Unscaled("0"); + result.multiply(left, new Unscaled("0")); + Assert.assertEquals(new Unscaled("0"), result); + } + + @Test + public void testDivide() { + Unscaled left = new Unscaled("16"); + Unscaled result = new Unscaled(); + result.divide(left, new Unscaled("5")); + Assert.assertEquals(new Unscaled("3"), result); + + left = new Unscaled("83"); + result.divide(left, new Unscaled("9")); + Assert.assertEquals(new Unscaled("9"), result); + + left = new Unscaled("7"); + result.divide(left, new Unscaled("7")); + Assert.assertEquals(new Unscaled("1"), result); + + left = new Unscaled("0"); + result.divide(left, new Unscaled("1")); + Assert.assertEquals(new Unscaled("0"), result); + + left = new Unscaled("13"); + result.divide(left, new Unscaled("3")); + Assert.assertEquals(new Unscaled(String.valueOf(13 / 3)), result); + + left = new Unscaled("-13"); + result.divide(left, new Unscaled("-3")); + Assert.assertEquals(new Unscaled(String.valueOf(-13 / -3)), result); + + left = new Unscaled("-13"); + result.divide(left, new Unscaled("3")); + Assert.assertEquals(new Unscaled(String.valueOf(-13 / 3)), result); + + left = new Unscaled("13"); + result.divide(left, new Unscaled("-3")); + Assert.assertEquals(new Unscaled(String.valueOf(13 / -3)), result); + } + + @Test + public void testRemainder() { + Unscaled left = new Unscaled("16"); + Unscaled result = new Unscaled(); + result.remainder(left, new Unscaled("5")); + Assert.assertEquals(new Unscaled("1"), result); + + left = new Unscaled("83"); + result.remainder(left, new Unscaled("9")); + Assert.assertEquals(new Unscaled("2"), result); + + left = new Unscaled("7"); + result.remainder(left, new Unscaled("7")); + Assert.assertEquals(new Unscaled("0"), result); + + left = new Unscaled("0"); + result.remainder(left, new Unscaled("1")); + Assert.assertEquals(new Unscaled("0"), result); + + left = new Unscaled("13"); + result.remainder(left, new Unscaled("3")); + Assert.assertEquals(new Unscaled(String.valueOf(13 % 3)), result); + + left = new Unscaled("-13"); + result.remainder(left, new Unscaled("-3")); + Assert.assertEquals(new Unscaled(String.valueOf(-13 % -3)), result); + + left = new Unscaled("-13"); + result.remainder(left, new Unscaled("3")); + Assert.assertEquals(new Unscaled(String.valueOf(-13 % 3)), result); + + left = new Unscaled("13"); + result.remainder(left, new Unscaled("-3")); + Assert.assertEquals(new Unscaled(String.valueOf(13 % -3)), result); + } +} diff --git itests/hive-jmh/src/main/java/org/apache/hive/benchmark/vectorization/AbstractDecimalExpression.java itests/hive-jmh/src/main/java/org/apache/hive/benchmark/vectorization/AbstractDecimalExpression.java new file mode 100644 index 0000000..48ced0a --- /dev/null +++ itests/hive-jmh/src/main/java/org/apache/hive/benchmark/vectorization/AbstractDecimalExpression.java @@ -0,0 +1,66 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hive.benchmark.vectorization; + +import org.apache.hadoop.hive.common.type.HiveDecimal; +import org.apache.hadoop.hive.common.type.decimalv2.DecimalV2; +import org.apache.hadoop.hive.ql.exec.vector.DecimalColumnVector; +import org.apache.hadoop.hive.ql.exec.vector.decimalv2.DecimalV2ColumnVector; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Warmup; + +import java.util.Random; +import java.util.concurrent.TimeUnit; + +public abstract class AbstractDecimalExpression extends AbstractExpression { + private static final int DECIMAL_ITER_TIME = 100000; + protected static final int DECIMAL_BATCH_SIZE = 128; + + @Benchmark + @Warmup(iterations = 2, time = 2, timeUnit = TimeUnit.MILLISECONDS) + @Measurement(iterations = 2, time = 2, timeUnit = TimeUnit.MILLISECONDS) + public void bench() { + for (int i = 0; i < DECIMAL_ITER_TIME; i++) { + expression.evaluate(rowBatch); + } + } + + 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(i, new DecimalV2(Long.toString(random.nextLong()))); + } else { + columnVector.set(i, new DecimalV2(Integer.toString(random.nextInt()))); + } + } + return columnVector; + } +} 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..42856b1 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,9 +13,17 @@ */ package org.apache.hive.benchmark.vectorization; +import org.apache.hadoop.hive.ql.exec.vector.DecimalColumnVector; +import org.apache.hadoop.hive.ql.exec.vector.decimalv2.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.DecimalV2ColAddDecimalV2Column; +import org.apache.hadoop.hive.ql.exec.vector.expressions.DecimalV2ColDivideV2DecimalColumn; +import org.apache.hadoop.hive.ql.exec.vector.expressions.DecimalV2ColMultiplyDecimalV2Column; import org.apache.hadoop.hive.ql.exec.vector.expressions.LongColDivideLongColumn; +import org.apache.hadoop.hive.ql.exec.vector.expressions.gen.DecimalColAddDecimalColumn; +import org.apache.hadoop.hive.ql.exec.vector.expressions.gen.DecimalColDivideDecimalColumn; +import org.apache.hadoop.hive.ql.exec.vector.expressions.gen.DecimalColMultiplyDecimalColumn; 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; @@ -104,6 +112,70 @@ public void setup() { } } + public static class DecimalColAddDecimalColColumnBench extends AbstractDecimalExpression { + @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 AbstractDecimalExpression { + @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 DecimalV2ColAddDecimalV2Column(0, 1, 2); + } + } + + public static class DecimalColDivideDecimalColColumnBench extends AbstractDecimalExpression { + @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 AbstractDecimalExpression { + @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 DecimalV2ColDivideV2DecimalColumn(0, 1, 2); + } + } + + public static class DecimalColMultiplyDecimalColColumnBench extends AbstractDecimalExpression { + @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 AbstractDecimalExpression { + @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 DecimalV2ColMultiplyDecimalV2Column(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/DecimalV2ColAddDecimalV2Column.java ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ColAddDecimalV2Column.java new file mode 100644 index 0000000..635db91 --- /dev/null +++ ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ColAddDecimalV2Column.java @@ -0,0 +1,91 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.hive.ql.exec.vector.expressions; + +import org.apache.hadoop.hive.ql.exec.vector.decimalv2.DecimalV2ColumnVector; +import org.apache.hadoop.hive.ql.exec.vector.VectorExpressionDescriptor; +import org.apache.hadoop.hive.ql.exec.vector.VectorizedRowBatch; + +public class DecimalV2ColAddDecimalV2Column extends VectorExpression { + + private static final long serialVersionUID = 1L; + + private int colNum1; + private int colNum2; + private int outputColumn; + + public DecimalV2ColAddDecimalV2Column(int colNum1, int colNum2, int outputColumn) { + this.colNum1 = colNum1; + this.colNum2 = colNum2; + this.outputColumn = outputColumn; + this.outputType = "decimal"; + } + + public DecimalV2ColAddDecimalV2Column() { + 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/DecimalV2ColDivideV2DecimalColumn.java ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ColDivideV2DecimalColumn.java new file mode 100644 index 0000000..0e9863f --- /dev/null +++ ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ColDivideV2DecimalColumn.java @@ -0,0 +1,91 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.hive.ql.exec.vector.expressions; + +import org.apache.hadoop.hive.ql.exec.vector.decimalv2.DecimalV2ColumnVector; +import org.apache.hadoop.hive.ql.exec.vector.VectorExpressionDescriptor; +import org.apache.hadoop.hive.ql.exec.vector.VectorizedRowBatch; + +public class DecimalV2ColDivideV2DecimalColumn extends VectorExpression { + + private static final long serialVersionUID = 1L; + + private int colNum1; + private int colNum2; + private int outputColumn; + + public DecimalV2ColDivideV2DecimalColumn(int colNum1, int colNum2, int outputColumn) { + this.colNum1 = colNum1; + this.colNum2 = colNum2; + this.outputColumn = outputColumn; + this.outputType = "decimal"; + } + + public DecimalV2ColDivideV2DecimalColumn() { + 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/DecimalV2ColMultiplyDecimalV2Column.java ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ColMultiplyDecimalV2Column.java new file mode 100644 index 0000000..2005bb4 --- /dev/null +++ ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ColMultiplyDecimalV2Column.java @@ -0,0 +1,91 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.hive.ql.exec.vector.expressions; + +import org.apache.hadoop.hive.ql.exec.vector.decimalv2.DecimalV2ColumnVector; +import org.apache.hadoop.hive.ql.exec.vector.VectorExpressionDescriptor; +import org.apache.hadoop.hive.ql.exec.vector.VectorizedRowBatch; + +public class DecimalV2ColMultiplyDecimalV2Column extends VectorExpression { + + private static final long serialVersionUID = 1L; + + private int colNum1; + private int colNum2; + private int outputColumn; + + public DecimalV2ColMultiplyDecimalV2Column(int colNum1, int colNum2, int outputColumn) { + this.colNum1 = colNum1; + this.colNum2 = colNum2; + this.outputColumn = outputColumn; + this.outputType = "decimal"; + } + + public DecimalV2ColMultiplyDecimalV2Column() { + 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/DecimalV2.java storage-api/src/java/org/apache/hadoop/hive/common/type/decimalv2/DecimalV2.java new file mode 100644 index 0000000..fc6720a --- /dev/null +++ storage-api/src/java/org/apache/hadoop/hive/common/type/decimalv2/DecimalV2.java @@ -0,0 +1,508 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.hive.common.type.decimalv2; + +import org.apache.hadoop.hive.common.type.HiveDecimal; +import org.apache.hadoop.hive.ql.exec.vector.decimalv2.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 Unscaled unscaled; + + /* + * Constructors + */ + public DecimalV2() { + unscaled = new Unscaled(); + } + + 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 Unscaled(); + + // 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) { + Unscaled remainder = new Unscaled(); + for (int j = scaleDiff; j < 0; j++) { + unscaled.divideByTen(unscaled, remainder); + } + } + + // Rounding + if (addOne) { + unscaled.add(unscaled, Unscaled.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(Unscaled.ZERO)) { + builder.append('0'); + return; + } + + // Make it positive + StringBuilder innerBuilder = new StringBuilder(); + Unscaled unscaledCopy = unscaled.copy(); + int signum = unscaledCopy.getSignum(); + if (signum == -1) { + unscaledCopy.negate(unscaledCopy); + } + + // Digitize from the smallest + boolean digitizedNonZero = false; + Unscaled remainder = new Unscaled(); + 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.v4 == 0)) { + innerBuilder.append(remainder.v4); + digitizedNonZero = true; + } + + // Remove heading zeros + if (unscaledCopy.equals(Unscaled.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(Unscaled.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); + } + + // 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); + + this.unscaled.multiplyByTen(right.unscaled, scaleDiff); + this.unscaled.remainder(left.unscaled, this.unscaled); + } + + /* + * Precision and scale methods + */ + public Unscaled 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 Unscaled integerPart() { + Unscaled unscaledCopy = unscaled.copy(); + Unscaled remainder = new Unscaled(); + for (int i = 0; i < scale; i++) { + unscaledCopy.divideByTen(unscaledCopy, remainder); + } + return unscaledCopy; + } + + public long longValue() { + Unscaled integerPart = integerPart(); + return integerPart.v4 | (integerPart.v3 << 31) | + ((integerPart.v2 & 1L) << 62) | (integerPart.v0 & Long.MIN_VALUE); + } + + public int intValue() { + Unscaled integerPart = integerPart(); + int value = integerPart.v4 | (integerPart.v0 & 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/decimalv2/HiveDecimalV2.java storage-api/src/java/org/apache/hadoop/hive/common/type/decimalv2/HiveDecimalV2.java new file mode 100644 index 0000000..da40335 --- /dev/null +++ storage-api/src/java/org/apache/hadoop/hive/common/type/decimalv2/HiveDecimalV2.java @@ -0,0 +1,299 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.hive.common.type.decimalv2; + +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/decimalv2/Unscaled.java storage-api/src/java/org/apache/hadoop/hive/common/type/decimalv2/Unscaled.java new file mode 100644 index 0000000..36b265b --- /dev/null +++ storage-api/src/java/org/apache/hadoop/hive/common/type/decimalv2/Unscaled.java @@ -0,0 +1,540 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.hive.common.type.decimalv2; + +import java.math.BigInteger; + +/** + * A mutable integer class with 38 digits. + */ +public class Unscaled implements Comparable { + public static final Unscaled ZERO = new Unscaled(); + public static final Unscaled ONE = new Unscaled("1"); + public static final Unscaled FIVE_TIMES_2_POWER_124 = new Unscaled("5"); + public static final Unscaled[] POWER_OF_TEN = new Unscaled[38]; + + public static final int MAX_DIGITS = 38; + + // Big endian. The most important digit comes first. v0 is the most important. + public int v0; + public int v1; + public int v2; + public int v3; + public int v4; + + static { + FIVE_TIMES_2_POWER_124.shiftLeft(FIVE_TIMES_2_POWER_124, 124); + POWER_OF_TEN[0] = ONE; + Unscaled ten = new Unscaled("10"); + for (int i = 0; i < 37; i++) { + POWER_OF_TEN[i + 1] = new Unscaled(); + POWER_OF_TEN[i + 1].multiply(POWER_OF_TEN[i], ten); + } + } + + /* + * Constructors + */ + public Unscaled() { + } + + public Unscaled(String value) { + for (byte b : value.getBytes()) { + parse(b); + } + if (value.charAt(0) == '-') { + negate(this); + } + } + + public void set(Unscaled value) { + v4 = value.v4; + v3 = value.v3; + v2 = value.v2; + v1 = value.v1; + v0 = value.v0; + } + + public Unscaled copy() { + Unscaled copy = new Unscaled(); + 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 + Unscaled copy = new Unscaled(); + if (getSignum() == 1) { + copy.set(this); + } else { + givenBuilder.append('-'); + copy.negate(this); + } + + // Positive or negated negative + Unscaled remainder = new Unscaled(); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < MAX_DIGITS; i++) { + copy.divideByTen(copy, remainder); + builder.append(remainder.v4); + if (copy.equals(ZERO)) { + break; + } + } + builder.reverse(); + givenBuilder.append(builder); + } + + /* + * Comparison methods + */ + @Override + public boolean equals(Object obj) { + Unscaled right = (Unscaled) obj; + return (v4 == right.v4) && + (v3 == right.v3) && (v2 == right.v2) && + (v1 == right.v1) && (v0 == right.v0); + } + + @Override + public int compareTo(Unscaled right) { + if (v0 < right.v0) { + return -1; + } + if (v0 > right.v0) { + return 1; + } + if (v1 < right.v1) { + return -1; + } + if (v1 > right.v1) { + return 1; + } + if (v2 < right.v2) { + return -1; + } + if (v2 > right.v2) { + return 1; + } + if (v3 < right.v3) { + return -1; + } + if (v3 > right.v3) { + return 1; + } + if (v4 < right.v4) { + return -1; + } + if (v4 > right.v4) { + return 1; + } + return 0; + } + + @Override + public int hashCode() { + return v4 ^ v3 ^ v2 ^ v1 ^ v0; + } + + // + // Arithmetic methods + // + public void add(Unscaled left, int right) { + + // Add with carries + this.v4 = left.v4 + right; + this.v3 = left.v3 + (this.v4 >>> 31); + this.v2 = left.v2 + (this.v3 >>> 31); + this.v1 = left.v1 + (this.v2 >>> 31); + this.v0 = left.v0 + (this.v1 >>> 31); + + // Clear carries + this.v4 &= Integer.MAX_VALUE; + this.v3 &= Integer.MAX_VALUE; + this.v2 &= Integer.MAX_VALUE; + this.v1 &= Integer.MAX_VALUE; + + if (this.v0 >= 8) { + throw new IllegalArgumentException("Overflow"); + } + if (this.v0 < -8) { + throw new IllegalArgumentException("Underflow"); + } + } + + public void add(Unscaled left, Unscaled right) { + // Add with carries + this.v4 = left.v4 + right.v4; + this.v3 = left.v3 + right.v3 + (this.v4 >>> 31); + this.v2 = left.v2 + right.v2 + (this.v3 >>> 31); + this.v1 = left.v1 + right.v1 + (this.v2 >>> 31); + this.v0 = left.v0 + right.v0 + (this.v1 >>> 31); + + // Clear carries + this.v4 &= Integer.MAX_VALUE; + this.v3 &= Integer.MAX_VALUE; + this.v2 &= Integer.MAX_VALUE; + this.v1 &= Integer.MAX_VALUE; + + if (this.v0 >= 8) { + throw new IllegalArgumentException("Overflow"); + } + if (this.v0 < -8) { + throw new IllegalArgumentException("Underflow"); + } + } + + public void subtract(Unscaled left, Unscaled right) { + // Add with borrows + this.v4 = left.v4 - right.v4; + this.v3 = left.v3 - right.v3 - (this.v4 >>> 31); + this.v2 = left.v2 - right.v2 - (this.v3 >>> 31); + this.v1 = left.v1 - right.v1 - (this.v2 >>> 31); + this.v0 = left.v0 - right.v0 - (this.v1 >>> 31); + + // Clear borrows + this.v4 &= Integer.MAX_VALUE; + this.v3 &= Integer.MAX_VALUE; + this.v2 &= Integer.MAX_VALUE; + this.v1 &= Integer.MAX_VALUE; + + if (this.v0 >= 8) { + throw new IllegalArgumentException("Overflow"); + } + if (this.v0 < -8) { + throw new IllegalArgumentException("Underflow"); + } + } + + public void multiply(Unscaled l, Unscaled r) { + // Make all positive + int leftSignum = l.getSignum(); + int rightSignum = r.getSignum(); + + Unscaled uL = new Unscaled(); + Unscaled uR = new Unscaled(); + + uL.abs(l); + uR.abs(r); + + // Do a multiplication with shifts and adds + this.set(ZERO); + + Unscaled high = new Unscaled(); + Unscaled low = new Unscaled(); + + multiply(uL.v4, uL.v4, uL.v4, uL.v4, uL.v4, uR.v0, uR.v1, uR.v2, uR.v3, uR.v4, high, low); + multiply(uL.v3, uL.v3, uL.v3, uL.v3, 0, uR.v1, uR.v2, uR.v3, uR.v4, 0, high, low); + multiply(uL.v2, uL.v2, uL.v2, 0, 0, uR.v2, uR.v3, uR.v4, 0, 0, high, low); + multiply(uL.v1, uL.v1, 0, 0, 0, uR.v3, uR.v4, 0, 0, 0, high, low); + multiply(uL.v0, 0, 0, 0, 0, uR.v4, 0, 0, 0, 0, high, low); + + // Handle signs + if (leftSignum != rightSignum) { + this.negate(this); + } + } + + private void multiply(int l4, int l3, int l2, int l1, int l0, + int r4, int r3, int r2, int r1, int r0, + Unscaled high, Unscaled low) { + low.v0 = ((l4 * r4) & 0x7fffffff); + + long result = (long) l3 * r3; + high.v0 = (int) (result >> 31); + low.v1 = (int) (result & 0x7fffffff); + + result = (long) l2 * r2; + high.v1 = (int) (result >> 31); + low.v2 = (int) (result & 0x7fffffff); + + result = (long) l1 * r1; + high.v2 = (int) (result >> 31); + low.v3 = (int) (result & 0x7fffffff); + + result = (long) l0 * r0; + high.v3 = (int) (result >> 31); + low.v4 = (int) (result & 0x7fffffff); + + high.v4 = 0; + + this.add(this, high); + this.add(this, low); + } + + public void multiplyByTen(Unscaled left) { + // x * 10 = x * 8 + x * 2 + Unscaled toAdd = new Unscaled(); + toAdd.shiftLeft(left, 3); + this.shiftLeft(left, 1); + this.add(left, toAdd); + } + + public void multiplyByTen(Unscaled left, int times) { + multiply(POWER_OF_TEN[times], left); + } + + public void divide(Unscaled left, Unscaled right) { + Unscaled unsignedLeft = new Unscaled(); + unsignedLeft.abs(left); + + Unscaled unsignedRight = new Unscaled(); + unsignedRight.abs(right); + + if (right.equals(Unscaled.ZERO)) { + throw new IllegalArgumentException(); + } else { + boolean negate = (left.getSignum() != right.getSignum()); + BigInteger leftBigInteger = unsignedLeft.getBigInteger(); + BigInteger rightBigInteger = unsignedRight.getBigInteger(); + this.setBigInteger(leftBigInteger.divide(rightBigInteger)); + if (negate) { + this.negate(this); + } + } + } + + public void remainder(Unscaled left, Unscaled right) { + Unscaled unsignedLeft = new Unscaled(); + unsignedLeft.abs(left); + + Unscaled unsignedRight = new Unscaled(); + unsignedRight.abs(right); + + if (right.equals(Unscaled.ZERO)) { + throw new IllegalArgumentException(); + } else { + boolean negate = left.getSignum() == -1; + BigInteger leftBigInteger = unsignedLeft.getBigInteger(); + BigInteger rightBigInteger = unsignedRight.getBigInteger(); + this.setBigInteger(leftBigInteger.remainder(rightBigInteger)); + if (negate) { + this.negate(this); + } + } + } + + private void setBigInteger(BigInteger bigInteger) { + byte[] b = new byte[16]; + byte[] byteArray = bigInteger.toByteArray(); + int len = byteArray.length; + final int castMask = 0xff; + + for (int j = 0; j < len; j++) { + b[j + (16 - len)] = byteArray[j]; + } + + this.v4 = (((b[12] & castMask) << 24) | + ((b[13] & castMask) << 16) | + ((b[14] & castMask) << 8) | + b[15] & castMask) & + Integer.MAX_VALUE; + + this.v3 = (((b[8] & castMask) << 25) | + ((b[9] & castMask) << 17) | + ((b[10] & castMask) << 9) | + ((b[11] & castMask) << 1) | + ((b[12] & castMask) >>> 7)) & + Integer.MAX_VALUE; + + this.v2 = (((b[4] & castMask) << 26) | + ((b[5] & castMask) << 18) | + ((b[6] & castMask) << 10) | + ((b[7] & castMask) << 2) | + ((b[8] & castMask) >>> 6)) & + Integer.MAX_VALUE; + + this.v1 = (((b[0] & castMask) << 27) | + ((b[1] & castMask) << 19) | + ((b[2] & castMask) << 11) | + ((b[3] & castMask) << 3) | + ((b[4] & castMask) >>> 5)) & + Integer.MAX_VALUE; + + this.v0 = (((b[0] & castMask) >>> 4)); + } + + public BigInteger getBigInteger() { + byte[] b = new byte[16]; + + b[15] = (byte) (this.v4); + b[14] = (byte) (this.v4 >>> 8); + b[13] = (byte) (this.v4 >>> 16); + b[12] = (byte) ((this.v3 << 7) | (this.v4 >>> 24)); + + b[11] = (byte) (this.v3 >>> 1); + b[10] = (byte) (this.v3 >>> 9); + b[9] = (byte) (this.v3 >>> 17); + b[8] = (byte) ((this.v2 << 6) | (this.v3 >>> 25)); + + b[7] = (byte) (this.v2 >>> 2); + b[6] = (byte) (this.v2 >>> 10); + b[5] = (byte) (this.v2 >>> 18); + b[4] = (byte) ((this.v1 << 5) | (this.v2 >>> 26)); + + b[3] = (byte) (this.v1 >>> 3); + b[2] = (byte) (this.v1 >>> 11); + b[1] = (byte) (this.v1 >>> 19); + b[0] = (byte) ((this.v0 << 4) | (this.v1 >>> 27)); + + return new BigInteger(b); + } + + public void divideByTen(Unscaled left, Unscaled remainder) { + // Make all positive + int leftSignum = left.getSignum(); + if (leftSignum == 1) { + remainder.set(left); + } else { + remainder.negate(left); + } + + Unscaled 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.v4 |= 1; + } + copy.shiftRight(copy, 1); + } + + if (leftSignum == -1) { + this.negate(this); + remainder.negate(remainder); + } + } + + public void divideByTen(Unscaled left, Unscaled remainder, int times) { + for (int i = 0; i < times; i++) { + divideByTen(left, remainder); + } + } + + /* + * Bitwise methods + */ + public void shiftLeft(Unscaled left, int right) { + if (right > 31) { + shiftLeft(left, 31); + shiftLeft(left, right - 31); + return; + } + this.v0 = ((left.v0 << right) | (left.v1 >>> (31 - right))) & Integer.MAX_VALUE; + this.v1 = ((left.v1 << right) | (left.v2 >>> (31 - right))) & Integer.MAX_VALUE; + this.v2 = ((left.v2 << right) | (left.v3 >>> (31 - right))) & Integer.MAX_VALUE; + this.v3 = ((left.v3 << right) | (left.v4 >>> (31 - right))) & Integer.MAX_VALUE; + this.v4 = (left.v4 << right) & Integer.MAX_VALUE; + + if (this.v0 >= 8) { + throw new IllegalArgumentException("Overflow"); + } + if (this.v0 < -8) { + throw new IllegalArgumentException("Underflow"); + } + } + + private void shiftLeftIgnoreOverflow(Unscaled left, int right) { + if (right > 31) { + shiftLeftIgnoreOverflow(left, 31); + shiftLeftIgnoreOverflow(left, right - 31); + return; + } + this.v0 = ((left.v0 << right) | (left.v1 >>> (31 - right))) & Integer.MAX_VALUE; + this.v1 = ((left.v1 << right) | (left.v2 >>> (31 - right))) & Integer.MAX_VALUE; + this.v2 = ((left.v2 << right) | (left.v3 >>> (31 - right))) & Integer.MAX_VALUE; + this.v3 = ((left.v3 << right) | (left.v4 >>> (31 - right))) & Integer.MAX_VALUE; + this.v4 = (left.v4 << right) & Integer.MAX_VALUE; + } + + public void shiftRight(Unscaled left, int right) { + if (right > 31) { + shiftRight(left, 31); + shiftRight(left, right - 31); + return; + } + this.v4 = ((left.v4 >>> right) | (left.v3 << (31 - right))) & Integer.MAX_VALUE; + this.v3 = ((left.v3 >>> right) | (left.v2 << (31 - right))) & Integer.MAX_VALUE; + this.v2 = ((left.v2 >>> right) | (left.v1 << (31 - right))) & Integer.MAX_VALUE; + this.v1 = ((left.v1 >>> right) | (left.v0 << (31 - right))) & Integer.MAX_VALUE; + this.v0 = (left.v0 >>> right) & Integer.MAX_VALUE; + + if (this.v0 >= 8) { + throw new IllegalArgumentException("Overflow"); + } + if (this.v0 < -8) { + throw new IllegalArgumentException("Underflow"); + } + } + + /* + * Sign methods + */ + public void negate(Unscaled left) { + this.v4 = -left.v4; + this.v3 = -left.v3 - (this.v4 >>> 31); + this.v2 = -left.v2 - (this.v3 >>> 31); + this.v1 = -left.v1 - (this.v2 >>> 31); + this.v0 = -left.v0 - (this.v1 >>> 31); + + this.v4 &= Integer.MAX_VALUE; + this.v3 &= Integer.MAX_VALUE; + this.v2 &= Integer.MAX_VALUE; + this.v1 &= Integer.MAX_VALUE; + + if (this.v0 >= 8) { + throw new IllegalArgumentException("Overflow"); + } + if (this.v0 < -8) { + throw new IllegalArgumentException("Underflow"); + } + } + + public int getSignum() { + return (v0 >> 3) | 1; + } + + public void abs(Unscaled left) { + if (left.getSignum() == -1) { + negate(left); + } else { + set(left); + } + } +} diff --git storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/DecimalColAddSubtract.java storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/DecimalColAddSubtract.java new file mode 100644 index 0000000..931faa0 --- /dev/null +++ storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/DecimalColAddSubtract.java @@ -0,0 +1,149 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.hive.ql.exec.vector.decimalv2; + +import org.apache.hadoop.hive.common.type.decimalv2.DecimalV2; +import org.apache.hadoop.hive.common.type.decimalv2.Unscaled; + +class DecimalColAddSubtract { + private DecimalV2ColumnVector col; + + DecimalColAddSubtract(DecimalV2ColumnVector col) { + this.col = col; + } + + void add(DecimalV2ColumnVector left, DecimalV2ColumnVector right) { + // Precision and scale + col.precision = Math.max(left.precision, right.precision); + col.scale = Math.max(left.scale, right.scale); + int scaleDiff = left.scale - right.scale; + + // Same scale + if (scaleDiff == 0) { + col.unscaled.addSubtract.add(left.unscaled, right.unscaled); + } + + // Left scale is greater + else if (scaleDiff > 0) { + col.unscaled.multiplyDivide.multiplyByTen(right.unscaled, scaleDiff); + col.unscaled.addSubtract.add(left.unscaled, col.unscaled); + } + + // Right scale is greater + else { + col.unscaled.multiplyDivide.multiplyByTen(left.unscaled, -scaleDiff); + col.unscaled.addSubtract.add(col.unscaled, right.unscaled); + } + } + + void add(DecimalV2ColumnVector left, DecimalV2 right) { + // Precision and scale + col.precision = Math.max(left.precision, right.precision); + col.scale = Math.max(left.scale, right.scale); + int scaleDiff = left.scale - right.scale; + + // Same scale + if (scaleDiff == 0) { + col.unscaled.addSubtract.add(left.unscaled, right.unscaled); + } + + // Left scale is greater + else if (scaleDiff > 0) { + Unscaled multipliedRight = new Unscaled(); + multipliedRight.multiplyByTen(right.unscaled, scaleDiff); + col.unscaled.addSubtract.add(left.unscaled, multipliedRight); + } + + // Right scale is greater + else { + col.unscaled.multiplyDivide.multiplyByTen(left.unscaled, -scaleDiff); + col.unscaled.addSubtract.add(col.unscaled, right.unscaled); + } + } + + void subtract(DecimalV2ColumnVector left, DecimalV2ColumnVector right) { + // Precision and scale + col.precision = Math.max(left.precision, right.precision); + col.scale = Math.max(left.scale, right.scale); + int scaleDiff = left.scale - right.scale; + + // Same scale + if (scaleDiff == 0) { + col.unscaled.addSubtract.subtract(left.unscaled, right.unscaled); + } + + // Left scale is greater + else if (scaleDiff > 0) { + col.unscaled.multiplyDivide.multiplyByTen(right.unscaled, scaleDiff); + col.unscaled.addSubtract.subtract(left.unscaled, col.unscaled); + } + + // Right scale is greater + else { + col.unscaled.multiplyDivide.multiplyByTen(left.unscaled, -scaleDiff); + col.unscaled.addSubtract.subtract(col.unscaled, right.unscaled); + } + } + + void subtract(DecimalV2ColumnVector left, DecimalV2 right) { + // Precision and scale + col.precision = Math.max(left.precision, right.precision); + col.scale = Math.max(left.scale, right.scale); + int scaleDiff = left.scale - right.scale; + + // Same scale + if (scaleDiff == 0) { + col.unscaled.addSubtract.subtract(left.unscaled, right.unscaled); + } + + // Left scale is greater + else if (scaleDiff > 0) { + Unscaled multipliedRight = new Unscaled(); + multipliedRight.multiplyByTen(right.unscaled, scaleDiff); + col.unscaled.addSubtract.subtract(left.unscaled, multipliedRight); + } + + // Right scale is greater + else { + col.unscaled.multiplyDivide.multiplyByTen(left.unscaled, -scaleDiff); + col.unscaled.addSubtract.subtract(col.unscaled, right.unscaled); + } + } + + void subtract(DecimalV2 left, DecimalV2ColumnVector right) { + // Precision and scale + col.precision = Math.max(left.precision, right.precision); + col.scale = Math.max(left.scale, right.scale); + int scaleDiff = left.scale - right.scale; + + // Same scale + if (scaleDiff == 0) { + col.unscaled.addSubtract.subtract(left.unscaled, right.unscaled); + } + + // Left scale is greater + else if (scaleDiff > 0) { + col.unscaled.multiplyDivide.multiplyByTen(right.unscaled, scaleDiff); + col.unscaled.addSubtract.subtract(left.unscaled, col.unscaled); + } + + // Right scale is greater + else { + Unscaled multipliedLeft = new Unscaled(); + multipliedLeft.multiplyByTen(left.unscaled, -scaleDiff); + col.unscaled.addSubtract.subtract(multipliedLeft, right.unscaled); + } + } +} diff --git storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/DecimalColMultiplyDivide.java storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/DecimalColMultiplyDivide.java new file mode 100644 index 0000000..13e009e --- /dev/null +++ storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/DecimalColMultiplyDivide.java @@ -0,0 +1,118 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.hive.ql.exec.vector.decimalv2; + +import org.apache.hadoop.hive.common.type.decimalv2.DecimalV2; +import org.apache.hadoop.hive.common.type.HiveDecimal; +import org.apache.hadoop.hive.common.type.decimalv2.Unscaled; + +class DecimalColMultiplyDivide { + private DecimalV2ColumnVector col; + + DecimalColMultiplyDivide(DecimalV2ColumnVector col) { + this.col = col; + } + + void multiply(DecimalV2ColumnVector left, DecimalV2ColumnVector right) { + col.precision = left.precision + right.precision; + col.scale = left.scale + right.scale; + + if (col.scale >= HiveDecimal.MAX_PRECISION) { + throw new IllegalArgumentException("Underflow"); + } + col.unscaled.multiplyDivide.multiply(left.unscaled, right.unscaled); + } + + void multiply(DecimalV2ColumnVector left, DecimalV2 right) { + col.precision = left.precision + right.precision; + col.scale = left.scale + right.scale; + + if (col.scale >= HiveDecimal.MAX_PRECISION) { + throw new IllegalArgumentException("Underflow"); + } + col.unscaled.multiplyDivide.multiply(left.unscaled, right.unscaled); + } + + void divide(DecimalV2ColumnVector left, DecimalV2ColumnVector right) { + int p1 = left.precision; + int p2 = right.precision; + int s1 = left.scale; + int s2 = right.scale; + + col.scale = Math.max(6, s1 + p2 + 1); + col.precision = p1 - s1 + s2 + col.scale; + int scaleDiff = col.scale - s1 + s2; + + col.unscaled.multiplyDivide.multiplyByTen(left.unscaled, scaleDiff); + col.unscaled.multiplyDivide.divide(col.unscaled, right.unscaled); + } + + void divide(DecimalV2ColumnVector left, DecimalV2 right) { + int p1 = left.precision; + int p2 = right.precision; + int s1 = left.scale; + int s2 = right.scale; + + col.scale = Math.max(6, s1 + p2 + 1); + col.precision = p1 - s1 + s2 + col.scale; + int scaleDiff = col.scale - s1 + s2; + + col.unscaled.multiplyDivide.multiplyByTen(left.unscaled, scaleDiff); + col.unscaled.multiplyDivide.divide(col.unscaled, right.unscaled); + } + + void divide(DecimalV2 left, DecimalV2ColumnVector right) { + int p1 = left.precision; + int p2 = right.precision; + int s1 = left.scale; + int s2 = right.scale; + + col.scale = Math.max(6, s1 + p2 + 1); + col.precision = p1 - s1 + s2 + col.scale; + int scaleDiff = col.scale - s1 + s2; + + Unscaled multiplied = new Unscaled(); + multiplied.multiplyByTen(left.unscaled, scaleDiff); + col.unscaled.multiplyDivide.divide(multiplied, right.unscaled); + } + + void remainder(DecimalV2ColumnVector left, DecimalV2ColumnVector right) { + int scaleDiff = left.scale - right.scale; + col.scale = Math.max(left.scale, right.scale); + col.precision = Math.max(left.precision, right.precision); + + col.unscaled.multiplyDivide.multiplyByTen(right.unscaled, scaleDiff); + col.unscaled.multiplyDivide.remainder(left.unscaled, col.unscaled); + } + + void remainder(DecimalV2ColumnVector left, DecimalV2 right) { + int scaleDiff = left.scale - right.scale; + col.scale = Math.max(left.scale, right.scale); + col.precision = Math.max(left.precision, right.precision); + + Unscaled multiplied = new Unscaled(); + multiplied.multiplyByTen(right.unscaled, scaleDiff); + col.unscaled.multiplyDivide.remainder(left.unscaled, multiplied); + } + + void remainder(DecimalV2 left, DecimalV2ColumnVector right) { + int scaleDiff = left.scale - right.scale; + col.scale = Math.max(left.scale, right.scale); + col.precision = Math.max(left.precision, right.precision); + + col.unscaled.multiplyDivide.multiplyByTen(right.unscaled, scaleDiff); + col.unscaled.multiplyDivide.remainder(left.unscaled, col.unscaled); + } +} diff --git storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/DecimalV2ColumnVector.java storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/DecimalV2ColumnVector.java new file mode 100644 index 0000000..de47844 --- /dev/null +++ storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/DecimalV2ColumnVector.java @@ -0,0 +1,367 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.hive.ql.exec.vector.decimalv2; + +import org.apache.hadoop.hive.common.type.decimalv2.DecimalV2; +import org.apache.hadoop.hive.common.type.decimalv2.HiveDecimalV2; +import org.apache.hadoop.hive.common.type.decimalv2.Unscaled; +import org.apache.hadoop.hive.ql.exec.vector.ColumnVector; +import org.apache.hadoop.hive.ql.exec.vector.VectorizedRowBatch; + +import java.math.BigDecimal; + +/** + * A vector of {@code DecimalV2}. + * @see DecimalV2 + */ + +public class DecimalV2ColumnVector extends ColumnVector { + public int precision; + public int scale; + + public UnscaledColumn unscaled; + + private DecimalColAddSubtract addSubtract = new DecimalColAddSubtract(this); + private DecimalColMultiplyDivide multiplyDivide = new DecimalColMultiplyDivide(this); + + // + // Constructors + // + public DecimalV2ColumnVector() { + this(VectorizedRowBatch.DEFAULT_SIZE); + } + + public DecimalV2ColumnVector(int len) { + super(len); + unscaled = new UnscaledColumn(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; + + unscaled = new UnscaledColumn(len); + } + + @Override + public void flatten(boolean selectedInUse, int[] sel, int size) { + unscaled.flatten(selectedInUse, sel, size); + } + + @Override + public void setElement(int outElementNum, int inputElementNum, ColumnVector inputVector) { + setElement(outElementNum, inputElementNum, inputVector); + } + + @Override + public void stringifyValue(StringBuilder builder, int row) { + // 0 + if (isZero(row)) { + builder.append('0'); + return; + } + + // Make it positive + StringBuilder innerBuilder = new StringBuilder(); + Unscaled unscaledCopy = new Unscaled(); + unscaled.get(row, unscaledCopy); + int signum = unscaledCopy.getSignum(); + if (signum == -1) { + unscaledCopy.negate(unscaledCopy); + } + + // Digitize from the smallest + boolean digitizedNonZero = false; + Unscaled remainder = new Unscaled(); + 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.v4 == 0)) { + innerBuilder.append(remainder.v4); + digitizedNonZero = true; + } + + // Remove heading zeros + if (unscaledCopy.equals(Unscaled.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(i, decimalV2); + } + + public void set(DecimalV2ColumnVector left) { + int scaleDiff = this.scale - left.scale; + this.scale = left.scale; + this.precision = left.precision; + if (scaleDiff > 0) { + this.unscaled.multiplyDivide.multiplyByTen(left.unscaled, scaleDiff); + } else { + this.unscaled.multiplyDivide.divideByTen(left.unscaled, + new UnscaledColumn(unscaled.length), -scaleDiff); + } + } + + public void set(int i, DecimalV2 left) { + 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 Unscaled(), -scaleDiff); + } + this.unscaled.v4[i] = left.unscaled.v4; + this.unscaled.v3[i] = left.unscaled.v3; + this.unscaled.v2[i] = left.unscaled.v2; + this.unscaled.v1[i] = left.unscaled.v1; + this.unscaled.v0[i] = left.unscaled.v0; + } + + public DecimalV2ColumnVector copy() { + DecimalV2ColumnVector copy = new DecimalV2ColumnVector(); + copy.set(this); + return copy; + } + + public DecimalV2 get(int i, DecimalV2 result) { + result.precision = precision; + result.scale = scale; + + result.unscaled.v4 = unscaled.v4[i]; + result.unscaled.v3 = unscaled.v3[i]; + result.unscaled.v2 = unscaled.v2[i]; + result.unscaled.v1 = unscaled.v1[i]; + result.unscaled.v0 = unscaled.v0[i]; + + return result; + } + + // + // Comparison methods + // + public boolean equals(DecimalV2 right, int i) { + DecimalV2 result = new DecimalV2(); + this.get(i, result); + result.subtract(result, right); + return result.isZero(); + } + + public int compareTo(DecimalV2ColumnVector rightVector, int i) { + DecimalV2 left = new DecimalV2(); + DecimalV2 result = new DecimalV2(); + this.get(i, left); + rightVector.get(i, result); + result.subtract(left, result); + if (result.isZero()) { + return 0; + } else { + return result.getSignum(); + } + } + + public boolean isZero(int i) { + return unscaled.v4[i] == 0 && unscaled.v3[i] == 0 && + unscaled.v2[i] == 0 && unscaled.v1[i] == 0 && unscaled.v0[i] == 0; + } + + // + // Precision and scale methods + // + public UnscaledColumn 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.bitwise.negate(unscaled); + } + + public void abs() { + unscaled.abs(unscaled); + } + + public int getSignum(int i) { + return unscaled.getSignum(i); + } + + + // + // Type conversion methods + // + private Unscaled integer(int row) { + Unscaled unscaled = new Unscaled(); + this.unscaled.get(row, unscaled); + Unscaled unscaledCopy = unscaled.copy(); + Unscaled remainder = new Unscaled(); + for (int i = 0; i < scale; i++) { + unscaledCopy.divideByTen(unscaledCopy, remainder); + } + return unscaledCopy; + } + + public long longValue(int i) { + Unscaled integerPart = integer(i); + return integerPart.v4 | (integerPart.v3 << 31) | + ((integerPart.v2 & 1L) << 62) | (integerPart.v0 & Long.MIN_VALUE); + } + + public int intValue(int i) { + Unscaled integerPart = integer(i); + int value = integerPart.v4 | (integerPart.v0 & 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)); + } + + public HiveDecimalV2 hiveDecimalV2(int i) { + DecimalV2 decimalV2 = new DecimalV2(); + get(i, decimalV2); + return new HiveDecimalV2(decimalV2); + } + + // + // Arithmetic methods + // + public void add(DecimalV2ColumnVector left, DecimalV2ColumnVector right) { + addSubtract.add(left, right); + } + + public void add(DecimalV2ColumnVector left, DecimalV2 right) { + addSubtract.add(left, right); + } + + public void add(DecimalV2 left, DecimalV2ColumnVector right) { + addSubtract.add(right, left); + } + + + public void subtract(DecimalV2ColumnVector left, DecimalV2ColumnVector right) { + addSubtract.subtract(left, right); + } + + public void subtract(DecimalV2ColumnVector left, DecimalV2 right) { + addSubtract.subtract(left, right); + } + + public void subtract(DecimalV2 left, DecimalV2ColumnVector right) { + addSubtract.subtract(left, right); + } + + + public void multiply(DecimalV2ColumnVector left, DecimalV2ColumnVector right) { + multiplyDivide.multiply(left, right); + } + + public void multiply(DecimalV2ColumnVector left, DecimalV2 right) { + multiplyDivide.multiply(left, right); + } + + public void multiply(DecimalV2 left, DecimalV2ColumnVector right) { + multiplyDivide.multiply(right, left); + } + + + public void divide(DecimalV2ColumnVector left, DecimalV2ColumnVector right) { + multiplyDivide.divide(left, right); + } + + public void divide(DecimalV2ColumnVector left, DecimalV2 right) { + multiplyDivide.divide(left, right); + } + + public void divide(DecimalV2 left, DecimalV2ColumnVector right) { + multiplyDivide.divide(left, right); + } + + + public void remainder(DecimalV2ColumnVector left, DecimalV2ColumnVector right) { + multiplyDivide.remainder(left, right); + } + + public void remainder(DecimalV2ColumnVector left, DecimalV2 right) { + multiplyDivide.remainder(left, right); + } + + public void remainder(DecimalV2 left, DecimalV2ColumnVector right) { + multiplyDivide.remainder(left, right); + } +} diff --git storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/UnscaledColAddSubtract.java storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/UnscaledColAddSubtract.java new file mode 100644 index 0000000..c14359b --- /dev/null +++ storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/UnscaledColAddSubtract.java @@ -0,0 +1,249 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.hive.ql.exec.vector.decimalv2; + +import org.apache.hadoop.hive.common.type.decimalv2.Unscaled; + +class UnscaledColAddSubtract { + private UnscaledColumn col; + + UnscaledColAddSubtract(UnscaledColumn col) { + this.col = col; + } + + /** + * Vectorized addition + * + * @param left + * @param right + */ + void add(UnscaledColumn left, int right) { + + // Add with carries + for (int i = 0; i < col.length; i++) { + col.v4[i] = left.v4[i] + right; + } + for (int i = 0; i < col.length; i++) { + col.v3[i] = left.v3[i] + (col.v4[i] >>> 31); + } + for (int i = 0; i < col.length; i++) { + col.v2[i] = left.v2[i] + (col.v3[i] >>> 31); + } + for (int i = 0; i < col.length; i++) { + col.v1[i] = left.v1[i] + (col.v2[i] >>> 31); + } + for (int i = 0; i < col.length; i++) { + col.v0[i] = left.v0[i] + (col.v1[i] >>> 31); + } + + // Clear carries + for (int i = 0; i < col.length; i++) { + col.v4[i] &= Integer.MAX_VALUE; + col.v3[i] &= Integer.MAX_VALUE; + col.v2[i] &= Integer.MAX_VALUE; + col.v1[i] &= Integer.MAX_VALUE; + } + + // Overflow + for (int i = 0; i < col.length; i++) { + int value = col.v0[i]; + col.isNull[i] |= (value >= 8 || value < -8); + } + } + + void add(UnscaledColumn left, UnscaledColumn right) { + // Add with carries + for (int i = 0; i < col.length; i++) { + col.v4[i] = left.v4[i] + right.v4[i]; + } + for (int i = 0; i < col.length; i++) { + col.v3[i] = left.v3[i] + right.v3[i] + (col.v4[i] >>> 31); + } + for (int i = 0; i < col.length; i++) { + col.v2[i] = left.v2[i] + right.v2[i] + (col.v3[i] >>> 31); + } + for (int i = 0; i < col.length; i++) { + col.v1[i] = left.v1[i] + right.v1[i] + (col.v2[i] >>> 31); + } + for (int i = 0; i < col.length; i++) { + col.v0[i] = left.v0[i] + right.v0[i] + (col.v1[i] >>> 31); + } + + // Clear carries + for (int i = 0; i < col.length; i++) { + col.v4[i] &= Integer.MAX_VALUE; + col.v3[i] &= Integer.MAX_VALUE; + col.v2[i] &= Integer.MAX_VALUE; + col.v1[i] &= Integer.MAX_VALUE; + } + + // Overflow + for (int i = 0; i < col.length; i++) { + int value = col.v0[i]; + col.isNull[i] |= value >= 8 || value < -8; + } + } + + void add(UnscaledColumn left, Unscaled right) { + // Add with carries + for (int i = 0; i < col.length; i++) { + col.v4[i] = left.v4[i] + right.v4; + } + for (int i = 0; i < col.length; i++) { + col.v3[i] = left.v3[i] + right.v3 + (col.v4[i] >>> 31); + } + for (int i = 0; i < col.length; i++) { + col.v2[i] = left.v2[i] + right.v2 + (col.v3[i] >>> 31); + } + for (int i = 0; i < col.length; i++) { + col.v1[i] = left.v1[i] + right.v1 + (col.v2[i] >>> 31); + } + for (int i = 0; i < col.length; i++) { + col.v0[i] = left.v0[i] + right.v0 + (col.v1[i] >>> 31); + } + + // Clear carries + for (int i = 0; i < col.length; i++) { + col.v4[i] &= Integer.MAX_VALUE; + col.v3[i] &= Integer.MAX_VALUE; + col.v2[i] &= Integer.MAX_VALUE; + col.v1[i] &= Integer.MAX_VALUE; + } + + // Overflow + for (int i = 0; i < col.length; i++) { + int value = col.v0[i]; + col.isNull[i] |= value >= 8 || value < -8; + } + } + + void subtract(UnscaledColumn left, UnscaledColumn right) { + // Add with carries + for (int i = 0; i < col.length; i++) { + col.v4[i] = left.v4[i] - right.v4[i]; + } + for (int i = 0; i < col.length; i++) { + col.v3[i] = left.v3[i] - right.v3[i] - (col.v4[i] >>> 31); + } + for (int i = 0; i < col.length; i++) { + col.v2[i] = left.v2[i] - right.v2[i] - (col.v3[i] >>> 31); + } + for (int i = 0; i < col.length; i++) { + col.v1[i] = left.v1[i] - right.v1[i] - (col.v2[i] >>> 31); + } + for (int i = 0; i < col.length; i++) { + col.v0[i] = left.v0[i] - right.v0[i] - (col.v1[i] >>> 31); + } + + // Clear carries + for (int i = 0; i < col.length; i++) { + col.v4[i] &= Integer.MAX_VALUE; + col.v3[i] &= Integer.MAX_VALUE; + col.v2[i] &= Integer.MAX_VALUE; + col.v1[i] &= Integer.MAX_VALUE; + } + + // Overflow + for (int i = 0; i < col.length; i++) { + int value = col.v0[i]; + col.isNull[i] |= (value >= 8 || value < -8); + } + } + + void subtract(UnscaledColumn left, Unscaled right) { + // Add with carries + for (int i = 0; i < col.length; i++) { + col.v4[i] = left.v4[i] - right.v4; + } + for (int i = 0; i < col.length; i++) { + col.v3[i] = left.v3[i] - right.v3 - (col.v4[i] >>> 31); + } + for (int i = 0; i < col.length; i++) { + col.v2[i] = left.v2[i] - right.v2 - (col.v3[i] >>> 31); + } + for (int i = 0; i < col.length; i++) { + col.v1[i] = left.v1[i] - right.v1 - (col.v2[i] >>> 31); + } + for (int i = 0; i < col.length; i++) { + col.v0[i] = left.v0[i] - right.v0 - (col.v1[i] >>> 31); + } + + // Clear carries + for (int i = 0; i < col.length; i++) { + col.v4[i] &= Integer.MAX_VALUE; + col.v3[i] &= Integer.MAX_VALUE; + col.v2[i] &= Integer.MAX_VALUE; + col.v1[i] &= Integer.MAX_VALUE; + } + + // Overflow + for (int i = 0; i < col.length; i++) { + int value = col.v0[i]; + col.isNull[i] |= (value >= 8 || value < -8); + } + } + + void subtract(Unscaled left, UnscaledColumn right) { + // Add with carries + for (int i = 0; i < col.length; i++) { + col.v4[i] = left.v4 - right.v4[i]; + } + for (int i = 0; i < col.length; i++) { + col.v3[i] = left.v3 - right.v3[i] - (col.v4[i] >>> 31); + } + for (int i = 0; i < col.length; i++) { + col.v2[i] = left.v2 - right.v2[i] - (col.v3[i] >>> 31); + } + for (int i = 0; i < col.length; i++) { + col.v1[i] = left.v1 - right.v1[i] - (col.v2[i] >>> 31); + } + for (int i = 0; i < col.length; i++) { + col.v0[i] = left.v0 - right.v0[i] - (col.v1[i] >>> 31); + } + + // Clear carries + for (int i = 0; i < col.length; i++) { + col.v4[i] &= Integer.MAX_VALUE; + col.v3[i] &= Integer.MAX_VALUE; + col.v2[i] &= Integer.MAX_VALUE; + col.v1[i] &= Integer.MAX_VALUE; + } + + // Overflow + for (int i = 0; i < col.length; i++) { + int value = col.v0[i]; + col.isNull[i] |= (value >= 8 || value < -8); + } + } + + void subtract(UnscaledColumn left, UnscaledColumn right, int i) { + // Add with carries + col.v4[i] = left.v4[i] - right.v4[i]; + col.v3[i] = left.v3[i] - right.v3[i] - (col.v4[i] >>> 31); + col.v2[i] = left.v2[i] - right.v2[i] - (col.v3[i] >>> 31); + col.v1[i] = left.v1[i] - right.v1[i] - (col.v2[i] >>> 31); + col.v0[i] = left.v0[i] - right.v0[i] - (col.v1[i] >>> 31); + + // Clear carries + col.v4[i] &= Integer.MAX_VALUE; + col.v3[i] &= Integer.MAX_VALUE; + col.v2[i] &= Integer.MAX_VALUE; + col.v1[i] &= Integer.MAX_VALUE; + + // Overflow + int value = col.v0[i]; + col.isNull[i] |= (value >= 8 || value < -8); + } +} diff --git storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/UnscaledColBitwise.java storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/UnscaledColBitwise.java new file mode 100644 index 0000000..41eccfc --- /dev/null +++ storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/UnscaledColBitwise.java @@ -0,0 +1,151 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.hive.ql.exec.vector.decimalv2; + +class UnscaledColBitwise { + private UnscaledColumn col; + + UnscaledColBitwise(UnscaledColumn col) { + this.col = col; + } + + void shiftLeft(UnscaledColumn left, int right) { + if (right > 31) { + shiftLeft(left, 31); + shiftLeft(left, right - 31); + return; + } + + // Shift + for (int i = 0; i < col.length; i++) { + col.v0[i] = ((left.v0[i] << right) | (left.v1[i] >>> (31 - right))) & Integer.MAX_VALUE; + } + for (int i = 0; i < col.length; i++) { + col.v1[i] = ((left.v1[i] << right) | (left.v2[i] >>> (31 - right))) & Integer.MAX_VALUE; + } + for (int i = 0; i < col.length; i++) { + col.v2[i] = ((left.v2[i] << right) | (left.v3[i] >>> (31 - right))) & Integer.MAX_VALUE; + } + for (int i = 0; i < col.length; i++) { + col.v3[i] = ((left.v3[i] << right) | (left.v4[i] >>> (31 - right))) & Integer.MAX_VALUE; + } + for (int i = 0; i < col.length; i++) { + col.v4[i] = (left.v4[i] << right) & Integer.MAX_VALUE; + } + + // Overflow + for (int i = 0; i < col.length; i++) { + int value = col.v0[i]; + col.isNull[i] |= value >= 8 || value < -8; + } + } + + void shiftLeftIgnoreOverflow(UnscaledColumn left, int right) { + if (right > 31) { + shiftLeftIgnoreOverflow(left, 31); + shiftLeftIgnoreOverflow(left, right - 31); + return; + } + for (int i = 0; i < col.length; i++) { + col.v0[i] = ((left.v0[i] << right) | (left.v1[i] >>> (31 - right))) & Integer.MAX_VALUE; + } + for (int i = 0; i < col.length; i++) { + col.v1[i] = ((left.v1[i] << right) | (left.v2[i] >>> (31 - right))) & Integer.MAX_VALUE; + } + for (int i = 0; i < col.length; i++) { + col.v2[i] = ((left.v2[i] << right) | (left.v3[i] >>> (31 - right))) & Integer.MAX_VALUE; + } + for (int i = 0; i < col.length; i++) { + col.v3[i] = ((left.v3[i] << right) | (left.v4[i] >>> (31 - right))) & Integer.MAX_VALUE; + } + for (int i = 0; i < col.length; i++) { + col.v4[i] = (left.v4[i] << right) & Integer.MAX_VALUE; + } + } + + void shiftRight(UnscaledColumn left, int right) { + if (right > 31) { + shiftRight(left, 31); + shiftRight(left, right - 31); + return; + } + for (int i = 0; i < col.length; i++) { + col.v4[i] = ((left.v4[i] >>> right) | (left.v3[i] << (31 - right))) & Integer.MAX_VALUE; + } + for (int i = 0; i < col.length; i++) { + col.v3[i] = ((left.v3[i] >>> right) | (left.v2[i] << (31 - right))) & Integer.MAX_VALUE; + } + for (int i = 0; i < col.length; i++) { + col.v2[i] = ((left.v2[i] >>> right) | (left.v1[i] << (31 - right))) & Integer.MAX_VALUE; + } + for (int i = 0; i < col.length; i++) { + col.v1[i] = ((left.v1[i] >>> right) | (left.v0[i] << (31 - right))) & Integer.MAX_VALUE; + } + for (int i = 0; i < col.length; i++) { + col.v0[i] = (left.v0[i] >>> right) & Integer.MAX_VALUE; + } + + for (int i = 0; i < col.length; i++) { + col.isNull[i] |= (col.v0[i] >= 8 || col.v0[i] < -8); + } + } + + /* + * Sign methods + */ + void negate(UnscaledColumn left) { + for (int i = 0; i < col.length; i++) { + col.v4[i] = -left.v4[i]; + } + for (int i = 0; i < col.length; i++) { + col.v3[i] = -left.v3[i] - (col.v4[i] >>> 31); + } + for (int i = 0; i < col.length; i++) { + col.v2[i] = -left.v2[i] - (col.v3[i] >>> 31); + } + for (int i = 0; i < col.length; i++) { + col.v1[i] = -left.v1[i] - (col.v2[i] >>> 31); + } + for (int i = 0; i < col.length; i++) { + col.v0[i] = -left.v0[i] - (col.v1[i] >>> 31); + } + + for (int i = 0; i < col.length; i++) { + col.v4[i] &= Integer.MAX_VALUE; + col.v3[i] &= Integer.MAX_VALUE; + col.v2[i] &= Integer.MAX_VALUE; + col.v1[i] &= Integer.MAX_VALUE; + } + + for (int i = 0; i < col.length; i++) { + col.isNull[i] |= (col.v0[i] >= 8 || col.v0[i] < -8); + } + } + + void negate(UnscaledColumn left, int i) { + col.v4[i] = -left.v4[i]; + col.v3[i] = -left.v3[i] - (col.v4[i] >>> 31); + col.v2[i] = -left.v2[i] - (col.v3[i] >>> 31); + col.v1[i] = -left.v1[i] - (col.v2[i] >>> 31); + col.v0[i] = -left.v0[i] - (col.v1[i] >>> 31); + + col.v4[i] &= Integer.MAX_VALUE; + col.v3[i] &= Integer.MAX_VALUE; + col.v2[i] &= Integer.MAX_VALUE; + col.v1[i] &= Integer.MAX_VALUE; + + col.isNull[i] |= (col.v0[i] >= 8 || col.v0[i] < -8); + } +} diff --git storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/UnscaledColComparison.java storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/UnscaledColComparison.java new file mode 100644 index 0000000..d6ab982 --- /dev/null +++ storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/UnscaledColComparison.java @@ -0,0 +1,71 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.hive.ql.exec.vector.decimalv2; + +import org.apache.hadoop.hive.common.type.decimalv2.Unscaled; + +class UnscaledColComparison { + private UnscaledColumn col; + + UnscaledColComparison(UnscaledColumn col) { + this.col = col; + } + + boolean equals(UnscaledColumn right, int i) { + return (col.v4[i] == right.v4[i]) && + (col.v3[i] == right.v3[i]) && (col.v2[i] == right.v2[i]) && + (col.v1[i] == right.v1[i]) && (col.v0[i] == right.v0[i]); + } + + boolean equals(Unscaled right, int i) { + return (col.v4[i] == right.v4) && + (col.v3[i] == right.v3) && (col.v2[i] == right.v2) && + (col.v1[i] == right.v1) && (col.v0[i] == right.v0); + } + + int compareTo(UnscaledColumn right, int i) { + if (col.v0[i] < right.v0[i]) { + return -1; + } + if (col.v0[i] > right.v0[i]) { + return 1; + } + if (col.v1[i] < right.v1[i]) { + return -1; + } + if (col.v1[i] > right.v1[i]) { + return 1; + } + if (col.v2[i] < right.v2[i]) { + return -1; + } + if (col.v2[i] > right.v2[i]) { + return 1; + } + if (col.v3[i] < right.v3[i]) { + return -1; + } + if (col.v3[i] > right.v3[i]) { + return 1; + } + if (col.v4[i] < right.v4[i]) { + return -1; + } + if (col.v4[i] > right.v4[i]) { + return 1; + } + return 0; + } +} diff --git storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/UnscaledColMultiplyDivide.java storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/UnscaledColMultiplyDivide.java new file mode 100644 index 0000000..82b1204 --- /dev/null +++ storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/UnscaledColMultiplyDivide.java @@ -0,0 +1,375 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.hive.ql.exec.vector.decimalv2; + +import org.apache.hadoop.hive.common.type.decimalv2.Unscaled; + +import java.math.BigInteger; + +class UnscaledColMultiplyDivide { + private UnscaledColumn col; + + private UnscaledColumn highCol; + private UnscaledColumn lowCol; + + private UnscaledColumn uL; + private UnscaledColumn uR; + + UnscaledColMultiplyDivide(UnscaledColumn col) { + this.col = col; + } + + /** + * Vectorized multiplication + * + * @param left + * @param right + */ + void multiply(UnscaledColumn left, UnscaledColumn right) { + + if (highCol == null) { + uL = new UnscaledColumn(col.length); + uR = new UnscaledColumn(col.length); + + highCol = new UnscaledColumn(col.length); + lowCol = new UnscaledColumn(col.length); + } + + uL.abs(left); + uR.abs(right); + + col.set(Unscaled.ZERO); + + multiplyAndAdd(uL.v4, uL.v4, uL.v4, uL.v4, uL.v4, uR.v0, uR.v1, uR.v2, uR.v3, uR.v4); + multiplyAndAdd(uL.v3, uL.v3, uL.v3, uL.v3, null, uR.v1, uR.v2, uR.v3, uR.v4, null); + multiplyAndAdd(uL.v2, uL.v2, uL.v2, null, null, uR.v2, uR.v3, uR.v4, null, null); + multiplyAndAdd(uL.v1, uL.v1, null, null, null, uR.v3, uR.v4, null, null, null); + multiplyAndAdd(uL.v0, null, null, null, null, uR.v4, null, null, null, null); + + // Handle signs + for (int i = 0; i < col.length; i++) { + if (((left.v0[i] ^ right.v0[i]) & 4) == 4) { + col.bitwise.negate(col, 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 < col.length; i++) { + lowCol.v0[i] = (l4[i] * r4[i]) & 0x7fffffff; + } + } else { + for (int i = 0; i < col.length; i++) { + lowCol.v0[i] = 0; + } + } + + multiply(l3, r3, highCol.v0, lowCol.v1); + multiply(l2, r2, highCol.v1, lowCol.v2); + multiply(l1, r1, highCol.v2, lowCol.v3); + multiply(l0, r0, highCol.v3, lowCol.v4); + + for (int i = 0; i < col.length; i++) { + highCol.v4[i] = 0; + } + + col.addSubtract.add(col, highCol); + col.addSubtract.add(col, lowCol); + } + + private void multiply(int[] left, int[] right, int[] high, int[] low) { + if (left != null && right != null) { + for (int i = 0; i < col.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 < col.length; i++) { + high[i] = 0; + low[i] = 0; + } + } + } + + void multiply(UnscaledColumn left, Unscaled right) { + Unscaled uR = new Unscaled(); + + if (highCol == null) { + uL = new UnscaledColumn(col.length); + + highCol = new UnscaledColumn(col.length); + lowCol = new UnscaledColumn(col.length); + } + + uL.abs(left); + uR.abs(right); + + col.set(Unscaled.ZERO); + + multiplyAndAdd(uL.v4, uL.v4, uL.v4, uL.v4, uL.v4, uR.v0, uR.v1, uR.v2, uR.v3, uR.v4); + multiplyAndAdd(uL.v3, uL.v3, uL.v3, uL.v3, null, uR.v1, uR.v2, uR.v3, uR.v4, 0); + multiplyAndAdd(uL.v2, uL.v2, uL.v2, null, null, uR.v2, uR.v3, uR.v4, 0, 0); + multiplyAndAdd(uL.v1, uL.v1, null, null, null, uR.v3, uR.v4, 0, 0, 0); + multiplyAndAdd(uL.v0, null, null, null, null, uR.v4, 0, 0, 0, 0); + + // Handle signs + for (int i = 0; i < col.length; i++) { + if (((right.v0 ^ left.v0[i]) & 4) == 4) { + col.bitwise.negate(col, i); + } + } + } + + private void multiplyAndAdd(int[] r4, int[] r3, int[] r2, int[] r1, int[] r0, + int l4, int l3, int l2, int l1, int l0) { + if (r4 != null) { + for (int i = 0; i < col.length; i++) { + lowCol.v0[i] = (l4 * r4[i]) & 0x7fffffff; + } + } else { + for (int i = 0; i < col.length; i++) { + lowCol.v0[i] = 0; + } + } + + multiply(l3, r3, highCol.v0, lowCol.v1); + multiply(l2, r2, highCol.v1, lowCol.v2); + multiply(l1, r1, highCol.v2, lowCol.v3); + multiply(l0, r0, highCol.v3, lowCol.v4); + + for (int i = 0; i < col.length; i++) { + highCol.v4[i] = 0; + } + + col.addSubtract.add(col, highCol); + col.addSubtract.add(col, lowCol); + } + + private void multiply(int left, int[] right, int[] high, int[] low) { + if (right != null) { + for (int i = 0; i < col.length; i++) { + long result = (long) left * right[i]; + high[i] = (int) (result >> 31); + low[i] = (int) (result & 0x7fffffff); + } + } else { + for (int i = 0; i < col.length; i++) { + high[i] = 0; + low[i] = 0; + } + } + } + + void multiplyByTen(UnscaledColumn left) { + // x * 10 = x * 8 + x * 2 + UnscaledColumn toAdd = new UnscaledColumn(col.length); + toAdd.bitwise.shiftLeft(left, 3); + col.bitwise.shiftLeft(left, 1); + col.addSubtract.add(left, toAdd); + } + + void multiplyByTen(UnscaledColumn left, int times) { + multiply(left, Unscaled.POWER_OF_TEN[times]); + } + + /** + * @param left + * @param right + * @see Unscaled#divide(Unscaled, Unscaled) + */ + void divide(UnscaledColumn left, UnscaledColumn right) { + UnscaledColumn unsignedLeft = new UnscaledColumn(col.length); + unsignedLeft.abs(left); + + UnscaledColumn unsignedRight = new UnscaledColumn(col.length); + unsignedRight.abs(right); + + for (int i = 0; i < col.length; i++) { + if (right.comparison.equals(Unscaled.ZERO, i)) { + col.isNull[i] = true; + } else { + boolean negate = (left.getSignum(i) != right.getSignum(i)); + BigInteger leftBigInteger = unsignedLeft.getBigInteger(i); + BigInteger rightBigInteger = unsignedRight.getBigInteger(i); + col.setBigInteger(i, leftBigInteger.divide(rightBigInteger)); + if (negate) { + col.bitwise.negate(col, i); + } + } + } + } + + void divide(Unscaled left, UnscaledColumn right) { + Unscaled unsignedLeft = new Unscaled(); + unsignedLeft.abs(left); + + UnscaledColumn unsignedRight = new UnscaledColumn(col.length); + unsignedRight.abs(right); + int leftSignum = left.getSignum(); + + for (int i = 0; i < col.length; i++) { + if (right.comparison.equals(Unscaled.ZERO, i)) { + col.isNull[i] = true; + } else { + boolean negate = (leftSignum != right.getSignum(i)); + BigInteger leftBigInteger = unsignedLeft.getBigInteger(); + BigInteger rightBigInteger = unsignedRight.getBigInteger(i); + col.setBigInteger(i, leftBigInteger.divide(rightBigInteger)); + if (negate) { + col.bitwise.negate(col, i); + } + } + } + } + + void divide(UnscaledColumn left, Unscaled right) { + UnscaledColumn unsignedLeft = new UnscaledColumn(col.length); + unsignedLeft.abs(left); + + Unscaled unsignedRight = new Unscaled(); + unsignedRight.abs(right); + int rightSignum = right.getSignum(); + + for (int i = 0; i < col.length; i++) { + if (right.equals(Unscaled.ZERO)) { + col.isNull[i] = true; + } else { + boolean negate = (left.getSignum(i) != rightSignum); + BigInteger leftBigInteger = unsignedLeft.getBigInteger(i); + BigInteger rightBigInteger = unsignedRight.getBigInteger(); + col.setBigInteger(i, leftBigInteger.divide(rightBigInteger)); + if (negate) { + col.bitwise.negate(col, i); + } + } + } + } + + void remainder(UnscaledColumn left, UnscaledColumn right) { + UnscaledColumn unsignedLeft = new UnscaledColumn(); + unsignedLeft.abs(left); + + UnscaledColumn unsignedRight = new UnscaledColumn(); + unsignedRight.abs(right); + + for (int i = 0; i < col.length; i++) { + if (right.equals(Unscaled.ZERO)) { + throw new IllegalArgumentException(); + } else { + BigInteger leftBigInteger = unsignedLeft.getBigInteger(i); + if (left.getSignum(i) == -1) { + leftBigInteger = leftBigInteger.negate(); + } + BigInteger rightBigInteger = unsignedRight.getBigInteger(i); + if (right.getSignum(i) == -1) { + rightBigInteger = rightBigInteger.negate(); + } + BigInteger remainder = leftBigInteger.remainder(rightBigInteger); + if (remainder.signum() == -1) { + col.setBigInteger(i, remainder.negate()); + col.bitwise.negate(col); + } else { + col.setBigInteger(i, remainder); + } + } + } + } + + void remainder(Unscaled left, UnscaledColumn right) { + Unscaled unsignedLeft = new Unscaled(); + unsignedLeft.abs(left); + + UnscaledColumn unsignedRight = new UnscaledColumn(); + unsignedRight.abs(right); + + for (int i = 0; i < col.length; i++) { + if (right.equals(Unscaled.ZERO)) { + throw new IllegalArgumentException(); + } else { + BigInteger leftBigInteger = unsignedLeft.getBigInteger(); + if (left.getSignum() == -1) { + leftBigInteger = leftBigInteger.negate(); + } + BigInteger rightBigInteger = unsignedRight.getBigInteger(i); + if (right.getSignum(i) == -1) { + rightBigInteger = rightBigInteger.negate(); + } + BigInteger remainder = leftBigInteger.remainder(rightBigInteger); + if (remainder.signum() == -1) { + col.setBigInteger(i, remainder.negate()); + col.bitwise.negate(col); + } else { + col.setBigInteger(i, remainder); + } + } + } + } + + void remainder(UnscaledColumn left, Unscaled right) { + UnscaledColumn unsignedLeft = new UnscaledColumn(); + unsignedLeft.abs(left); + + Unscaled unsignedRight = new Unscaled(); + unsignedRight.abs(right); + + for (int i = 0; i < col.length; i++) { + if (right.equals(Unscaled.ZERO)) { + throw new IllegalArgumentException(); + } else { + BigInteger leftBigInteger = unsignedLeft.getBigInteger(i); + if (left.getSignum(i) == -1) { + leftBigInteger = leftBigInteger.negate(); + } + BigInteger rightBigInteger = unsignedRight.getBigInteger(); + if (right.getSignum() == -1) { + rightBigInteger = rightBigInteger.negate(); + } + BigInteger remainder = leftBigInteger.remainder(rightBigInteger); + if (remainder.signum() == -1) { + col.setBigInteger(i, remainder.negate()); + col.bitwise.negate(col); + } else { + col.setBigInteger(i, remainder); + } + } + } + } + + void divideByTen(UnscaledColumn left, UnscaledColumn remainder, int times) { + for (int i = 0; i < times; i++) { + divideByTen(left, remainder); + } + } + + void divideByTen(UnscaledColumn left, UnscaledColumn remainder) { + remainder.set(left); + UnscaledColumn copy = new UnscaledColumn(col.length); + copy.set(Unscaled.FIVE_TIMES_2_POWER_124); + col.set(Unscaled.ZERO); + for (int i = 0; i < 124; i++) { + col.bitwise.shiftLeft(col, 1); + for (int j = 0; j < col.length; j++) { + if (remainder.comparison.compareTo(copy, j) >= 0) { + remainder.addSubtract.subtract(remainder, copy, j); + col.v4[j] |= 1; + } + } + copy.bitwise.shiftRight(copy, 1); + } + } +} diff --git storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/UnscaledColumn.java storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/UnscaledColumn.java new file mode 100644 index 0000000..23ab0c7 --- /dev/null +++ storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/UnscaledColumn.java @@ -0,0 +1,258 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.hive.ql.exec.vector.decimalv2; + +import org.apache.hadoop.hive.common.type.decimalv2.Unscaled; +import org.apache.hadoop.hive.ql.exec.vector.ColumnVector; +import org.apache.hadoop.hive.ql.exec.vector.VectorizedRowBatch; + +import java.math.BigInteger; +import java.util.Arrays; + +/** + * A vector of {@code Unscaled}. + * @see Unscaled + */ + +class UnscaledColumn extends ColumnVector { + UnscaledColBitwise bitwise = new UnscaledColBitwise(this); + UnscaledColAddSubtract addSubtract = new UnscaledColAddSubtract(this); + UnscaledColMultiplyDivide multiplyDivide = new UnscaledColMultiplyDivide(this); + UnscaledColComparison comparison = new UnscaledColComparison(this); + + int length; + + int[] v0; + int[] v1; + int[] v2; + int[] v3; + int[] v4; + + UnscaledColumn() { + this(VectorizedRowBatch.DEFAULT_SIZE); + } + + UnscaledColumn(int len) { + super(len); + length = len; + v4 = new int[len]; + v3 = new int[len]; + v2 = new int[len]; + v1 = new int[len]; + v0 = new int[len]; + } + + @Override + public void flatten(boolean selectedInUse, int[] sel, int size) { + flattenPush(); + if (isRepeating) { + isRepeating = false; + + int i4Val = v4[0]; + int i3Val = v3[0]; + int i2Val = v2[0]; + int i1Val = v1[0]; + int i0Val = v0[0]; + + if (selectedInUse) { + for (int j = 0; j < size; j++) { + int i = sel[j]; + v4[i] = i4Val; + v3[i] = i3Val; + v2[i] = i2Val; + v1[i] = i1Val; + v0[i] = i0Val; + } + } else { + Arrays.fill(v4, 0, size, i4Val); + Arrays.fill(v3, 0, size, i3Val); + Arrays.fill(v2, 0, size, i2Val); + Arrays.fill(v1, 0, size, i1Val); + Arrays.fill(v0, 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; + v4[outElementNum] = ((UnscaledColumn) inputVector).v4[inputElementNum]; + v3[outElementNum] = ((UnscaledColumn) inputVector).v3[inputElementNum]; + v2[outElementNum] = ((UnscaledColumn) inputVector).v2[inputElementNum]; + v1[outElementNum] = ((UnscaledColumn) inputVector).v1[inputElementNum]; + v0[outElementNum] = ((UnscaledColumn) inputVector).v0[inputElementNum]; + } else { + isNull[outElementNum] = true; + noNulls = false; + } + } + + void set(UnscaledColumn value) { + System.arraycopy(value.v4, 0, this.v4, 0, length); + System.arraycopy(value.v3, 0, this.v3, 0, length); + System.arraycopy(value.v2, 0, this.v2, 0, length); + System.arraycopy(value.v1, 0, this.v1, 0, length); + System.arraycopy(value.v0, 0, this.v0, 0, length); + } + + void set(Unscaled value) { + Arrays.fill(this.v4, value.v4); + Arrays.fill(this.v3, value.v3); + Arrays.fill(this.v2, value.v2); + Arrays.fill(this.v1, value.v1); + Arrays.fill(this.v0, value.v0); + } + + void set(int i, UnscaledColumn value) { + this.v4[i] = value.v4[i]; + this.v3[i] = value.v3[i]; + this.v2[i] = value.v2[i]; + this.v1[i] = value.v1[i]; + this.v0[i] = value.v0[i]; + } + + void parse(byte b) { + if ('0' <= b && b <= '9') { + multiplyDivide.multiplyByTen(this); + addSubtract.add(this, b - '0'); + } + } + + @Override + public void stringifyValue(StringBuilder givenBuilder, int i) { + // Zero + if (comparison.equals(Unscaled.ZERO, i)) { + givenBuilder.append('0'); + return; + } + + // Negative + Unscaled copy = new Unscaled(); + if (getSignum(i) == 1) { + this.get(i, copy); + } else { + givenBuilder.append('-'); + this.get(i, copy); + copy.negate(copy); + } + + // Positive or negated negative + Unscaled remainder = new Unscaled(); + StringBuilder builder = new StringBuilder(); + for (int j = 0; j < Unscaled.MAX_DIGITS; j++) { + copy.divideByTen(copy, remainder); + builder.append(remainder.v4); + if (copy.equals(Unscaled.ZERO)) { + break; + } + } + builder.reverse(); + givenBuilder.append(builder); + } + + void get(int i, Unscaled unscaled) { + unscaled.v4 = v4[i]; + unscaled.v3 = v3[i]; + unscaled.v2 = v2[i]; + unscaled.v1 = v1[i]; + unscaled.v0 = v0[i]; + } + + void setBigInteger(int i, BigInteger bigInteger) { + byte[] b = new byte[16]; + byte[] byteArray = bigInteger.toByteArray(); + int len = byteArray.length; + final int castMask = 0xff; + + for (int j = 0; j < len; j++) { + b[j + (16 - len)] = byteArray[j]; + } + + this.v4[i] = (((b[12] & castMask) << 24) | + ((b[13] & castMask) << 16) | + ((b[14] & castMask) << 8) | + b[15] & castMask) & + Integer.MAX_VALUE; + + this.v3[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.v2[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.v1[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.v0[i] = (((b[0] & castMask) >>> 4)); + } + + BigInteger getBigInteger(int i) { + byte[] b = new byte[16]; + + b[15] = (byte) (this.v4[i]); + b[14] = (byte) (this.v4[i] >>> 8); + b[13] = (byte) (this.v4[i] >>> 16); + b[12] = (byte) ((this.v3[i] << 7) | (this.v4[i] >>> 24)); + + b[11] = (byte) (this.v3[i] >>> 1); + b[10] = (byte) (this.v3[i] >>> 9); + b[9] = (byte) (this.v3[i] >>> 17); + b[8] = (byte) ((this.v2[i] << 6) | (this.v3[i] >>> 25)); + + b[7] = (byte) (this.v2[i] >>> 2); + b[6] = (byte) (this.v2[i] >>> 10); + b[5] = (byte) (this.v2[i] >>> 18); + b[4] = (byte) ((this.v1[i] << 5) | (this.v2[i] >>> 26)); + + b[3] = (byte) (this.v1[i] >>> 3); + b[2] = (byte) (this.v1[i] >>> 11); + b[1] = (byte) (this.v1[i] >>> 19); + b[0] = (byte) ((this.v0[i] << 4) | (this.v1[i] >>> 27)); + + return new BigInteger(b); + } + + int getSignum(int i) { + return (v0[i] >> 3) | 1; + } + + void abs(UnscaledColumn left) { + for (int i = 0; i < length; i++) { + if (left.getSignum(i) == -1) { + this.bitwise.negate(left, i); + } else { + set(i, left); + } + } + } +} diff --git storage-api/src/java/org/apache/hadoop/hive/serde2/io/HiveDecimalV2Writable.java storage-api/src/java/org/apache/hadoop/hive/serde2/io/HiveDecimalV2Writable.java new file mode 100644 index 0000000..1cf7cde --- /dev/null +++ storage-api/src/java/org/apache/hadoop/hive/serde2/io/HiveDecimalV2Writable.java @@ -0,0 +1,166 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.hive.serde2.io; + +import org.apache.hadoop.hive.common.type.decimalv2.HiveDecimalV2; +import org.apache.hadoop.io.WritableComparable; +import org.apache.hadoop.io.WritableUtils; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.math.BigInteger; + +public class HiveDecimalV2Writable implements WritableComparable { + + private byte[] internalStorage = new byte[0]; + private int scale; + + public HiveDecimalV2Writable() { + } + + public HiveDecimalV2Writable(String value) { + set(HiveDecimalV2.create(value)); + } + + public HiveDecimalV2Writable(byte[] bytes, int scale) { + set(bytes, scale); + } + + public HiveDecimalV2Writable(HiveDecimalV2Writable writable) { + set(writable.getHiveDecimalV2()); + } + + public HiveDecimalV2Writable(HiveDecimalV2 value) { + set(value); + } + + public HiveDecimalV2Writable(long value) { + set((HiveDecimalV2.create(value))); + } + + public void set(HiveDecimalV2 value) { + set(value.unscaledValue().toByteArray(), value.scale()); + } + + public void set(HiveDecimalV2 value, int maxPrecision, int maxScale) { + set(HiveDecimalV2.enforcePrecisionScale(value, maxPrecision, maxScale)); + } + + public void set(HiveDecimalV2Writable writable) { + set(writable.getHiveDecimalV2()); + } + + public void set(byte[] bytes, int scale) { + this.internalStorage = bytes; + this.scale = scale; + } + + public HiveDecimalV2 getHiveDecimalV2() { + return HiveDecimalV2.create(new BigInteger(internalStorage), scale); + } + + /** + * Get a HiveDecimal instance from the writable and constraint it with maximum precision/scale. + * + * @param maxPrecision maximum precision + * @param maxScale maximum scale + * @return HiveDecimal instance + */ + public HiveDecimalV2 getHiveDecimalV2(int maxPrecision, int maxScale) { + return HiveDecimalV2.enforcePrecisionScale(HiveDecimalV2. + create(new BigInteger(internalStorage), scale), + maxPrecision, maxScale); + } + + @Override + public void readFields(DataInput in) throws IOException { + scale = WritableUtils.readVInt(in); + int byteArrayLen = WritableUtils.readVInt(in); + if (internalStorage.length != byteArrayLen) { + internalStorage = new byte[byteArrayLen]; + } + in.readFully(internalStorage); + } + + @Override + public void write(DataOutput out) throws IOException { + WritableUtils.writeVInt(out, scale); + WritableUtils.writeVInt(out, internalStorage.length); + out.write(internalStorage); + } + + @Override + public int compareTo(HiveDecimalV2Writable that) { + return getHiveDecimalV2().compareTo(that.getHiveDecimalV2()); + } + + @Override + public String toString() { + return getHiveDecimalV2().toString(); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + HiveDecimalV2Writable bdw = (HiveDecimalV2Writable) other; + + // 'equals' and 'compareTo' are not compatible with HiveDecimals. We want + // compareTo which returns true iff the numbers are equal (e.g.: 3.14 is + // the same as 3.140). 'Equals' returns true iff equal and the same scale + // is set in the decimals (e.g.: 3.14 is not the same as 3.140) + return getHiveDecimalV2().compareTo(bdw.getHiveDecimalV2()) == 0; + } + + @Override + public int hashCode() { + return getHiveDecimalV2().hashCode(); + } + + /* (non-Javadoc) + * In order to update a Decimal128 fast (w/o allocation) we need to expose access to the + * internal storage bytes and scale. + * @return + */ + public byte[] getInternalStorage() { + return internalStorage; + } + + /* (non-Javadoc) + * In order to update a Decimal128 fast (w/o allocation) we need to expose access to the + * internal storage bytes and scale. + */ + public int getScale() { + return scale; + } + + public static + HiveDecimalV2Writable enforcePrecisionScale(HiveDecimalV2Writable writable, + int precision, int scale) { + if (writable == null) { + return null; + } + + HiveDecimalV2 dec = + HiveDecimalV2.enforcePrecisionScale(writable.getHiveDecimalV2(), precision, + scale); + return dec == null ? null : new HiveDecimalV2Writable(dec); + } +} 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..083716f --- /dev/null +++ storage-api/src/test/org/apache/hadoop/hive/ql/exec/vector/TestDecimalV2ColumnVector.java @@ -0,0 +1,167 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.hive.ql.exec.vector; + +import junit.framework.Assert; +import org.apache.hadoop.hive.common.type.decimalv2.DecimalV2; +import org.apache.hadoop.hive.ql.exec.vector.decimalv2.DecimalV2ColumnVector; +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(0, new DecimalV2("1", 10, 2)); + vector1.set(1, new DecimalV2("1", 2, 0)); + vector1.set(2, new DecimalV2("1.23", 10, 2)); + vector1.set(3, new DecimalV2("-1.23", 10, 2)); + vector1.set(4, new DecimalV2("0.5", 10, 2)); + vector1.set(5, new DecimalV2("0.05", 10, 2)); + vector1.set(6, new DecimalV2("1.234", 10, 2)); + + 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(0, new DecimalV2("9999999999999999999999999999.9999999999", 38, 10)); + vector2.set(1, new DecimalV2("-9999999999999999999999999999.9999999999", 38, 10)); + + assertEquals("9999999999999999999999999999.9999999999", vector2.toString(0)); + assertEquals("-9999999999999999999999999999.9999999999", vector2.toString(1)); + } + + @Test + public void testAdd() { + DecimalV2ColumnVector left = new DecimalV2ColumnVector(10, 2); + left.set(0, new DecimalV2("1.2", 10, 2)); + left.set(1, new DecimalV2("-1.2", 10, 2)); + left.set(2, new DecimalV2("-1.23", 10, 2)); + left.set(3, new DecimalV2("-1.23", 10, 2)); + left.set(4, new DecimalV2("1.2", 11, 3)); + left.set(5, new DecimalV2("1.2", 10, 2)); + + DecimalV2ColumnVector right = new DecimalV2ColumnVector(10, 2); + right.set(0, new DecimalV2("1.23", 10, 2)); + right.set(1, new DecimalV2("1.23", 10, 2)); + right.set(2, new DecimalV2("1.23", 10, 2)); + right.set(3, new DecimalV2("1.2", 10, 2)); + right.set(4, new DecimalV2("1.2", 10, 2)); + right.set(5, new DecimalV2("1.2", 11, 3)); + + DecimalV2ColumnVector result = new DecimalV2ColumnVector(10, 2); + result.add(left, right); + DecimalV2 decimalV2 = new DecimalV2(); + assertEquals(new DecimalV2("2.43", 10, 2), result.get(0, decimalV2)); + assertEquals(new DecimalV2("0.03", 10, 2), result.get(1, decimalV2)); + assertEquals(new DecimalV2("0.00", 10, 2), result.get(2, decimalV2)); + assertEquals(new DecimalV2("-0.03", 10, 2), result.get(3, decimalV2)); + assertEquals(new DecimalV2("2.4", 11, 3), result.get(4, decimalV2)); + assertEquals(new DecimalV2("2.4", 11, 3), result.get(5, decimalV2)); + } + + @Test + public void testSubtract() { + DecimalV2ColumnVector left = new DecimalV2ColumnVector(10, 2); + left.set(0, new DecimalV2("1.2", 10, 2)); + left.set(1, new DecimalV2("1.23", 10, 2)); + left.set(2, new DecimalV2("1.23", 10, 2)); + left.set(3, new DecimalV2("1.2", 10, 2)); + left.set(4, new DecimalV2("2.76", 11, 3)); + left.set(5, new DecimalV2("2.76", 10, 2)); + + DecimalV2ColumnVector right = new DecimalV2ColumnVector(10, 2); + right.set(0, new DecimalV2("-1.23", 10, 2)); + right.set(1, new DecimalV2("1.2", 10, 2)); + right.set(2, new DecimalV2("1.23", 10, 2)); + right.set(3, new DecimalV2("1.23", 10, 2)); + right.set(4, new DecimalV2("2.76", 10, 2)); + right.set(5, new DecimalV2("2.76", 11, 3)); + + DecimalV2ColumnVector result = new DecimalV2ColumnVector(10, 2); + result.subtract(left, right); + DecimalV2 decimalV2 = new DecimalV2(); + assertEquals(new DecimalV2("2.43", 10, 2), result.get(0, decimalV2)); + assertEquals(new DecimalV2("0.03", 10, 2), result.get(1, decimalV2)); + assertEquals(new DecimalV2("0.00", 10, 2), result.get(2, decimalV2)); + assertEquals(new DecimalV2("-0.03", 10, 2), result.get(3, decimalV2)); + assertEquals(new DecimalV2("0", 11, 3), result.get(4, decimalV2)); + assertEquals(new DecimalV2("0", 11, 3), result.get(5, decimalV2)); + } + + @Test + public void testMultiply() { + DecimalV2ColumnVector left = new DecimalV2ColumnVector(10, 2); + left.set(0, new DecimalV2("1.2", 10, 2)); + left.set(1, new DecimalV2("1.2", 10, 2)); + left.set(2, new DecimalV2("1.2", 10, 2)); + left.set(3, new DecimalV2("-1.2", 10, 2)); + left.set(4, new DecimalV2("-1.2", 11, 3)); + + DecimalV2ColumnVector right = new DecimalV2ColumnVector(10, 2); + right.set(0, new DecimalV2("2.3", 10, 2)); + right.set(1, new DecimalV2("0", 10, 2)); + right.set(2, new DecimalV2("-2.3", 10, 2)); + right.set(3, new DecimalV2("2.3", 10, 2)); + right.set(4, new DecimalV2("-2.3", 10, 2)); + + DecimalV2ColumnVector result = new DecimalV2ColumnVector(10, 2); + result.multiply(left, right); + DecimalV2 decimalV2 = new DecimalV2(); + assertEquals(new DecimalV2("2.76", 10, 2), result.get(0, decimalV2)); + assertEquals(new DecimalV2("0", 10, 2), result.get(1, decimalV2)); + assertEquals(new DecimalV2("-2.76", 10, 2), result.get(2, decimalV2)); + assertEquals(new DecimalV2("-2.76", 10, 2), result.get(3, decimalV2)); + assertEquals(new DecimalV2("2.76", 11, 3), result.get(4, decimalV2)); + } + + @Test + public void testDivide() { + DecimalV2ColumnVector left = new DecimalV2ColumnVector(10, 2); + left.set(0, new DecimalV2("1", 10, 2)); + left.set(1, new DecimalV2("1", 10, 2)); + left.set(2, new DecimalV2("-10", 10, 2)); + left.set(3, new DecimalV2("-1.5", 10, 2)); + + DecimalV2ColumnVector right = new DecimalV2ColumnVector(10, 2); + right.set(0, new DecimalV2("3", 10, 2)); + right.set(1, new DecimalV2("-10", 10, 2)); + right.set(2, new DecimalV2("-1", 10, 2)); + right.set(3, new DecimalV2("0.5", 10, 2)); + + DecimalV2ColumnVector result = new DecimalV2ColumnVector(10, 2); + result.divide(left, right); + DecimalV2 decimalV2 = new DecimalV2(); + assertEquals(new DecimalV2("0.3333333333333", 20, 14), result.get(0, decimalV2)); + assertEquals(new DecimalV2("-0.1", 20, 14), result.get(1, decimalV2)); + assertEquals(new DecimalV2("10", 20, 14), result.get(2, decimalV2)); + assertEquals(new DecimalV2("-3", 20, 14), result.get(3, decimalV2)); + } + + @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); + } +}