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