blob: a3763cd34f1a14beec8e1c3c91132ce384e19978 [file] [log] [blame]
/**
* 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.arrow.vector.util;
import io.netty.buffer.ArrowBuf;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.UnpooledByteBufAllocator;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.apache.arrow.vector.holders.Decimal38SparseHolder;
public class DecimalUtility extends CoreDecimalUtility{
public final static int MAX_DIGITS = 9;
public final static int DIGITS_BASE = 1000000000;
public final static int DIGITS_MAX = 999999999;
public final static int INTEGER_SIZE = (Integer.SIZE/8);
public final static String[] decimalToString = {"",
"0",
"00",
"000",
"0000",
"00000",
"000000",
"0000000",
"00000000",
"000000000"};
public final static long[] scale_long_constants = {
1,
10,
100,
1000,
10000,
100000,
1000000,
10000000,
100000000,
1000000000,
10000000000l,
100000000000l,
1000000000000l,
10000000000000l,
100000000000000l,
1000000000000000l,
10000000000000000l,
100000000000000000l,
1000000000000000000l};
/*
* Simple function that returns the static precomputed
* power of ten, instead of using Math.pow
*/
public static long getPowerOfTen(int power) {
assert power >= 0 && power < scale_long_constants.length;
return scale_long_constants[(power)];
}
/*
* Math.pow returns a double and while multiplying with large digits
* in the decimal data type we encounter noise. So instead of multiplying
* with Math.pow we use the static constants to perform the multiplication
*/
public static long adjustScaleMultiply(long input, int factor) {
int index = Math.abs(factor);
assert index >= 0 && index < scale_long_constants.length;
if (factor >= 0) {
return input * scale_long_constants[index];
} else {
return input / scale_long_constants[index];
}
}
public static long adjustScaleDivide(long input, int factor) {
int index = Math.abs(factor);
assert index >= 0 && index < scale_long_constants.length;
if (factor >= 0) {
return input / scale_long_constants[index];
} else {
return input * scale_long_constants[index];
}
}
/* Given the number of actual digits this function returns the
* number of indexes it will occupy in the array of integers
* which are stored in base 1 billion
*/
public static int roundUp(int ndigits) {
return (ndigits + MAX_DIGITS - 1)/MAX_DIGITS;
}
/* Returns a string representation of the given integer
* If the length of the given integer is less than the
* passed length, this function will prepend zeroes to the string
*/
public static StringBuilder toStringWithZeroes(int number, int desiredLength) {
String value = ((Integer) number).toString();
int length = value.length();
StringBuilder str = new StringBuilder();
str.append(decimalToString[desiredLength - length]);
str.append(value);
return str;
}
public static StringBuilder toStringWithZeroes(long number, int desiredLength) {
String value = ((Long) number).toString();
int length = value.length();
StringBuilder str = new StringBuilder();
// Desired length can be > MAX_DIGITS
int zeroesLength = desiredLength - length;
while (zeroesLength > MAX_DIGITS) {
str.append(decimalToString[MAX_DIGITS]);
zeroesLength -= MAX_DIGITS;
}
str.append(decimalToString[zeroesLength]);
str.append(value);
return str;
}
public static BigDecimal getBigDecimalFromIntermediate(ByteBuf data, int startIndex, int nDecimalDigits, int scale) {
// In the intermediate representation we don't pad the scale with zeroes, so set truncate = false
return getBigDecimalFromArrowBuf(data, startIndex, nDecimalDigits, scale, false);
}
public static BigDecimal getBigDecimalFromSparse(ArrowBuf data, int startIndex, int nDecimalDigits, int scale) {
// In the sparse representation we pad the scale with zeroes for ease of arithmetic, need to truncate
return getBigDecimalFromArrowBuf(data, startIndex, nDecimalDigits, scale, true);
}
public static BigDecimal getBigDecimalFromArrowBuf(ArrowBuf bytebuf, int start, int length, int scale) {
byte[] value = new byte[length];
bytebuf.getBytes(start, value, 0, length);
BigInteger unscaledValue = new BigInteger(value);
return new BigDecimal(unscaledValue, scale);
}
public static BigDecimal getBigDecimalFromByteBuffer(ByteBuffer bytebuf, int start, int length, int scale) {
byte[] value = new byte[length];
bytebuf.get(value);
BigInteger unscaledValue = new BigInteger(value);
return new BigDecimal(unscaledValue, scale);
}
/* Create a BigDecimal object using the data in the ArrowBuf.
* This function assumes that data is provided in a non-dense format
* It works on both sparse and intermediate representations.
*/
public static BigDecimal getBigDecimalFromArrowBuf(ByteBuf data, int startIndex, int nDecimalDigits, int scale,
boolean truncateScale) {
// For sparse decimal type we have padded zeroes at the end, strip them while converting to BigDecimal.
int actualDigits;
// Initialize the BigDecimal, first digit in the ArrowBuf has the sign so mask it out
BigInteger decimalDigits = BigInteger.valueOf((data.getInt(startIndex)) & 0x7FFFFFFF);
BigInteger base = BigInteger.valueOf(DIGITS_BASE);
for (int i = 1; i < nDecimalDigits; i++) {
BigInteger temp = BigInteger.valueOf(data.getInt(startIndex + (i * INTEGER_SIZE)));
decimalDigits = decimalDigits.multiply(base);
decimalDigits = decimalDigits.add(temp);
}
// Truncate any additional padding we might have added
if (truncateScale == true && scale > 0 && (actualDigits = scale % MAX_DIGITS) != 0) {
BigInteger truncate = BigInteger.valueOf((int)Math.pow(10, (MAX_DIGITS - actualDigits)));
decimalDigits = decimalDigits.divide(truncate);
}
// set the sign
if ((data.getInt(startIndex) & 0x80000000) != 0) {
decimalDigits = decimalDigits.negate();
}
BigDecimal decimal = new BigDecimal(decimalDigits, scale);
return decimal;
}
/* This function returns a BigDecimal object from the dense decimal representation.
* First step is to convert the dense representation into an intermediate representation
* and then invoke getBigDecimalFromArrowBuf() to get the BigDecimal object
*/
public static BigDecimal getBigDecimalFromDense(ArrowBuf data, int startIndex, int nDecimalDigits, int scale, int maxPrecision, int width) {
/* This method converts the dense representation to
* an intermediate representation. The intermediate
* representation has one more integer than the dense
* representation.
*/
byte[] intermediateBytes = new byte[((nDecimalDigits + 1) * INTEGER_SIZE)];
// Start storing from the least significant byte of the first integer
int intermediateIndex = 3;
int[] mask = {0x03, 0x0F, 0x3F, 0xFF};
int[] reverseMask = {0xFC, 0xF0, 0xC0, 0x00};
int maskIndex;
int shiftOrder;
byte shiftBits;
// TODO: Some of the logic here is common with casting from Dense to Sparse types, factor out common code
if (maxPrecision == 38) {
maskIndex = 0;
shiftOrder = 6;
shiftBits = 0x00;
intermediateBytes[intermediateIndex++] = (byte) (data.getByte(startIndex) & 0x7F);
} else if (maxPrecision == 28) {
maskIndex = 1;
shiftOrder = 4;
shiftBits = (byte) ((data.getByte(startIndex) & 0x03) << shiftOrder);
intermediateBytes[intermediateIndex++] = (byte) (((data.getByte(startIndex) & 0x3C) & 0xFF) >>> 2);
} else {
throw new UnsupportedOperationException("Dense types with max precision 38 and 28 are only supported");
}
int inputIndex = 1;
boolean sign = false;
if ((data.getByte(startIndex) & 0x80) != 0) {
sign = true;
}
while (inputIndex < width) {
intermediateBytes[intermediateIndex] = (byte) ((shiftBits) | (((data.getByte(startIndex + inputIndex) & reverseMask[maskIndex]) & 0xFF) >>> (8 - shiftOrder)));
shiftBits = (byte) ((data.getByte(startIndex + inputIndex) & mask[maskIndex]) << shiftOrder);
inputIndex++;
intermediateIndex++;
if (((inputIndex - 1) % INTEGER_SIZE) == 0) {
shiftBits = (byte) ((shiftBits & 0xFF) >>> 2);
maskIndex++;
shiftOrder -= 2;
}
}
/* copy the last byte */
intermediateBytes[intermediateIndex] = shiftBits;
if (sign == true) {
intermediateBytes[0] = (byte) (intermediateBytes[0] | 0x80);
}
final ByteBuf intermediate = UnpooledByteBufAllocator.DEFAULT.buffer(intermediateBytes.length);
try {
intermediate.setBytes(0, intermediateBytes);
BigDecimal ret = getBigDecimalFromIntermediate(intermediate, 0, nDecimalDigits + 1, scale);
return ret;
} finally {
intermediate.release();
}
}
/*
* Function converts the BigDecimal and stores it in out internal sparse representation
*/
public static void getSparseFromBigDecimal(BigDecimal input, ByteBuf data, int startIndex, int scale, int precision,
int nDecimalDigits) {
// Initialize the buffer
for (int i = 0; i < nDecimalDigits; i++) {
data.setInt(startIndex + (i * INTEGER_SIZE), 0);
}
boolean sign = false;
if (input.signum() == -1) {
// negative input
sign = true;
input = input.abs();
}
// Truncate the input as per the scale provided
input = input.setScale(scale, BigDecimal.ROUND_HALF_UP);
// Separate out the integer part
BigDecimal integerPart = input.setScale(0, BigDecimal.ROUND_DOWN);
int destIndex = nDecimalDigits - roundUp(scale) - 1;
// we use base 1 billion integer digits for out integernal representation
BigDecimal base = new BigDecimal(DIGITS_BASE);
while (integerPart.compareTo(BigDecimal.ZERO) == 1) {
// store the modulo as the integer value
data.setInt(startIndex + (destIndex * INTEGER_SIZE), (integerPart.remainder(base)).intValue());
destIndex--;
// Divide by base 1 billion
integerPart = (integerPart.divide(base)).setScale(0, BigDecimal.ROUND_DOWN);
}
/* Sparse representation contains padding of additional zeroes
* so each digit contains MAX_DIGITS for ease of arithmetic
*/
int actualDigits;
if ((actualDigits = (scale % MAX_DIGITS)) != 0) {
// Pad additional zeroes
scale = scale + (MAX_DIGITS - actualDigits);
input = input.setScale(scale, BigDecimal.ROUND_DOWN);
}
//separate out the fractional part
BigDecimal fractionalPart = input.remainder(BigDecimal.ONE).movePointRight(scale);
destIndex = nDecimalDigits - 1;
while (scale > 0) {
// Get next set of MAX_DIGITS (9) store it in the ArrowBuf
fractionalPart = fractionalPart.movePointLeft(MAX_DIGITS);
BigDecimal temp = fractionalPart.remainder(BigDecimal.ONE);
data.setInt(startIndex + (destIndex * INTEGER_SIZE), (temp.unscaledValue().intValue()));
destIndex--;
fractionalPart = fractionalPart.setScale(0, BigDecimal.ROUND_DOWN);
scale -= MAX_DIGITS;
}
// Set the negative sign
if (sign == true) {
data.setInt(startIndex, data.getInt(startIndex) | 0x80000000);
}
}
public static long getDecimal18FromBigDecimal(BigDecimal input, int scale, int precision) {
// Truncate or pad to set the input to the correct scale
input = input.setScale(scale, BigDecimal.ROUND_HALF_UP);
return (input.unscaledValue().longValue());
}
public static BigDecimal getBigDecimalFromPrimitiveTypes(int input, int scale, int precision) {
return BigDecimal.valueOf(input, scale);
}
public static BigDecimal getBigDecimalFromPrimitiveTypes(long input, int scale, int precision) {
return BigDecimal.valueOf(input, scale);
}
public static int compareDenseBytes(ArrowBuf left, int leftStart, boolean leftSign, ArrowBuf right, int rightStart, boolean rightSign, int width) {
int invert = 1;
/* If signs are different then simply look at the
* sign of the two inputs and determine which is greater
*/
if (leftSign != rightSign) {
return((leftSign == true) ? -1 : 1);
} else if(leftSign == true) {
/* Both inputs are negative, at the end we will
* have to invert the comparison
*/
invert = -1;
}
int cmp = 0;
for (int i = 0; i < width; i++) {
byte leftByte = left.getByte(leftStart + i);
byte rightByte = right.getByte(rightStart + i);
// Unsigned byte comparison
if ((leftByte & 0xFF) > (rightByte & 0xFF)) {
cmp = 1;
break;
} else if ((leftByte & 0xFF) < (rightByte & 0xFF)) {
cmp = -1;
break;
}
}
cmp *= invert; // invert the comparison if both were negative values
return cmp;
}
public static int getIntegerFromSparseBuffer(ArrowBuf buffer, int start, int index) {
int value = buffer.getInt(start + (index * 4));
if (index == 0) {
/* the first byte contains sign bit, return value without it */
value = (value & 0x7FFFFFFF);
}
return value;
}
public static void setInteger(ArrowBuf buffer, int start, int index, int value) {
buffer.setInt(start + (index * 4), value);
}
public static int compareSparseBytes(ArrowBuf left, int leftStart, boolean leftSign, int leftScale, int leftPrecision, ArrowBuf right, int rightStart, boolean rightSign, int rightPrecision, int rightScale, int width, int nDecimalDigits, boolean absCompare) {
int invert = 1;
if (absCompare == false) {
if (leftSign != rightSign) {
return (leftSign == true) ? -1 : 1;
}
// Both values are negative invert the outcome of the comparison
if (leftSign == true) {
invert = -1;
}
}
int cmp = compareSparseBytesInner(left, leftStart, leftSign, leftScale, leftPrecision, right, rightStart, rightSign, rightPrecision, rightScale, width, nDecimalDigits);
return cmp * invert;
}
public static int compareSparseBytesInner(ArrowBuf left, int leftStart, boolean leftSign, int leftScale, int leftPrecision, ArrowBuf right, int rightStart, boolean rightSign, int rightPrecision, int rightScale, int width, int nDecimalDigits) {
/* compute the number of integer digits in each decimal */
int leftInt = leftPrecision - leftScale;
int rightInt = rightPrecision - rightScale;
/* compute the number of indexes required for storing integer digits */
int leftIntRoundedUp = org.apache.arrow.vector.util.DecimalUtility.roundUp(leftInt);
int rightIntRoundedUp = org.apache.arrow.vector.util.DecimalUtility.roundUp(rightInt);
/* compute number of indexes required for storing scale */
int leftScaleRoundedUp = org.apache.arrow.vector.util.DecimalUtility.roundUp(leftScale);
int rightScaleRoundedUp = org.apache.arrow.vector.util.DecimalUtility.roundUp(rightScale);
/* compute index of the most significant integer digits */
int leftIndex1 = nDecimalDigits - leftScaleRoundedUp - leftIntRoundedUp;
int rightIndex1 = nDecimalDigits - rightScaleRoundedUp - rightIntRoundedUp;
int leftStopIndex = nDecimalDigits - leftScaleRoundedUp;
int rightStopIndex = nDecimalDigits - rightScaleRoundedUp;
/* Discard the zeroes in the integer part */
while (leftIndex1 < leftStopIndex) {
if (getIntegerFromSparseBuffer(left, leftStart, leftIndex1) != 0) {
break;
}
/* Digit in this location is zero, decrement the actual number
* of integer digits
*/
leftIntRoundedUp--;
leftIndex1++;
}
/* If we reached the stop index then the number of integers is zero */
if (leftIndex1 == leftStopIndex) {
leftIntRoundedUp = 0;
}
while (rightIndex1 < rightStopIndex) {
if (getIntegerFromSparseBuffer(right, rightStart, rightIndex1) != 0) {
break;
}
/* Digit in this location is zero, decrement the actual number
* of integer digits
*/
rightIntRoundedUp--;
rightIndex1++;
}
if (rightIndex1 == rightStopIndex) {
rightIntRoundedUp = 0;
}
/* We have the accurate number of non-zero integer digits,
* if the number of integer digits are different then we can determine
* which decimal is larger and needn't go down to comparing individual values
*/
if (leftIntRoundedUp > rightIntRoundedUp) {
return 1;
}
else if (rightIntRoundedUp > leftIntRoundedUp) {
return -1;
}
/* The number of integer digits are the same, set the each index
* to the first non-zero integer and compare each digit
*/
leftIndex1 = nDecimalDigits - leftScaleRoundedUp - leftIntRoundedUp;
rightIndex1 = nDecimalDigits - rightScaleRoundedUp - rightIntRoundedUp;
while (leftIndex1 < leftStopIndex && rightIndex1 < rightStopIndex) {
if (getIntegerFromSparseBuffer(left, leftStart, leftIndex1) > getIntegerFromSparseBuffer(right, rightStart, rightIndex1)) {
return 1;
}
else if (getIntegerFromSparseBuffer(right, rightStart, rightIndex1) > getIntegerFromSparseBuffer(left, leftStart, leftIndex1)) {
return -1;
}
leftIndex1++;
rightIndex1++;
}
/* The integer part of both the decimal's are equal, now compare
* each individual fractional part. Set the index to be at the
* beginning of the fractional part
*/
leftIndex1 = leftStopIndex;
rightIndex1 = rightStopIndex;
/* Stop indexes will be the end of the array */
leftStopIndex = nDecimalDigits;
rightStopIndex = nDecimalDigits;
/* compare the two fractional parts of the decimal */
while (leftIndex1 < leftStopIndex && rightIndex1 < rightStopIndex) {
if (getIntegerFromSparseBuffer(left, leftStart, leftIndex1) > getIntegerFromSparseBuffer(right, rightStart, rightIndex1)) {
return 1;
}
else if (getIntegerFromSparseBuffer(right, rightStart, rightIndex1) > getIntegerFromSparseBuffer(left, leftStart, leftIndex1)) {
return -1;
}
leftIndex1++;
rightIndex1++;
}
/* Till now the fractional part of the decimals are equal, check
* if one of the decimal has fractional part that is remaining
* and is non-zero
*/
while (leftIndex1 < leftStopIndex) {
if (getIntegerFromSparseBuffer(left, leftStart, leftIndex1) != 0) {
return 1;
}
leftIndex1++;
}
while(rightIndex1 < rightStopIndex) {
if (getIntegerFromSparseBuffer(right, rightStart, rightIndex1) != 0) {
return -1;
}
rightIndex1++;
}
/* Both decimal values are equal */
return 0;
}
public static BigDecimal getBigDecimalFromByteArray(byte[] bytes, int start, int length, int scale) {
byte[] value = Arrays.copyOfRange(bytes, start, start + length);
BigInteger unscaledValue = new BigInteger(value);
return new BigDecimal(unscaledValue, scale);
}
public static void roundDecimal(ArrowBuf result, int start, int nDecimalDigits, int desiredScale, int currentScale) {
int newScaleRoundedUp = org.apache.arrow.vector.util.DecimalUtility.roundUp(desiredScale);
int origScaleRoundedUp = org.apache.arrow.vector.util.DecimalUtility.roundUp(currentScale);
if (desiredScale < currentScale) {
boolean roundUp = false;
//Extract the first digit to be truncated to check if we need to round up
int truncatedScaleIndex = desiredScale + 1;
if (truncatedScaleIndex <= currentScale) {
int extractDigitIndex = nDecimalDigits - origScaleRoundedUp -1;
extractDigitIndex += org.apache.arrow.vector.util.DecimalUtility.roundUp(truncatedScaleIndex);
int extractDigit = getIntegerFromSparseBuffer(result, start, extractDigitIndex);
int temp = org.apache.arrow.vector.util.DecimalUtility.MAX_DIGITS - (truncatedScaleIndex % org.apache.arrow.vector.util.DecimalUtility.MAX_DIGITS);
if (temp != 0) {
extractDigit = extractDigit / (int) (Math.pow(10, temp));
}
if ((extractDigit % 10) > 4) {
roundUp = true;
}
}
// Get the source index beyond which we will truncate
int srcIntIndex = nDecimalDigits - origScaleRoundedUp - 1;
int srcIndex = srcIntIndex + newScaleRoundedUp;
// Truncate the remaining fractional part, move the integer part
int destIndex = nDecimalDigits - 1;
if (srcIndex != destIndex) {
while (srcIndex >= 0) {
setInteger(result, start, destIndex--, getIntegerFromSparseBuffer(result, start, srcIndex--));
}
// Set the remaining portion of the decimal to be zeroes
while (destIndex >= 0) {
setInteger(result, start, destIndex--, 0);
}
srcIndex = nDecimalDigits - 1;
}
// We truncated the decimal digit. Now we need to truncate within the base 1 billion fractional digit
int truncateFactor = org.apache.arrow.vector.util.DecimalUtility.MAX_DIGITS - (desiredScale % org.apache.arrow.vector.util.DecimalUtility.MAX_DIGITS);
if (truncateFactor != org.apache.arrow.vector.util.DecimalUtility.MAX_DIGITS) {
truncateFactor = (int) Math.pow(10, truncateFactor);
int fractionalDigits = getIntegerFromSparseBuffer(result, start, nDecimalDigits - 1);
fractionalDigits /= truncateFactor;
setInteger(result, start, nDecimalDigits - 1, fractionalDigits * truncateFactor);
}
// Finally round up the digit if needed
if (roundUp == true) {
srcIndex = nDecimalDigits - 1;
int carry;
if (truncateFactor != org.apache.arrow.vector.util.DecimalUtility.MAX_DIGITS) {
carry = truncateFactor;
} else {
carry = 1;
}
while (srcIndex >= 0) {
int value = getIntegerFromSparseBuffer(result, start, srcIndex);
value += carry;
if (value >= org.apache.arrow.vector.util.DecimalUtility.DIGITS_BASE) {
setInteger(result, start, srcIndex--, value % org.apache.arrow.vector.util.DecimalUtility.DIGITS_BASE);
carry = value / org.apache.arrow.vector.util.DecimalUtility.DIGITS_BASE;
} else {
setInteger(result, start, srcIndex--, value);
carry = 0;
break;
}
}
}
} else if (desiredScale > currentScale) {
// Add fractional digits to the decimal
// Check if we need to shift the decimal digits to the left
if (newScaleRoundedUp > origScaleRoundedUp) {
int srcIndex = 0;
int destIndex = newScaleRoundedUp - origScaleRoundedUp;
// Check while extending scale, we are not overwriting integer part
while (srcIndex < destIndex) {
if (getIntegerFromSparseBuffer(result, start, srcIndex++) != 0) {
throw new RuntimeException("Truncate resulting in loss of integer part, reduce scale specified");
}
}
srcIndex = 0;
while (destIndex < nDecimalDigits) {
setInteger(result, start, srcIndex++, getIntegerFromSparseBuffer(result, start, destIndex++));
}
// Clear the remaining part
while (srcIndex < nDecimalDigits) {
setInteger(result, start, srcIndex++, 0);
}
}
}
}
public static int getFirstFractionalDigit(int decimal, int scale) {
if (scale == 0) {
return 0;
}
int temp = (int) adjustScaleDivide(decimal, scale - 1);
return Math.abs(temp % 10);
}
public static int getFirstFractionalDigit(long decimal, int scale) {
if (scale == 0) {
return 0;
}
long temp = adjustScaleDivide(decimal, scale - 1);
return (int) (Math.abs(temp % 10));
}
public static int getFirstFractionalDigit(ArrowBuf data, int scale, int start, int nDecimalDigits) {
if (scale == 0) {
return 0;
}
int index = nDecimalDigits - roundUp(scale);
return (int) (adjustScaleDivide(data.getInt(start + (index * INTEGER_SIZE)), MAX_DIGITS - 1));
}
public static int compareSparseSamePrecScale(ArrowBuf left, int lStart, byte[] right, int length) {
// check the sign first
boolean lSign = (left.getInt(lStart) & 0x80000000) != 0;
boolean rSign = ByteFunctionHelpers.getSign(right);
int cmp = 0;
if (lSign != rSign) {
return (lSign == false) ? 1 : -1;
}
// invert the comparison if we are comparing negative numbers
int invert = (lSign == true) ? -1 : 1;
// compare byte by byte
int n = 0;
int lPos = lStart;
int rPos = 0;
while (n < length/4) {
int leftInt = Decimal38SparseHolder.getInteger(n, lStart, left);
int rightInt = ByteFunctionHelpers.getInteger(right, n);
if (leftInt != rightInt) {
cmp = (leftInt - rightInt ) > 0 ? 1 : -1;
break;
}
n++;
}
return cmp * invert;
}
}