diff --git itests/hive-jmh/src/main/java/org/apache/hive/benchmark/vectorization/AbstractExpression.java itests/hive-jmh/src/main/java/org/apache/hive/benchmark/vectorization/AbstractExpression.java index 94af3e0..0f6d943 100644 --- itests/hive-jmh/src/main/java/org/apache/hive/benchmark/vectorization/AbstractExpression.java +++ itests/hive-jmh/src/main/java/org/apache/hive/benchmark/vectorization/AbstractExpression.java @@ -37,7 +37,8 @@ @State(Scope.Thread) @OutputTimeUnit(TimeUnit.NANOSECONDS) public abstract class AbstractExpression { - private static final int DEFAULT_ITER_TIME = 1000000; + protected static final int DEFAULT_ITER_TIME = 1000000; + protected int iterTime = DEFAULT_ITER_TIME; protected VectorExpression expression; protected VectorizedRowBatch rowBatch; @@ -58,7 +59,7 @@ protected VectorizedRowBatch buildRowBatch(ColumnVector output, int colNum, Colu @Warmup(iterations = 2, time = 2, timeUnit = TimeUnit.MILLISECONDS) @Measurement(iterations = 2, time = 2, timeUnit = TimeUnit.MILLISECONDS) public void bench() { - for (int i = 0; i < DEFAULT_ITER_TIME; i++) { + for (int i = 0; i < iterTime; i++) { expression.evaluate(rowBatch); } } diff --git itests/hive-jmh/src/main/java/org/apache/hive/benchmark/vectorization/VectorizedDecimalBench.java itests/hive-jmh/src/main/java/org/apache/hive/benchmark/vectorization/VectorizedDecimalBench.java new file mode 100644 index 0000000..71ca04f --- /dev/null +++ itests/hive-jmh/src/main/java/org/apache/hive/benchmark/vectorization/VectorizedDecimalBench.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.hive.benchmark.vectorization; + +import org.apache.hadoop.hive.common.type.HiveDecimal; +import org.apache.hadoop.hive.common.type.HiveDecimalV2; +import org.apache.hadoop.hive.ql.exec.vector.BytesColumnVector; +import org.apache.hadoop.hive.ql.exec.vector.DecimalColumnVector; +import org.apache.hadoop.hive.ql.exec.vector.VectorizedRowBatch; +import org.apache.hadoop.hive.ql.exec.vector.decimalv2.DecimalColumnVectorV2; +import org.apache.hadoop.hive.ql.exec.vector.expressions.*; +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.DecimalColSubtractDecimalColumn; +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.Arrays; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +/** + * This test measures the performance for vectorization. + *

+ * This test uses JMH framework for benchmarking. + * You may execute this benchmark tool using JMH command line in different ways: + *

+ * To use the settings shown in the main() function, use: + * $ java -cp target/benchmarks.jar org.apache.hive.benchmark.vectorization.VectorizedArithmeticBench + *

+ * To use the default settings used by JMH, use: + * $ java -jar target/benchmarks.jar org.apache.hive.benchmark.vectorization.VectorizedArithmeticBench + *

+ * To specify different parameters, use: + * - This command will use 10 warm-up iterations, 5 test iterations, and 2 forks. And it will + * display the Average Time (avgt) in Microseconds (us) + * - Benchmark mode. Available modes are: + * [Throughput/thrpt, AverageTime/avgt, SampleTime/sample, SingleShotTime/ss, All/all] + * - Output time unit. Available time units are: [m, s, ms, us, ns]. + *

+ * $ java -jar target/benchmarks.jar org.apache.hive.benchmark.vectorization.VectorizedDecimalBench + * -wi 10 -i 5 -f 2 -bm avgt -tu us + */ +@State(Scope.Benchmark) +public class VectorizedDecimalBench { + + @BenchmarkMode(Mode.AverageTime) + @Fork(1) + @State(Scope.Thread) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + private static abstract class AbstractDecimalExpression extends AbstractExpression { + private boolean firstRun = true; + + @Override + @Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.MILLISECONDS) + public void bench() { + if (firstRun) { + iterTime = DEFAULT_ITER_TIME; + rowBatch.size = 1; + rowBatch.ensureSize(1); + } else { + iterTime = 1; + rowBatch.size = VectorizedRowBatch.DEFAULT_SIZE; + rowBatch.ensureSize(VectorizedRowBatch.DEFAULT_SIZE); + } + firstRun = false; + super.bench(); + } + } + + private static DecimalColumnVector getDecimalColumnVector(int precision, int scale) { + DecimalColumnVector vector = new DecimalColumnVector(precision, scale); + StringBuilder builder = new StringBuilder(); + Random random = new Random(); + for (int i = 0; i < precision - scale; i++) { + builder.append(random.nextInt(10)); + } + String string = builder.toString(); + for (int i = 0; i < VectorizedRowBatch.DEFAULT_SIZE; i++) { + HiveDecimal hiveDecimal = HiveDecimal.create(string); + vector.set(i, hiveDecimal); + } + return vector; + } + + private static DecimalColumnVectorV2 getDecimalColumnVectorV2(int precision, int scale) { + DecimalColumnVectorV2 vector = new DecimalColumnVectorV2(precision, scale); + StringBuilder builder = new StringBuilder(); + Random random = new Random(); + for (int i = 0; i < precision - scale; i++) { + builder.append(random.nextInt(10)); + } + HiveDecimalV2 decimalV2 = HiveDecimalV2.create(builder.toString()); + Arrays.fill(vector.getUpper(), decimalV2.getUpper()); + Arrays.fill(vector.getLower(), decimalV2.getLower()); + return vector; + } + + public static class V1ColAdd64ColBench extends AbstractDecimalExpression { + @Override + public void setup() { + rowBatch = buildRowBatch(new DecimalColumnVector(11, 2), 2, + getDecimalColumnVector(10, 2), getDecimalColumnVector(10, 2)); + expression = new DecimalColAddDecimalColumn(0, 1, 2); + } + } + + public static class V2ColAdd64ColBench extends AbstractDecimalExpression { + @Override + public void setup() { + rowBatch = buildRowBatch(new DecimalColumnVectorV2(11, 2), 2, + getDecimalColumnVectorV2(10, 2), getDecimalColumnVectorV2(10, 2)); + expression = new DecimalV2ColAddDecimalColumn(0, 1, 2); + } + } + + public static class V1ColAdd128ColBench extends AbstractDecimalExpression { + @Override + public void setup() { + rowBatch = buildRowBatch(new DecimalColumnVector(21, 2), 2, + getDecimalColumnVector(20, 2), getDecimalColumnVector(20, 2)); + expression = new DecimalColAddDecimalColumn(0, 1, 2); + } + } + + public static class V2ColAdd128ColBench extends AbstractDecimalExpression { + @Override + public void setup() { + rowBatch = buildRowBatch(new DecimalColumnVectorV2(21, 2), 2, + getDecimalColumnVectorV2(20, 2), getDecimalColumnVectorV2(20, 2)); + expression = new DecimalV2ColAddDecimalColumn(0, 1, 2); + } + } + + public static class V1ColSub64ColBench extends AbstractDecimalExpression { + @Override + public void setup() { + rowBatch = buildRowBatch(new DecimalColumnVector(21, 2), 2, + getDecimalColumnVector(10, 2), getDecimalColumnVector(10, 2)); + expression = new DecimalColSubtractDecimalColumn(0, 1, 2); + } + } + + public static class V2ColSub64ColBench extends AbstractDecimalExpression { + @Override + public void setup() { + rowBatch = buildRowBatch(new DecimalColumnVectorV2(21, 2), 2, + getDecimalColumnVectorV2(10, 2), getDecimalColumnVectorV2(10, 2)); + expression = new DecimalV2ColSubtractDecimalColumn(0, 1, 2); + } + } + + public static class V1ColSub128ColBench extends AbstractDecimalExpression { + @Override + public void setup() { + rowBatch = buildRowBatch(new DecimalColumnVector(21, 2), 2, + getDecimalColumnVector(20, 2), getDecimalColumnVector(20, 2)); + expression = new DecimalColSubtractDecimalColumn(0, 1, 2); + } + } + + public static class V2ColSub128ColBench extends AbstractDecimalExpression { + @Override + public void setup() { + rowBatch = buildRowBatch(new DecimalColumnVectorV2(21, 2), 2, + getDecimalColumnVectorV2(20, 2), getDecimalColumnVectorV2(20, 2)); + expression = new DecimalV2ColSubtractDecimalColumn(0, 1, 2); + } + } + + public static class V1ColDiv64ColBench extends AbstractDecimalExpression { + @Override + public void setup() { + rowBatch = buildRowBatch(new DecimalColumnVector(20, 2), 2, + getDecimalColumnVector(4, 0), getDecimalColumnVector(2, 0)); + expression = new DecimalColDivideDecimalColumn(0, 1, 2); + } + } + + public static class V2ColDiv64ColBench extends AbstractDecimalExpression { + @Override + public void setup() { + rowBatch = buildRowBatch(new DecimalColumnVectorV2(20, 2), 2, + getDecimalColumnVectorV2(4, 0), getDecimalColumnVectorV2(2, 0)); + expression = new DecimalV2ColDivideDecimalColumn(0, 1, 2); + } + } + + public static class V1ColDiv128ColBench extends AbstractDecimalExpression { + @Override + public void setup() { + rowBatch = buildRowBatch(new DecimalColumnVector(20, 2), 2, + getDecimalColumnVector(20, 0), getDecimalColumnVector(10, 0)); + expression = new DecimalColDivideDecimalColumn(0, 1, 2); + } + } + + public static class V2ColDiv128ColBench extends AbstractDecimalExpression { + @Override + public void setup() { + rowBatch = buildRowBatch(new DecimalColumnVectorV2(20, 2), 2, + getDecimalColumnVectorV2(20, 0), getDecimalColumnVectorV2(10, 0)); + expression = new DecimalV2ColDivideDecimalColumn(0, 1, 2); + } + } + + public static class V1ColDiv128By16ColBench extends AbstractDecimalExpression { + @Override + public void setup() { + rowBatch = buildRowBatch(new DecimalColumnVector(20, 2), 2, + getDecimalColumnVector(20, 0), getDecimalColumnVector(4, 0)); + expression = new DecimalColDivideDecimalColumn(0, 1, 2); + } + } + + public static class V2ColDiv128By16ColBench extends AbstractDecimalExpression { + @Override + public void setup() { + rowBatch = buildRowBatch(new DecimalColumnVectorV2(20, 2), 2, + getDecimalColumnVectorV2(20, 0), getDecimalColumnVectorV2(4, 0)); + expression = new DecimalV2ColDivideDecimalColumn(0, 1, 2); + } + } + + public static class V1ColMul64ColBench extends AbstractDecimalExpression { + @Override + public void setup() { + rowBatch = buildRowBatch(new DecimalColumnVector(11, 4), 2, + getDecimalColumnVector(5, 2), getDecimalColumnVector(5, 2)); + expression = new DecimalColMultiplyDecimalColumn(0, 1, 2); + } + } + + public static class V2ColMul64ColBench extends AbstractDecimalExpression { + @Override + public void setup() { + rowBatch = buildRowBatch(new DecimalColumnVectorV2(11, 4), 2, + getDecimalColumnVectorV2(5, 2), getDecimalColumnVectorV2(5, 2)); + expression = new DecimalV2ColMultiplyDecimalColumn(0, 1, 2); + } + } + + public static class V1ColMul128ColBench extends AbstractDecimalExpression { + @Override + public void setup() { + rowBatch = buildRowBatch(new DecimalColumnVector(31, 4), 2, + getDecimalColumnVector(15, 2), getDecimalColumnVector(15, 2)); + expression = new DecimalColMultiplyDecimalColumn(0, 1, 2); + } + } + + public static class V2ColMul128ColBench extends AbstractDecimalExpression { + @Override + public void setup() { + rowBatch = buildRowBatch(new DecimalColumnVectorV2(31, 4), 2, + getDecimalColumnVectorV2(15, 2), getDecimalColumnVectorV2(15, 2)); + expression = new DecimalV2ColMultiplyDecimalColumn(0, 1, 2); + } + } + + public static class V1ToString128ColBench extends AbstractDecimalExpression { + @Override + public void setup() { + rowBatch = buildRowBatch(new BytesColumnVector(), 1, + getDecimalColumnVector(20, 2)); + expression = new CastDecimalToString(0, 1); + } + } + + public static class V2ToString128ColBench extends AbstractDecimalExpression { + @Override + public void setup() { + rowBatch = buildRowBatch(new BytesColumnVector(), 1, + getDecimalColumnVectorV2(20, 2)); + expression = new CastDecimalV2ToString(0, 1); + } + } + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder().include(".*" + VectorizedDecimalBench.class.getSimpleName() + + ".*").build(); + new Runner(opt).run(); + } +} diff --git ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/CastDecimalV2ToString.java ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/CastDecimalV2ToString.java new file mode 100644 index 0000000..28e2475 --- /dev/null +++ ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/CastDecimalV2ToString.java @@ -0,0 +1,59 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.BytesColumnVector; +import org.apache.hadoop.hive.ql.exec.vector.DecimalColumnVector; +import org.apache.hadoop.hive.ql.exec.vector.decimalv2.DecimalColumnVectorV2; + +/** + * To support vectorized cast of decimal to string. + */ +public class CastDecimalV2ToString extends DecimalV2ToStringUnaryUDF { + + private static final long serialVersionUID = 1L; + private StringBuilder builder = new StringBuilder(); + + public CastDecimalV2ToString() { + super(); + } + + public CastDecimalV2ToString(int inputColumn, int outputColumn) { + super(inputColumn, outputColumn); + } + + // The assign method will be overridden for CHAR and VARCHAR. + protected void assign(BytesColumnVector outV, int i, byte[] bytes, int length) { + outV.setVal(i, bytes, 0, length); + } + + @Override + protected void func(BytesColumnVector outV, DecimalColumnVectorV2 inV, int i) { + builder.delete(0, builder.length()); + inV.stringifyValue(builder, i); + byte[] b = null; + try { + b = builder.toString().getBytes("UTF-8"); + } catch (Exception e) { + // This should never happen. If it does, there is a bug. + throw new RuntimeException("Internal error: unable to convert decimal to string", e); + } + assign(outV, i, b, b.length); + } +} diff --git ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ColAddDecimalColumn.java ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ColAddDecimalColumn.java new file mode 100644 index 0000000..20f47c3 --- /dev/null +++ ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ColAddDecimalColumn.java @@ -0,0 +1,121 @@ +package org.apache.hadoop.hive.ql.exec.vector.expressions; + +import org.apache.hadoop.hive.common.type.HiveDecimalV2; +import org.apache.hadoop.hive.ql.exec.vector.VectorExpressionDescriptor; +import org.apache.hadoop.hive.ql.exec.vector.VectorizedRowBatch; +import org.apache.hadoop.hive.ql.exec.vector.decimalv2.DecimalColumnVectorV2; + +public class DecimalV2ColAddDecimalColumn extends VectorExpression { + private static final long serialVersionUID = 1L; + int colNum1; + int colNum2; + int outputColumn; + + public DecimalV2ColAddDecimalColumn(int colNum1, int colNum2, int outputColumn) { + this(); + this.colNum1 = colNum1; + this.colNum2 = colNum2; + this.outputColumn = outputColumn; + } + + public DecimalV2ColAddDecimalColumn() { + super(); + } + + @Override + public void evaluate(VectorizedRowBatch batch) { + if (childExpressions != null) { + super.evaluateChildren(batch); + } + + DecimalColumnVectorV2 inputColVector1 = (DecimalColumnVectorV2) batch.cols[colNum1]; + DecimalColumnVectorV2 inputColVector2 = (DecimalColumnVectorV2) batch.cols[colNum2]; + DecimalColumnVectorV2 outputColVector = (DecimalColumnVectorV2) batch.cols[outputColumn]; + int[] sel = batch.selected; + int n = batch.size; + + // return immediately if batch is empty + if (n == 0) { + return; + } + + outputColVector.isRepeating = + inputColVector1.isRepeating && inputColVector2.isRepeating + || inputColVector1.isRepeating && !inputColVector1.noNulls && inputColVector1.isNull[0] + || inputColVector2.isRepeating && !inputColVector2.noNulls && inputColVector2.isNull[0]; + + if (inputColVector1.noNulls && inputColVector2.noNulls) { + + /* Initialize output vector NULL values to false. This is necessary + * since the decimal operation may produce a NULL result even for + * a non-null input vector value, and convert the output vector + * to have noNulls = false; + */ + NullUtil.initOutputNullsToFalse(outputColVector, + inputColVector1.isRepeating && inputColVector2.isRepeating, + batch.selectedInUse, sel, n); + } + + // Handle nulls first + NullUtil.propagateNullsColCol( + inputColVector1, inputColVector2, outputColVector, sel, n, batch.selectedInUse); + + if (inputColVector1.isRepeating && inputColVector2.isRepeating) { + HiveDecimalV2 element = new HiveDecimalV2(); + element.add(inputColVector1.getElement(0), inputColVector2.getElement(0)); + outputColVector.fill(element); + } else { + if (inputColVector1.isRepeating) { + inputColVector1.fill(inputColVector1.getElement(0)); + } + if (inputColVector2.isRepeating) { + inputColVector2.fill(inputColVector2.getElement(0)); + } + outputColVector.add(inputColVector1, inputColVector2); + } + } + + @Override + public int getOutputColumn() { + return outputColumn; + } + + @Override + public String getOutputType() { + return "decimalv2"; + } + + 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.DECIMAL, + VectorExpressionDescriptor.ArgumentType.DECIMAL) + .setInputExpressionTypes( + VectorExpressionDescriptor.InputExpressionType.COLUMN, + VectorExpressionDescriptor.InputExpressionType.COLUMN).build(); + } +} diff --git ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ColDivideDecimalColumn.java ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ColDivideDecimalColumn.java new file mode 100644 index 0000000..2be7a0d --- /dev/null +++ ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ColDivideDecimalColumn.java @@ -0,0 +1,121 @@ +package org.apache.hadoop.hive.ql.exec.vector.expressions; + +import org.apache.hadoop.hive.common.type.HiveDecimalV2; +import org.apache.hadoop.hive.ql.exec.vector.VectorExpressionDescriptor; +import org.apache.hadoop.hive.ql.exec.vector.VectorizedRowBatch; +import org.apache.hadoop.hive.ql.exec.vector.decimalv2.DecimalColumnVectorV2; + +public class DecimalV2ColDivideDecimalColumn extends VectorExpression { + private static final long serialVersionUID = 1L; + int colNum1; + int colNum2; + int outputColumn; + + public DecimalV2ColDivideDecimalColumn(int colNum1, int colNum2, int outputColumn) { + this(); + this.colNum1 = colNum1; + this.colNum2 = colNum2; + this.outputColumn = outputColumn; + } + + public DecimalV2ColDivideDecimalColumn() { + super(); + } + + @Override + public void evaluate(VectorizedRowBatch batch) { + if (childExpressions != null) { + super.evaluateChildren(batch); + } + + DecimalColumnVectorV2 inputColVector1 = (DecimalColumnVectorV2) batch.cols[colNum1]; + DecimalColumnVectorV2 inputColVector2 = (DecimalColumnVectorV2) batch.cols[colNum2]; + DecimalColumnVectorV2 outputColVector = (DecimalColumnVectorV2) batch.cols[outputColumn]; + int[] sel = batch.selected; + int n = batch.size; + + // return immediately if batch is empty + if (n == 0) { + return; + } + + outputColVector.isRepeating = + inputColVector1.isRepeating && inputColVector2.isRepeating + || inputColVector1.isRepeating && !inputColVector1.noNulls && inputColVector1.isNull[0] + || inputColVector2.isRepeating && !inputColVector2.noNulls && inputColVector2.isNull[0]; + + if (inputColVector1.noNulls && inputColVector2.noNulls) { + + /* Initialize output vector NULL values to false. This is necessary + * since the decimal operation may produce a NULL result even for + * a non-null input vector value, and convert the output vector + * to have noNulls = false; + */ + NullUtil.initOutputNullsToFalse(outputColVector, + inputColVector1.isRepeating && inputColVector2.isRepeating, + batch.selectedInUse, sel, n); + } + + // Handle nulls first + NullUtil.propagateNullsColCol( + inputColVector1, inputColVector2, outputColVector, sel, n, batch.selectedInUse); + + if (inputColVector1.isRepeating && inputColVector2.isRepeating) { + HiveDecimalV2 element = new HiveDecimalV2(); + element.divide(inputColVector1.getElement(0), inputColVector2.getElement(0)); + outputColVector.fill(element); + } else { + if (inputColVector1.isRepeating) { + inputColVector1.fill(inputColVector1.getElement(0)); + } + if (inputColVector2.isRepeating) { + inputColVector2.fill(inputColVector2.getElement(0)); + } + outputColVector.divide(inputColVector1, inputColVector2); + } + } + + @Override + public int getOutputColumn() { + return outputColumn; + } + + @Override + public String getOutputType() { + return "decimalv2"; + } + + 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.DECIMAL, + VectorExpressionDescriptor.ArgumentType.DECIMAL) + .setInputExpressionTypes( + VectorExpressionDescriptor.InputExpressionType.COLUMN, + VectorExpressionDescriptor.InputExpressionType.COLUMN).build(); + } +} diff --git ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ColMultiplyDecimalColumn.java ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ColMultiplyDecimalColumn.java new file mode 100644 index 0000000..edad268 --- /dev/null +++ ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ColMultiplyDecimalColumn.java @@ -0,0 +1,121 @@ +package org.apache.hadoop.hive.ql.exec.vector.expressions; + +import org.apache.hadoop.hive.common.type.HiveDecimalV2; +import org.apache.hadoop.hive.ql.exec.vector.VectorExpressionDescriptor; +import org.apache.hadoop.hive.ql.exec.vector.VectorizedRowBatch; +import org.apache.hadoop.hive.ql.exec.vector.decimalv2.DecimalColumnVectorV2; + +public class DecimalV2ColMultiplyDecimalColumn extends VectorExpression { + private static final long serialVersionUID = 1L; + int colNum1; + int colNum2; + int outputColumn; + + public DecimalV2ColMultiplyDecimalColumn(int colNum1, int colNum2, int outputColumn) { + this(); + this.colNum1 = colNum1; + this.colNum2 = colNum2; + this.outputColumn = outputColumn; + } + + public DecimalV2ColMultiplyDecimalColumn() { + super(); + } + + @Override + public void evaluate(VectorizedRowBatch batch) { + if (childExpressions != null) { + super.evaluateChildren(batch); + } + + DecimalColumnVectorV2 inputColVector1 = (DecimalColumnVectorV2) batch.cols[colNum1]; + DecimalColumnVectorV2 inputColVector2 = (DecimalColumnVectorV2) batch.cols[colNum2]; + DecimalColumnVectorV2 outputColVector = (DecimalColumnVectorV2) batch.cols[outputColumn]; + int[] sel = batch.selected; + int n = batch.size; + + // return immediately if batch is empty + if (n == 0) { + return; + } + + outputColVector.isRepeating = + inputColVector1.isRepeating && inputColVector2.isRepeating + || inputColVector1.isRepeating && !inputColVector1.noNulls && inputColVector1.isNull[0] + || inputColVector2.isRepeating && !inputColVector2.noNulls && inputColVector2.isNull[0]; + + if (inputColVector1.noNulls && inputColVector2.noNulls) { + + /* Initialize output vector NULL values to false. This is necessary + * since the decimal operation may produce a NULL result even for + * a non-null input vector value, and convert the output vector + * to have noNulls = false; + */ + NullUtil.initOutputNullsToFalse(outputColVector, + inputColVector1.isRepeating && inputColVector2.isRepeating, + batch.selectedInUse, sel, n); + } + + // Handle nulls first + NullUtil.propagateNullsColCol( + inputColVector1, inputColVector2, outputColVector, sel, n, batch.selectedInUse); + + if (inputColVector1.isRepeating && inputColVector2.isRepeating) { + HiveDecimalV2 element = new HiveDecimalV2(); + element.multiply(inputColVector1.getElement(0), inputColVector2.getElement(0)); + outputColVector.fill(element); + } else { + if (inputColVector1.isRepeating) { + inputColVector1.fill(inputColVector1.getElement(0)); + } + if (inputColVector2.isRepeating) { + inputColVector2.fill(inputColVector2.getElement(0)); + } + outputColVector.multiply(inputColVector1, inputColVector2); + } + } + + @Override + public int getOutputColumn() { + return outputColumn; + } + + @Override + public String getOutputType() { + return "decimalv2"; + } + + 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.DECIMAL, + VectorExpressionDescriptor.ArgumentType.DECIMAL) + .setInputExpressionTypes( + VectorExpressionDescriptor.InputExpressionType.COLUMN, + VectorExpressionDescriptor.InputExpressionType.COLUMN).build(); + } +} diff --git ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ColSubtractDecimalColumn.java ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ColSubtractDecimalColumn.java new file mode 100644 index 0000000..30c9424 --- /dev/null +++ ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ColSubtractDecimalColumn.java @@ -0,0 +1,121 @@ +package org.apache.hadoop.hive.ql.exec.vector.expressions; + +import org.apache.hadoop.hive.common.type.HiveDecimalV2; +import org.apache.hadoop.hive.ql.exec.vector.VectorExpressionDescriptor; +import org.apache.hadoop.hive.ql.exec.vector.VectorizedRowBatch; +import org.apache.hadoop.hive.ql.exec.vector.decimalv2.DecimalColumnVectorV2; + +public class DecimalV2ColSubtractDecimalColumn extends VectorExpression { + private static final long serialVersionUID = 1L; + int colNum1; + int colNum2; + int outputColumn; + + public DecimalV2ColSubtractDecimalColumn(int colNum1, int colNum2, int outputColumn) { + this(); + this.colNum1 = colNum1; + this.colNum2 = colNum2; + this.outputColumn = outputColumn; + } + + public DecimalV2ColSubtractDecimalColumn() { + super(); + } + + @Override + public void evaluate(VectorizedRowBatch batch) { + if (childExpressions != null) { + super.evaluateChildren(batch); + } + + DecimalColumnVectorV2 inputColVector1 = (DecimalColumnVectorV2) batch.cols[colNum1]; + DecimalColumnVectorV2 inputColVector2 = (DecimalColumnVectorV2) batch.cols[colNum2]; + DecimalColumnVectorV2 outputColVector = (DecimalColumnVectorV2) batch.cols[outputColumn]; + int[] sel = batch.selected; + int n = batch.size; + + // return immediately if batch is empty + if (n == 0) { + return; + } + + outputColVector.isRepeating = + inputColVector1.isRepeating && inputColVector2.isRepeating + || inputColVector1.isRepeating && !inputColVector1.noNulls && inputColVector1.isNull[0] + || inputColVector2.isRepeating && !inputColVector2.noNulls && inputColVector2.isNull[0]; + + if (inputColVector1.noNulls && inputColVector2.noNulls) { + + /* Initialize output vector NULL values to false. This is necessary + * since the decimal operation may produce a NULL result even for + * a non-null input vector value, and convert the output vector + * to have noNulls = false; + */ + NullUtil.initOutputNullsToFalse(outputColVector, + inputColVector1.isRepeating && inputColVector2.isRepeating, + batch.selectedInUse, sel, n); + } + + // Handle nulls first + NullUtil.propagateNullsColCol( + inputColVector1, inputColVector2, outputColVector, sel, n, batch.selectedInUse); + + if (inputColVector1.isRepeating && inputColVector2.isRepeating) { + HiveDecimalV2 element = new HiveDecimalV2(); + element.subtract(inputColVector1.getElement(0), inputColVector2.getElement(0)); + outputColVector.fill(element); + } else { + if (inputColVector1.isRepeating) { + inputColVector1.fill(inputColVector1.getElement(0)); + } + if (inputColVector2.isRepeating) { + inputColVector2.fill(inputColVector2.getElement(0)); + } + outputColVector.subtract(inputColVector1, inputColVector2); + } + } + + @Override + public int getOutputColumn() { + return outputColumn; + } + + @Override + public String getOutputType() { + return "decimalv2"; + } + + 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.DECIMAL, + VectorExpressionDescriptor.ArgumentType.DECIMAL) + .setInputExpressionTypes( + VectorExpressionDescriptor.InputExpressionType.COLUMN, + VectorExpressionDescriptor.InputExpressionType.COLUMN).build(); + } +} diff --git ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ToStringUnaryUDF.java ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ToStringUnaryUDF.java new file mode 100644 index 0000000..031f1d6 --- /dev/null +++ ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalV2ToStringUnaryUDF.java @@ -0,0 +1,148 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.BytesColumnVector; +import org.apache.hadoop.hive.ql.exec.vector.DecimalColumnVector; +import org.apache.hadoop.hive.ql.exec.vector.VectorExpressionDescriptor; +import org.apache.hadoop.hive.ql.exec.vector.VectorizedRowBatch; +import org.apache.hadoop.hive.ql.exec.vector.decimalv2.DecimalColumnVectorV2; + +/** + * This is a superclass for unary decimal functions returning strings that operate directly on the + * input and set the output. + */ +abstract public class DecimalV2ToStringUnaryUDF extends VectorExpression { + private static final long serialVersionUID = 1L; + int inputColumn; + int outputColumn; + + public DecimalV2ToStringUnaryUDF(int inputColumn, int outputColumn) { + this.inputColumn = inputColumn; + this.outputColumn = outputColumn; + } + + public DecimalV2ToStringUnaryUDF() { + super(); + } + + abstract protected void func(BytesColumnVector outV, DecimalColumnVectorV2 inV, int i); + + @Override + public void evaluate(VectorizedRowBatch batch) { + + if (childExpressions != null) { + super.evaluateChildren(batch); + } + + DecimalColumnVectorV2 inV = (DecimalColumnVectorV2) batch.cols[inputColumn]; + int[] sel = batch.selected; + int n = batch.size; + BytesColumnVector outV = (BytesColumnVector) batch.cols[outputColumn]; + outV.initBuffer(); + + if (n == 0) { + //Nothing to do + return; + } + + if (inV.noNulls) { + outV.noNulls = true; + if (inV.isRepeating) { + outV.isRepeating = true; + func(outV, inV, 0); + } else if (batch.selectedInUse) { + for(int j = 0; j != n; j++) { + int i = sel[j]; + func(outV, inV, i); + } + outV.isRepeating = false; + } else { + for(int i = 0; i != n; i++) { + func(outV, inV, i); + } + outV.isRepeating = false; + } + } else { + + // Handle case with nulls. Don't do function if the value is null, + // because the data may be undefined for a null value. + outV.noNulls = false; + if (inV.isRepeating) { + outV.isRepeating = true; + outV.isNull[0] = inV.isNull[0]; + if (!inV.isNull[0]) { + func(outV, inV, 0); + } + } else if (batch.selectedInUse) { + for(int j = 0; j != n; j++) { + int i = sel[j]; + outV.isNull[i] = inV.isNull[i]; + if (!inV.isNull[i]) { + func(outV, inV, i); + } + } + outV.isRepeating = false; + } else { + System.arraycopy(inV.isNull, 0, outV.isNull, 0, n); + for(int i = 0; i != n; i++) { + if (!inV.isNull[i]) { + func(outV, inV, i); + } + } + outV.isRepeating = false; + } + } + } + + + @Override + public int getOutputColumn() { + return outputColumn; + } + + public void setOutputColumn(int outputColumn) { + this.outputColumn = outputColumn; + } + + public int getInputColumn() { + return inputColumn; + } + + public void setInputColumn(int inputColumn) { + this.inputColumn = inputColumn; + } + + @Override + public String getOutputType() { + return "String"; + } + + @Override + public VectorExpressionDescriptor.Descriptor getDescriptor() { + VectorExpressionDescriptor.Builder b = new VectorExpressionDescriptor.Builder(); + b.setMode(VectorExpressionDescriptor.Mode.PROJECTION) + .setNumArguments(1) + .setArgumentTypes( + VectorExpressionDescriptor.ArgumentType.DECIMAL) + .setInputExpressionTypes( + VectorExpressionDescriptor.InputExpressionType.COLUMN); + return b.build(); + } +} \ No newline at end of file diff --git storage-api/src/java/org/apache/hadoop/hive/common/type/HiveDecimalV2.java storage-api/src/java/org/apache/hadoop/hive/common/type/HiveDecimalV2.java new file mode 100644 index 0000000..e3094e7 --- /dev/null +++ storage-api/src/java/org/apache/hadoop/hive/common/type/HiveDecimalV2.java @@ -0,0 +1,150 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.hadoop.hive.ql.exec.vector.decimalv2.DecimalColumnVectorV2; + +/** + * HiveDecimalV2 is a special case of DecimalColumnVectorV2 with a single element. + */ +public class HiveDecimalV2 implements ScaledDecimal, Comparable { + public DecimalColumnVectorV2 vector = new DecimalColumnVectorV2(1); + + public HiveDecimalV2(int precision, int scale, long lower) { + this(precision, scale, 0, lower); + } + + public HiveDecimalV2(int precision, int scale, long upper, long lower) { + vector.setPrecision(precision); + vector.setScale(scale); + setUpper(upper); + setLower(lower); + } + + public HiveDecimalV2() { + } + + HiveDecimalV2(String string) { + vector.set(0, string); + } + + public static HiveDecimalV2 create(String string) { + return new HiveDecimalV2(string); + } + + public long getUpper() { + return vector.getUpper()[0]; + } + + public long getLower() { + return vector.getLower()[0]; + } + + public void setUpper(long upper) { + vector.getUpper()[0] = upper; + } + + public void setLower(long lower) { + vector.getLower()[0] = lower; + } + + public int getPrecision() { + return vector.getPrecision(); + } + + public int getScale() { + return vector.getScale(); + } + + @Override + public void add(HiveDecimalV2 left, HiveDecimalV2 right) { + this.vector.add(left.vector, right.vector); + } + + @Override + public void subtract(HiveDecimalV2 left, HiveDecimalV2 right) { + this.vector.subtract(left.vector, right.vector); + } + + @Override + public void multiply(HiveDecimalV2 left, HiveDecimalV2 right) { + this.vector.multiply(left.vector, right.vector); + } + + @Override + public void divide(HiveDecimalV2 left, HiveDecimalV2 right) { + this.vector.divide(left.vector, right.vector); + } + + @Override + public void remainder(HiveDecimalV2 left, HiveDecimalV2 right) { + this.vector.remainder(left.vector, right.vector); + } + + @Override + public void scaleByPowerOfTen(HiveDecimalV2 input, int scale) { + this.vector.scaleByPowerOfTen(input.vector, scale); + } + + @Override + public void negate(HiveDecimalV2 input) { + this.vector.negate(input.vector); + } + + @Override + public void set(HiveDecimalV2 input) { + this.vector.set(input.vector); + } + + @Override + public int hashCode() { + long upper = getUpper(); + long lower = getLower(); + return (int) ((upper >>> 32) ^ (upper & 0xFFFFFFFFFFFFL) ^ + (lower >>> 32) ^ (lower & 0xFFFFFFFFFFFFL)) ^ + getPrecision() ^ getScale(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof HiveDecimalV2)) { + return false; + } + + HiveDecimalV2 that = (HiveDecimalV2) obj; + return (this.getPrecision() == that.getPrecision()) && (this.getScale() == that.getScale()) && + (this.getUpper() == that.getUpper()) && (this.getLower() == that.getLower()); + } + + @Override + public int compareTo(HiveDecimalV2 other) { + HiveDecimalV2 comparison = new HiveDecimalV2(); + + comparison.subtract(this, other); + if ((comparison.getUpper() == 0) && + (comparison.getLower() == 0)) { + return 0; + } + if (comparison.getUpper() < 0) { + return -1; + } else { + return 1; + } + } +} diff --git storage-api/src/java/org/apache/hadoop/hive/common/type/ScaledDecimal.java storage-api/src/java/org/apache/hadoop/hive/common/type/ScaledDecimal.java new file mode 100644 index 0000000..dd43ce6 --- /dev/null +++ storage-api/src/java/org/apache/hadoop/hive/common/type/ScaledDecimal.java @@ -0,0 +1,79 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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; + +/** + * A scaled decimal vector or value. + * @param + */ +public interface ScaledDecimal { + /** + * Negate input + * @param input + */ + void negate(V input); + + /** + * Set as input + * @param input + */ + void set(V input); + + /** + * Add left and right + * @param left + * @param right + */ + void add(V left, V right); + + /** + * Subtract right from left + * @param left + * @param right + */ + void subtract(V left, V right); + + /** + * Multiply left by right + * @param left + * @param right + */ + void multiply(V left, V right); + + /** + * Divide left by right + * @param left + * @param right + */ + void divide(V left, V right); + + /** + * Take a remainder of division + * @param left + * @param right + */ + void remainder(V left, V right); + + /** + * Scale input by power of ten + * @param input + * @param scale + */ + void scaleByPowerOfTen(V input, int scale); +} diff --git storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/DecimalColumnVectorV2.java storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/DecimalColumnVectorV2.java new file mode 100644 index 0000000..ddef8e3 --- /dev/null +++ storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/DecimalColumnVectorV2.java @@ -0,0 +1,219 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.HiveDecimalV2; +import org.apache.hadoop.hive.common.type.ScaledDecimal; +import org.apache.hadoop.hive.ql.exec.vector.ColumnVector; +import org.apache.hadoop.hive.ql.exec.vector.VectorizedRowBatch; + +public class DecimalColumnVectorV2 extends ColumnVector implements ScaledDecimal { + public static final int MAX_PRECISION = 38; + public static final int MAX_HALF_PRECISION = 19; + + ScaledVector scaled; + private int size; + + public int getPrecision() { + return scaled.precision; + } + + public int getScale() { + return scaled.scale; + } + + public void setPrecision(int precision) { + scaled.precision = precision; + } + + public void setScale(int scale) { + scaled.scale = scale; + } + + public long[] getUpper() { + return scaled.unscaled.upper.array; + } + + public long[] getLower() { + return scaled.unscaled.lower.array; + } + + /** + * Constructor for super-class ColumnVector. This is not called directly, + * but used to initialize inherited fields. + * + * @param len Vector length + */ + public DecimalColumnVectorV2(int len) { + this(0, 0, len); + } + + public DecimalColumnVectorV2() { + this(VectorizedRowBatch.DEFAULT_SIZE); + } + + public DecimalColumnVectorV2(int precision, int scale, int len) { + super(len); + scaled = new ScaledVector(precision, scale, len); + size = len; + } + + public DecimalColumnVectorV2(int precision, int scale) { + this(precision, scale, VectorizedRowBatch.DEFAULT_SIZE); + } + + public void set(int index, String string) { + if (scaled.is64Bit()) { + scaled.parse64(string.getBytes(), 0, string.length(), index); + } else { + scaled.parse128(string.getBytes(), 0, string.length(), index); + } + } + + @Override + public void flatten(boolean selectedInUse, int[] sel, int size) { + + } + + @Override + public void setElement(int outElementNum, int inputElementNum, ColumnVector inputVector) { + + } + + @Override + public void stringifyValue(StringBuilder buffer, int row) { + if (scaled.precision <= MAX_HALF_PRECISION) { + this.scaled.stringify64(buffer, row, isNull); + } else { + this.scaled.stringify128(buffer, row, isNull); + } + } + + @Override + public void set(DecimalColumnVectorV2 input) { + scaled.set(input.scaled); + } + + @Override + public void negate(DecimalColumnVectorV2 input) { + scaled.negate(input.scaled); + } + + @Override + public void add(DecimalColumnVectorV2 left, DecimalColumnVectorV2 right) { + scaled.add(left.scaled, right.scaled, isNull); + } + + @Override + public void subtract(DecimalColumnVectorV2 left, DecimalColumnVectorV2 right) { + scaled.subtract(left.scaled, right.scaled, isNull); + } + + @Override + public void multiply(DecimalColumnVectorV2 left, DecimalColumnVectorV2 right) { + scaled.multiply(left.scaled, right.scaled, isNull); + } + + @Override + public void divide(DecimalColumnVectorV2 left, DecimalColumnVectorV2 right) { + scaled.divide(left.scaled, right.scaled, isNull); + } + + @Override + public void remainder(DecimalColumnVectorV2 left, DecimalColumnVectorV2 right) { + scaled.remainder(left.scaled, right.scaled, isNull); + } + + @Override + public void scaleByPowerOfTen(DecimalColumnVectorV2 input, int scale) { + scaled.scaleByPowerOfTen(input.scaled, scale); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof DecimalColumnVectorV2)) { + return false; + } + DecimalColumnVectorV2 that = (DecimalColumnVectorV2) obj; + if (!((this.scaled.precision == that.scaled.precision) && + (this.scaled.scale == that.scaled.scale))) { + return false; + } + if (scaled.is64Bit()) { + for (int i = 0; i < size; i++) { + boolean isNull = this.isNull[i]; + if (isNull != that.isNull[i]) { + return false; + } + if (!isNull) { + if (this.scaled.unscaled.lower.array[i] != that.scaled.unscaled.lower.array[i]) { + return false; + } + } + } + } else { + for (int i = 0; i < size; i++) { + boolean isNull = this.isNull[i]; + if (isNull != that.isNull[i]) { + return false; + } + if (!isNull) { + if (this.scaled.unscaled.upper.array[i] != that.scaled.unscaled.upper.array[i]) { + return false; + } + if (this.scaled.unscaled.lower.array[i] != that.scaled.unscaled.lower.array[i]) { + return false; + } + } + } + } + return true; + } + + @Override + public String toString() { + return scaled.toString(isNull); + } + + @Override + public void ensureSize(int size, boolean presesrveData) { + this.size = size; + scaled.ensureSize(size); + super.ensureSize(size, presesrveData); + } + + public HiveDecimalV2 getElement(int i) { + return new HiveDecimalV2(getPrecision(), getScale(), getUpper()[i], getLower()[i]); + } + + public void fill(HiveDecimalV2 element) { + HiveDecimalV2 scaled = new HiveDecimalV2(getPrecision(), getScale(), 0, 0); + scaled.vector.scaled.rescale(element.vector.scaled, getScale()); + + long[] upperArray = getUpper(); + long upperValue = scaled.getUpper(); + long[] lowerArray = getLower(); + long lowerValue = scaled.getLower(); + + for (int i = 0; i < size; i++) { + upperArray[i] = upperValue; + lowerArray[i] = lowerValue; + } + } +} diff --git storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/ScaledVector.java storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/ScaledVector.java new file mode 100644 index 0000000..86bba31 --- /dev/null +++ storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/ScaledVector.java @@ -0,0 +1,512 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 java.util.Arrays; + +import static org.apache.hadoop.hive.ql.exec.vector.decimalv2.DecimalColumnVectorV2.MAX_HALF_PRECISION; +import static org.apache.hadoop.hive.ql.exec.vector.decimalv2.DecimalColumnVectorV2.MAX_PRECISION; + +/** + * Internal scaled vector. + */ +class ScaledVector { + Unscaled128Vector unscaled; + int precision; + int scale; + + private int size; + + // Internal operation classes + private Add add; + private Multiply multiply; + private Divide divide; + private Remainder remainder; + private ScaledVectorStringify stringify; + + ScaledVector() { + unscaled = new Unscaled128Vector(); + } + + ScaledVector(int precision, int scale, int len) { + size = len; + unscaled = new Unscaled128Vector(size); + this.precision = precision; + this.scale = scale; + } + + static void ensureSize(int size, ScaledVector ... list) { + for (ScaledVector item : list) { + item.ensureSize(size); + } + } + + void ensureSize(int size) { + this.size = size; + Unscaled128Vector.ensureSize(size, unscaled); + } + + ScaledVector negate(ScaledVector input) { + if (input == this) { + throw new IllegalArgumentException(); + } + + this.precision = input.precision; + this.scale = input.scale; + if (input.is64Bit()) { + unscaled.lower.negate(input.unscaled.lower); + unscaled.propagateSign(); + } else { + unscaled.negate(input.unscaled); + } + return this; + } + + ScaledVector set(ScaledVector input) { + precision = input.precision; + scale = input.scale; + unscaled.set(input.unscaled); + return this; + } + + ScaledVector scaleByPowerOfTen(ScaledVector input, int scale) { + if (scale > 0) { + return this.multiplyByPowerOfTen(input, scale); + } else if (scale < 0) { + return this.divideByPowerOfTen(input, -scale); + } + return this.set(input); + } + + ScaledVector rescale(ScaledVector input, int scale) { + int scaleDiff = scale - input.scale; + + this.scale = input.scale + scaleDiff; + this.precision = input.precision + scaleDiff; + + if (scaleDiff > 0) { + if (this.is64Bit()) { + unscaled.lower.multiplyByPowerOfTen(input.unscaled.lower, scaleDiff); + unscaled.propagateSign(); + } else { + unscaled.multiplyByPowerOfTen(input.unscaled, scaleDiff); + } + } else if (scaleDiff < 0) { + if (this.is64Bit()) { + unscaled.lower.divideByPowerOfTen(input.unscaled.lower, -scaleDiff); + unscaled.propagateSign(); + } else { + unscaled.divideByPowerOfTen(input.unscaled, -scaleDiff); + } + } else { + unscaled.set(input.unscaled); + } + + return this; + } + + String toString(boolean[] isNull) { + StringBuilder builder = new StringBuilder(); + if (this.is64Bit()) { + builder.append("[(Precision: "); + builder.append(precision); + builder.append(", Scale: "); + builder.append(scale); + builder.append("), "); + if (size > 0) { + stringify64(builder, 0, isNull); + } + for (int i = 1; i < size; i++) { + builder.append(", "); + stringify64(builder, i, isNull); + } + builder.append(']'); + } else { + builder.append("[(Precision: "); + builder.append(precision); + builder.append(", Scale: "); + builder.append(scale); + builder.append("), "); + if (size > 0) { + stringify128(builder, 0, isNull); + } + for (int i = 1; i < size; i++) { + builder.append(", "); + stringify128(builder, i, isNull); + } + builder.append(']'); + } + return builder.toString(); + } + + ScaledVector add(ScaledVector left, ScaledVector right, boolean[] isNull) { + return getAdd().add(left, right, isNull); + } + + ScaledVector subtract(ScaledVector left, ScaledVector right, boolean[] isNull) { + return getAdd().subtract(left, right, isNull); + } + + ScaledVector multiply(ScaledVector left, ScaledVector right, boolean[] isNull) { + return getMultiply().multiply(left, right, isNull); + } + + ScaledVector remainder(ScaledVector left, ScaledVector right, boolean[] isNull) { + return getRemainder().remainder(left, right, isNull); + } + + ScaledVector divide(ScaledVector left, ScaledVector right, boolean[] isNull) { + return getDivide().divide(left, right, isNull); + } + + void stringify128(StringBuilder output, int row, boolean[] isNull) { + getStringify().stringify128(output, row, isNull); + } + + void stringify64(StringBuilder output, int row, boolean[] isNull) { + getStringify().stringify64(output, row, isNull); + } + + void parse128(byte[] bytes, int start, int length, int row) { + getStringify().parse128(bytes, start, length, row); + } + + void parse64(byte[] bytes, int start, int length, int row) { + getStringify().parse64(bytes, start, length, row); + } + + boolean is64Bit() { + return this.precision <= MAX_HALF_PRECISION; + } + + private ScaledVector fillNull(boolean[] isNull) { + Arrays.fill(isNull, 0, size, true); + return this; + } + + private ScaledVector multiplyByPowerOfTen(ScaledVector input, int scale) { + this.precision = input.precision + scale; + this.scale = input.scale + scale; + + if (this.is64Bit()) { + unscaled.lower.multiplyByPowerOfTen(input.unscaled.lower, scale); + unscaled.propagateSign(); + } else { + unscaled.multiplyByPowerOfTen(input.unscaled, scale); + } + return this; + } + + private ScaledVector divideByPowerOfTen(ScaledVector input, int scale) { + this.precision = input.precision - scale; + this.scale = input.scale - scale; + + if (input.is64Bit()) { + unscaled.lower.divideByPowerOfTen(input.unscaled.lower, scale, true); + unscaled.propagateSign(); + } else { + unscaled.divideByPowerOfTen(input.unscaled, scale, true); + } + return this; + } + + private class Add { + private ScaledVector leftAdjust = new ScaledVector(); + private ScaledVector rightAdjust = new ScaledVector(); + + ScaledVector subtract(ScaledVector left, ScaledVector right, boolean[] isNull) { + if (!this.prepareAddOrSubtract(left, right, isNull)) { + return ScaledVector.this; + } + + if (is64Bit()) { + unscaled.lower.subtract(leftAdjust.unscaled.lower, rightAdjust.unscaled.lower); + unscaled.propagateSign(); + } else { + unscaled.subtract(leftAdjust.unscaled, rightAdjust.unscaled); + } + return ScaledVector.this; + } + + ScaledVector add(ScaledVector left, ScaledVector right, boolean[] isNull) { + if (!this.prepareAddOrSubtract(left, right, isNull)) { + return ScaledVector.this; + } + + if (is64Bit()) { + unscaled.lower.add(leftAdjust.unscaled.lower, rightAdjust.unscaled.lower); + unscaled.propagateSign(); + } else { + unscaled.add(leftAdjust.unscaled, rightAdjust.unscaled); + } + return ScaledVector.this; + } + + private boolean prepareAddOrSubtract(ScaledVector left, ScaledVector right, boolean[] isNull) { + // Prepare values + ScaledVector.ensureSize(size, leftAdjust, rightAdjust); + final int leftInteger = left.precision - left.scale; + final int rightInteger = right.precision - right.scale; + final int actualResultInteger = Math.max(leftInteger, rightInteger) + 1; + final int preferredResultScale = Math.max(left.scale, right.scale); + final int preferredResultPrecision = actualResultInteger + preferredResultScale; + int actualResultScale = preferredResultScale; + int actualResultPrecision = preferredResultPrecision; + + // If the result is too large, try to rescale inputs + if (preferredResultPrecision > MAX_PRECISION) { + + // If it's adjustable, rescale inputs + if (actualResultInteger <= MAX_PRECISION) { + actualResultPrecision = MAX_PRECISION; + actualResultScale = actualResultPrecision - actualResultInteger; + } + + // If it's not adjustable, fill nulls + else { + fillNull(isNull); + return false; + } + } + + // Rescale + leftAdjust.rescale(left, actualResultScale); + rightAdjust.rescale(right, actualResultScale); + + scale = actualResultScale; + precision = actualResultPrecision; + return true; + } + } + + private class Multiply { + private ScaledVector leftAdjust = new ScaledVector(); + private ScaledVector rightAdjust = new ScaledVector(); + + ScaledVector multiply(ScaledVector left, ScaledVector right, boolean[] isNull) { + // Sanity check + if (left == ScaledVector.this || right == ScaledVector.this) { + throw new IllegalArgumentException(); + } + + // Prepare values + ScaledVector.ensureSize(size, leftAdjust, rightAdjust); + final int preferredResultScale = left.scale + right.scale; + final int preferredResultPrecision = left.precision + right.precision + 1; + int actualResultScale = preferredResultScale; + int actualResultPrecision = preferredResultPrecision; + int actualLeftScale = left.scale; + int actualRightScale = right.scale; + + // If result precision exceeds the maximum + if (preferredResultPrecision > MAX_PRECISION) { + + // If it's adjustable, try to rescale inputs + int integer = preferredResultPrecision - preferredResultScale; + if (integer <= MAX_PRECISION) { + actualResultPrecision = MAX_PRECISION; + actualResultScale = actualResultPrecision - integer; + int scaleDiff = preferredResultScale - actualResultScale; + int leftScaleDiff = scaleDiff / 2; + int rightScaleDiff = scaleDiff - leftScaleDiff; + actualLeftScale = left.scale - leftScaleDiff; + actualRightScale = right.scale - rightScaleDiff; + } + + // If it's not adjustable, fill nulls + else { + for (int i = 0; i < size; i++) { + isNull[i] = true; + } + return ScaledVector.this; + } + } + + // Rescale inputs + leftAdjust.rescale(left, actualLeftScale); + rightAdjust.rescale(right, actualRightScale); + + // Result precision and scale + precision = actualResultPrecision; + scale = actualResultScale; + + // Multiplication + if (is64Bit()) { + unscaled.lower.multiply(left.unscaled.lower, right.unscaled.lower); + unscaled.propagateSign(); + } else { + unscaled.multiply(leftAdjust.unscaled, rightAdjust.unscaled); + } + return ScaledVector.this; + } + } + + private class Remainder { + private ScaledVector leftAdjust = new ScaledVector(); + private ScaledVector rightAdjust = new ScaledVector(); + + ScaledVector remainder(ScaledVector left, ScaledVector right, boolean[] isNull) { + if (left == ScaledVector.this || right == ScaledVector.this) { + throw new IllegalArgumentException(); + } + + ScaledVector.ensureSize(size, leftAdjust, rightAdjust); + + // Prepare variables + final int leftInteger = left.precision - left.scale; + final int rightInteger = right.precision - right.scale; + final int preferredScale = Math.max(left.scale, right.scale); + final int resultInteger = Math.min(leftInteger, rightInteger); + final int integer = Math.max(leftInteger, rightInteger); + int actualScale = preferredScale; + + // Precision and scale + if (integer + preferredScale > MAX_PRECISION) { + if (integer <= MAX_PRECISION) { + actualScale = MAX_PRECISION - integer; + } else { + fillNull(isNull); + return ScaledVector.this; + } + } + scale = actualScale; + precision = scale + resultInteger; + leftAdjust.rescale(left, actualScale); + rightAdjust.rescale(right, actualScale); + + // Remainder + if (left.is64Bit() && right.is64Bit()) { + unscaled.lower.remainderChecked(leftAdjust.unscaled.lower, rightAdjust.unscaled.lower, isNull, leftAdjust.precision, rightAdjust.precision); + unscaled.propagateSign(); + } else { + unscaled.remainderChecked(leftAdjust.unscaled, rightAdjust.unscaled, isNull, leftAdjust.precision, rightAdjust.precision); + } + return ScaledVector.this; + } + } + + private class Divide { + private ScaledVector leftAdjusted = new ScaledVector(); + private ScaledVector resultX10 = new ScaledVector(); + + ScaledVector divide(ScaledVector left, ScaledVector right, boolean[] isNull) { + if (left == ScaledVector.this || right == ScaledVector.this) { + throw new IllegalArgumentException(); + } + + // Prepare values + ScaledVector.ensureSize(size, leftAdjusted, resultX10); + final int leftInteger = left.precision - left.scale; + final int resultInteger = leftInteger + right.scale; + final int minimumResultScale = 6; + final int preferredResultScaleAddOne = + Math.max(left.scale + right.precision + 1, minimumResultScale) + 1; + final int preferredResultPrecisionAddOne = + preferredResultScaleAddOne + resultInteger; + final int preferredLeftScaleAddOne = + right.scale + preferredResultScaleAddOne; + int actualResultScaleAddOne = preferredResultScaleAddOne; + int actualLeftScaleAddOne = preferredLeftScaleAddOne; + + // If result precision exceeds the maximum precision + if (preferredResultPrecisionAddOne > MAX_PRECISION) { + + // Fit to the maximum + actualResultScaleAddOne = MAX_PRECISION - resultInteger; + int scaleDiff = preferredResultScaleAddOne - actualResultScaleAddOne; + actualLeftScaleAddOne = preferredLeftScaleAddOne - scaleDiff; + } + + // Scale + leftAdjusted.rescale(left, actualLeftScaleAddOne); + precision = actualResultScaleAddOne + resultInteger - 1; + scale = actualResultScaleAddOne - 1; + + // Divide + if (leftAdjusted.is64Bit() && right.is64Bit()) { + resultX10.unscaled.lower.divideChecked( + leftAdjusted.unscaled.lower, right.unscaled.lower, isNull, leftAdjusted.precision, right.precision); + resultX10.unscaled.propagateSign(); + } else { + resultX10.unscaled.divideChecked(leftAdjusted.unscaled, right.unscaled, isNull, leftAdjusted.precision, right.precision); + } + if (is64Bit()) { + unscaled.lower.divideByPowerOfTen(resultX10.unscaled.lower, 1, true); + unscaled.propagateSign(); + } else { + unscaled.divideByPowerOfTen(resultX10.unscaled, 1, true); + } + + return ScaledVector.this; + } + } + + private Add getAdd() { + if (add == null) { + add = new Add(); + } + return add; + } + + private ScaledVectorStringify getStringify() { + if (stringify == null) { + stringify = new ScaledVectorStringify(this); + } + return stringify; + } + + private Multiply getMultiply() { + if (multiply == null) { + multiply = new Multiply(); + } + return multiply; + } + + private Divide getDivide() { + if (divide == null) { + divide = new Divide(); + } + return divide; + } + + private Remainder getRemainder() { + if (remainder == null) { + remainder = new Remainder(); + } + return remainder; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof ScaledVector)) { + return false; + } + ScaledVector that = (ScaledVector) obj; + return (this.precision == that.precision && this.scale == that.scale && + this.unscaled.equals(that.unscaled)); + } + + @Override + public String toString() { + return "[Precision: " + precision + ", Scale:" + scale + ", " + unscaled + "]"; + } +} diff --git storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/ScaledVectorStringify.java storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/ScaledVectorStringify.java new file mode 100644 index 0000000..3bd465e --- /dev/null +++ storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/ScaledVectorStringify.java @@ -0,0 +1,458 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 static org.apache.hadoop.hive.ql.exec.vector.decimalv2.DecimalColumnVectorV2.MAX_PRECISION; + +/** + * Stringify or parse a scaled vector. + */ +class ScaledVectorStringify { + private ScaledVector scaledVector; + + private long upper; + private long lower; + private StringBuilder reversed = new StringBuilder(); + + private Unscaled128Value parsed128Val = new Unscaled128Value(); + private Unscaled128Value parsed128ValCopy = new Unscaled128Value(); + private Unscaled64Value parsed64Val = new Unscaled64Value(); + private Unscaled64Value parsed64ValCopy = new Unscaled64Value(); + + private static final char[] digitForTen = { + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', + '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', + '3', '3', '3', '3', '3', '3', '3', '3', '3', '3', + '4', '4', '4', '4', '4', '4', '4', '4', '4', '4', + '5', '5', '5', '5', '5', '5', '5', '5', '5', '5', + '6', '6', '6', '6', '6', '6', '6', '6', '6', '6', + '7', '7', '7', '7', '7', '7', '7', '7', '7', '7', + '8', '8', '8', '8', '8', '8', '8', '8', '8', '8', + '9', '9', '9', '9', '9', '9', '9', '9', '9', '9' + }; + + private static final char[] digitForOne = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' + }; + + ScaledVectorStringify(ScaledVector scaledVector) { + this.scaledVector = scaledVector; + } + + void parse128(byte[] bytes, int start, int length, int row) { + parse(bytes, start, length, row, parsed128Val, parsed128ValCopy); + } + + void parse64(byte[] bytes, int start, int length, int row) { + parse(bytes, start, length, row, parsed64Val, parsed64ValCopy); + } + + void stringify128(StringBuilder output, int row, boolean[] isNull) { + if (isNull[row]) { + output.append("NULL"); + return; + } + + int i = 0; + + long strUpper = scaledVector.unscaled.upper.array[row]; + long strLower = scaledVector.unscaled.lower.array[row]; + + // Clear + reversed.delete(0, reversed.length()); + boolean negated; + + // Absolute + if (strUpper < 0) { + upper = ~strUpper; + lower = ~strLower + 1; + long carry = strLower; + upper += ((carry & 2) >> 1) + ((carry & 1) & ~(lower >>> 63)); + negated = true; + } else { + upper = strUpper; + lower = strLower; + negated = false; + } + + // Skip trailing zeros + while ((i < scaledVector.scale) && (remainder128(100) == 0)) { + divide128(100); + i += 2; + } + if ((i < scaledVector.scale) && (remainder128(10) == 0)) { + divide128(10); + i += 1; + } + + // Fraction + while (i < scaledVector.scale - 1) { + int digit = divideAndRemainder128(100); + reversed.append(digitForOne[digit]).append(digitForTen[digit]); + i += 2; + } + if (i < scaledVector.scale) { + int digit = divideAndRemainder128(10); + reversed.append(digit); + i += 1; + } + + // Dot + if (reversed.length() > 0) { + reversed.append('.'); + } + int integerStartsAt = reversed.length(); + + // Integer + while ((i < MAX_PRECISION) && !((upper == 0) && (lower == 0))) { + if ((upper == 0) && (lower < 10)) { + int digit = divideAndRemainder128(100); + reversed.append(digitForOne[digit]); + i += 1; + } else { + int digit = divideAndRemainder128(100); + reversed.append(digitForOne[digit]).append(digitForTen[digit]); + i += 2; + } + } + if ((i < MAX_PRECISION) && !((upper == 0) && (lower == 0))) { + int digit = divideAndRemainder128(10); + reversed.append(digit); + i += 1; + } + + // No integer digit + if (reversed.length() == integerStartsAt) { + reversed.append('0'); + } + + // Sign + if (negated) { + reversed.append('-'); + } + + // Reverse + reversed.reverse(); + output.append(reversed); + } + + void stringify64(StringBuilder output, int row, boolean[] isNull) { + if (isNull[row]) { + output.append("NULL"); + return; + } + + boolean negated = false; + int i = 0; + + long strLower = scaledVector.unscaled.lower.array[row]; + + // Clear + reversed.delete(0, reversed.length()); + + // Absolute + if (strLower < 0) { + lower = -strLower; + negated = true; + } else { + lower = strLower; + negated = false; + } + + // Skip trailing zeros + while (i < scaledVector.scale) { + // Read + long digit = lower % 10; + + // Skip + if (digit > 0) { + break; + } + + // Next + lower /= 10; + i++; + } + + // Fraction + while (i < scaledVector.scale) { + // Read + long digit = lower % 10; + + // Next + lower /= 10; + reversed.append(digit); + i++; + } + + // Dot + if (reversed.length() > 0) { + reversed.append('.'); + } + int integerStartsAt = reversed.length(); + + // Integer + while (i < MAX_PRECISION) { + // Skip heading zeros + if ((upper == 0) && (lower == 0)) { + break; + } + + // Read + long digit = lower % 10; + + // Next + lower /= 10; + reversed.append(digit); + i++; + } + + // No integer digit + if (reversed.length() == integerStartsAt) { + reversed.append('0'); + } + + // Sign + if (negated) { + reversed.append('-'); + } + + // Reverse + reversed.reverse(); + output.append(reversed); + } + + private void parse(byte[] bytes, int start, int length, int row, + UnscaledValue parsedVal, UnscaledValue parsedValCopy) { + int position = start; + int end = start + length; + int integerStart; + int integerEnd; + int fractionStart = 0; + int fractionEnd = 0; + int exponentStart = 0; + int exponentEnd = 0; + + boolean hasIntegerDigits = false; + boolean hasNegativeIntegerSign = false; + boolean hasFractionDigits = false; + boolean hasExponentDigits = false; + boolean hasNegativeExponentSign = false; + + byte b; + + // Integer sign (optional) + b = bytes[position]; + if (b == '-') { + hasNegativeIntegerSign = true; + position++; + } else if (b == '+') { + hasNegativeIntegerSign = false; + position++; + } + + // Integer (required) + integerStart = position; + while (position < end) { + b = bytes[position]; + if (b >= '0' && b <= '9') { + hasIntegerDigits = true; + position++; + } else { + break; + } + } + integerEnd = position; + + // Dot (optional) + if (position < end) { + b = bytes[position]; + if (b == '.') { + position++; + + // Fraction (optional) + fractionStart = position; + while (position < end) { + b = bytes[position]; + if (b >= '0' && b <= '9') { + hasFractionDigits = true; + position++; + } else { + break; + } + } + fractionEnd = position; + } + } + + // 'E' (optional) + if (position < end) { + b = bytes[position]; + if (b == 'E' || b == 'e') { + position++; + + // Exponent sign (optional) + if (position < end) { + b = bytes[position]; + if (b == '-') { + hasNegativeExponentSign = true; + position++; + } else if (b == '+') { + hasNegativeExponentSign = false; + position++; + } + } + + // Exponent (optional) + exponentStart = position; + while (position < end) { + b = bytes[position]; + if (b >= '0' && b <= '9') { + hasExponentDigits = true; + position++; + } else { + break; + } + } + exponentEnd = position; + } + } + + // Should meet the end + if (position != end) { + throw new IllegalArgumentException("Illegal format"); + } + + parsedVal.set(0, 0); + parsedValCopy.set(0, 0); + int exponent = 0; + int scale = 0; + int scaleDiff = 0; + + // Handle integer + if (hasIntegerDigits) { + for (int i = integerStart; i < integerEnd; i++) { + parsedValCopy.multiplyByPowerOfTen(parsedVal, 1); + parsedVal.add(parsedValCopy, bytes[i] - '0'); + } + } else { + throw new IllegalArgumentException("Should have an integer part"); + } + + // Handle fraction + if (hasFractionDigits) { + for (int i = fractionStart; i < fractionEnd; i++) { + parsedValCopy.multiplyByPowerOfTen(parsedVal, 1); + parsedVal.add(parsedValCopy, bytes[i] - '0'); + } + } + + // Handle exponent + if (hasExponentDigits) { + for (int i = exponentStart; i < exponentEnd; i++) { + exponent *= 10; + exponent += bytes[i] - '0'; + } + + if (hasNegativeExponentSign) { + exponent = -exponent; + } + } + + // Handle scale + scale = fractionEnd - fractionStart - exponent; + scaleDiff = scaledVector.scale - scale; + + // Multiply + if (scaleDiff > 0) { + parsedValCopy.set(parsedVal); + parsedVal.multiplyByPowerOfTen(parsedValCopy, scaleDiff); + } + + // Divide with rounding + else if (scaleDiff < 0) { + parsedValCopy.set(parsedVal); + parsedVal.divideByPowerOfTen(parsedValCopy, -scaleDiff, true); + } + + // Handle negation + if (hasNegativeIntegerSign) { + parsedValCopy.negate(parsedVal); + scaledVector.unscaled.upper.array[row] = parsedValCopy.getUpper(); + scaledVector.unscaled.lower.array[row] = parsedValCopy.getLower(); + } else { + scaledVector.unscaled.upper.array[row] = parsedVal.getUpper(); + scaledVector.unscaled.lower.array[row] = parsedVal.getLower(); + } + } + + private int remainder128(int by) { + long leftUpper = upper; + long leftUpperLower = lower >>> 32; + long leftLowerLower = lower & 0xFFFFFFFFL; + + long remainderUpper = leftUpper % by; + long remainderUpperLower = ((remainderUpper << 32) | leftUpperLower) % by; + long remainderLowerLower = ((remainderUpperLower << 32) | leftLowerLower) % by; + + return (int) remainderLowerLower; + } + + private void divide128(int by) { + long leftUpper = upper; + long leftUpperLower = lower >>> 32; + long leftLowerLower = lower & 0xFFFFFFFFL; + + long remainderUpper = leftUpper % by; + long remainderUpperLower = ((remainderUpper << 32) | leftUpperLower) % by; + + upper = leftUpper / by; + long outUpLower = ((remainderUpper << 32) | leftUpperLower) / by; + long outLoLower = ((remainderUpperLower << 32) | leftLowerLower) / by; + lower = (outUpLower << 32) | outLoLower; + } + + private int divideAndRemainder128(int by) { + long leftUpper = upper; + long leftUpperLower = lower >>> 32; + long leftLowerLower = lower & 0xFFFFFFFFL; + + long remainderUpper = leftUpper % by; + long remainderUpperLower = ((remainderUpper << 32) | leftUpperLower) % by; + + upper = leftUpper / by; + long outUpLower = ((remainderUpper << 32) | leftUpperLower) / by; + long outLoLower = ((remainderUpperLower << 32) | leftLowerLower) / by; + lower = (outUpLower << 32) | outLoLower; + + long remainderLowerLower = ((remainderUpperLower << 32) | leftLowerLower) % by; + + return (int) remainderLowerLower; + } +} diff --git storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/Unscaled.java storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/Unscaled.java new file mode 100644 index 0000000..d650e10 --- /dev/null +++ storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/Unscaled.java @@ -0,0 +1,177 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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; + +/** + * Unscaled vector or value + * @param + */ +interface Unscaled { + /** + * Set as input + * @param input + * @return + */ + V set(V input); + + /** + * Negate input + * @param input + * @return + */ + V negate(V input); + + /** + * Add left and right + * @param left + * @param right + * @return + */ + V add(V left, long right); + + /** + * Add left and right + * @param left + * @param right + * @return + */ + V add(V left, V right); + + /** + * Subtract left by right + * @param left + * @param right + * @return + */ + V subtract(V left, V right); + + /** + * Multiply left by right + * @param left + * @param right + * @return + */ + V multiply(V left, V right); + + /** + * Divide left by right, with null check + * @param left + * @param right + * @param isNull + * @return + */ + V divideChecked(V left, V right, boolean[] isNull, int leftPrecision, int rightPrecision); + + /** + * Divide left by right, without null check + * @param left + * @param right + * @return + */ + V divideUnchecked(V left, V right, int leftPrecision, int rightPrecision); + + /** + * Take a remainder of division + * @param left + * @param right + * @param isNull + * @return + */ + V remainderChecked(V left, V right, boolean[] isNull, int leftPrecision, int rightPrecision); + + /** + * Multiply left by right + * @param left + * @param right + * @return + */ + V multiplyByPowerOfTen(V left, int right); + + /** + * Divide left by right + * @param left + * @param right + * @param rounding + * @return + */ + V divideByPowerOfTen(V left, int right, boolean rounding); + + /** + * Bitwise NOT of input + * @param input + * @return + */ + V not(V input); + + /** + * Bitwise AND of left and right + * @param left + * @param right + * @return + */ + V and(V left, V right); + + /** + * Bitwise OR of left and right + * @param left + * @param right + * @return + */ + V or(V left, V right); + + /** + * Bitwise XOR of left and right + * @param left + * @param right + * @return + */ + V xor(V left, V right); + + /** + * Bitwise SHIFT LEFT of left by right + * @param left + * @param right + * @return + */ + V shiftLeft(V left, int right); + + /** + * Bitwise UNSIGNED SHIFT RIGHT of left by right + * @param left + * @param right + * @return + */ + V unsignedShiftRight(V left, int right); + + /** + * Bitwise SIGNED SHIFT RIGHT of left by right + * @param left + * @param right + * @return + */ + V signedShiftRight(V left, int right); + + /** + * Sign + * @param input + * @param sign + * @return + */ + V sign(V input, V sign); +} diff --git storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/Unscaled128Value.java storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/Unscaled128Value.java new file mode 100644 index 0000000..15a77c3 --- /dev/null +++ storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/Unscaled128Value.java @@ -0,0 +1,189 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 Unscaled128Value implements UnscaledValue { + Unscaled128Vector vector = new Unscaled128Vector(1); + + Unscaled128Value(long upper, long lower) { + set(upper, lower); + } + + Unscaled128Value() { + } + + Unscaled128Value(long lower) { + setLower(lower); + } + + @Override + public Unscaled128Value set(Unscaled128Value input) { + vector.set(input.vector); + return this; + } + + @Override + public Unscaled128Value negate(Unscaled128Value input) { + vector.negate(input.vector); + return this; + } + + @Override + public Unscaled128Value add(Unscaled128Value left, Unscaled128Value right) { + vector.add(left.vector, right.vector); + return this; + } + + @Override + public Unscaled128Value add(Unscaled128Value left, long right) { + vector.add(left.vector, right); + return this; + } + + @Override + public Unscaled128Value subtract(Unscaled128Value left, Unscaled128Value right) { + vector.subtract(left.vector, right.vector); + return this; + } + + @Override + public Unscaled128Value multiply(Unscaled128Value left, Unscaled128Value right) { + vector.multiply(left.vector, right.vector); + return this; + } + + @Override + public Unscaled128Value divideChecked(Unscaled128Value left, Unscaled128Value right, boolean[] isNull, int leftPrecision, int rightPrecision) { + vector.divideChecked(left.vector, right.vector, isNull, leftPrecision, rightPrecision); + return this; + } + + @Override + public Unscaled128Value divideUnchecked(Unscaled128Value left, Unscaled128Value right, int leftPrecision, int rightPrecision) { + vector.divideUnchecked(left.vector, right.vector, leftPrecision, rightPrecision); + return this; + } + + @Override + public Unscaled128Value remainderChecked(Unscaled128Value left, Unscaled128Value right, boolean[] isNull, int leftPrecision, int rightPrecision) { + vector.remainderChecked(left.vector, right.vector, isNull, leftPrecision, rightPrecision); + return this; + } + + @Override + public Unscaled128Value multiplyByPowerOfTen(Unscaled128Value left, int right) { + vector.multiplyByPowerOfTen(left.vector, right); + return this; + } + + @Override + public Unscaled128Value divideByPowerOfTen(Unscaled128Value left, int right, boolean rounding) { + vector.divideByPowerOfTen(left.vector, right, rounding); + return this; + } + + @Override + public Unscaled128Value not(Unscaled128Value input) { + vector.not(input.vector); + return this; + } + + @Override + public Unscaled128Value and(Unscaled128Value left, Unscaled128Value right) { + vector.and(left.vector, right.vector); + return this; + } + + @Override + public Unscaled128Value or(Unscaled128Value left, Unscaled128Value right) { + vector.or(left.vector, right.vector); + return this; + } + + @Override + public Unscaled128Value xor(Unscaled128Value left, Unscaled128Value right) { + vector.xor(left.vector, right.vector); + return this; + } + + @Override + public Unscaled128Value shiftLeft(Unscaled128Value left, int right) { + vector.shiftLeft(left.vector, right); + return this; + } + + @Override + public Unscaled128Value unsignedShiftRight(Unscaled128Value left, int right) { + vector.unsignedShiftRight(left.vector, right); + return this; + } + + @Override + public Unscaled128Value signedShiftRight(Unscaled128Value left, int right) { + vector.signedShiftRight(left.vector, right); + return this; + } + + @Override + public Unscaled128Value sign(Unscaled128Value input, Unscaled128Value sign) { + vector.sign(input.vector, sign.vector); + return this; + } + + @Override + public void setLower(long lower) { + vector.lower.array[0] = lower; + } + + @Override + public void setUpper(long upper) { + vector.upper.array[0] = upper; + } + + @Override + public long getUpper() { + return vector.upper.array[0]; + } + + @Override + public long getLower() { + return vector.lower.array[0]; + } + + @Override + public void set(long upper, long lower) { + setUpper(upper); + setLower(lower); + } + + public int compareTo(Unscaled128Value that) { + Unscaled128Value comparison = new Unscaled128Value(); + + comparison.subtract(this, that); + if ((comparison.getUpper() == 0) && + (comparison.getLower() == 0)) { + return 0; + } + if (comparison.getUpper() < 0) { + return -1; + } else { + return 1; + } + } +} diff --git storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/Unscaled128Vector.java storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/Unscaled128Vector.java new file mode 100644 index 0000000..3ecaaa7 --- /dev/null +++ storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/Unscaled128Vector.java @@ -0,0 +1,548 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.ql.exec.vector.VectorizedRowBatch; + +class Unscaled128Vector implements Unscaled { + Unscaled64Vector upper; + Unscaled64Vector lower; + + int size; + private Subtract subtract; + private Unscaled128VectorDivide divide; + private Multiply multiply; + private Sign sign; + + Unscaled128Vector(int size) { + this.size = size; + upper = new Unscaled64Vector(size); + lower = new Unscaled64Vector(size); + } + + Unscaled128Vector() { + this(VectorizedRowBatch.DEFAULT_SIZE); + } + + static void ensureSize(int size, Unscaled128Vector... list) { + for (Unscaled128Vector item : list) { + item.size = size; + Unscaled64Vector.ensureSize(size, item.upper, item.lower); + } + } + + public Unscaled128Vector propagateSign() { + upper.signedShiftRight(lower, 63); + return this; + } + + @Override + public Unscaled128Vector signedShiftRight(Unscaled128Vector left, int right) { + long[] leftUpper = left.upper.array; + long[] leftLower = left.lower.array; + long[] outputLower = this.lower.array; + + if (right == 0) { + this.set(left); + } else if (right == 64) { + upper.fill(0); + lower.set(left.upper); + } else if (right > 64) { + upper.fill(0); + lower.signedShiftRight(left.upper, right - 64); + } else { // right < 64 + upper.signedShiftRight(left.upper, right); + for (int i = 0; i < size; i++) { + outputLower[i] = (leftLower[i] >> right) | (leftUpper[i] << (64 - right)); + } + } + return this; + } + + @Override + public Unscaled128Vector unsignedShiftRight(Unscaled128Vector left, int right) { + long[] leftUpper = left.upper.array; + long[] leftLower = left.lower.array; + long[] outputLower = this.lower.array; + + if (right == 0) { + this.set(left); + } else if (right == 64) { + upper.fill(0); + lower.set(left.upper); + } else if (right > 64) { + upper.fill(0); + lower.unsignedShiftRight(left.upper, right - 64); + } else { // right < 64 + upper.unsignedShiftRight(left.upper, right); + for (int i = 0; i < size; i++) { + outputLower[i] = (leftLower[i] >>> right) | (leftUpper[i] << (64 - right)); + } + } + return this; + } + + @Override + public Unscaled128Vector not(Unscaled128Vector input) { + upper.not(input.upper); + lower.not(input.lower); + return this; + } + + @Override + public Unscaled128Vector add(Unscaled128Vector left, Unscaled128Vector right) { + return this.add(left, right.upper, right.lower); + } + + @Override + public Unscaled128Vector add(Unscaled128Vector left, long right) { + return this.add(left, 0, right); + } + + @Override + public Unscaled128Vector negate(Unscaled128Vector input) { + return getSubtract().negate(input); + } + + @Override + public Unscaled128Vector subtract(Unscaled128Vector left, Unscaled128Vector right) { + return this.subtract(left, right.upper, right.lower); + } + + @Override + public Unscaled128Vector multiply(Unscaled128Vector multiplicand, Unscaled128Vector multiplier) { + return getMultiply().multiply(multiplicand, multiplier); + } + + @Override + public Unscaled128Vector divideChecked(Unscaled128Vector left, Unscaled128Vector right, boolean[] isNull, int leftPrecision, int rightPrecision) { + return getDivide().divideChecked(left, right, isNull, leftPrecision, rightPrecision); + } + + @Override + public Unscaled128Vector divideUnchecked(Unscaled128Vector left, Unscaled128Vector right, int leftPrecision, int rightPrecision) { + return getDivide().divideUnchecked(left, right, leftPrecision, rightPrecision); + } + + @Override + public Unscaled128Vector remainderChecked(Unscaled128Vector left, Unscaled128Vector right, boolean[] isNull, int leftPrecision, int rightPrecision) { + return getDivide().remainderChecked(left, right, isNull, leftPrecision, rightPrecision); + } + + @Override + public Unscaled128Vector multiplyByPowerOfTen(Unscaled128Vector left, int right) { + return getMultiply().multiplyByPowerOfTen(left, right); + } + + @Override + public Unscaled128Vector divideByPowerOfTen(Unscaled128Vector left, int right, boolean rounding) { + return getDivide().divideByPowerOfTen(left, right, rounding); + } + + @Override + public Unscaled128Vector set(Unscaled128Vector input) { + return this.set(input.upper, input.lower); + } + + @Override + public Unscaled128Vector and(Unscaled128Vector left, Unscaled128Vector right) { + return this.and(left, right.upper, right.lower); + } + + @Override + public Unscaled128Vector or(Unscaled128Vector left, Unscaled128Vector right) { + return this.or(left, right.upper, right.lower); + } + + @Override + public Unscaled128Vector xor(Unscaled128Vector left, Unscaled128Vector right) { + return xor(left, right.upper, right.lower); + } + + @Override + public Unscaled128Vector shiftLeft(Unscaled128Vector left, int right) { + return shiftLeft(left.upper, left.lower, right); + } + + @Override + public Unscaled128Vector sign(Unscaled128Vector input, Unscaled128Vector sign) { + return getSign().sign(input, sign); + } + + private Sign getSign() { + if (sign == null) { + sign = new Sign(); + } + return sign; + } + + Unscaled128Vector andNot(Unscaled128Vector left, Unscaled64Vector rightUpper, Unscaled64Vector rightLower) { + this.upper.andNot(left.upper, rightUpper); + this.lower.andNot(left.lower, rightLower); + return this; + } + + private class Sign { + private Unscaled128Vector copy = new Unscaled128Vector(); + + private Unscaled128Vector sign(Unscaled128Vector input, Unscaled128Vector sign) { + Unscaled128Vector output = Unscaled128Vector.this; + Unscaled128Vector.ensureSize(size, copy); + copy.negate(sign); + copy.xor(input, copy); + return output.add(copy, sign); + } + } + + Unscaled128Vector fill(Unscaled128Value value) { + return this.fill(value.getUpper(), value.getLower()); + } + + Unscaled128Vector fill(long inputUpper, long inputLower) { + upper.fill(inputUpper); + lower.fill(inputLower); + return this; + } + + Unscaled128Vector set(Unscaled64Vector inputUpper, Unscaled64Vector inputLower) { + upper.set(inputUpper); + lower.set(inputLower); + return this; + } + + Unscaled128Vector set(long inputUpper, Unscaled64Vector inputLower) { + upper.fill(inputUpper); + lower.set(inputLower); + return this; + } + + Unscaled128Vector add(Unscaled128Vector left, Unscaled64Vector rightUpper, Unscaled64Vector rightLower) { + lower.add(left.lower, rightLower); + upper.add(left.upper, rightUpper); + + long[] leftLower = left.lower.array; + long[] outputUpper = upper.array; + long[] outputLower = lower.array; + long[] rightLowerArray = rightLower.array; + + for (int i = 0; i < size; i++) { + // if (leftLower.0 == 1 && rightLower.0 == 1) return outputUpper += 1; + // if (leftLower.0 == 0 && rightLower.0 == 0) return outputUpper += 0; + // if (outputLower.0 == 0) return outputUpper += 1; + // if (outputLower.0 == 1) return outputUpper += 0; + + // if (leftLower.0 + rightLower.0 == 2) return outputUpper += 1; + // if (leftLower.0 + rightLower.0 == 0) return outputUpper += 0; + // if (leftLower.0 + rightLower.0 == 1) return outputUpper += outputLower.0; + + long carry = (leftLower[i] >>> 63) + (rightLowerArray[i] >>> 63); + outputUpper[i] += ((carry & 2) >> 1) | ((carry & 1) & ~(outputLower[i] >>> 63)); + } + return this; + } + + Unscaled128Vector subtract(Unscaled128Vector left, Unscaled64Vector rightUpper, Unscaled64Vector rightLower) { + lower.subtract(left.lower, rightLower); + upper.subtract(left.upper, rightUpper); + + long[] leftLower = left.lower.array; + long[] outputUpper = upper.array; + long[] outputLower = lower.array; + long[] rightLowerArray = rightLower.array; + + for (int i = 0; i < size; i++) { + // if (leftLower.0 == 0 && rightLower.0 == 1) return outputUpper -= 1; + // if (leftLower.0 == 1 && rightLower.0 == 0) return outputUpper -= 0; + // if (outputLower.0 == 0) return outputUpper -= 0; + // if (outputLower.0 == 1) return outputUpper -= 1; + + // if (~leftLower.0 + rightLower.0 == 2) return outputUpper -= 1; + // if (~leftLower.0 + rightLower.0 == 0) return outputUpper -= 0; + // if (~leftLower.0 + rightLower.0 == 1) return outputUpper -= outputLower.0; + + long borrow = (((~leftLower[i]) >>> 63) + (rightLowerArray[i] >>> 63)); + outputUpper[i] -= ((borrow & 2) >> 1) | ((borrow & 1) & (outputLower[i] >>> 63)); + } + return this; + } + + Unscaled128Vector add(Unscaled128Vector left, long rightUpper, Unscaled64Vector rightLower) { + lower.add(left.lower, rightLower); + upper.add(left.upper, rightUpper); + + long[] leftLower = left.lower.array; + long[] outputUpper = upper.array; + long[] outputLower = lower.array; + long[] rightLowerArray = rightLower.array; + + for (int i = 0; i < size; i++) { + long carry = (leftLower[i] >>> 63) + (rightLowerArray[i] >>> 63); + outputUpper[i] += ((carry & 2) >> 1) + ((carry & 1) & ~(outputLower[i] >>> 63)); + } + return this; + } + + Unscaled128Vector subtract(Unscaled128Vector left, long rightUpper, Unscaled64Vector rightLower) { + lower.subtract(left.lower, rightLower); + upper.subtract(left.upper, rightUpper); + + long[] leftLower = left.lower.array; + long[] outputUpper = upper.array; + long[] outputLower = lower.array; + long[] rightLowerArray = rightLower.array; + + for (int i = 0; i < size; i++) { + long borrow = (((~leftLower[i]) >>> 63) + (rightLowerArray[i] >>> 63)); + outputUpper[i] -= ((borrow & 2) >> 1) | ((borrow & 1) & (outputLower[i] >>> 63)); + } + return this; + } + + Unscaled128Vector or(Unscaled128Vector left, Unscaled64Vector rightUpper, Unscaled64Vector rightLower) { + upper.or(left.upper, rightUpper); + lower.or(left.lower, rightLower); + return this; + } + + Unscaled128Vector or(Unscaled128Vector left, long rightUpper, Unscaled64Vector rightLower) { + upper.or(left.upper, rightUpper); + lower.or(left.lower, rightLower); + return this; + } + + Unscaled128Vector not(Unscaled64Vector upper, Unscaled64Vector lower) { + this.upper.not(upper); + this.lower.not(lower); + return this; + } + + Unscaled128Vector xor(Unscaled128Vector left, Unscaled64Vector rightUpper, Unscaled64Vector rightLower) { + upper.xor(left.upper, rightUpper); + lower.xor(left.lower, rightLower); + return this; + } + + Unscaled128Vector and(Unscaled128Vector left, Unscaled64Vector rightUpper, Unscaled64Vector rightLower) { + upper.and(left.upper, rightUpper); + lower.and(left.lower, rightLower); + return this; + } + + Unscaled128Vector shiftLeft(Unscaled64Vector leftUpper, Unscaled64Vector leftLower, int right) { + long[] leftUpperArray = leftUpper.array; + long[] leftLowerArray = leftLower.array; + long[] outputUpper = upper.array; + + if (right == 0) { + this.set(leftUpper, leftLower); + } else if (right == 64) { + upper.set(leftLower); + lower.fill(0); + } else if (right > 64) { + upper.shiftLeft(leftLower, right - 64); + lower.fill(0); + } else { // right < 64 + lower.shiftLeft(leftLower, right); + for (int i = 0; i < size; i++) { + outputUpper[i] = (leftUpperArray[i] << right) | (leftLowerArray[i] >>> (64 - right)); + } + } + return this; + } + + Unscaled128Vector shiftLeft(long leftUpper, Unscaled64Vector leftLower, int right) { + long[] leftLowerArray = leftLower.array; + long[] outputUpper = upper.array; + + if (right == 0) { + this.set(leftUpper, leftLower); + } else if (right == 64) { + upper.set(leftLower); + lower.fill(0); + } else if (right > 64) { + upper.shiftLeft(leftLower, right - 64); + lower.fill(0); + } else { // right < 64 + lower.shiftLeft(leftLower, right); + for (int i = 0; i < size; i++) { + outputUpper[i] = (leftUpper << right) | (leftLowerArray[i] >>> (64 - right)); + } + } + return this; + } + + Unscaled128Vector divideByPowerOfTen(Unscaled128Vector left, int right) { + return divideByPowerOfTen(left, right, false); + } + + Unscaled128Vector unsignedMultiply(Unscaled128Vector left, Unscaled128Vector right) { + return getMultiply().unsignedMultiply(left, right); + } + + private class Subtract { + Unscaled128Vector not = new Unscaled128Vector(); + + Unscaled128Vector negate(Unscaled128Vector input) { + ensureSize(size, (Unscaled128Vector) not); + Unscaled128Vector output = Unscaled128Vector.this; + not.not(input); + output.add(not, 0, 1); + return output; + } + } + + private class Multiply { + private Unscaled128Vector leftAbs = new Unscaled128Vector(); + private Unscaled128Vector leftSign = new Unscaled128Vector(); + private Unscaled64Vector leftTemp = new Unscaled64Vector(); + private Unscaled128Vector rightAbs = new Unscaled128Vector(); + private Unscaled128Vector rightSign = new Unscaled128Vector(); + private Unscaled64Vector rightTemp = new Unscaled64Vector(); + private Unscaled128Vector outputShift = new Unscaled128Vector(); + private Unscaled128Vector outputCopy = new Unscaled128Vector(); + private Unscaled64Vector outputTemp = new Unscaled64Vector(); + private Unscaled128Vector combinedSign = new Unscaled128Vector(); + + Unscaled128Vector multiply(Unscaled128Vector left, Unscaled128Vector right) { + Unscaled128Vector output = Unscaled128Vector.this; + Unscaled128Vector.ensureSize(size, leftAbs, rightAbs, outputCopy, leftSign, rightSign, combinedSign); + + // Absolute + leftSign.unsignedShiftRight(left, 127); + leftAbs.sign(left, leftSign); + rightSign.unsignedShiftRight(right, 127); + rightAbs.sign(right, rightSign); + + unsignedMultiply(leftAbs, rightAbs); + + // Reverse absolute + return output.sign(outputCopy.set(output), combinedSign.xor(leftSign, rightSign)); + } + + Unscaled128Vector unsignedMultiply(Unscaled128Vector left, Unscaled128Vector right) { + Unscaled128Vector output = Unscaled128Vector.this; + + long[] leftLower = left.lower.array; + long[] rightLower = right.lower.array; + long[] outputLower = output.lower.array; + + long[] leftUpper = left.upper.array; + long[] rightUpper = right.upper.array; + long[] outputUpper = output.upper.array; + + // Divide it into high and low bits + // ((a << 96) + (b << 64) + (c << 32) + d) * ((e << 96) + (f << 64) + (g << 32) + i) + // = (ae) << 192 + (af+be) << 160 + (ag+bf+ce) << 128 + (ai+bg+cf+de) << 96 + (bi+cg+df) << 64 + (ci+dg) << 32 + di + for (int row = 0; row < size; row++) { + long c = leftLower[row] >>> 32; + long d = leftLower[row] & 0xFFFFFFFFL; + long g = rightLower[row] >>> 32; + long i = rightLower[row] & 0xFFFFFFFFL; + + long dgci = d * g + c * i; + outputLower[row] = (dgci << 32) + (d * i); + outputUpper[row] = (dgci >>> 32) + (c * g) + (c * rightUpper[row]) + (leftUpper[row] * g); + } + + return output; + } + + Unscaled128Vector multiplyByPowerOfTen(Unscaled128Vector input, int scale) { + Unscaled128Vector output = Unscaled128Vector.this; + Unscaled128Vector.ensureSize(size, outputCopy, outputShift); + output.set(input); + for (int i = 0; i < scale; i++) { + outputCopy.shiftLeft(output, 1); + output.add(outputCopy, outputShift.shiftLeft(outputCopy, 2)); + } + return output; + } + } + + private Unscaled128Vector add(Unscaled128Vector left, long rightUpper, long rightLower) { + lower.add(left.lower, rightLower); + upper.add(left.upper, rightUpper); + + long[] leftLower = left.lower.array; + long[] outputUpper = this.upper.array; + long[] outputLower = this.lower.array; + + for (int i = 0; i < size; i++) { + long carry = (leftLower[i] >>> 63) + (rightLower >>> 63); + outputUpper[i] += ((carry & 2) >> 1) + ((carry & 1) & ~(outputLower[i] >>> 63)); + } + return this; + } + + private Subtract getSubtract() { + if (subtract == null) { + subtract = new Subtract(); + } + return subtract; + } + + private Multiply getMultiply() { + if (multiply == null) { + multiply = new Multiply(); + } + return multiply; + } + + private Unscaled128VectorDivide getDivide() { + if (divide == null) { + divide = new Unscaled128VectorDivide(this); + } + return divide; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Unscaled128Vector)) { + return false; + } + Unscaled128Vector that = (Unscaled128Vector) obj; + return this.upper.equals(that.upper) && this.lower.equals(that.lower); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append('['); + if (size > 0) { + builder.append('('); + builder.append(upper.array[0]); + builder.append(", "); + builder.append(lower.array[0]); + builder.append(')'); + } + for (int i = 1; i < size; i++) { + builder.append(", "); + builder.append('('); + builder.append(upper.array[i]); + builder.append(", "); + builder.append(lower.array[i]); + builder.append(')'); + } + builder.append(']'); + return builder.toString(); + } +} diff --git storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/Unscaled128VectorDivide.java storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/Unscaled128VectorDivide.java new file mode 100644 index 0000000..3a97aa0 --- /dev/null +++ storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/Unscaled128VectorDivide.java @@ -0,0 +1,303 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 Unscaled128VectorDivide { + Unscaled128Vector vector; + + // Left + Unscaled128Vector leftAbs = new Unscaled128Vector(); + Unscaled128Vector leftSign = new Unscaled128Vector(); + + // Right + Unscaled128Vector rightAbs = new Unscaled128Vector(); + Unscaled128Vector rightSign = new Unscaled128Vector(); + + // Quotient + Unscaled128Vector quotient = new Unscaled128Vector(); + Unscaled128Vector quotientCopy = new Unscaled128Vector(); + + // Remainder + Unscaled128Vector rem = new Unscaled128Vector(); + Unscaled128Vector remCopy = new Unscaled128Vector(); + Unscaled128Vector remRead = new Unscaled128Vector(); + Unscaled128Vector remShift = new Unscaled128Vector(); + Unscaled128Vector remSubtract = new Unscaled128Vector(); + Unscaled64Vector remSign = new Unscaled64Vector(); + Unscaled128Vector combinedSign = new Unscaled128Vector(); + + // Zero, power of ten + Unscaled64Vector zero = new Unscaled64Vector(); + + // Rounding + Unscaled64Vector remainder = new Unscaled64Vector(); + Unscaled64Vector round = new Unscaled64Vector(); + Unscaled64Vector shift = new Unscaled64Vector(); + Unscaled64Vector middle = new Unscaled64Vector(); + Unscaled64Vector last = new Unscaled64Vector(); + + public Unscaled128VectorDivide(Unscaled128Vector vector) { + this.vector = vector; + } + + void divideOrRemainderUnchecked128By128(Unscaled128Vector left, Unscaled128Vector right) { + int size = vector.size; + + // Prepare variables + Unscaled64Vector.ensureSize(size, zero, remSign); + Unscaled128Vector.ensureSize(size, leftAbs, rightAbs, remShift, remCopy, rem, + quotient, quotientCopy, remRead, remSubtract, leftSign, rightSign, combinedSign); + + rem.fill(0, 0); + quotient.fill(0, 0); + + // Absolute + leftSign.unsignedShiftRight(left, 127); + leftAbs.sign(left, leftSign); + + rightSign.unsignedShiftRight(right, 127); + rightAbs.sign(right, rightSign); + + long leftUpper; + long leftLower; + long rightUpper; + long rightLower; + long remainderUpper; + long remainderLower; + long remainderSubtractUpper; + long remainderSubtractLower; + long quotientUpper; + long quotientLower; + long[] leftUpperArray = leftAbs.upper.array; + long[] leftLowerArray = leftAbs.lower.array; + long[] rightUpperArray = rightAbs.upper.array; + long[] rightLowerArray = rightAbs.lower.array; + long[] quotientUpperArray = quotient.upper.array; + long[] quotientLowerArray = quotient.lower.array; + long[] remainderUpperArray = rem.upper.array; + long[] remainderLowerArray = rem.lower.array; + + for (int row = 0; row < size; row++) { + leftUpper = leftUpperArray[row]; + leftLower = leftLowerArray[row]; + rightUpper = rightUpperArray[row]; + rightLower = rightLowerArray[row]; + remainderUpper = 0; + remainderLower = 0; + quotientUpper = 0; + quotientLower = 0; + + int leftLeadingZeros; + if (leftUpper > 0) { + leftLeadingZeros = Long.numberOfLeadingZeros(leftUpper); + } else { + leftLeadingZeros = Long.numberOfLeadingZeros(leftLower) + 64; + } + + int rightLeadingZeros; + if (rightUpper > 0) { + rightLeadingZeros = Long.numberOfLeadingZeros(rightUpper); + } else { + rightLeadingZeros = Long.numberOfLeadingZeros(rightUpper) + 64; + } + + if (leftLeadingZeros < 64) { + int end = Math.min(rightLeadingZeros, 64); + for (int i = leftLeadingZeros; i < end; i++) { + // Shift the new remainder to left + remainderUpper = (remainderUpper << 1) | (remainderLower >>> 63); + remainderLower <<= 1; + + // Read bits into the remainder + remainderLower |= ((leftUpper >>> (63 - i)) & 1); + + // Subtract the remainder by the divisor + remainderSubtractUpper = remainderUpper - rightUpper; + remainderSubtractLower = remainderLower - rightLower; + long borrow = ((~remainderLower) >>> 63) + (rightLower >>> 63); + remainderSubtractUpper -= ((borrow & 2) >> 1) | ((borrow & 1) & (remainderSubtractLower >>> 63)); + + if (remainderSubtractUpper >= 0) { + remainderUpper = remainderSubtractUpper; + remainderLower = remainderSubtractLower; + quotientUpper |= (1L << (63 - i)); + } + } + } + + int start; + if (leftLeadingZeros > 64) { + start = leftLeadingZeros; + } else { + start = 64; + } + for (int i = start; i < rightLeadingZeros; i++) { + // Shift the new remainder to left + remainderUpper = (remainderUpper << 1) | (remainderLower >>> 63); + remainderLower <<= 1; + + // Read bits into the remainder + remainderLower |= ((leftLower >>> (127 - i)) & 1); + + // Subtract the remainder by the divisor + remainderSubtractUpper = remainderUpper - rightUpper; + remainderSubtractLower = remainderLower - rightLower; + long borrow = (((~remainderLower) >>> 63) + (rightLower >>> 63)); + remainderSubtractUpper -= ((borrow & 2) >> 1) | ((borrow & 1) & (remainderSubtractLower >>> 63)); + + if (remainderSubtractUpper >= 0) { + remainderUpper = remainderSubtractUpper; + remainderLower = remainderSubtractLower; + quotientLower |= (1L << (127 - i)); + } + } + + quotientUpperArray[row] = quotientUpper; + quotientLowerArray[row] = quotientLower; + remainderUpperArray[row] = remainderUpper; + remainderLowerArray[row] = remainderLower; + } + } + + Unscaled128Vector divideUnchecked(Unscaled128Vector left, Unscaled128Vector right, int leftPrecision, int rightPrecision) { + if (rightPrecision > 4) { + divideOrRemainderUnchecked128By128(left, right); + } else { + divideOrRemainderUnchecked128By16(left, right); + } + + // Sign the quotient + quotient.sign(quotient, combinedSign.xor(leftSign, rightSign)); + + // Put the quotient into the output + return vector.set(quotient); + } + + Unscaled128Vector divideChecked(Unscaled128Vector left, Unscaled128Vector right, boolean[] isNull, int leftPrecision, int rightPrecision) { + long[] rightUpper = right.upper.array; + long[] rightLower = right.lower.array; + + // Handle divide by zero + for (int i = 0; i < vector.size; i++) { + if ((rightUpper[i] == 0) && (rightLower[i] == 0)) { + isNull[i] = true; + rightLower[i] = 1; + } + } + + return this.divideUnchecked(left, right, leftPrecision, rightPrecision); + } + + Unscaled128Vector remainderChecked(Unscaled128Vector left, Unscaled128Vector right, boolean[] isNull, int leftPrecision, int rightPrecision) { + long[] rightUpper = right.upper.array; + long[] rightLower = right.lower.array; + + for (int i = 0; i < vector.size; i++) { + if ((rightUpper[i] | rightLower[i]) == 0) { + isNull[i] = true; + } + } + + if (rightPrecision > 4) { + divideOrRemainderUnchecked128By128(left, right); + } else { + divideOrRemainderUnchecked128By16(left, right); + } + return vector.sign(rem, leftSign); + } + + void divideOrRemainderUnchecked128By16(Unscaled128Vector left, Unscaled128Vector right) { + int size = vector.size; + Unscaled128Vector.ensureSize(size, quotient, leftSign, leftAbs); + Unscaled64Vector.ensureSize(size, remainder, shift, middle, last, round); + + // Absolute + leftSign.unsignedShiftRight(left, 127); + leftAbs.sign(left, leftSign); + + last.fill(0); + remainder.fill(0); + quotient.set(leftAbs); + + Unscaled64Vector first = quotient.upper; + middle.upper(quotient.lower); + last.lower(quotient.lower); + + remainder.remainderUnchecked(first, right.lower); + first.divideUnchecked(first, right.lower, DecimalColumnVectorV2.MAX_PRECISION, 4); + + middle.or(shift.shiftLeft(remainder, 32), middle); // Carry + remainder.remainderUnchecked(middle, right.lower); + middle.divideUnchecked(middle, right.lower, DecimalColumnVectorV2.MAX_PRECISION, 4); + + last.or(shift.shiftLeft(remainder, 32), last); // Carry + remainder.remainderUnchecked(last, right.lower); + last.divideUnchecked(last, right.lower, DecimalColumnVectorV2.MAX_PRECISION, 4); + + quotient.lower.or(shift.shiftLeft(middle, 32), last); + } + + Unscaled128Vector divideByPowerOfTen(Unscaled128Vector left, int right, boolean rounding) { + int size = left.size; + Unscaled128Vector.ensureSize(size, leftSign, leftAbs); + Unscaled64Vector.ensureSize(size, remainder, shift, middle, last, round); + + // Absolute + leftSign.unsignedShiftRight(left, 127); + leftAbs.sign(left, leftSign); + + last.fill(0); + remainder.fill(0); + vector.set(leftAbs); + + Unscaled64Vector first = vector.upper; + for (int i = 0; i < right; i++) { + middle.upper(vector.lower); + last.lower(vector.lower); + + remainder.remainderByTen(first); + first.divideByTen(first); + + middle.or(shift.shiftLeft(remainder, 32), middle); // Carry + remainder.remainderByTen(middle); + middle.divideByTen(middle); + + last.or(shift.shiftLeft(remainder, 32), last); // Carry + if (i == right - 1) { + remainder.remainderByTen(last); + } + last.divideByTen(last); + + vector.lower.or(shift.shiftLeft(middle, 32), last); + } + + if (rounding) { + long[] remainderArray = remainder.array; + long[] roundArray = round.array; + for (int i = 0; i < size; i++) { + roundArray[i] = (4 - remainderArray[i]) >>> 63; + } + } + + // Sign the quotient + vector.add(vector, 0, round); + vector.sign(vector, leftSign); + return vector; + } +} diff --git storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/Unscaled64Value.java storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/Unscaled64Value.java new file mode 100644 index 0000000..84198d3 --- /dev/null +++ storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/Unscaled64Value.java @@ -0,0 +1,180 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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; + +/** + * Unscaled 64 bit value. It uses a vector that holds only one element. + */ +class Unscaled64Value implements UnscaledValue { + private Unscaled64Vector vector = new Unscaled64Vector(1); + + @Override + public void set(long upper, long lower) { + setLower(lower); + } + + @Override + public void setUpper(long upper) { + // Ignore + } + + @Override + public void setLower(long lower) { + vector.array[0] = lower; + } + + @Override + public long getUpper() { + return getLower() >> 63; + } + + @Override + public long getLower() { + return vector.array[0]; + } + + @Override + public int compareTo(Unscaled64Value that) { + Unscaled64Value comparison = new Unscaled64Value(); + + comparison.subtract(this, that); + if ((comparison.getLower() == 0)) { + return 0; + } + if (comparison.getLower() < 0) { + return -1; + } else { + return 1; + } + } + + @Override + public Unscaled64Value set(Unscaled64Value input) { + vector.set(input.vector); + return this; + } + + @Override + public Unscaled64Value negate(Unscaled64Value input) { + vector.negate(input.vector); + return this; + } + + @Override + public Unscaled64Value add(Unscaled64Value left, long right) { + vector.add(left.vector, right); + return this; + } + + @Override + public Unscaled64Value add(Unscaled64Value left, Unscaled64Value right) { + vector.add(left.vector, right.vector); + return this; + } + + @Override + public Unscaled64Value subtract(Unscaled64Value left, Unscaled64Value right) { + vector.subtract(left.vector, right.vector); + return this; + } + + @Override + public Unscaled64Value multiply(Unscaled64Value left, Unscaled64Value right) { + vector.multiply(left.vector, right.vector); + return this; + } + + @Override + public Unscaled64Value divideChecked(Unscaled64Value left, Unscaled64Value right, boolean[] isNull, int leftPrecision, int rightPrecision) { + vector.divideChecked(left.vector, right.vector, isNull, leftPrecision, rightPrecision); + return this; + } + + @Override + public Unscaled64Value divideUnchecked(Unscaled64Value left, Unscaled64Value right, int leftPrecision, int rightPrecision) { + vector.divideUnchecked(left.vector, right.vector, leftPrecision, rightPrecision); + return this; + } + + @Override + public Unscaled64Value remainderChecked(Unscaled64Value left, Unscaled64Value right, boolean[] isNull, int leftPrecision, int rightPrecision) { + vector.remainderChecked(left.vector, right.vector, isNull, leftPrecision, rightPrecision); + return this; + } + + @Override + public Unscaled64Value multiplyByPowerOfTen(Unscaled64Value left, int right) { + vector.multiplyByPowerOfTen(left.vector, right); + return this; + } + + @Override + public Unscaled64Value divideByPowerOfTen(Unscaled64Value left, int right, boolean rounding) { + vector.divideByPowerOfTen(left.vector, right, rounding); + return this; + } + + @Override + public Unscaled64Value not(Unscaled64Value input) { + vector.not(input.vector); + return this; + } + + @Override + public Unscaled64Value and(Unscaled64Value left, Unscaled64Value right) { + vector.and(left.vector, right.vector); + return this; + } + + @Override + public Unscaled64Value or(Unscaled64Value left, Unscaled64Value right) { + vector.or(left.vector, right.vector); + return this; + } + + @Override + public Unscaled64Value xor(Unscaled64Value left, Unscaled64Value right) { + vector.xor(left.vector, right.vector); + return this; + } + + @Override + public Unscaled64Value shiftLeft(Unscaled64Value left, int right) { + vector.shiftLeft(left.vector, right); + return this; + } + + @Override + public Unscaled64Value unsignedShiftRight(Unscaled64Value left, int right) { + vector.unsignedShiftRight(left.vector, right); + return this; + } + + @Override + public Unscaled64Value signedShiftRight(Unscaled64Value left, int right) { + vector.signedShiftRight(left.vector, right); + return this; + } + + @Override + public Unscaled64Value sign(Unscaled64Value input, Unscaled64Value sign) { + vector.sign(input.vector, sign.vector); + return this; + } +} diff --git storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/Unscaled64Vector.java storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/Unscaled64Vector.java new file mode 100644 index 0000000..0ed266c --- /dev/null +++ storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/Unscaled64Vector.java @@ -0,0 +1,459 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.ql.exec.vector.VectorizedRowBatch; + +class Unscaled64Vector implements Unscaled { + // Children + Divide divide; + public long[] array; + + private int size; + + Unscaled64Vector(int size) { + this.size = size; + array = new long[size]; + } + + Unscaled64Vector() { + this(VectorizedRowBatch.DEFAULT_SIZE); + } + + static void ensureSize(int length, Unscaled64Vector... list) { + for (Unscaled64Vector item : list) { + item.size = length; + if (item.array.length < length) { + item.array = new long[item.array.length * 2]; + } + } + } + + @Override + public Unscaled64Vector add(Unscaled64Vector left, Unscaled64Vector right) { + long[] outputArray = this.array; + long[] leftArray = left.array; + long[] rightArray = right.array; + + for (int i = 0; i < this.size; i++) { + outputArray[i] = leftArray[i] + rightArray[i]; + } + return this; + } + + @Override + public Unscaled64Vector add(Unscaled64Vector left, long right) { + long[] outputArray = this.array; + long[] leftArray = left.array; + + for (int i = 0; i < this.size; i++) { + outputArray[i] = leftArray[i] + right; + } + return this; + } + + @Override + public Unscaled64Vector negate(Unscaled64Vector input) { + long[] outputArray = this.array; + long[] inputArray = input.array; + + for (int i = 0; i < this.size; i++) { + outputArray[i] = -inputArray[i]; + } + return this; + } + + @Override + public Unscaled64Vector subtract(Unscaled64Vector left, Unscaled64Vector right) { + long[] outputArray = this.array; + long[] leftArray = left.array; + long[] rightArray = right.array; + + for (int i = 0; i < this.size; i++) { + outputArray[i] = leftArray[i] - rightArray[i]; + } + return this; + } + + Unscaled64Vector subtract(Unscaled64Vector left, long right) { + long[] outputArray = this.array; + long[] leftArray = left.array; + + for (int i = 0; i < this.size; i++) { + outputArray[i] = leftArray[i] - right; + } + return this; + } + @Override + public Unscaled64Vector multiply(Unscaled64Vector left, Unscaled64Vector right) { + long[] leftArray = left.array; + long[] rightArray = right.array; + long[] outputArray = this.array; + + for (int i = 0; i < this.size; i++) { + outputArray[i] = leftArray[i] * rightArray[i]; + } + return this; + } + + @Override + public Unscaled64Vector divideChecked( + Unscaled64Vector left, Unscaled64Vector right, boolean[] isNull, int leftPrecision, int rightPrecision) { + long[] rightArray = right.array; + + for (int i = 0; i < this.size; i++) { + if (rightArray[i] == 0) { + isNull[i] = true; + rightArray[i] = 1; + } + } + return this.divideUnchecked(left, right, leftPrecision, rightPrecision); + } + + @Override + public Unscaled64Vector divideUnchecked(Unscaled64Vector left, Unscaled64Vector right, int leftPrecision, int rightPrecision) { + long[] outputArray = this.array; + long[] leftArray = left.array; + long[] rightArray = right.array; + + for (int i = 0; i < this.size; i++) { + outputArray[i] = leftArray[i] / rightArray[i]; + } + return this; + } + + @Override + public Unscaled64Vector remainderChecked( + Unscaled64Vector left, Unscaled64Vector right, boolean[] isNull, int leftPrecision, int rightPrecision) { + long[] outputArray = this.array; + long[] leftArray = left.array; + long[] rightArray = right.array; + + for (int i = 0; i < this.size; i++) { + if (right.array[i] == 0) { + isNull[i] = true; + right.array[i] = 1; + } + } + for (int i = 0; i < this.size; i++) { + outputArray[i] = leftArray[i] % rightArray[i]; + } + return this; + } + + @Override + public Unscaled64Vector xor(Unscaled64Vector left, Unscaled64Vector right) { + long[] outputArray = this.array; + long[] leftArray = left.array; + long[] rightArray = right.array; + + for (int i = 0; i < this.size; i++) { + outputArray[i] = leftArray[i] ^ rightArray[i]; + } + return this; + } + + @Override + public Unscaled64Vector unsignedShiftRight(Unscaled64Vector left, int right) { + long[] leftArray = left.array; + + for (int i = 0; i < this.size; i++) { + array[i] = leftArray[i] >>> right; + } + return this; + } + + @Override + public Unscaled64Vector signedShiftRight(Unscaled64Vector left, int right) { + long[] leftArray = left.array; + + for (int i = 0; i < this.size; i++) { + array[i] = leftArray[i] >> right; + } + return this; + } + + @Override + public Unscaled64Vector shiftLeft(Unscaled64Vector left, int right) { + long[] outputArray = this.array; + long[] leftArray = left.array; + + for (int i = 0; i < this.size; i++) { + outputArray[i] = leftArray[i] << right; + } + return this; + } + + @Override + public Unscaled64Vector divideByPowerOfTen(Unscaled64Vector left, int right, boolean rounding) { + return getDivide().divideByPowerOfTen(left, right, rounding); + } + + @Override + public Unscaled64Vector multiplyByPowerOfTen(Unscaled64Vector input, int scale) { + long powerOfTen = 1; + for (int i = 0; i < scale; i++) { + powerOfTen *= 10; + } + + long[] inputArray = input.array; + long[] outputArray = this.array; + for (int i = 0; i < this.size; i++) { + outputArray[i] = inputArray[i] * powerOfTen; + } + return this; + } + + @Override + public Unscaled64Vector set(Unscaled64Vector input) { + System.arraycopy(input.array, 0, this.array, 0, this.size); + return this; + } + + @Override + public Unscaled64Vector not(Unscaled64Vector input) { + long[] inputArray = input.array; + long[] outputArray = this.array; + + for (int i = 0; i < this.size; i++) { + outputArray[i] = ~inputArray[i]; + } + return this; + } + + @Override + public Unscaled64Vector and(Unscaled64Vector left, Unscaled64Vector right) { + long[] leftArray = left.array; + long[] rightArray = right.array; + long[] outputArray = this.array; + + for (int i = 0; i < this.size; i++) { + outputArray[i] = leftArray[i] & rightArray[i]; + } + return this; + } + + @Override + public Unscaled64Vector or(Unscaled64Vector left, Unscaled64Vector right) { + long[] leftArray = left.array; + long[] rightArray = right.array; + + for (int i = 0; i < size; i++) { + array[i] = leftArray[i] | rightArray[i]; + } + return this; + } + + Unscaled64Vector divideByPowerOfTen(Unscaled64Vector left, int right) { + return divideByPowerOfTen(left, right, false); + } + + Unscaled64Vector fill(long input) { + long[] inputArray = this.array; + + for (int i = 0; i < this.size; i++) { + inputArray[i] = input; + } + return this; + } + + Unscaled64Vector and(Unscaled64Vector left, long right) { + long[] leftArray = left.array; + long[] outputArray = this.array; + + for (int i = 0; i < this.size; i++) { + outputArray[i] = leftArray[i] & right; + } + return this; + } + + Unscaled64Vector or(Unscaled64Vector left, long right) { + long[] leftArray = left.array; + + for (int i = 0; i < size; i++) { + array[i] = leftArray[i] | right; + } + return this; + } + + Unscaled64Vector xor(Unscaled64Vector left, long right) { + long[] outputArray = this.array; + long[] leftArray = left.array; + + for (int i = 0; i < this.size; i++) { + outputArray[i] = leftArray[i] ^ right; + } + return this; + } + + Unscaled64Vector upper(Unscaled64Vector input) { + return this.unsignedShiftRight(input, 32); + } + + Unscaled64Vector lower(Unscaled64Vector input) { + return this.and(input, 0xFFFFFFFFL); + } + + Unscaled64Vector remainderByTen(Unscaled64Vector input) { + long[] inputArray = input.array; + for (int i = 0; i < size; i++) { + array[i] = inputArray[i] % 10; + } + return this; + } + + Unscaled64Vector divideByTen(Unscaled64Vector input) { + long[] inputArray = input.array; + for (int i = 0; i < size; i++) { + array[i] = inputArray[i] / 10; + } + return this; + } + + Unscaled64Vector andNot(Unscaled64Vector left, Unscaled64Vector right) { + long[] leftArray = left.array; + long[] rightArray = right.array; + for (int i = 0; i < size; i++) { + array[i] = leftArray[i] & ~rightArray[i]; + } + return this; + } + + private class Divide { + private Unscaled64Vector remainder = new Unscaled64Vector(); + private Unscaled64Vector sign = new Unscaled64Vector(); + private Unscaled64Vector temp = new Unscaled64Vector(); + + Unscaled64Vector divideByPowerOfTen(Unscaled64Vector left, int right, boolean rounding) { + Unscaled64Vector output = Unscaled64Vector.this; + ensureSize(size, remainder, sign); + + long powerOfTen = 1; + for (int i = 0; i < right; i++) { + powerOfTen *= 10; + } + output.divideUnchecked(left, powerOfTen); + + if (rounding) { + remainder.remainderUnchecked(left, powerOfTen); + + // Absolute + sign.unsignedShiftRight(left, 63); + output.sign(output, sign); + remainder.sign(remainder, sign); + + long roundingBase = powerOfTen / 2 - 1; + temp.fill(roundingBase); + temp.subtract(temp, remainder); + temp.unsignedShiftRight(temp, 63); + output.add(output, temp); + + // Reverse absolute + output.sign(output, sign); + } + + return output; + } + } + + @Override + public Unscaled64Vector sign(Unscaled64Vector input, Unscaled64Vector sign) { + long[] inputArray = input.array; + long[] signArray = sign.array; + + for (int i = 0; i < size; i++) { + array[i] = (inputArray[i] ^ (-signArray[i])) + signArray[i]; + } + + return this; + } + + private Unscaled64Vector divideUnchecked(Unscaled64Vector left, long right) { + long[] outputArray = this.array; + long[] leftArray = left.array; + + for (int i = 0; i < this.size; i++) { + outputArray[i] = leftArray[i] / right; + } + return this; + } + + private Unscaled64Vector remainderUnchecked(Unscaled64Vector left, long right) { + long[] remainderArray = this.array; + long[] leftArray = left.array; + + for (int i = 0; i < size; i++) { + remainderArray[i] = leftArray[i] % right; + } + return this; + } + + Unscaled64Vector remainderUnchecked(Unscaled64Vector left, Unscaled64Vector right) { + long[] remainderArray = this.array; + long[] leftArray = left.array; + long[] rightArray = right.array; + + for (int i = 0; i < size; i++) { + remainderArray[i] = leftArray[i] % rightArray[i]; + } + return this; + } + + private Divide getDivide() { + if (divide == null) { + divide = new Divide(); + } + return divide; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Unscaled64Vector)) { + return false; + } + Unscaled64Vector that = (Unscaled64Vector) obj; + if (this.size != that.size) { + return false; + } + for (int i = 0; i < size; i++) { + if (this.array[i] != that.array[i]) { + return false; + } + } + return true; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append('['); + if (size > 0) { + builder.append(array[0]); + } + for (int i = 1; i < size; i++) { + builder.append(", "); + builder.append(array[i]); + } + builder.append(']'); + return builder.toString(); + } +} diff --git storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/UnscaledValue.java storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/UnscaledValue.java new file mode 100644 index 0000000..774314e --- /dev/null +++ storage-api/src/java/org/apache/hadoop/hive/ql/exec/vector/decimalv2/UnscaledValue.java @@ -0,0 +1,27 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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; + +interface UnscaledValue extends Unscaled, Comparable { + void set(long upper, long lower); + void setUpper(long upper); + void setLower(long lower); + long getUpper(); + long getLower(); +} diff --git storage-api/src/test/org/apache/hadoop/hive/ql/exec/vector/decimalv2/TestDecimalColumnVectorV2.java storage-api/src/test/org/apache/hadoop/hive/ql/exec/vector/decimalv2/TestDecimalColumnVectorV2.java new file mode 100644 index 0000000..3c7e1ce --- /dev/null +++ storage-api/src/test/org/apache/hadoop/hive/ql/exec/vector/decimalv2/TestDecimalColumnVectorV2.java @@ -0,0 +1,473 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class TestDecimalColumnVectorV2 { + DecimalColumnVectorV2 left; + DecimalColumnVectorV2 right; + DecimalColumnVectorV2 output; + DecimalColumnVectorV2 expected; + + int size = 9; + boolean[] isNull; + + @Before + public void before() { + left = new DecimalColumnVectorV2(size); + right = new DecimalColumnVectorV2(size); + output = new DecimalColumnVectorV2(size); + expected = new DecimalColumnVectorV2(size); + isNull = new boolean[] {false, false, false, false, false, false, false, false, false}; + } + + void set(DecimalColumnVectorV2 vector, int precision, int scale, long[] upper, long[] lower) { + set(vector, precision, scale, upper, lower, + new boolean[] {false, false, false, false, false, false, false, false, false}); + } + + void set(DecimalColumnVectorV2 vector, int precision, int scale, long[] upper, long[] lower, boolean[] isNull) { + vector.scaled.precision = precision; + vector.scaled.scale = scale; + + System.arraycopy(isNull, 0, vector.isNull, 0, size); + System.arraycopy(upper, 0, vector.scaled.unscaled.upper.array, 0, size); + System.arraycopy(lower, 0, vector.scaled.unscaled.lower.array, 0, size); + } + + @Test + public void testSet64() throws Exception { + set(left, 1, 0, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + + output.set(left); + + set(expected, 1, 0, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + assertEquals(expected, output); + } + + @Test + public void testNegate64() throws Exception { + set(left, 1, 0, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + + output.negate(left); + + set(expected, 1, 0, + new long[] {0, 0, 0, 0, 0, 0, -1, -1, -1}, + new long[] {1, 1, 1, 0, 0, 0, -1, -1, -1}); + assertEquals(expected, output); + } + + @Test + public void testAdd64() throws Exception { + // Same scale + set(left, 1, 0, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + set(right, 1, 0, + new long[] {-1, 0, 0, -1, 0, 0, -1, 0, 0}, + new long[] {-1, 0, 1, -1, 0, 1, -1, 0, 1}); + + output.add(left, right); + + set(expected, 2, 0, + new long[] {-1, -1, 0, -1, 0, 0, 0, 0, 0}, + new long[] {-2, -1, 0, -1, 0, 1, 0, 1, 2}); + assertEquals(expected, output); + + // Different scale + set(left, 2, 1, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-10, -10, -10, 0, 0, 0, 10, 10, 10}); + set(right, 1, 0, + new long[] {-1, 0, 0, -1, 0, 0, -1, 0, 0}, + new long[] {-1, 0, 1, -1, 0, 1, -1, 0, 1}); + + output.add(left, right); + + set(expected, 3, 1, + new long[] {-1, -1, 0, -1, 0, 0, 0, 0, 0}, + new long[] {-20, -10, 0, -10, 0, 10, 0, 10, 20}); + assertEquals(expected, output); + } + + @Test + public void testSubtract64() throws Exception { + set(left, 1, 0, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + set(right, 1, 0, + new long[] {-1, 0, 0, -1, 0, 0, -1, 0, 0}, + new long[] {-1, 0, 1, -1, 0, 1, -1, 0, 1}); + + output.subtract(left, right); + + set(expected, 2, 0, + new long[] {0, -1, -1, 0, 0, -1, 0, 0, 0}, + new long[] {0, -1, -2, 1, 0, -1, 2, 1, 0}); + assertEquals(expected, output); + } + + @Test + public void testMultiply64() throws Exception { + set(left, 1, 0, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + set(right, 1, 0, + new long[] {-1, 0, 0, -1, 0, 0, -1, 0, 0}, + new long[] {-1, 0, 1, -1, 0, 1, -1, 0, 1}); + + output.multiply(left, right); + + set(expected, 3, 0, + new long[] {0, 0, -1, 0, 0, 0, -1, 0, 0}, + new long[] {1, 0, -1, 0, 0, 0, -1, 0, 1}); + assertEquals(expected, output); + } + + @Test + public void testDivide64() throws Exception { + set(left, 3, 1, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-123, -123, -123, 0, 0, 0, 123, 123, 123}); + set(right, 2, 1, + new long[] {-1, 0, 0, -1, 0, 0, -1, 0, 0}, + new long[] {-45, 0, 45, -45, 0, 45, -45, 0, 45}); + + output.divide(left, right); + + set(expected, 9, 6, + new long[] {0, -1, -1, 0, 0, 0, -1, 0, 0}, + new long[] {2733333, -2733333, -2733333, 0, 0, 0, -2733333, 2733333, 2733333}, + new boolean[] {false, true, false, false, true, false, false, true, false}); + assertEquals(expected, output); + + + set(left, 5, 3, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-12300, -12300, -12300, 0, 0, 0, 12300, 12300, 12300}); + set(right, 4, 3, + new long[] {-1, 0, 0, -1, 0, 0, -1, 0, 0}, + new long[] {-4500, 0, 4500, -4500, 0, 4500, -4500, 0, 4500}); + + output.divide(left, right); + + set(expected, 13, 8, + new long[] {0, -1, -1, 0, 0, 0, -1, 0, 0}, + new long[] {273333333, -273333333, -273333333, 0, 0, 0, -273333333, 273333333, 273333333}, + new boolean[] {false, true, false, false, true, false, false, true, false}); + assertEquals(expected, output); + } + + @Test + public void testRemainder64() throws Exception { + set(left, 1, 0, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + set(right, 1, 0, + new long[] {-1, 0, 0, -1, 0, 0, -1, 0, 0}, + new long[] {-1, 0, 1, -1, 0, 1, -1, 0, 1}); + + output.remainder(left, right); + + set(expected, 1, 0, + new long[] {0, 0, 0, 0, 0, 0, 0, 0, 0}, + new long[] {0, -1, 0, 0, 0, 0, 0, 1, 0}, + new boolean[] {false, true, false, false, true, false, false, true, false}); + assertEquals(expected, output); + } + + @Test + public void testScaleByPowerOfTen64() throws Exception { + set(left, 1, 0, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + + output.scaleByPowerOfTen(left, 1); + set(expected, 2, 1, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-10, -10, -10, 0, 0, 0, 10, 10, 10}); + assertEquals(expected, output); + + left.scaleByPowerOfTen(output, -1); + output.set(left); + set(expected, 1, 0, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + assertEquals(expected, output); + } + + @Test + public void testStringifyValue64() throws Exception { + set(left, 4, 2, + new long[] {0, 0, -1, 0, -1, 0, -1, 0, -1}, + new long[] {0, 1234, -1234, 1100, -1100, 11, -11, 110, -110}); + + String[] expected = {"0", "12.34", "-12.34", "11", "-11", "0.11", "-0.11", "1.1", "-1.1"}; + String[] actual = new String[size]; + + StringBuilder buffer = new StringBuilder(); + for (int i = 0; i < size; i++) { + left.scaled.stringify64(buffer, i, isNull); + actual[i] = buffer.toString(); + buffer.delete(0, buffer.length()); + } + + Assert.assertArrayEquals(expected, actual); + } + + @Test + public void testParse64() throws Exception { + String[] input = {"0", "12.34", "-12.34", "11", "-11", "0.11", "-0.11", "1.1", "-1.1"}; + output.setPrecision(4); + output.setScale(2); + + for (int i = 0; i < size; i++) { + output.scaled.parse64(input[i].getBytes(), 0, input[i].length(), i); + } + + set(expected, 4, 2, + new long[] {0, 0, -1, 0, -1, 0, -1, 0, -1}, + new long[] {0, 1234, -1234, 1100, -1100, 11, -11, 110, -110}); + assertEquals(expected, output); + } + + @Test + public void testParseScientificNotation64() throws Exception { + String[] input = {"1.234E0", "1.234E1", "1.234E+1", + "1.234E-1", "1.234E2", "-1.234E0", + "-1.234E1", "-1.234E+1", "-1.234E-1"}; + output.setPrecision(6); + output.setScale(3); + + for (int i = 0; i < size; i++) { + output.scaled.parse64(input[i].getBytes(), 0, input[i].length(), i); + } + + set(expected, 6, 3, + new long[] {0, 0, 0, 0, 0, -1, -1, -1, -1}, + new long[] {1234, 12340, 12340, 123, 123400, -1234, -12340, -12340, -123}); + assertEquals(expected, output); + } + + @Test + public void testParse128() throws Exception { + String[] input = {"0", "12.34", "-12.34", "11", "-11", "0.11", "-0.11", "1.1", "-1.1"}; + output.setPrecision(4); + output.setScale(2); + + for (int i = 0; i < size; i++) { + output.scaled.parse128(input[i].getBytes(), 0, input[i].length(), i); + } + + set(expected, 4, 2, + new long[] {0, 0, -1, 0, -1, 0, -1, 0, -1}, + new long[] {0, 1234, -1234, 1100, -1100, 11, -11, 110, -110}); + assertEquals(expected, output); + } + + @Test + public void testParseScientificNotation128() throws Exception { + String[] input = {"1.234E0", "1.234E1", "1.234E+1", + "1.234E-1", "1.234E2", "-1.234E0", "-1.234E1", "-1.234E+1", "-1.234E-1"}; + output.setPrecision(6); + output.setScale(3); + + for (int i = 0; i < size; i++) { + output.scaled.parse128(input[i].getBytes(), 0, input[i].length(), i); + } + + set(expected, 6, 3, + new long[] {0, 0, 0, 0, 0, -1, -1, -1, -1}, + new long[] {1234, 12340, 12340, 123, 123400, -1234, -12340, -12340, -123}); + assertEquals(expected, output); + } + + @Test + public void testStringifyValue128() throws Exception { + set(left, 4, 2, + new long[] {0, 0, -1, 0, -1, 0, -1, 0, -1}, + new long[] {0, 1234, -1234, 1100, -1100, 11, -11, 110, -110}); + right.scaleByPowerOfTen(left, DecimalColumnVectorV2.MAX_HALF_PRECISION); + left.set(right); + + String[] expected = {"0", "12.34", "-12.34", "11", "-11", "0.11", "-0.11", "1.1", "-1.1"}; + String[] actual = new String[size]; + + StringBuilder buffer = new StringBuilder(); + for (int i = 0; i < size; i++) { + left.scaled.stringify128(buffer, i, isNull); + actual[i] = buffer.toString(); + buffer.delete(0, buffer.length()); + } + + Assert.assertArrayEquals(expected, actual); + } + + @Test + public void testSet128() throws Exception { + set(left, 20, 10, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + + output.set(left); + + assertEquals(left, output); + } + + @Test + public void testNegate128() throws Exception { + set(left, 20, 10, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + + output.negate(left); + + set(expected, 20, 10, + new long[] {0, 0, 0, 0, 0, 0, -1, -1, -1}, + new long[] {1, 1, 1, 0, 0, 0, -1, -1, -1}); + assertEquals(expected, output); + } + + @Test + public void testAdd128() throws Exception { + set(left, 20, 10, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + set(right, 20, 10, + new long[] {-1, 0, 0, -1, 0, 0, -1, 0, 0}, + new long[] {-1, 0, 1, -1, 0, 1, -1, 0, 1}); + + output.add(left, right); + + set(expected, 21, 10, + new long[] {-1, -1, 0, -1, 0, 0, 0, 0, 0}, + new long[] {-2, -1, 0, -1, 0, 1, 0, 1, 2}); + assertEquals(expected, output); + } + + @Test + public void testSubtract128() throws Exception { + set(left, 20, 10, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + set(right, 20, 10, + new long[] {-1, 0, 0, -1, 0, 0, -1, 0, 0}, + new long[] {-1, 0, 1, -1, 0, 1, -1, 0, 1}); + + output.subtract(left, right); + + set(expected, 21, 10, + new long[] {0, -1, -1, 0, 0, -1, 0, 0, 0}, + new long[] {0, -1, -2, 1, 0, -1, 2, 1, 0}); + assertEquals(expected, output); + } + + @Test + public void testMultiply128() throws Exception { + set(left, 20, 10, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + set(right, 1, 0, + new long[] {-1, 0, 0, -1, 0, 0, -1, 0, 0}, + new long[] {-1, 0, 1, -1, 0, 1, -1, 0, 1}); + + output.multiply(left, right); + + set(expected, 22, 10, + new long[] {0, 0, -1, 0, 0, 0, -1, 0, 0}, + new long[] {1, 0, -1, 0, 0, 0, -1, 0, 1}); + assertEquals(expected, output); + } + + @Test + public void testDivide128() { + set(output, 3, 1, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-123, -123, -123, 0, 0, 0, 123, 123, 123}); + left.scaled.rescale(output.scaled, 8); + + set(output, 2, 1, + new long[] {-1, 0, 0, -1, 0, 0, -1, 0, 0}, + new long[] {-45, 0, 45, -45, 0, 45, -45, 0, 45}); + right.scaled.rescale(output.scaled, 8); + + output.divide(left, right); + + set(expected, 28, 18, + new long[] {0, 0, -1, 0, 0, 0, -1, 0, 0}, + new long[] {2733333333333333333L, 0, -2733333333333333333L, + 0, 0, 0, -2733333333333333333L, 0, 2733333333333333333L}, + new boolean[] {false, true, false, false, true, false, false, true, false}); + assertEquals(expected, output); + } + + @Test + public void testRemainder128() throws Exception { + set(left, 20, 10, + new long[] {0, 0, 0, 0, 0, 0, 0, -1, -1}, + new long[] {2, 3, 5, 7, 0, 7, 7, -7, -7}); + set(right, 20, 10, + new long[] {0, 0, 0, 0, 0, 0, -1, 0, -1}, + new long[] {3, 3, 3, 3, 0, 3, -3, 3, -3}); + + output.remainder(left, right); + + set(expected, 20, 10, + new long[] {0, 0, 0, 0, 0, 0, 0, -1, -1}, + new long[] {2, 0, 2, 1, 0, 1, 1, -1, -1}, + new boolean[] {false, false, false, false, true, false, false, false, false}); + assertEquals(expected, output); + } + + @Test + public void testScaleByPowerOfTen128() throws Exception { + set(left, 20, 10, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + set(right, 1, 0, + new long[] {-1, 0, 0, -1, 0, 0, -1, 0, 0}, + new long[] {-1, 0, 1, -1, 0, 1, -1, 0, 1}); + + output.scaleByPowerOfTen(left, 1); + + set(expected, 21, 11, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-10, -10, -10, 0, 0, 0, 10, 10, 10}); + assertEquals(expected, output); + + right.scaleByPowerOfTen(output, -1); + output.set(right); + + set(expected, 20, 10, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + assertEquals(expected, output); + } +} \ No newline at end of file diff --git storage-api/src/test/org/apache/hadoop/hive/ql/exec/vector/decimalv2/TestUnscaled128Vector.java storage-api/src/test/org/apache/hadoop/hive/ql/exec/vector/decimalv2/TestUnscaled128Vector.java new file mode 100644 index 0000000..91ffa84 --- /dev/null +++ storage-api/src/test/org/apache/hadoop/hive/ql/exec/vector/decimalv2/TestUnscaled128Vector.java @@ -0,0 +1,383 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class TestUnscaled128Vector { + Unscaled128Vector left; + Unscaled128Vector right; + Unscaled128Vector output; + Unscaled128Vector expected; + int size = 9; + boolean[] isNull; + + void set(Unscaled128Vector output, long[] upper, long[] lower) { + System.arraycopy(upper, 0, output.upper.array, 0, size); + System.arraycopy(lower, 0, output.lower.array, 0, size); + } + + @Before + public void setUp() { + left = new Unscaled128Vector(); + right = new Unscaled128Vector(); + output = new Unscaled128Vector(); + expected = new Unscaled128Vector(); + isNull = new boolean[] {false, false, false, false, false, false, false, false, false}; + + Unscaled128Vector.ensureSize(size, left, right, output, expected); + } + + @Test + public void testAdd() throws Exception { + set(left, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + set(right, + new long[] {-1, 0, 0, -1, 0, 0, -1, 0, 0}, + new long[] {-1, 0, 1, -1, 0, 1, -1, 0, 1}); + + output.add(left, right); + + set(expected, + new long[] {-1, -1, 0, -1, 0, 0, 0, 0, 0}, + new long[] {-2, -1, 0, -1, 0, 1, 0, 1, 2}); + assertEquals(expected, output); + } + + @Test + public void testAdd2x64() throws Exception { + set(left, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + set(right, + new long[] {-1, 0, 0, -1, 0, 0, -1, 0, 0}, + new long[] {-1, 0, 1, -1, 0, 1, -1, 0, 1}); + + output.add(left, right.upper, right.lower); + + set(expected, + new long[] {-1, -1, 0, -1, 0, 0, 0, 0, 0}, + new long[] {-2, -1, 0, -1, 0, 1, 0, 1, 2}); + assertEquals(expected, output); + } + + @Test + public void testNegate() throws Exception { + set(left, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + + output.negate(left); + + set(expected, + new long[] {0, 0, 0, 0, 0, 0, -1, -1, -1}, + new long[] {1, 1, 1, 0, 0, 0, -1, -1, -1}); + assertEquals(expected, output); + } + + @Test + public void testSubtract() throws Exception { + set(left, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + set(right, + new long[] {-1, 0, 0, -1, 0, 0, -1, 0, 0}, + new long[] {-1, 0, 1, -1, 0, 1, -1, 0, 1}); + + output.subtract(left, right); + + set(expected, + new long[] {0, -1, -1, 0, 0, -1, 0, 0, 0}, + new long[] {0, -1, -2, 1, 0, -1, 2, 1, 0}); + assertEquals(expected, output); + } + + @Test + public void testMultiply() throws Exception { + set(left, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + set(right, + new long[] {-1, 0, 0, -1, 0, 0, -1, 0, 0}, + new long[] {-1, 0, 1, -1, 0, 1, -1, 0, 1}); + + output.multiply(left, right); + + set(expected, + new long[] {0, 0, -1, 0, 0, 0, -1, 0, 0}, + new long[] {1, 0, -1, 0, 0, 0, -1, 0, 1}); + assertEquals(expected, output); + } + + @Test + public void testDivide() throws Exception { + set(left, + new long[] {0, 0, 0, 0, 0, 0, 0, 0, 0}, + new long[] {1, 2, 3, 10, 20, 30, 100, 200, 300}); + set(right, + new long[] {0, 0, 0, 0, 0, 0, 0, 0, 0}, + new long[] {20, 20, 20, 20, 20, 20, 20, 20, 20}); + + output.divideUnchecked(left, right, 3, 2); + + set(expected, + new long[] {0, 0, 0, 0, 0, 0, 0, 0, 0}, + new long[] {0, 0, 0, 0, 1, 1, 5, 10, 15}); + assertEquals(expected, output); + } + + @Test + public void testDivideUnchecked() throws Exception { + + } + + @Test + public void testRemainder() throws Exception { + + } + + @Test + public void testMultiplyByPowerOfTen() throws Exception { + set(left, + new long[] {-1, -1, -1, -1, 0, 0, 0, 0, 0}, + new long[] {-1000, -100, -10, -1, 0, 1, 10, 100, 1000}); + + output.multiplyByPowerOfTen(left, 1); + + set(expected, + new long[] {-1, -1, -1, -1, 0, 0, 0, 0, 0}, + new long[] {-10000, -1000, -100, -10, 0, 10, 100, 1000, 10000}); + assertEquals(expected, output); + } + + @Test + public void testDivideByPowerOfTen() throws Exception { + set(left, + new long[] {-1, -1, -1, -1, 0, 0, 0, 0, 0}, + new long[] {-1000, -100, -10, -1, 0, 1, 10, 100, 1000}); + + output.divideByPowerOfTen(left, 1); + + set(expected, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-100, -10, -1, 0, 0, 0, 1, 10, 100}); + assertEquals(expected, output); + } + + @Test + public void testDivideByTenWithRounding() throws Exception { + set(left, + new long[] {-1, -1, -1, -1, 0, 0, 0, 0, 0}, + new long[] {-15, -14, -5, -4, 0, 4, 5, 14, 15}); + + output.divideByPowerOfTen(left, 1, true); + + set(expected, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-2, -1, -1, 0, 0, 0, 1, 1, 2}); + assertEquals(expected, output); + } + + @Test + public void testFill() throws Exception { + set(left, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + + output.fill(0, 1); + + set(expected, + new long[] {0, 0, 0, 0, 0, 0, 0, 0, 0}, + new long[] {1, 1, 1, 1, 1, 1, 1, 1, 1}); + assertEquals(expected, output); + } + + @Test + public void testSet() throws Exception { + set(left, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + + output.set(left); + + set(expected, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + assertEquals(expected, output); + } + + @Test + public void testSet2x64() throws Exception { + set(left, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + + output.set(left.upper, left.lower); + + set(expected, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + assertEquals(expected, output); + } + + @Test + public void testNot() throws Exception { + set(left, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + + output.not(left); + + set(expected, + new long[] {0, 0, 0, -1, -1, -1, -1, -1, -1}, + new long[] {0, 0, 0, -1, -1, -1, -2, -2, -2}); + assertEquals(expected, output); + } + + @Test + public void testAnd() throws Exception { + set(left, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + set(right, + new long[] {-1, 0, 0, -1, 0, 0, -1, 0, 0}, + new long[] {-1, 0, 1, -1, 0, 1, -1, 0, 1}); + + output.and(left, right); + + set(expected, + new long[] {-1, 0, 0, 0, 0, 0, 0, 0, 0}, + new long[] {-1, 0, 1, 0, 0, 0, 1, 0, 1}); + assertEquals(expected, output); + } + + @Test + public void testAnd2x64() throws Exception { + set(left, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + set(right, + new long[] {-1, 0, 0, -1, 0, 0, -1, 0, 0}, + new long[] {-1, 0, 1, -1, 0, 1, -1, 0, 1}); + + output.and(left, right.upper, right.lower); + + set(expected, + new long[] {-1, 0, 0, 0, 0, 0, 0, 0, 0}, + new long[] {-1, 0, 1, 0, 0, 0, 1, 0, 1}); + assertEquals(expected, output); + } + + @Test + public void testOr() throws Exception { + set(left, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + set(right, + new long[] {-1, 0, 0, -1, 0, 0, -1, 0, 0}, + new long[] {-1, 0, 1, -1, 0, 1, -1, 0, 1}); + + output.or(left, right); + + set(expected, + new long[] {-1, -1, -1, -1, 0, 0, -1, 0, 0}, + new long[] {-1, -1, -1, -1, 0, 1, -1, 1, 1}); + assertEquals(expected, output); + } + + @Test + public void testOr2x64() throws Exception { + set(left, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + set(right, + new long[] {-1, 0, 0, -1, 0, 0, -1, 0, 0}, + new long[] {-1, 0, 1, -1, 0, 1, -1, 0, 1}); + + output.or(left, right.upper, right.lower); + + set(expected, + new long[] {-1, -1, -1, -1, 0, 0, -1, 0, 0}, + new long[] {-1, -1, -1, -1, 0, 1, -1, 1, 1}); + assertEquals(expected, output); + } + + @Test + public void testXor() throws Exception { + set(left, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + set(right, + new long[] {-1, 0, 0, -1, 0, 0, -1, 0, 0}, + new long[] {-1, 0, 1, -1, 0, 1, -1, 0, 1}); + + output.xor(left, right.upper, right.lower); + + set(expected, + new long[] {0, -1, -1, -1, 0, 0, -1, 0, 0}, + new long[] {0, -1, -2, -1, 0, 1, -2, 1, 0}); + assertEquals(expected, output); + } + + @Test + public void testUnsignedShiftRight() throws Exception { + set(left, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-10, -10, -10, 0, 0, 0, 10, 10, 10}); + + output.unsignedShiftRight(left, 1); + + set(expected, + new long[] {-1L >>> 1, -1L >>> 1, -1L >>> 1, 0, 0, 0, 0, 0, 0}, + new long[] {-5, -5, -5, 0, 0, 0, 5, 5, 5}); + assertEquals(expected, output); + } + + @Test + public void testShiftLeft() throws Exception { + set(left, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + + output.shiftLeft(left, 1); + + set(expected, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-2, -2, -2, 0, 0, 0, 2, 2, 2}); + assertEquals(expected, output); + } + + @Test + public void testShiftLeft2x64() throws Exception { + set(left, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-1, -1, -1, 0, 0, 0, 1, 1, 1}); + + output.shiftLeft(left.upper, left.lower, 1); + + set(expected, + new long[] {-1, -1, -1, 0, 0, 0, 0, 0, 0}, + new long[] {-2, -2, -2, 0, 0, 0, 2, 2, 2}); + assertEquals(expected, output); + } +} \ No newline at end of file diff --git storage-api/src/test/org/apache/hadoop/hive/ql/exec/vector/decimalv2/TestUnscaled64Vector.java storage-api/src/test/org/apache/hadoop/hive/ql/exec/vector/decimalv2/TestUnscaled64Vector.java new file mode 100644 index 0000000..d26b5f1 --- /dev/null +++ storage-api/src/test/org/apache/hadoop/hive/ql/exec/vector/decimalv2/TestUnscaled64Vector.java @@ -0,0 +1,314 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class TestUnscaled64Vector { + Unscaled64Vector left; + Unscaled64Vector right; + Unscaled64Vector output; + Unscaled64Vector expected; + int size = 9; + boolean[] isNull; + + void set(Unscaled64Vector output, long ... input) { + for (int i = 0; i < size; i++) { + output.array[i] = input[i]; + } + } + + @Before + public void setUp() { + DecimalColumnVectorV2 leftDcv = new DecimalColumnVectorV2(); + DecimalColumnVectorV2 rightDcv = new DecimalColumnVectorV2(); + DecimalColumnVectorV2 outputDcv = new DecimalColumnVectorV2(); + DecimalColumnVectorV2 expectedDcv = new DecimalColumnVectorV2(); + + left = leftDcv.scaled.unscaled.lower; + right = rightDcv.scaled.unscaled.lower; + output = outputDcv.scaled.unscaled.lower; + expected = expectedDcv.scaled.unscaled.lower; + isNull = new boolean[] {false, false, false, false, false, false, false, false, false}; + + Unscaled64Vector.ensureSize(size, left, right, output, expected); + } + + @Test + public void testAdd() throws Exception { + set(left, -1, -1, -1, 0, 0, 0, 1, 1, 1); + set(right, -1, 0, 1, -1, 0, 1, -1, 0, 1); + + output.add(left, right); + + set(expected, -2, -1, 0, -1, 0, 1, 0, 1, 2); + + assertEquals(expected, output); + } + + @Test + public void testAddLong() throws Exception { + set(left, -1, -1, -1, 0, 0, 0, 1, 1, 1); + set(right, -1, 0, 1, -1, 0, 1, -1, 0, 1); + + output.add(left, -1); + set(expected, -2, -2, -2, -1, -1, -1, 0, 0, 0); + assertEquals(expected, output); + + output.add(left, 0); + set(expected, -1, -1, -1, 0, 0, 0, 1, 1, 1); + assertEquals(expected, output); + + output.add(left, 1); + set(expected, 0, 0, 0, 1, 1, 1, 2, 2, 2); + assertEquals(expected, output); + } + + @Test + public void testNegate() throws Exception { + set(left, -1, -1, -1, 0, 0, 0, 1, 1, 1); + set(right, -1, 0, 1, -1, 0, 1, -1, 0, 1); + + output.negate(left); + + set(expected, 1, 1, 1, 0, 0, 0, -1, -1, -1); + assertEquals(expected, output); + } + + @Test + public void testSubtract() throws Exception { + set(left, -1, -1, -1, 0, 0, 0, 1, 1, 1); + set(right, -1, 0, 1, -1, 0, 1, -1, 0, 1); + + output.subtract(left, right); + + set(expected, 0, -1, -2, 1, 0, -1, 2, 1, 0); + assertEquals(expected, output); + } + + @Test + public void testMultiply() throws Exception { + set(left, -1, -1, -1, 0, 0, 0, 1, 1, 1); + set(right, -1, 0, 1, -1, 0, 1, -1, 0, 1); + + output.multiply(left, right); + + set(expected, 1, 0, -1, 0, 0, 0, -1, 0, 1); + assertEquals(expected, output); + } + + @Test + public void testDivide() throws Exception { + set(left, -4, -3, -2, -1, 0, 1, 2, 3, 4); + set(right, 2, 2, 2, 2, 2, 2, 2, 2, 2); + + output.divideChecked(left, right, isNull, 1, 1); + + set(expected, -2, -1, -1, 0, 0, 0, 1, 1, 2); + assertEquals(expected, output); + } + + @Test + public void testDivideUnchecked() throws Exception { + set(left, -1, -1, -1, 0, 0, 0, 1, 1, 1); + set(right, -1, 1, 1, -1, 1, 1, -1, 1, 1); + + output.divideUnchecked(left, right, 1, 1); + + set(expected, 1, -1, -1, 0, 0, 0, -1, 1, 1); + assertEquals(expected, output); + } + + @Test + public void testRemainder() throws Exception { + set(left, -4, -3, -2, -1, 0, 1, 2, 3, 4); + set(right, 2, 2, 2, 2, 2, 2, 2, 2, 2); + + output.remainderChecked(left, right, isNull, 1, 1); + + set(expected, 0, -1, 0, -1, 0, 1, 0, 1, 0); + assertEquals(expected, output); + } + + @Test + public void testDivideByPowerOfTen() throws Exception { + set(left, -1000, -100, -10, -1, 0, 1, 10, 100, 1000); + + output.divideByPowerOfTen(left, 1); + + set(expected, -100, -10, -1, 0, 0, 0, 1, 10, 100); + assertEquals(expected, output); + } + + @Test + public void testDivideByPowerOfTenWithRounding() throws Exception { + set(left, -15, -14, -5, -4, 0, 4, 5, 14, 15); + + output.divideByPowerOfTen(left, 1, true); + + set(expected, -2, -1, -1, 0, 0, 0, 1, 1, 2); + assertEquals(expected, output); + } + + @Test + public void testMultiplyByPowerOfTen() throws Exception { + set(left, -1000, -100, -10, -1, 0, 1, 10, 100, 1000); + + output.multiplyByPowerOfTen(left, 1); + + set(expected, -10000, -1000, -100, -10, 0, 10, 100, 1000, 10000); + assertEquals(expected, output); + } + + @Test + public void testFill() throws Exception { + set(left, -1, -1, -1, 0, 0, 0, 1, 1, 1); + + output.fill(1); + + set(expected, 1, 1, 1, 1, 1, 1, 1, 1, 1); + assertEquals(expected, output); + } + + @Test + public void testSet() throws Exception { + set(left, -1, -1, -1, 0, 0, 0, 1, 1, 1); + + output.set(left); + + set(expected, -1, -1, -1, 0, 0, 0, 1, 1, 1); + assertEquals(expected, output); + } + + @Test + public void testNot() throws Exception { + set(left, -1, -1, -1, 0, 0, 0, 1, 1, 1); + + output.not(left); + + set(expected, 0, 0, 0, -1, -1, -1, -2, -2, -2); + assertEquals(expected, output); + } + + @Test + public void testAnd() throws Exception { + set(left, -1, -1, -1, 0, 0, 0, 1, 1, 1); + set(right, -1, 0, 1, -1, 0, 1, -1, 0, 1); + + output.and(left, right); + + set(expected, -1, 0, 1, 0, 0, 0, 1, 0, 1); + assertEquals(expected, output); + } + + @Test + public void testAndLong() throws Exception { + set(left, -1, -1, -1, 0, 0, 0, 1, 1, 1); + + output.and(left, 1); + + set(expected, 1, 1, 1, 0, 0, 0, 1, 1, 1); + assertEquals(expected, output); + } + + @Test + public void testOr() throws Exception { + set(left, -1, -1, -1, 0, 0, 0, 1, 1, 1); + set(right, -1, 0, 1, -1, 0, 1, -1, 0, 1); + + output.or(left, right); + + set(expected, -1, -1, -1, -1, 0, 1, -1, 1, 1); + assertEquals(expected, output); + } + + @Test + public void testXor() throws Exception { + set(left, -1, -1, -1, 0, 0, 0, 1, 1, 1); + set(right, -1, 0, 1, -1, 0, 1, -1, 0, 1); + + output.xor(left, right); + + set(expected, 0, -1, -2, -1, 0, 1, -2, 1, 0); + assertEquals(expected, output); + } + + @Test + public void testXorLong() throws Exception { + set(left, -1, -1, -1, 0, 0, 0, 1, 1, 1); + + output.xor(left, 1); + + set(expected, -2, -2, -2, 1, 1, 1, 0, 0, 0); + assertEquals(expected, output); + } + + @Test + public void testUnsignedShiftRight() throws Exception { + set(left, -1, -1, -1, 0, 0, 0, 1, 1, 1); + + output.unsignedShiftRight(left, 1); + + set(expected, -1L >>> 1, -1L >>> 1, -1L >>> 1, 0, 0, 0, 0, 0, 0); + assertEquals(expected, output); + } + + @Test + public void testShiftLeft() throws Exception { + set(left, -1, -1, -1, 0, 0, 0, 1, 1, 1); + + output.shiftLeft(left, 1); + + set(expected, -2, -2, -2, 0, 0, 0, 2, 2, 2); + assertEquals(expected, output); + } + + @Test + public void testUpper() throws Exception { + set(left, -1, -1, -1, 0, 0, 0, 1, 1, 1); + + output.upper(left); + + set(expected, 0xFFFFFFFFL, 0xFFFFFFFFL, 0xFFFFFFFFL, 0, 0, 0, 0, 0, 0); + assertEquals(expected, output); + } + + @Test + public void testLower() throws Exception { + set(left, -1, -1, -1, 0, 0, 0, 1, 1, 1); + + output.lower(left); + + set(expected, 0xFFFFFFFFL, 0xFFFFFFFFL, 0xFFFFFFFFL, 0, 0, 0, 1, 1, 1); + assertEquals(expected, output); + } + + @Test + public void testSignedShiftRight() throws Exception { + set(left, -1, -1, -1, 0, 0, 0, 1, 1, 1); + + output.signedShiftRight(left, 63); + + set(expected, -1, -1, -1, 0, 0, 0, 0, 0, 0); + assertEquals(expected, output); + } +} \ No newline at end of file