1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *    http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package org.apache.arrow.gandiva.evaluator;
19 
20 import org.apache.arrow.vector.types.pojo.ArrowType.Decimal;
21 
22 /**
23  * Utility methods for working with {@link Decimal} values.
24  */
25 public class DecimalTypeUtil {
DecimalTypeUtil()26   private DecimalTypeUtil() {}
27 
28   /**
29    * Enum for supported mathematical operations.
30    */
31   public enum OperationType {
32     ADD,
33     SUBTRACT,
34     MULTIPLY,
35     DIVIDE,
36     MOD
37   }
38 
39   private static final int MIN_ADJUSTED_SCALE = 6;
40   /// The maximum precision representable by a 16-byte decimal
41   private static final int MAX_PRECISION = 38;
42 
43   /**
44    * Determines the scale and precision of applying the given operation to the operands.
45    */
getResultTypeForOperation(OperationType operation, Decimal operand1, Decimal operand2)46   public static Decimal getResultTypeForOperation(OperationType operation, Decimal operand1, Decimal
47           operand2) {
48     int s1 = operand1.getScale();
49     int s2 = operand2.getScale();
50     int p1 = operand1.getPrecision();
51     int p2 = operand2.getPrecision();
52     int resultScale = 0;
53     int resultPrecision = 0;
54     switch (operation) {
55       case ADD:
56       case SUBTRACT:
57         resultScale = Math.max(operand1.getScale(), operand2.getScale());
58         resultPrecision = resultScale + Math.max(operand1.getPrecision() - operand1.getScale(),
59                 operand2.getPrecision() - operand2.getScale()) + 1;
60         break;
61       case MULTIPLY:
62         resultScale = s1 + s2;
63         resultPrecision = p1 + p2 + 1;
64         break;
65       case DIVIDE:
66         resultScale =
67                 Math.max(MIN_ADJUSTED_SCALE, operand1.getScale() + operand2.getPrecision() + 1);
68         resultPrecision =
69                 operand1.getPrecision() - operand1.getScale() + operand2.getScale() + resultScale;
70         break;
71       case MOD:
72         resultScale = Math.max(operand1.getScale(), operand2.getScale());
73         resultPrecision = Math.min(operand1.getPrecision() - operand1.getScale(),
74                                     operand2.getPrecision() - operand2.getScale()) +
75                            resultScale;
76         break;
77       default:
78         throw new RuntimeException("Needs support");
79     }
80     return adjustScaleIfNeeded(resultPrecision, resultScale);
81   }
82 
adjustScaleIfNeeded(int precision, int scale)83   private static Decimal adjustScaleIfNeeded(int precision, int scale) {
84     if (precision > MAX_PRECISION) {
85       int minScale = Math.min(scale, MIN_ADJUSTED_SCALE);
86       int delta = precision - MAX_PRECISION;
87       precision = MAX_PRECISION;
88       scale = Math.max(scale - delta, minScale);
89     }
90     return new Decimal(precision, scale);
91   }
92 
93 }
94 
95