/** * 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 java.io.Serializable; import java.math.BigInteger; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.util.Arrays; import org.apache.hive.common.util.Decimal128FastBuffer; /** * This code was based on code from Microsoft's PolyBase. * * Represents an unsigned 128-bit integer. This is the basis for * {@link Decimal128} and {@link SignedInt128}. This object is much faster and * more compact than BigInteger, but has many limitations below. * <ul> * <li>Always consumes 128 bits (4 int32) even if the values is, say, 3.</li> * <li>Cannot handle values larger than 10**38.</li> * <li>Does not support some of arithmetic operations that is not required in * SQL (e.g., exact POWER/SQRT).</li> * </ul> */ public final class UnsignedInt128 implements Comparable<UnsignedInt128>, Serializable { /** Number of ints to store this object. */ public static final int INT_COUNT = 4; /** Number of bytes to store this object. */ public static final int BYTE_SIZE = 4 * INT_COUNT; /** Can hold up to 10^38. */ public static final int MAX_DIGITS = 38; /** Maximum value that can be represented in this class. */ public static final UnsignedInt128 MAX_VALUE = new UnsignedInt128(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF); /** Minimum value that can be represented in this class. */ public static final UnsignedInt128 MIN_VALUE = new UnsignedInt128(0); /** A special value representing 10**38. */ public static final UnsignedInt128 TEN_TO_THIRTYEIGHT = new UnsignedInt128(0, 0x98a2240, 0x5a86c47a, 0x4b3b4ca8); /** * Int32 elements as little-endian (v[0] is least significant) unsigned * integers. */ private int[] v = new int[INT_COUNT]; /** * Number of leading non-zero elements in {@link #v}. For example, if the * value of this object is 123 (v0=123, v1=v2=v3=0), then 1. 0 to 4. 0 means * that this object represents zero. * * @see #updateCount() */ private byte count; /** * Determines the number of ints to store one value. * * @param precision * precision (0-38) * @return the number of ints to store one value */ public static int getIntsPerElement(int precision) { assert (precision >= 0 && precision <= 38); if (precision <= 9) { return 1; } else if (precision <= 19) { return 2; } else if (precision <= 28) { return 3; } return 4; } /** Creates an instance that represents zero. */ public UnsignedInt128() { zeroClear(); } /** * Copy constructor. * * @param o * The instance to copy from */ public UnsignedInt128(UnsignedInt128 o) { update(o); } /** * Creates an instance that has the given values. * * @param v0 * v0 * @param v1 * v1 * @param v2 * v2 * @param v3 * v3 */ public UnsignedInt128(int v0, int v1, int v2, int v3) { update(v0, v1, v2, v3); } /** * Constructs from the given long value. * * @param v * long value */ public UnsignedInt128(long v) { update(v); } /** * Constructs from the given string. * * @param str * string */ public UnsignedInt128(String str) { update(str); } /** * Constructs from the given string with given offset and length. * * @param str * string * @param offset * offset * @param length * length */ public UnsignedInt128(char[] str, int offset, int length) { update(str, offset, length); } /** * Constructs from the given BigInteger * * @param bigInt * java BigInteger */ public UnsignedInt128(BigInteger bigInt) { update(bigInt); } /** * Updates the value of this object from the given {@link BigInteger}. * Only positive BigIntegers are expected and behavior is undefined for * negative BigIntegers. * * @param bigInt * java BigInteger */ public void update(BigInteger bigInt) { int v0 = bigInt.intValue(); int v1 = bigInt.shiftRight(32).intValue(); int v2 = bigInt.shiftRight(64).intValue(); int v3 = bigInt.shiftRight(96).intValue(); update(v0, v1, v2, v3); } /** @return v[0] */ public int getV0() { return v[0]; } /** @return v[1] */ public int getV1() { return v[1]; } /** @return v[2] */ public int getV2() { return v[2]; } /** @return v[3] */ public int getV3() { return v[3]; } /** * Setter for v0. * * @param val * value to set */ public void setV0(int val) { v[0] = val; updateCount(); } /** * Setter for v1. * * @param val * value to set */ public void setV1(int val) { v[1] = val; updateCount(); } /** * Setter for v2. * * @param val * value to set */ public void setV2(int val) { v[2] = val; updateCount(); } /** * Setter for v3. * * @param val * value to set */ public void setV3(int val) { v[3] = val; updateCount(); } /** * Returns if we overflowed 10**38, but not 2**128. This code is equivalent to * CSsNumeric::FGt10_38 in SQLServer (numeric.cpp). However, be aware that the * elements are signed ints, not UI4 in SQLServer. * * @return whether this value is equal to or larger than 10**38 */ public boolean exceedsTenToThirtyEight() { // 10**38= // v[0]=0(0),v[1]=160047680(98a2240),v[2]=1518781562(5a86c47a),v[3]=1262177448(4b3b4ca8) // check most significant part first if (v[3] != 0x4b3b4ca8) { return (v[3] < 0 || v[3] > 0x4b3b4ca8); } // check second most significant part if (v[2] != 0x5a86c47a) { return (v[2] < 0 || v[2] > 0x5a86c47a); } return (v[1] < 0 || v[1] > 0x098a2240); } /** * Used to check overflows. This is NOT used in {@link UnsignedInt128} itself * because this overflow semantics is Decimal's. (throws - but not a checked * exception) ArithmeticException if this value is equal to or exceed 10**38. */ public void throwIfExceedsTenToThirtyEight() { if (exceedsTenToThirtyEight()) { SqlMathUtil.throwOverflowException(); } } /** * Returns the value of this object as long, throwing error if the value * exceeds long. * * @return the value this object represents */ public long asLong() { if (this.count > 2 || v[1] < 0) { SqlMathUtil.throwOverflowException(); } return (((long) v[1]) << 32L) | v[0]; } /** Make the value to zero. */ public void zeroClear() { this.v[0] = 0; this.v[1] = 0; this.v[2] = 0; this.v[3] = 0; this.count = 0; } /** @return whether the value is zero */ public boolean isZero() { return this.count == 0; } /** @return whether the value is one */ public boolean isOne() { return this.v[0] == 1 && this.count == 1; } /** @return whether 32bits int is enough to represent this value */ public boolean fitsInt32() { return this.count <= 1; } /** * Copy from the given object. * * @param o * The instance to copy from */ public void update(UnsignedInt128 o) { update(o.v[0], o.v[1], o.v[2], o.v[3]); } /** * Updates the value of this object with the given long value. * * @param v * long value */ public void update(long v) { assert (v >= 0); update((int) v, (int) (v >> 32), 0, 0); } /** * Updates the value of this object with the given values. * * @param v0 * v0 * @param v1 * v1 * @param v2 * v2 * @param v3 * v3 */ public void update(int v0, int v1, int v2, int v3) { this.v[0] = v0; this.v[1] = v1; this.v[2] = v2; this.v[3] = v3; updateCount(); } /** * Updates the value of this object by reading from ByteBuffer, using the * required number of ints for the given precision. * * @param buf * ByteBuffer to read values from * @param precision * 0 to 38. Decimal digits. */ public void update(IntBuffer buf, int precision) { switch (getIntsPerElement(precision)) { case 1: update32(buf); break; case 2: update64(buf); break; case 3: update96(buf); break; case 4: update128(buf); break; default: throw new RuntimeException(); } } /** * Updates the value of this object by reading from ByteBuffer, receiving 128 * bits data (full ranges). * * @param buf * ByteBuffer to read values from */ public void update128(IntBuffer buf) { buf.get(v, 0, INT_COUNT); updateCount(); } /** * Updates the value of this object by reading from ByteBuffer, receiving only * 96 bits data. * * @param buf * ByteBuffer to read values from */ public void update96(IntBuffer buf) { buf.get(v, 0, 3); v[3] = 0; updateCount(); } /** * Updates the value of this object by reading from ByteBuffer, receiving only * 64 bits data. * * @param buf * ByteBuffer to read values from */ public void update64(IntBuffer buf) { buf.get(v, 0, 2); v[2] = 0; v[3] = 0; updateCount(); } /** * Updates the value of this object by reading from ByteBuffer, receiving only * 32 bits data. * * @param buf * ByteBuffer to read values from */ public void update32(IntBuffer buf) { v[0] = buf.get(); v[1] = 0; v[2] = 0; v[3] = 0; updateCount(); } /** * Updates the value of this object by reading from the given array, using the * required number of ints for the given precision. * * @param array * array to read values from * @param offset * offset of the long array * @param precision * 0 to 38. Decimal digits. */ public void update(int[] array, int offset, int precision) { switch (getIntsPerElement(precision)) { case 1: update32(array, offset); break; case 2: update64(array, offset); break; case 3: update96(array, offset); break; case 4: update128(array, offset); break; default: throw new RuntimeException(); } } /** * Updates the value of this object by reading from the given array, receiving * 128 bits data (full ranges). * * @param array * array to read values from * @param offset * offset of the long array */ public void update128(int[] array, int offset) { System.arraycopy(array, offset, v, 0, 4); updateCount(); } /** * Updates the value of this object by reading from the given array, receiving * only 96 bits data. * * @param array * array to read values from * @param offset * offset of the long array */ public void update96(int[] array, int offset) { System.arraycopy(array, offset, v, 0, 3); v[3] = 0; updateCount(); } /** * Updates the value of this object by reading from the given array, receiving * only 64 bits data. * * @param array * array to read values from * @param offset * offset of the long array */ public void update64(int[] array, int offset) { System.arraycopy(array, offset, v, 0, 2); v[2] = 0; v[3] = 0; updateCount(); } /** * Updates the value of this object by reading from the given array, receiving * only 32 bits data. * * @param array * array to read values from * @param offset * offset of the long array */ public void update32(int[] array, int offset) { v[0] = array[offset]; v[1] = 0; v[2] = 0; v[3] = 0; updateCount(); } /** * Updates the value of this object with the given string. * * @param str * string */ public void update(String str) { update(str.toCharArray(), 0, str.length()); } /** * Updates the value of this object from the given string with given offset * and length. * * @param str * string * @param offset * offset * @param length * length */ public void update(char[] str, int offset, int length) { // Skip leading zeros and compute number of digits in magnitude final int end = offset + length; assert (end <= str.length); int cursor = offset; while (cursor < end && str[cursor] == '0') { ++cursor; } if (cursor == end) { zeroClear(); return; } if (end - cursor > MAX_DIGITS) { SqlMathUtil.throwOverflowException(); } int accumulated = 0; int accumulatedCount = 0; while (cursor < end) { if (str[cursor] < '0' || str[cursor] > '9') { throw new NumberFormatException("Invalid string:" + new String(str, offset, length)); } if (accumulatedCount == 9) { scaleUpTenDestructive((short) accumulatedCount); addDestructive(accumulated); accumulated = 0; accumulatedCount = 0; } int digit = str[cursor] - '0'; accumulated = accumulated * 10 + digit; ++accumulatedCount; ++cursor; } if (accumulatedCount > 0) { scaleUpTenDestructive((short) accumulatedCount); addDestructive(accumulated); } } /** * Serialize this object to the given ByteBuffer, putting the required number * of ints for the given precision. * * @param buf * ByteBuffer to write values to * @param precision * 0 to 38. Decimal digits. */ public void serializeTo(IntBuffer buf, int precision) { buf.put(v, 0, getIntsPerElement(precision)); } /** * Serialize this object to the given ByteBuffer, putting 128 bits data (full * ranges). * * @param buf * ByteBuffer to write values to */ public void serializeTo128(IntBuffer buf) { buf.put(v, 0, 4); } /** * Serialize this object to the given ByteBuffer, putting only 96 bits data. * * @param buf * ByteBuffer to write values to */ public void serializeTo96(IntBuffer buf) { assert (v[3] == 0); buf.put(v, 0, 3); } /** * Serialize this object to the given ByteBuffer, putting only 64 bits data. * * @param buf * ByteBuffer to write values to */ public void serializeTo64(IntBuffer buf) { assert (v[2] == 0); assert (v[3] == 0); buf.put(v, 0, 2); } /** * Serialize this object to the given ByteBuffer, putting only 32 bits data. * * @param buf * ByteBuffer to write values to */ public void serializeTo32(IntBuffer buf) { assert (v[1] == 0); assert (v[2] == 0); assert (v[3] == 0); buf.put(v[0]); } /** * Serialize this object to the given array, putting the required number of * ints for the given precision. * * @param array * array to write values to * @param offset * offset of the int array * @param precision * 0 to 38. Decimal digits. */ public void serializeTo(int[] array, int offset, int precision) { System.arraycopy(v, 0, array, offset, getIntsPerElement(precision)); } /** * Serialize this object to the given array, putting 128 bits data (full * ranges). * * @param array * array to write values to * @param offset * offset of the int array */ public void serializeTo128(int[] array, int offset) { System.arraycopy(v, 0, array, offset, 4); } /** * Serialize this object to the given array, putting only 96 bits data. * * @param array * array to write values to * @param offset * offset of the int array */ public void serializeTo96(int[] array, int offset) { assert (v[3] == 0); System.arraycopy(v, 0, array, offset, 3); } /** * Serialize this object to the given array, putting only 64 bits data. * * @param array * array to write values to * @param offset * offset of the int array */ public void serializeTo64(int[] array, int offset) { assert (v[2] == 0); assert (v[3] == 0); System.arraycopy(v, 0, array, offset, 2); } /** * Serialize this object to the given array, putting only 32 bits data. * * @param array * array to write values to * @param offset * offset of the int array */ public void serializeTo32(int[] array, int offset) { assert (v[1] == 0); assert (v[2] == 0); assert (v[3] == 0); array[0] = v[0]; } @Override public int compareTo(UnsignedInt128 o) { return compareTo(o.v); } /** * @see #compareTo(UnsignedInt128) * @param o * the object to be compared. * @return a negative integer, zero, or a positive integer as this object is * less than, equal to, or greater than the specified object. */ public int compareTo(int[] o) { return compareTo(o[0], o[1], o[2], o[3]); } /** * @see #compareTo(UnsignedInt128) * @param o0 * o0 * @param o1 * o1 * @param o2 * o2 * @param o3 * o3 * @return a negative integer, zero, or a positive integer as this object is * less than, equal to, or greater than the specified object. */ public int compareTo(int o0, int o1, int o2, int o3) { if (v[3] != o3) { return SqlMathUtil.compareUnsignedInt(v[3], o3); } else if (v[2] != o2) { return SqlMathUtil.compareUnsignedInt(v[2], o2); } else if (v[1] != o1) { return SqlMathUtil.compareUnsignedInt(v[1], o1); } else { return SqlMathUtil.compareUnsignedInt(v[0], o0); } } /** * Compares with the given object after scaling up/down it for 10**scaleUp. * This method is not destructive. Used from {@link Decimal128}. * * @param o * the object to compare with * @param tenScale * power of 10 to scale up (if positive) or down (if negative) the * given object. * @return a negative integer, zero, or a positive integer as this object is * less than, equal to, or greater than the specified object. */ public int compareToScaleTen(UnsignedInt128 o, short tenScale) { // easier case. take a quick path if (tenScale == 0) { return compareTo(o); } // if o is zero, easy. if (o.isZero()) { return this.isZero() ? 0 : 1; } else if (this.isZero()) { // if this is zero, we need to check if o might become zero after // scaling down. // this is not easy because there might be rounding. if (tenScale > 0) { // scaling up, so o is definitely larger return -1; } if (tenScale < -SqlMathUtil.MAX_POWER_TEN_INT128) { // any value will become zero. even no possibility of rounding return 0; } else { // compare with 5 * 10**-tenScale // example: tenScale=-1. o will be zero after scaling if o>=5. boolean oZero = o .compareTo(SqlMathUtil.ROUND_POWER_TENS_INT128[-tenScale]) < 0; return oZero ? 0 : -1; } } // another quick path if (this.fitsInt32() && o.fitsInt32() && tenScale <= SqlMathUtil.MAX_POWER_TEN_INT31) { long v0Long = this.v[0] & SqlMathUtil.LONG_MASK; long o0; if (tenScale < 0) { if (tenScale < -SqlMathUtil.MAX_POWER_TEN_INT31) { // this scales down o.v[0] to 0 because 2^32 = 4.2E9. No // possibility of rounding. o0 = 0L; } else { // divide by 10**-tenScale. check for rounding. o0 = (o.v[0] & SqlMathUtil.LONG_MASK) / SqlMathUtil.POWER_TENS_INT31[-tenScale]; long remainder = (o.v[0] & SqlMathUtil.LONG_MASK) % SqlMathUtil.POWER_TENS_INT31[-tenScale]; if (remainder >= SqlMathUtil.ROUND_POWER_TENS_INT31[-tenScale]) { assert (o0 >= 0); ++o0; // this is safe because o0 is positive } } } else { // tenScale <= SqlMathUtil.MAX_POWER_TEN_INT31 // so, we can make this as a long comparison o0 = (o.v[0] & SqlMathUtil.LONG_MASK) * (SqlMathUtil.POWER_TENS_INT31[tenScale] & SqlMathUtil.LONG_MASK); } return SqlMathUtil.compareUnsignedLong(v0Long, o0); } // unfortunately no quick path. let's do scale up/down int[] ov = o.v.clone(); if (tenScale < 0) { // scale down. does rounding scaleDownTenArray4RoundUp(ov, (short) -tenScale); } else { // scale up boolean overflow = scaleUpTenArray(ov, tenScale); if (overflow) { // overflow is not an error here. it just means "this" is // smaller return -1; } } return compareTo(ov); } @Override public int hashCode() { // note: v[0] ^ v[1] ^ v[2] ^ v[3] would cause too many hash collisions return (v[0] * 0x2AB19E23) + (v[1] * 0x4918EACB) + (v[2] * 0xF03051A7) + v[3]; } @Override public boolean equals(Object obj) { if (!(obj instanceof UnsignedInt128)) { return false; } return equals((UnsignedInt128) obj); } /** * Specialized version. * * @see #equals(Object) * @param o * the object to compare with * @return whether this object is equal to the given object */ public boolean equals(UnsignedInt128 o) { return this.v[0] == o.v[0] && this.v[1] == o.v[1] && this.v[2] == o.v[2] && this.v[3] == o.v[3]; } /** * Specialized version. * * @see #equals(Object) * @param o0 * o0 * @param o1 * o1 * @param o2 * o2 * @param o3 * o3 * @return whether this object is equal to the given object */ public boolean equals(int o0, int o1, int o2, int o3) { return this.v[0] == o0 && this.v[1] == o1 && this.v[2] == o2 && this.v[3] == o3; } @Override protected Object clone() throws CloneNotSupportedException { return new UnsignedInt128(this); } /** * Convert this object to {@link BigInteger}. Do not use this method in a * performance sensitive place. * * @return BigInteger to represent this object */ public BigInteger toBigIntegerSlow() { BigInteger bigInt = BigInteger.valueOf(v[3] & SqlMathUtil.LONG_MASK); bigInt = bigInt.shiftLeft(32); bigInt = bigInt.add(BigInteger.valueOf(v[2] & SqlMathUtil.LONG_MASK)); bigInt = bigInt.shiftLeft(32); bigInt = bigInt.add(BigInteger.valueOf(v[1] & SqlMathUtil.LONG_MASK)); bigInt = bigInt.shiftLeft(32); bigInt = bigInt.add(BigInteger.valueOf(v[0] & SqlMathUtil.LONG_MASK)); return bigInt; } /** * Returns the formal string representation of this value. Unlike the debug * string returned by {@link #toString()}, this method returns a string that * can be used to re-construct this object. Remember, toString() is only for * debugging. * * @return string representation of this value */ public String toFormalString() { char[] buf = new char[MAX_DIGITS + 1]; int bufCount = 0; int nonZeroBufCount = 0; final int tenScale = SqlMathUtil.MAX_POWER_TEN_INT31; final int tenPower = SqlMathUtil.POWER_TENS_INT31[tenScale]; UnsignedInt128 tmp = new UnsignedInt128(this); while (!tmp.isZero()) { int remainder = tmp.divideDestructive(tenPower); for (int i = 0; i < tenScale && bufCount < buf.length; ++i) { int digit = remainder % 10; remainder /= 10; buf[bufCount] = (char) (digit + '0'); ++bufCount; if (digit != 0) { nonZeroBufCount = bufCount; } } } if (bufCount == 0) { return "0"; } else { char[] reversed = new char[nonZeroBufCount]; for (int i = 0; i < nonZeroBufCount; ++i) { reversed[i] = buf[nonZeroBufCount - i - 1]; } return new String(reversed); } } /** * Similar to {@link #toFormalString()} but returns an array of digits * instead of string. The length of the array and the count of trailing * zeros are returned in the array passed at first and second positions * respectively. * @param meta Array of size two that is populated with length of the returned array * and the count of trailing zeros. * @return Digits of the this value * @throws NullPointerException if meta is null. * @throws ArrayIndexOutOfBoundsException if meta is less than size two. */ public char [] getDigitsArray(int [] meta) { char[] buf = new char[MAX_DIGITS + 1]; int bufCount = 0; int nonZeroBufCount = 0; int trailingZeros = 0; final int tenScale = SqlMathUtil.MAX_POWER_TEN_INT31; final int tenPower = SqlMathUtil.POWER_TENS_INT31[tenScale]; UnsignedInt128 tmp = new UnsignedInt128(this); while (!tmp.isZero()) { int remainder = tmp.divideDestructive(tenPower); for (int i = 0; i < tenScale && bufCount < buf.length; ++i) { int digit = remainder % 10; remainder /= 10; buf[bufCount] = (char) (digit + '0'); ++bufCount; if (digit != 0) { nonZeroBufCount = bufCount; } if (nonZeroBufCount == 0) { // Count zeros until first non-zero digit is encountered. trailingZeros++; } } } if (bufCount == 0) { meta[0] = 1; meta[1] = 1; buf[0] = '0'; return buf; } else { // Reverse in place for (int i = 0, j = nonZeroBufCount - 1; i < j; i++, j--) { char t = buf[i]; buf[i] = buf[j]; buf[j] = t; } meta[0] = nonZeroBufCount; meta[1] = trailingZeros; return buf; } } @Override public String toString() { StringBuilder str = new StringBuilder(); str.append("Int128: count=" + count + ","); str.append("v[0]=" + v[0] + "(0x" + Integer.toHexString(v[0]) + "), "); str.append("v[1]=" + v[1] + "(0x" + Integer.toHexString(v[1]) + "), "); str.append("v[2]=" + v[2] + "(0x" + Integer.toHexString(v[2]) + "), "); str.append("v[3]=" + v[3] + "(0x" + Integer.toHexString(v[3]) + "), "); str.append("BigInteger#toString=" + toBigIntegerSlow().toString()); return new String(str); } /** * Adds the given value to this value. This version is destructive, meaning it * modifies this object. * * @param right * the value to add */ public void addDestructive(UnsignedInt128 right) { addDestructive(right.v); } /** * Adds the given value to this value. This version is destructive, meaning it * modifies this object. * * @param r * the value to add */ public void addDestructive(int[] r) { long sum = 0L; for (int i = 0; i < INT_COUNT; ++i) { sum = (this.v[i] & SqlMathUtil.LONG_MASK) + (r[i] & SqlMathUtil.LONG_MASK) + (sum >>> 32); this.v[i] = (int) sum; } updateCount(); if ((sum >> 32) != 0) { SqlMathUtil.throwOverflowException(); } } /** * Adds the given value to this value. This version is destructive, meaning it * modifies this object. * * @param r * the value to add */ public void addDestructive(int r) { if ((this.v[0] & SqlMathUtil.LONG_MASK) + (r & SqlMathUtil.LONG_MASK) >= (1L << 32L)) { this.v[0] += r; if (this.v[1] == SqlMathUtil.FULLBITS_32) { this.v[1] = 0; if (this.v[2] == SqlMathUtil.FULLBITS_32) { this.v[2] = 0; if (this.v[3] == SqlMathUtil.FULLBITS_32) { SqlMathUtil.throwOverflowException(); } else { ++this.v[3]; } } else { ++this.v[2]; } } else { ++this.v[1]; } } else { this.v[0] += r; } updateCount(); } /** * Adds one to this value. This version is destructive, meaning it modifies * this object. */ public void incrementDestructive() { incrementArray(v); updateCount(); } /** * Subtracts one from this value. This version is destructive, meaning it * modifies this object. */ public void decrementDestructive() { decrementArray(v); updateCount(); } /** * Adds the given value after scaling to this value. this := this + (right * * 10**tenScale). This version is destructive, meaning it modifies this * object. * * @param right * the value to add * @param tenScale * number of ten-based scaling. could be either positive or negative. */ public void addDestructiveScaleTen(UnsignedInt128 right, short tenScale) { if (tenScale == 0) { addDestructive(right); return; } // scale up/down final int[] r = right.v.clone(); if (tenScale < 0) { // scale down scaleDownTenArray4RoundUp(r, (short) -tenScale); } else if (tenScale > 0) { // scale up boolean overflow = scaleUpTenArray(r, tenScale); if (overflow) { SqlMathUtil.throwOverflowException(); } } addDestructive(r); } /** * Subtracts the given value from this value. In other words, this := this - * right. This method will throw overflow exception if right operand is larger * than this value. This version is destructive, meaning it modifies this * object. * * @param right * the value to subtract */ public void subtractDestructive(UnsignedInt128 right) { subtractDestructive(right.v); } /** * Subtracts the given value from this value. In other words, this := this - * right. This method doesn't work if right operand is larger than this value. * This version is destructive, meaning it modifies this object. * * @param r * the value to subtract */ public void subtractDestructive(int[] r) { long sum = 0L; for (int i = 0; i < INT_COUNT; ++i) { sum = (this.v[i] & SqlMathUtil.LONG_MASK) - (r[i] & SqlMathUtil.LONG_MASK) - ((int) -(sum >> 32)); this.v[i] = (int) sum; } updateCount(); if ((sum >> 32) != 0) { SqlMathUtil.throwOverflowException(); } } /** * Calculates absolute difference (remember that this is unsigned) of left and * right operator. <code>result := abs (left - right)</code> This is the core * implementation of subtract and signed add. * * @param left * left operand * @param right * right operand * @param result * the object to receive the result. can be same object as left or * right. * @return signum of the result. -1 if left was smaller than right, 1 if * larger, 0 if same (result is zero). */ public static byte difference(UnsignedInt128 left, UnsignedInt128 right, UnsignedInt128 result) { return differenceInternal(left, right.v, result); } /** * Calculates absolute difference of left and right operator after ten-based * scaling on right. * <code>result := abs (left - (right * 10**tenScale))</code> This is the core * implementation of subtract. * * @param left * left operand * @param right * right operand * @param result * the object to receive the result. can be same object as left or * right. * @param tenScale * number of ten-based scaling. could be either positive or negative. * @return signum of the result. -1 if left was smaller than right, 1 if * larger, 0 if same (result is zero). */ public static byte differenceScaleTen(UnsignedInt128 left, UnsignedInt128 right, UnsignedInt128 result, short tenScale) { if (tenScale == 0) { return difference(left, right, result); } // scale up/down int[] r = right.v.clone(); if (tenScale < 0) { // scale down scaleDownTenArray4RoundUp(r, (short) -tenScale); } else { // scale up boolean overflow = scaleUpTenArray(r, tenScale); if (overflow) { SqlMathUtil.throwOverflowException(); } } return differenceInternal(left, r, result); } /** * Multiplies this value with the given integer value. This version is * destructive, meaning it modifies this object. * * @param right * the value to multiply */ public void multiplyDestructive(int right) { if (right == 0) { zeroClear(); return; } else if (right == 1) { return; } long sum = 0L; long rightUnsigned = right & SqlMathUtil.LONG_MASK; for (int i = 0; i < INT_COUNT; ++i) { sum = (this.v[i] & SqlMathUtil.LONG_MASK) * rightUnsigned + (sum >>> 32); this.v[i] = (int) sum; } updateCount(); if ((sum >> 32) != 0) { SqlMathUtil.throwOverflowException(); } } /** * Multiplies this value with the given value. This version is destructive, * meaning it modifies this object. * * @param right * the value to multiply */ public void multiplyDestructive(UnsignedInt128 right) { if (this.fitsInt32() && right.fitsInt32()) { multiplyDestructiveFitsInt32(right, (short) 0, (short) 0); return; } multiplyArrays4And4To4NoOverflow(this.v, right.v); updateCount(); } /** * Multiplies this value with the given value, followed by right bit shifts to * scale it back to this object. This is used from division. This version is * destructive, meaning it modifies this object. * * @param right * the value to multiply * @param rightShifts * the number of right-shifts after multiplication */ public void multiplyShiftDestructive(UnsignedInt128 right, short rightShifts) { if (this.fitsInt32() && right.fitsInt32()) { multiplyDestructiveFitsInt32(right, rightShifts, (short) 0); return; } int[] z = multiplyArrays4And4To8(this.v, right.v); shiftRightArray(rightShifts, z, this.v, true); updateCount(); } /** * Multiply this value with the given value, followed by ten-based scale down. * This method does the two operations without cutting off, so it preserves * accuracy without throwing a wrong overflow exception. * * @param right * right operand * @param tenScale * distance to scale down */ public void multiplyScaleDownTenDestructive(UnsignedInt128 right, short tenScale) { assert (tenScale >= 0); if (this.fitsInt32() && right.fitsInt32()) { multiplyDestructiveFitsInt32(right, (short) 0, tenScale); return; } int[] z = multiplyArrays4And4To8(this.v, right.v); // Then, scale back. scaleDownTenArray8RoundUp(z, tenScale); update(z[0], z[1], z[2], z[3]); } /** * Divides this value with the given value. This version is destructive, * meaning it modifies this object. * * @param right * the value to divide * @param remainder * object to receive remainder */ public void divideDestructive(UnsignedInt128 right, UnsignedInt128 remainder) { if (right.isZero()) { assert (right.isZero()); SqlMathUtil.throwZeroDivisionException(); } if (right.count == 1) { assert (right.v[1] == 0); assert (right.v[2] == 0); assert (right.v[3] == 0); int rem = divideDestructive(right.v[0]); remainder.update(rem); return; } int[] quotient = new int[5]; int[] rem = SqlMathUtil.divideMultiPrecision(this.v, right.v, quotient); update(quotient[0], quotient[1], quotient[2], quotient[3]); remainder.update(rem[0], rem[1], rem[2], rem[3]); } /** * Scale up this object for 10**tenScale and then divides this value with the * given value. This version is destructive, meaning it modifies this object. * * @param right * the value to divide * @param tenScale * ten-based scale up distance * @param remainder * object to receive remainder */ public void divideScaleUpTenDestructive(UnsignedInt128 right, short tenScale, UnsignedInt128 remainder) { // in this case, we have to scale up _BEFORE_ division. otherwise we // might lose precision. if (tenScale > SqlMathUtil.MAX_POWER_TEN_INT128) { // in this case, the result will be surely more than 128 bit even // after division SqlMathUtil.throwOverflowException(); } int[] scaledUp = multiplyConstructive256(SqlMathUtil.POWER_TENS_INT128[tenScale]); int[] quotient = new int[5]; int[] rem = SqlMathUtil.divideMultiPrecision(scaledUp, right.v, quotient); update(quotient[0], quotient[1], quotient[2], quotient[3]); remainder.update(rem[0], rem[1], rem[2], rem[3]); } /** * Divides this value with the given value. This version is destructive, * meaning it modifies this object. * * @param right * the value to divide * @return remainder */ public int divideDestructive(int right) { assert (right >= 0); long rightUnsigned = right & SqlMathUtil.LONG_MASK; long quotient; long remainder = 0; for (int i = INT_COUNT - 1; i >= 0; --i) { remainder = ((this.v[i] & SqlMathUtil.LONG_MASK) + (remainder << 32)); quotient = remainder / rightUnsigned; remainder %= rightUnsigned; this.v[i] = (int) quotient; } updateCount(); return (int) remainder; } /** * Divides this value with the given value. This version is destructive, * meaning it modifies this object. * * @param right * the value to divide * @return remainder */ public long divideDestructive(long right) { assert (right >= 0); long quotient; long remainder = 0; for (int i = INT_COUNT - 1; i >= 0; --i) { remainder = ((this.v[i] & SqlMathUtil.LONG_MASK) + (remainder << 32)); quotient = remainder / right; remainder %= right; this.v[i] = (int) quotient; } updateCount(); return remainder; } /** * Right-shift for the given number of bits. This version is destructive, * meaning it modifies this object. * * @param bits * the number of bits. must be positive * @param roundUp * whether to round up the most significant bit that was discarded */ public void shiftRightDestructive(int bits, boolean roundUp) { assert (bits >= 0); shiftRightDestructive(bits / 32, bits % 32, roundUp); } /** * Left-shift for the given number of bits. This method does not throw an * error even if overflow happens. This version is destructive, meaning it * modifies this object. * * @param bits * the number of bits. must be positive */ public void shiftLeftDestructive(int bits) { assert (bits >= 0); shiftLeftDestructive(bits / 32, bits % 32); } /** * Left-shift for the given number of bits. This method throws an error even * if overflow happens. This version is destructive, meaning it modifies this * object. * * @param bits * the number of bits. must be positive */ public void shiftLeftDestructiveCheckOverflow(int bits) { if (bitLength() + bits >= 128) { SqlMathUtil.throwOverflowException(); } shiftLeftDestructive(bits); } /** * Scale down the value for 10**tenScale (this := this / 10**tenScale). This * method rounds-up, eg 44/10=4, 45/10=5. This version is destructive, meaning * it modifies this object. * * @param tenScale * scaling. must be positive */ public void scaleDownTenDestructive(short tenScale) { if (tenScale == 0) { return; } if (tenScale < 0) { throw new IllegalArgumentException(); } if (isZero()) { return; } scaleDownTenArray4RoundUp(v, tenScale); updateCount(); } /** * Scale down the value for 5**tenScale (this := this / 5**tenScale). This * method rounds-up, eg 42/5=8, 43/5=9. This version is destructive, meaning * it modifies this object. * * @param fiveScale * scaling. must be positive */ public void scaleDownFiveDestructive(short fiveScale) { if (fiveScale == 0) { return; } if (fiveScale < 0) { throw new IllegalArgumentException(); } if (isZero()) { return; } scaleDownFiveArrayRoundUp(v, fiveScale); updateCount(); } /** * Scale up the value for 10**tenScale (this := this * 10**tenScale). Scaling * up DOES throw an error when an overflow occurs. For example, 42.scaleUp(1) * = 420, 42.scaleUp(40) = ArithmeticException. This version is destructive, * meaning it modifies this object. * * @param tenScale * scaling. must be positive */ public void scaleUpTenDestructive(short tenScale) { if (tenScale == 0) { return; } if (tenScale < 0) { throw new IllegalArgumentException(); } if (isZero()) { return; } // First, scale up with 2. Check overflow shiftLeftDestructiveCheckOverflow(tenScale); // Then, scale up with 5 scaleUpFiveDestructive(tenScale); } /** * Scale up the value for 5**tenScale (this := this * 5**tenScale). Scaling up * DOES throw an error when an overflow occurs. This version is destructive, * meaning it modifies this object. * * @param fiveScale * scaling. must be positive */ public void scaleUpFiveDestructive(short fiveScale) { if (fiveScale == 0) { return; } if (fiveScale < 0) { throw new IllegalArgumentException(); } if (isZero()) { return; } // Scale up with 5. This is done via #multiplyDestructive(int). while (fiveScale > 0) { int powerFive = Math.min(fiveScale, SqlMathUtil.MAX_POWER_FIVE_INT31); multiplyDestructive(SqlMathUtil.POWER_FIVES_INT31[powerFive]); fiveScale -= powerFive; } } /** * This version returns the result as a new object, not modifying the give * objects. * * @param right * right operand * @return operation result as a new object */ public UnsignedInt128 addConstructive(UnsignedInt128 right) { UnsignedInt128 ret = new UnsignedInt128(this); ret.addDestructive(right); return ret; } /** * This version returns the result as a new object, not modifying the give * objects. * * @return operation result as a new object */ public UnsignedInt128 incrementConstructive() { UnsignedInt128 ret = new UnsignedInt128(this); ret.incrementDestructive(); return ret; } /** * This version returns the result as a new object, not modifying the give * objects. This method doesn't work if right operand is larger than this * value. * * @param right * right operand * @return operation result as a new object */ public UnsignedInt128 subtractConstructive(UnsignedInt128 right) { UnsignedInt128 ret = new UnsignedInt128(this); ret.subtractDestructive(right); return ret; } /** * This version returns the result as a new object, not modifying the give * objects. This method doesn't work if right operand is larger than this * value. * * @return operation result as a new object */ public UnsignedInt128 decrementConstructive() { UnsignedInt128 ret = new UnsignedInt128(this); ret.decrementDestructive(); return ret; } /** * This version returns the result as a new object, not modifying the give * objects. * * @param right * right operand * @return operation result as a new object */ public UnsignedInt128 multiplyConstructive(int right) { UnsignedInt128 ret = new UnsignedInt128(this); ret.multiplyDestructive(right); return ret; } /** * This version returns the result as a new object, not modifying the give * objects. * * @param right * right operand * @return operation result as a new object */ public UnsignedInt128 multiplyConstructive(UnsignedInt128 right) { UnsignedInt128 ret = new UnsignedInt128(this); ret.multiplyDestructive(right); return ret; } /** * This version returns the result of multiplication as 256bit data. * * @param right * right operand * @return operation result as 256bit data */ public int[] multiplyConstructive256(UnsignedInt128 right) { return multiplyArrays4And4To8(this.v, right.v); } /** * This version returns the result as a new object, not modifying the give * objects. Note that this method cannot receive remainder. Use destructive * version for it. * * @param right * right operand * @return operation result as a new object */ public UnsignedInt128 divideConstructive(int right) { UnsignedInt128 ret = new UnsignedInt128(this); ret.divideDestructive(right); return ret; } /** * This version returns the result as a new object, not modifying the give * objects. * * @param right * right operand * @param remainder * object to receive remainder * @return operation result as a new object */ public UnsignedInt128 divideConstructive(UnsignedInt128 right, UnsignedInt128 remainder) { UnsignedInt128 ret = new UnsignedInt128(this); ret.divideDestructive(right, remainder); return ret; } /** * This version returns the result as a new object, not modifying the give * objects. * * @param bits * the number of bits. must be positive * @param roundUp * whether to round up the most significant bit that was discarded * @return operation result as a new object */ public UnsignedInt128 shiftRightConstructive(int bits, boolean roundUp) { UnsignedInt128 ret = new UnsignedInt128(this); ret.shiftRightDestructive(bits, roundUp); return ret; } /** * This version returns the result as a new object, not modifying the give * objects. * * @param bits * the number of bits. must be positive * @return operation result as a new object */ public UnsignedInt128 shiftLeftConstructive(int bits) { UnsignedInt128 ret = new UnsignedInt128(this); ret.shiftLeftDestructive(bits); return ret; } private short bitLength() { return SqlMathUtil.bitLength(v[0], v[1], v[2], v[3]); } private void shiftRightDestructive(int wordShifts, int bitShiftsInWord, boolean roundUp) { if (wordShifts == 0 && bitShiftsInWord == 0) { return; } assert (wordShifts >= 0); assert (bitShiftsInWord >= 0); assert (bitShiftsInWord < 32); if (wordShifts >= 4) { zeroClear(); return; } final int shiftRestore = 32 - bitShiftsInWord; // check this because "123 << 32" will be 123. final boolean noRestore = bitShiftsInWord == 0; final int roundCarryNoRestoreMask = 1 << 31; final int roundCarryMask = (1 << (bitShiftsInWord - 1)); boolean roundCarry; int z0 = 0, z1 = 0, z2 = 0, z3 = 0; switch (wordShifts) { case 3: roundCarry = (noRestore ? (this.v[2] & roundCarryNoRestoreMask) : (this.v[3] & roundCarryMask)) != 0; z0 = this.v[3] >>> bitShiftsInWord; break; case 2: roundCarry = (noRestore ? (this.v[1] & roundCarryNoRestoreMask) : (this.v[2] & roundCarryMask)) != 0; z1 = this.v[3] >>> bitShiftsInWord; z0 = (noRestore ? 0 : this.v[3] << shiftRestore) | (this.v[2] >>> bitShiftsInWord); break; case 1: roundCarry = (noRestore ? (this.v[0] & roundCarryNoRestoreMask) : (this.v[1] & roundCarryMask)) != 0; z2 = this.v[3] >>> bitShiftsInWord; z1 = (noRestore ? 0 : this.v[3] << shiftRestore) | (this.v[2] >>> bitShiftsInWord); z0 = (noRestore ? 0 : this.v[2] << shiftRestore) | (this.v[1] >>> bitShiftsInWord); break; case 0: roundCarry = (noRestore ? 0 : (this.v[0] & roundCarryMask)) != 0; z3 = this.v[3] >>> bitShiftsInWord; z2 = (noRestore ? 0 : this.v[3] << shiftRestore) | (this.v[2] >>> bitShiftsInWord); z1 = (noRestore ? 0 : this.v[2] << shiftRestore) | (this.v[1] >>> bitShiftsInWord); z0 = (noRestore ? 0 : this.v[1] << shiftRestore) | (this.v[0] >>> bitShiftsInWord); break; default: assert (false); throw new RuntimeException(); } update(z0, z1, z2, z3); // round up if (roundUp && roundCarry) { incrementDestructive(); } } private void shiftLeftDestructive(int wordShifts, int bitShiftsInWord) { if (wordShifts == 0 && bitShiftsInWord == 0) { return; } assert (wordShifts >= 0); assert (bitShiftsInWord >= 0); assert (bitShiftsInWord < 32); if (wordShifts >= 4) { zeroClear(); return; } final int shiftRestore = 32 - bitShiftsInWord; // check this because "123 << 32" will be 123. final boolean noRestore = bitShiftsInWord == 0; int z0 = 0, z1 = 0, z2 = 0, z3 = 0; switch (wordShifts) { case 3: z3 = this.v[0] << bitShiftsInWord; break; case 2: z2 = (this.v[0] << bitShiftsInWord); z3 = (noRestore ? 0 : this.v[0] >>> shiftRestore) | this.v[1] << bitShiftsInWord; break; case 1: z1 = (this.v[0] << bitShiftsInWord); z2 = (noRestore ? 0 : this.v[0] >>> shiftRestore) | (this.v[1] << bitShiftsInWord); z3 = (noRestore ? 0 : this.v[1] >>> shiftRestore) | this.v[2] << bitShiftsInWord; break; case 0: z0 = (this.v[0] << bitShiftsInWord); z1 = (noRestore ? 0 : this.v[0] >>> shiftRestore) | (this.v[1] << bitShiftsInWord); z2 = (noRestore ? 0 : this.v[1] >>> shiftRestore) | (this.v[2] << bitShiftsInWord); z3 = (noRestore ? 0 : this.v[2] >>> shiftRestore) | this.v[3] << bitShiftsInWord; break; default: assert (false); } update(z0, z1, z2, z3); } /** * Multiplies this value with the given value. * * @param left * the value to multiply. in AND out. * @param right * the value to multiply. in */ private static void multiplyArrays4And4To4NoOverflow(int[] left, int[] right) { assert (left.length == 4); assert (right.length == 4); long product; product = (right[0] & SqlMathUtil.LONG_MASK) * (left[0] & SqlMathUtil.LONG_MASK); int z0 = (int) product; product = (right[0] & SqlMathUtil.LONG_MASK) * (left[1] & SqlMathUtil.LONG_MASK) + (right[1] & SqlMathUtil.LONG_MASK) * (left[0] & SqlMathUtil.LONG_MASK) + (product >>> 32); int z1 = (int) product; product = (right[0] & SqlMathUtil.LONG_MASK) * (left[2] & SqlMathUtil.LONG_MASK) + (right[1] & SqlMathUtil.LONG_MASK) * (left[1] & SqlMathUtil.LONG_MASK) + (right[2] & SqlMathUtil.LONG_MASK) * (left[0] & SqlMathUtil.LONG_MASK) + (product >>> 32); int z2 = (int) product; // v[3] product = (right[0] & SqlMathUtil.LONG_MASK) * (left[3] & SqlMathUtil.LONG_MASK) + (right[1] & SqlMathUtil.LONG_MASK) * (left[2] & SqlMathUtil.LONG_MASK) + (right[2] & SqlMathUtil.LONG_MASK) * (left[1] & SqlMathUtil.LONG_MASK) + (right[3] & SqlMathUtil.LONG_MASK) * (left[0] & SqlMathUtil.LONG_MASK) + (product >>> 32); int z3 = (int) product; if ((product >>> 32) != 0) { SqlMathUtil.throwOverflowException(); } // the combinations below definitely result in overflow if ((right[3] != 0 && (left[3] != 0 || left[2] != 0 || left[1] != 0)) || (right[2] != 0 && (left[3] != 0 || left[2] != 0)) || (right[1] != 0 && left[3] != 0)) { SqlMathUtil.throwOverflowException(); } left[0] = z0; left[1] = z1; left[2] = z2; left[3] = z3; } private static int[] multiplyArrays4And4To8(int[] left, int[] right) { assert (left.length == 4); assert (right.length == 4); long product; // this method could go beyond the integer ranges until we scale back // so, we need twice more variables. int[] z = new int[8]; product = (right[0] & SqlMathUtil.LONG_MASK) * (left[0] & SqlMathUtil.LONG_MASK); z[0] = (int) product; product = (right[0] & SqlMathUtil.LONG_MASK) * (left[1] & SqlMathUtil.LONG_MASK) + (right[1] & SqlMathUtil.LONG_MASK) * (left[0] & SqlMathUtil.LONG_MASK) + (product >>> 32); z[1] = (int) product; product = (right[0] & SqlMathUtil.LONG_MASK) * (left[2] & SqlMathUtil.LONG_MASK) + (right[1] & SqlMathUtil.LONG_MASK) * (left[1] & SqlMathUtil.LONG_MASK) + (right[2] & SqlMathUtil.LONG_MASK) * (left[0] & SqlMathUtil.LONG_MASK) + (product >>> 32); z[2] = (int) product; product = (right[0] & SqlMathUtil.LONG_MASK) * (left[3] & SqlMathUtil.LONG_MASK) + (right[1] & SqlMathUtil.LONG_MASK) * (left[2] & SqlMathUtil.LONG_MASK) + (right[2] & SqlMathUtil.LONG_MASK) * (left[1] & SqlMathUtil.LONG_MASK) + (right[3] & SqlMathUtil.LONG_MASK) * (left[0] & SqlMathUtil.LONG_MASK) + (product >>> 32); z[3] = (int) product; product = (right[1] & SqlMathUtil.LONG_MASK) * (left[3] & SqlMathUtil.LONG_MASK) + (right[2] & SqlMathUtil.LONG_MASK) * (left[2] & SqlMathUtil.LONG_MASK) + (right[3] & SqlMathUtil.LONG_MASK) * (left[1] & SqlMathUtil.LONG_MASK) + (product >>> 32); z[4] = (int) product; product = (right[2] & SqlMathUtil.LONG_MASK) * (left[3] & SqlMathUtil.LONG_MASK) + (right[3] & SqlMathUtil.LONG_MASK) * (left[2] & SqlMathUtil.LONG_MASK) + (product >>> 32); z[5] = (int) product; // v[1], v[0] product = (right[3] & SqlMathUtil.LONG_MASK) * (left[3] & SqlMathUtil.LONG_MASK) + (product >>> 32); z[6] = (int) product; z[7] = (int) (product >>> 32); return z; } private static void incrementArray(int[] array) { for (int i = 0; i < INT_COUNT; ++i) { if (array[i] != SqlMathUtil.FULLBITS_32) { array[i] = (int) ((array[i] & SqlMathUtil.LONG_MASK) + 1L); break; } array[i] = 0; if (i == INT_COUNT - 1) { SqlMathUtil.throwOverflowException(); } } } private static void decrementArray(int[] array) { for (int i = 0; i < INT_COUNT; ++i) { if (array[i] != 0) { array[i] = (int) ((array[i] & SqlMathUtil.LONG_MASK) - 1L); break; } array[i] = SqlMathUtil.FULLBITS_32; if (i == INT_COUNT - 1) { SqlMathUtil.throwOverflowException(); } } } /** common implementation of difference. */ private static byte differenceInternal(UnsignedInt128 left, int[] r, UnsignedInt128 result) { int cmp = left.compareTo(r); if (cmp == 0) { result.zeroClear(); return 0; } long sum = 0L; if (cmp > 0) { // left is larger for (int i = 0; i < INT_COUNT; ++i) { sum = (left.v[i] & SqlMathUtil.LONG_MASK) - (r[i] & SqlMathUtil.LONG_MASK) - ((int) -(sum >> 32)); result.v[i] = (int) sum; } } else { // right is larger for (int i = 0; i < INT_COUNT; ++i) { sum = (r[i] & SqlMathUtil.LONG_MASK) - (left.v[i] & SqlMathUtil.LONG_MASK) - ((int) -(sum >> 32)); result.v[i] = (int) sum; } } if ((sum >> 32) != 0) { SqlMathUtil.throwOverflowException(); } result.updateCount(); return cmp > 0 ? (byte) 1 : (byte) -1; } /** * @see #compareTo(UnsignedInt128) */ private static int compareTo(int l0, int l1, int l2, int l3, int r0, int r1, int r2, int r3) { if (l3 != r3) { return SqlMathUtil.compareUnsignedInt(l3, r3); } if (l2 != r2) { return SqlMathUtil.compareUnsignedInt(l2, r2); } if (l1 != r1) { return SqlMathUtil.compareUnsignedInt(l1, r1); } if (l0 != r0) { return SqlMathUtil.compareUnsignedInt(l0, r0); } return 0; } /** * @param array * @param tenScale * @return Whether it overflowed */ private static boolean scaleUpTenArray(int[] array, short tenScale) { while (tenScale > 0) { long sum = 0L; int powerTen = Math.min(tenScale, SqlMathUtil.MAX_POWER_TEN_INT31); tenScale -= powerTen; final long rightUnsigned = SqlMathUtil.POWER_TENS_INT31[powerTen] & SqlMathUtil.LONG_MASK; for (int i = 0; i < INT_COUNT; ++i) { sum = (array[i] & SqlMathUtil.LONG_MASK) * rightUnsigned + (sum >>> 32); array[i] = (int) sum; } if ((sum >> 32) != 0) { // overflow means this is smaller return true; } } return false; } /** * Scales down the given array (4 elements) for 10**tenScale. This method is * only used from add/subtract/compare, and most likely with smaller tenScale. * So, this method is not as optimized as * {@link #scaleDownTenArray8RoundUp(int[], short)}. * * @param array * array to scale down. in AND out. length must be 4. * @param tenScale * distance to scale down */ private static void scaleDownTenArray4RoundUp(int[] array, short tenScale) { scaleDownFiveArray(array, tenScale); shiftRightArray(tenScale, array, array, true); } /** * Scales down the given array (8 elements) for 10**tenScale. This method is * frequently called from multiply(), so highly optimized. It's lengthy, but * this is inevitable to avoid array/object creation. <h2>Summary of this * method</h2> * <p> * This method employs division by inverse multiplication (except easy cases). * because all inverses of powers of tens are pre-calculated, this is much * faster than doing divisions. The matrix multiplication benchmark hit 3x * performance with this. because the inverse is a little bit smaller than * real value, the result is same or smaller than accurate answer, not larger. * we take care of it after the first multiplication. * </p> * <p> * Then, multiply it with power of tens (which is accurate) to correct +1 * error and rounding up. let D = array - z * power: if D >= power/2, then * ++z. Otherwise, do nothing. * </p> * * @param array * array to scale down. in AND out. length must be 8. * @param tenScale * distance to scale down */ private static void scaleDownTenArray8RoundUp(int[] array, short tenScale) { assert (array.length == 8); if (tenScale > MAX_DIGITS) { // then definitely this will end up zero. Arrays.fill(array, 0); return; } if (tenScale <= SqlMathUtil.MAX_POWER_TEN_INT31) { int divisor = SqlMathUtil.POWER_TENS_INT31[tenScale]; assert (divisor > 0); boolean round = divideCheckRound(array, divisor); if (round) { incrementArray(array); } return; } // division by inverse multiplication. final int[] inverse = SqlMathUtil.INVERSE_POWER_TENS_INT128[tenScale].v; final int inverseWordShift = SqlMathUtil.INVERSE_POWER_TENS_INT128_WORD_SHIFTS[tenScale]; assert (inverseWordShift <= 3); assert (inverse[3] != 0); for (int i = 5 + inverseWordShift; i < 8; ++i) { if (array[i] != 0) { SqlMathUtil.throwOverflowException(); // because inverse[3] is // not zero } } int z4 = 0, z5 = 0, z6 = 0, z7 = 0; // because inverse is scaled 2^128, // these will become v0-v3 int z8 = 0, z9 = 0, z10 = 0; // for wordshift long product = 0L; product += (inverse[0] & SqlMathUtil.LONG_MASK) * (array[4] & SqlMathUtil.LONG_MASK) + (inverse[1] & SqlMathUtil.LONG_MASK) * (array[3] & SqlMathUtil.LONG_MASK) + (inverse[2] & SqlMathUtil.LONG_MASK) * (array[2] & SqlMathUtil.LONG_MASK) + (inverse[3] & SqlMathUtil.LONG_MASK) * (array[1] & SqlMathUtil.LONG_MASK); z4 = (int) product; product >>>= 32; product += (inverse[0] & SqlMathUtil.LONG_MASK) * (array[5] & SqlMathUtil.LONG_MASK) + (inverse[1] & SqlMathUtil.LONG_MASK) * (array[4] & SqlMathUtil.LONG_MASK) + (inverse[2] & SqlMathUtil.LONG_MASK) * (array[3] & SqlMathUtil.LONG_MASK) + (inverse[3] & SqlMathUtil.LONG_MASK) * (array[2] & SqlMathUtil.LONG_MASK); z5 = (int) product; product >>>= 32; product += (inverse[0] & SqlMathUtil.LONG_MASK) * (array[6] & SqlMathUtil.LONG_MASK) + (inverse[1] & SqlMathUtil.LONG_MASK) * (array[5] & SqlMathUtil.LONG_MASK) + (inverse[2] & SqlMathUtil.LONG_MASK) * (array[4] & SqlMathUtil.LONG_MASK) + (inverse[3] & SqlMathUtil.LONG_MASK) * (array[3] & SqlMathUtil.LONG_MASK); z6 = (int) product; product >>>= 32; product += (inverse[0] & SqlMathUtil.LONG_MASK) * (array[7] & SqlMathUtil.LONG_MASK) + (inverse[1] & SqlMathUtil.LONG_MASK) * (array[6] & SqlMathUtil.LONG_MASK) + (inverse[2] & SqlMathUtil.LONG_MASK) * (array[5] & SqlMathUtil.LONG_MASK) + (inverse[3] & SqlMathUtil.LONG_MASK) * (array[4] & SqlMathUtil.LONG_MASK); z7 = (int) product; product >>>= 32; if (inverseWordShift >= 1) { product += (inverse[1] & SqlMathUtil.LONG_MASK) * (array[7] & SqlMathUtil.LONG_MASK) + (inverse[2] & SqlMathUtil.LONG_MASK) * (array[6] & SqlMathUtil.LONG_MASK) + (inverse[3] & SqlMathUtil.LONG_MASK) * (array[5] & SqlMathUtil.LONG_MASK); z8 = (int) product; product >>>= 32; if (inverseWordShift >= 2) { product += (inverse[2] & SqlMathUtil.LONG_MASK) * (array[7] & SqlMathUtil.LONG_MASK) + (inverse[3] & SqlMathUtil.LONG_MASK) * (array[6] & SqlMathUtil.LONG_MASK); z9 = (int) product; product >>>= 32; if (inverseWordShift >= 3) { product += (inverse[3] & SqlMathUtil.LONG_MASK) * (array[7] & SqlMathUtil.LONG_MASK); z10 = (int) product; product >>>= 32; } } } if (product != 0) { SqlMathUtil.throwOverflowException(); } // if inverse is word-shifted for accuracy, shift it back here. switch (inverseWordShift) { case 1: z4 = z5; z5 = z6; z6 = z7; z7 = z8; break; case 2: z4 = z6; z5 = z7; z6 = z8; z7 = z9; break; case 3: z4 = z7; z5 = z8; z6 = z9; z7 = z10; break; default: break; } // now, correct +1 error and rounding up. final int[] power = SqlMathUtil.POWER_TENS_INT128[tenScale].v; final int[] half = SqlMathUtil.ROUND_POWER_TENS_INT128[tenScale].v; int d0, d1, d2, d3, d4; product = (array[0] & SqlMathUtil.LONG_MASK) - (power[0] & SqlMathUtil.LONG_MASK) * (z4 & SqlMathUtil.LONG_MASK); d0 = (int) product; product = (array[1] & SqlMathUtil.LONG_MASK) - (power[0] & SqlMathUtil.LONG_MASK) * (z5 & SqlMathUtil.LONG_MASK) - (power[1] & SqlMathUtil.LONG_MASK) * (z4 & SqlMathUtil.LONG_MASK) - ((int) -(product >> 32)); d1 = (int) product; product = (array[2] & SqlMathUtil.LONG_MASK) - (power[0] & SqlMathUtil.LONG_MASK) * (z6 & SqlMathUtil.LONG_MASK) - (power[1] & SqlMathUtil.LONG_MASK) * (z5 & SqlMathUtil.LONG_MASK) - (power[2] & SqlMathUtil.LONG_MASK) * (z4 & SqlMathUtil.LONG_MASK) - ((int) -(product >> 32)); d2 = (int) product; product = (array[3] & SqlMathUtil.LONG_MASK) - (power[0] & SqlMathUtil.LONG_MASK) * (z7 & SqlMathUtil.LONG_MASK) - (power[1] & SqlMathUtil.LONG_MASK) * (z6 & SqlMathUtil.LONG_MASK) - (power[2] & SqlMathUtil.LONG_MASK) * (z5 & SqlMathUtil.LONG_MASK) - (power[3] & SqlMathUtil.LONG_MASK) * (z4 & SqlMathUtil.LONG_MASK) - ((int) -(product >> 32)); d3 = (int) product; product = (array[4] & SqlMathUtil.LONG_MASK) - (power[1] & SqlMathUtil.LONG_MASK) * (z7 & SqlMathUtil.LONG_MASK) - (power[2] & SqlMathUtil.LONG_MASK) * (z6 & SqlMathUtil.LONG_MASK) - (power[3] & SqlMathUtil.LONG_MASK) * (z5 & SqlMathUtil.LONG_MASK) - ((int) -(product >> 32)); d4 = (int) product; // If the difference is larger than 2^128 (d4 != 0), then D is // definitely larger than power, so increment. // otherwise, compare it with power and half. boolean increment = (d4 != 0) || (compareTo(d0, d1, d2, d3, half[0], half[1], half[2], half[3]) >= 0); array[0] = z4; array[1] = z5; array[2] = z6; array[3] = z7; if (increment) { incrementArray(array); } } /** * Scales down the given array for 5**fiveScale. * * @param array * array to scale down * @param fiveScale * distance to scale down * @return Whether it requires incrementing if rounding */ private static boolean scaleDownFiveArray(int[] array, short fiveScale) { while (true) { int powerFive = Math.min(fiveScale, SqlMathUtil.MAX_POWER_FIVE_INT31); fiveScale -= powerFive; int divisor = SqlMathUtil.POWER_FIVES_INT31[powerFive]; assert (divisor > 0); if (fiveScale == 0) { return divideCheckRound(array, divisor); } else { divideCheckRound(array, divisor); } } } private static boolean divideCheckRound(int[] array, int divisor) { long remainder = 0; for (int i = array.length - 1; i >= 0; --i) { remainder = ((array[i] & SqlMathUtil.LONG_MASK) + (remainder << 32)); array[i] = (int) (remainder / divisor); remainder %= divisor; } return (remainder >= (divisor >> 1)); } private static void scaleDownFiveArrayRoundUp(int[] array, short tenScale) { boolean rounding = scaleDownFiveArray(array, tenScale); if (rounding) { incrementArray(array); } } /** * Internal method to apply the result of multiplication with right-shifting. * This method does round the value while right-shifting (SQL Numeric * semantics). * * @param rightShifts * distance of right-shifts */ private static void shiftRightArray(int rightShifts, int[] z, int[] result, boolean round) { assert (rightShifts >= 0); if (rightShifts == 0) { for (int i = 0; i < INT_COUNT; ++i) { if (z[i + INT_COUNT] != 0) { SqlMathUtil.throwOverflowException(); } } result[0] = z[0]; result[1] = z[1]; result[2] = z[2]; result[3] = z[3]; } else { final int wordShifts = rightShifts / 32; final int bitShiftsInWord = rightShifts % 32; final int shiftRestore = 32 - bitShiftsInWord; // check this because "123 << 32" will be 123. final boolean noRestore = bitShiftsInWord == 0; // overflow checks if (z.length > INT_COUNT) { if (wordShifts + INT_COUNT < z.length && (z[wordShifts + INT_COUNT] >>> bitShiftsInWord) != 0) { SqlMathUtil.throwOverflowException(); } for (int i = 1; i < INT_COUNT; ++i) { if (i + wordShifts < z.length - INT_COUNT && z[i + wordShifts + INT_COUNT] != 0) { SqlMathUtil.throwOverflowException(); } } } // check round-ups before settings values to result. // be aware that result could be the same object as z. boolean roundCarry = false; if (round) { if (bitShiftsInWord == 0) { assert (wordShifts > 0); roundCarry = z[wordShifts - 1] < 0; } else { roundCarry = (z[wordShifts] & (1 << (bitShiftsInWord - 1))) != 0; } } // extract the values. for (int i = 0; i < INT_COUNT; ++i) { int val = 0; if (!noRestore && i + wordShifts + 1 < z.length) { val = z[i + wordShifts + 1] << shiftRestore; } if (i + wordShifts < z.length) { val |= (z[i + wordShifts] >>> bitShiftsInWord); } result[i] = val; } if (roundCarry) { incrementArray(result); } } } /** * helper method for multiplication. used when either left/right fits int32. */ private void multiplyDestructiveFitsInt32(UnsignedInt128 right, short rightShifts, short tenScaleDown) { assert (this.fitsInt32() && right.fitsInt32()); assert (rightShifts == 0 || tenScaleDown == 0); // only one of them if (this.isZero()) { return; // zero. no need to shift/scale } else if (right.isZero()) { zeroClear(); return; // zero. no need to shift/scale } else if (this.isOne()) { this.update(right); } else { this.multiplyDestructive(right.v[0]); } if (rightShifts > 0) { this.shiftRightDestructive(rightShifts, true); } else if (tenScaleDown > 0) { this.scaleDownTenDestructive(tenScaleDown); } } /** Updates the value of {@link #count} by checking {@link #v}. */ private void updateCount() { if (v[3] != 0) { this.count = (byte) 4; } else if (v[2] != 0) { this.count = (byte) 3; } else if (v[1] != 0) { this.count = (byte) 2; } else if (v[0] != 0) { this.count = (byte) 1; } else { this.count = (byte) 0; } } /*(non-Javadoc) * Serializes one int part into the given @{link #ByteBuffer} * considering two's complement for negatives. */ private static void fastSerializeIntPartForHiveDecimal(ByteBuffer buf, int pos, int value, byte signum, boolean isFirstNonZero) { if (signum == -1 && value != 0) { value = (isFirstNonZero ? -value : ~value); } buf.putInt(pos, value); } /* (non-Javadoc) * Serializes this value into the format used by @{link #java.math.BigInteger} * This is used for fast assignment of a Decimal128 to a HiveDecimalWritable internal storage. * See OpenJDK BigInteger.toByteArray for a reference implementation. * @param scratch * @param signum * @return */ public int fastSerializeForHiveDecimal(Decimal128FastBuffer scratch, byte signum) { int bufferUsed = this.count; ByteBuffer buf = scratch.getByteBuffer(bufferUsed); buf.put(0, (byte) (signum == 1 ? 0 : signum)); int pos = 1; int firstNonZero = 0; while(firstNonZero < this.count && v[firstNonZero] == 0) { ++firstNonZero; } switch(this.count) { case 4: fastSerializeIntPartForHiveDecimal(buf, pos, v[3], signum, firstNonZero == 3); pos+=4; // intentional fall through case 3: fastSerializeIntPartForHiveDecimal(buf, pos, v[2], signum, firstNonZero == 2); pos+=4; // intentional fall through case 2: fastSerializeIntPartForHiveDecimal(buf, pos, v[1], signum, firstNonZero == 1); pos+=4; // intentional fall through case 1: fastSerializeIntPartForHiveDecimal(buf, pos, v[0], signum, true); } return bufferUsed; } /** * Updates this value from a serialized unscaled {@link java.math.BigInteger} representation. * This is used for fast update of a Decimal128 from a HiveDecimalWritable internal storage. * @param internalStorage * @return */ public byte fastUpdateFromInternalStorage(byte[] internalStorage) { byte signum = 0; int skip = 0; this.count = 0; // Skip over any leading 0s or 0xFFs byte firstByte = internalStorage[0]; if (firstByte == 0 || firstByte == -1) { while((skip < internalStorage.length) && (internalStorage[skip] == firstByte)) { ++skip; } } if (skip == internalStorage.length) { // The entire storage is 0x00s or 0xFFs // 0x00s means is 0 // 0xFFs means is -1 assert (firstByte == 0 || firstByte == -1); if (firstByte == -1) { signum = -1; this.count = 1; this.v[0] = 1; } else { signum = 0; } } else { // We skipped over leading 0x00s and 0xFFs // Important, signum is given by the firstByte, not by byte[keep]! signum = (firstByte < 0) ? (byte) -1 : (byte) 1; // Now we read the big-endian compacted two's complement int parts // Compacted means they are stripped of leading 0x00s and 0xFFs // This is why we do the intLength/pos tricks bellow // 'length' is all the bytes we have to read, after we skip 'skip' // 'pos' is where to start reading the current int // 'intLength' is how many bytes we read for the current int int length = internalStorage.length - skip; int pos = skip; int intLength = 0; switch(length) { case 16: ++intLength; //intentional fall through case 15: ++intLength; case 14: ++intLength; case 13: ++intLength; v[3] = fastUpdateIntFromInternalStorage(internalStorage, signum, pos, intLength); ++this.count; pos += intLength; intLength = 0; //intentional fall through case 12: ++intLength; //intentional fall through case 11: ++intLength; case 10: ++intLength; case 9: ++intLength; v[2] = fastUpdateIntFromInternalStorage(internalStorage, signum, pos, intLength); ++this.count; pos += intLength; intLength = 0; //intentional fall through case 8: ++intLength; //intentional fall through case 7: ++intLength; case 6: ++intLength; case 5: ++intLength; v[1] = fastUpdateIntFromInternalStorage(internalStorage, signum, pos, intLength); ++this.count; pos += intLength; intLength = 0; //intentional fall through case 4: ++intLength; //intentional fall through case 3: ++intLength; case 2: ++intLength; case 1: ++intLength; v[0] = fastUpdateIntFromInternalStorage(internalStorage, signum, pos, intLength); ++this.count; break; default: // This should not happen throw new RuntimeException("Impossible HiveDecimal internal storage length!"); } if (signum == -1) { // So far we've read the one's complement // add 1 to turn it into two's complement for(int i = 0; i < this.count; ++i) { if (v[i] != 0) { v[i] = (int)((v[i] & 0xFFFFFFFFL) + 1); if (v[i] != 0) { break; } } } } } return signum; } /** * reads one int part from the two's complement Big-Endian compacted representation, * starting from index pos * @param internalStorage {@link java.math.BigInteger} serialized representation * @param pos * @return */ private int fastUpdateIntFromInternalStorage(byte[] internalStorage, byte signum, int pos, int length) { // due to the way we use the allocation-free cast from HiveDecimalWriter to decimal128, // we do not have the luxury of a ByteBuffer... byte b0, b1, b2, b3; if (signum == -1) { b1=b2=b3 = (byte)-1; } else { b1=b2=b3=0; } switch(length) { case 4: b3 = internalStorage[pos]; ++pos; //intentional fall through case 3: b2 = internalStorage[pos]; ++pos; //intentional fall through case 2: b1 = internalStorage[pos]; ++pos; //intentional fall through case 1: b0 = internalStorage[pos]; break; default: // this should never happen throw new RuntimeException("Impossible HiveDecimal internal storage position!"); } int value = ((int)b0 & 0x000000FF) | (((int)b1 << 8) & 0x0000FF00) | (((int)b2 << 16) & 0x00FF0000) | (((int)b3 << 24) & 0xFF000000); if (signum == -1 && value != 0) { // Make one's complement, masked only for the bytes read int mask = -1 >>> (8*(4-length)); value = ~value & mask; } return value; } public int[] getV() { return v; } /** * This setter is only for de-serialization, should not be used otherwise. */ public void setV(int [] v) { this.v[0] = v[0]; this.v[1] = v[1]; this.v[2] = v[2]; this.v[3] = v[3]; updateCount(); } public byte getCount() { return count; } /** * This setter is only for de-serialization, should not be used otherwise. */ public void setCount(byte count) { this.count = count; } }