Skip to content

Commit

Permalink
fix: fix testcase literals type to have the requested type parameters (
Browse files Browse the repository at this point in the history
  • Loading branch information
scgkiran authored Jan 27, 2025
1 parent 1aaa0fa commit d6e63d9
Show file tree
Hide file tree
Showing 13 changed files with 691 additions and 68 deletions.
83 changes: 83 additions & 0 deletions expr/decimal_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,86 @@ func decimalBytesToString(decimalBytes [16]byte, scale int32) string {
apdBigInt := new(apd.BigInt).SetMathBigInt(intValue)
return apd.NewWithBigInt(apdBigInt, -scale).String()
}

func modifyDecimalPrecisionAndScale(decimalBytes [16]byte, scale, targetPrecision, targetScale int32) ([16]byte, int32, int32, error) {
var result [16]byte
if targetPrecision > 38 {
return result, 0, 0, fmt.Errorf("target precision %d exceeds maximum allowed precision of 38", targetPrecision)
}

isNegative := decimalBytes[15]&0x80 != 0

// Reverse the byte array to convert from little-endian to big-endian.
processingValue := make([]byte, 16)
for i := 0; i < 16; i++ {
processingValue[i] = decimalBytes[15-i]
}
if isNegative {
negate(processingValue[:])
}

// Convert the bytes into a big.Int and wrap it into an apd.Decimal.
intValue := new(big.Int).SetBytes(processingValue[:])
apdBigInt := new(apd.BigInt).SetMathBigInt(intValue)
dec := apd.NewWithBigInt(apdBigInt, -scale)

// Normalize the decimal by removing trailing zeros.
dec.Reduce(dec)

// Adjust the scale to the target scale
ctx := apd.BaseContext.WithPrecision(uint32(targetPrecision))
_, err := ctx.Quantize(dec, dec, -targetScale)
if err != nil {
return result, 0, 0, fmt.Errorf("error adjusting scale: %v", err)
}

err2 := validatePrecisionAndScale(dec, targetPrecision, targetScale)
if err2 != nil {
return result, 0, 0, err2
}

// Convert the adjusted decimal coefficient to a byte array.
byteArray := dec.Coeff.Bytes()
if len(byteArray) > 16 {
return result, 0, 0, fmt.Errorf("number exceeds 16 bytes")
}
copy(result[16-len(byteArray):], byteArray)

// Handle the sign by applying two's complement for negative numbers.
if isNegative {
negate(result[:])
}

// Reverse the byte array back to little-endian.
for i, j := 0, 15; i < j; i, j = i+1, j-1 {
result[i], result[j] = result[j], result[i]
}

return result, targetPrecision, targetScale, nil
}

func validatePrecisionAndScale(dec *apd.Decimal, targetPrecision int32, targetScale int32) error {
// Validate the minimum precision and scale.
minPrecision, minScale := getMinimumPrecisionAndScale(dec)
if targetPrecision < minPrecision {
return fmt.Errorf("number %s exceeds target precision %d, minimum precision needed is %d with target scale %d", dec.String(), targetPrecision, minPrecision, targetScale)
}
if targetScale < minScale {
return fmt.Errorf("number %v exceeds target scale %d, minimum scale needed is %d", dec.String(), targetScale, minScale)
}
if targetPrecision-targetScale < minPrecision-minScale {
return fmt.Errorf("number %v exceeds target precision %d with target scale %d, minimum precision needed is %d with minimum scale %d", dec.String(), targetPrecision, targetScale, minPrecision, minScale)
}
return nil
}

func getMinimumPrecisionAndScale(dec *apd.Decimal) (precision int32, scale int32) {
if dec.Exponent > 0 {
precision = int32(apd.NumDigits(&dec.Coeff)) + dec.Exponent
scale = 0
} else {
scale = -dec.Exponent
precision = max(int32(apd.NumDigits(&dec.Coeff)), scale+1)
}
return precision, scale
}
113 changes: 105 additions & 8 deletions expr/decimal_util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestDecimalStringToBytes(t *testing.T) {
Expand All @@ -21,10 +22,18 @@ func TestDecimalStringToBytes(t *testing.T) {
{"-12345", "c7cfffffffffffffffffffffffffffff", 5, 0, ""},
{"123.45", "39300000000000000000000000000000", 5, 2, ""},
{"-123.45", "c7cfffffffffffffffffffffffffffff", 5, 2, ""},
{"1", "01000000000000000000000000000000", 1, 0, ""},
{"-1.0", "f6ffffffffffffffffffffffffffffff", 2, 1, ""},
{"-1.00", "9cffffffffffffffffffffffffffffff", 3, 2, ""},
{"-1.000", "18fcffffffffffffffffffffffffffff", 4, 3, ""},
{"12345.6789", "15cd5b07000000000000000000000000", 9, 4, ""},
{"12345.67890000", "5004fb711f0100000000000000000000", 13, 8, ""},
{"0.123", "7b000000000000000000000000000000", 4, 3, ""},
{"-0.123", "85ffffffffffffffffffffffffffffff", 4, 3, ""},
{"9223372036854775807", "ffffffffffffff7f0000000000000000", 19, 0, ""}, // Max int64
{"-9223372036854775808", "0000000000000080ffffffffffffffff", 19, 0, ""}, // Min int64
{"9223372036854775807.0000", "f0d8ffffffffffff8713000000000000", 23, 4, ""},
{"-9223372036854775808.00", "0000000000000000ceffffffffffffff", 21, 2, ""},
{"99999999999999999999999999999999999999", "ffffffff3f228a097ac4865aa84c3b4b", 38, 0, ""},
{"+99999999999999999999999999999999999999", "ffffffff3f228a097ac4865aa84c3b4b", 38, 0, ""},
{"-99999999999999999999999999999999999999", "01000000c0dd75f6853b79a557b3c4b4", 38, 0, ""},
Expand All @@ -48,25 +57,47 @@ func TestDecimalStringToBytes(t *testing.T) {
{"1.23e20", "00000c6d51c8f7aa0600000000000000", 21, 0, "123000000000000000000"},
{"1.23e35", "00000000cebde644bc05f0425eb01700", 36, 0, "123000000000000000000000000000000000"},
{"1.23E35", "00000000cebde644bc05f0425eb01700", 36, 0, "123000000000000000000000000000000000"},
{"-123456789012345678901234567890.1234", "0e5069812fa37d21cd68009021c3ffff", 34, 4, ""},
}

for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
got, precision, scale, err := DecimalStringToBytes(tt.input)
assert.NoError(t, err)
require.NoError(t, err)
assert.Len(t, got, 16)
assert.Equal(t, hexToBytes(t, tt.hexWant), got[:])
assert.Equal(t, tt.expPrecision, precision)
assert.Equal(t, tt.expScale, scale)
if err == nil {
// verify that the conversion is correct
decStr := decimalBytesToString(got, scale)
if tt.expected == "" {
tt.expected = strings.TrimPrefix(tt.input, "+")
}
assert.Equal(t, tt.expected, decStr)

// verify that the conversion is correct
decStr := decimalBytesToString(got, scale)
if tt.expected == "" {
tt.expected = strings.TrimPrefix(tt.input, "+")
}
assert.Equal(t, tt.expected, decStr)

// test modifyDecimalPrecisionAndScale
targetPrecision := min(precision+2, 38)
targetScale := scale
if precision <= 36 {
targetScale = min(scale+2, targetPrecision)
}
newBytes, newPrecision, newScale, err := modifyDecimalPrecisionAndScale(got, scale, targetPrecision, targetScale)
require.NoError(t, err)
assert.Equal(t, targetPrecision, newPrecision)
decStr = decimalBytesToString(newBytes, newScale)
if tt.expected != decStr {
require.True(t, strings.HasPrefix(decStr, tt.expected))
suffix := decStr[len(tt.expected):]
assert.LessOrEqual(t, len(suffix), 3)
assert.NotEqual(t, 1, len(suffix))
switch len(suffix) {
case 2:
assert.Equal(t, "00", suffix)
case 3:
assert.Equal(t, ".00", suffix)
}
}
})
}
}
Expand Down Expand Up @@ -143,3 +174,69 @@ func TestDecimalBytesToString(t *testing.T) {
})
}
}

func TestModifyDecimalPrecisionAndScale(t *testing.T) {
tests := []struct {
input string
hexInput string
inputPrecision int32
inputScale int32
targetPrecision int32
targetScale int32
hexWant2 string
expected string
expectError bool
}{
{"12345", "39300000000000000000000000000000", 5, 0, 10, 0, "", "", false},
{"12345", "39300000000000000000000000000000", 5, 0, 20, 2, "44D61200000000000000000000000000", "12345.00", false},
{"12345.00", "44D61200000000000000000000000000", 20, 2, 5, 2, "", "12345.00", true},
{"12345.00", "44D61200000000000000000000000000", 20, 2, 5, 0, "39300000000000000000000000000000", "12345", false},
{"12345.6789", "15cd5b07000000000000000000000000", 9, 4, 12, 8, "15cd5b07000000000000000000000000", "12345.67890000", true},
{"12345.6789", "15cd5b07000000000000000000000000", 9, 4, 13, 8, "5004fb711f0100000000000000000000", "12345.67890000", false},
{"-1.00", "9cffffffffffffffffffffffffffffff", 3, 2, 5, 3, "18fcffffffffffffffffffffffffffff", "-1.000", false},
{"-1.0", "f6ffffffffffffffffffffffffffffff", 2, 1, 2, 0, "ffffffffffffffffffffffffffffffff", "-1", false},
{"1.0", "0a000000000000000000000000000000", 2, 1, 2, 0, "01000000000000000000000000000000", "1", false},
{"1.0", "0a000000000000000000000000000000", 2, 1, 40, 0, "", "", true},
{"1.0", "0a000000000000000000000000000000", 2, 1, 1, 0, "01000000000000000000000000000000", "1", false},
{"1", "01000000000000000000000000000000", 1, 0, 3, 2, "64000000000000000000000000000000", "1.00", false},
{"9223372036854775807", "ffffffffffffff7f0000000000000000", 19, 0, 30, 4, "f0d8ffffffffffff8713000000000000", "9223372036854775807.0000", false},
{"-9223372036854775808", "0000000000000080ffffffffffffffff", 19, 0, 30, 2, "0000000000000000ceffffffffffffff", "-9223372036854775808.00", false},
{"0.0000123", "7b000000000000000000000000000000", 8, 7, 10, 9, "0c300000000000000000000000000000", "0.000012300", false},
{"1230000000000000", "00e012b1ad5e04000000000000000000", 16, 0, 20, 2, "00805f2bd9fbb4010000000000000000", "1230000000000000.00", false},
{"123000000000000000000", "00000c6d51c8f7aa0600000000000000", 21, 0, 25, 2, "0000b098ce3fcac89a02000000000000", "123000000000000000000.00", false},
{"123000000000000000000000000000000000", "00000000cebde644bc05f0425eb01700", 36, 0, 38, 0, "00000000cebde644bc05f0425eb01700", "123000000000000000000000000000000000", false},
{"123000000000000000000000000000000000", "00000000cebde644bc05f0425eb01700", 36, 0, 38, 2, "00000000782422ea8a3dc225d2e44009", "123000000000000000000000000000000000.00", false},
{"1234567890123456.78901234", "f2af966ca0101f9b241a000000000000", 24, 8, 28, 10, "88badc6aaa7e22984c360a0000000000", "1234567890123456.7890123400", false},
{"-1234567890.1234567890", "2ef5e0147356ab54ffffffffffffffff", 20, 10, 24, 12, "f8c5df27f4c4ed12bdffffffffffffff", "-1234567890.123456789000", false},
{"123456789012345678901234567890.1234", "f2af967ed05c82de3297ff6fde3c0000", 34, 4, 38, 6, "88badc727141eceade0fd7bfe3c61700", "123456789012345678901234567890.123400", false},
{"-123456789012345678901234567890.1234", "0e5069812fa37d21cd68009021c3ffff", 34, 4, 38, 6, "7845238d8ebe131521f028401c39e8ff", "-123456789012345678901234567890.123400", false},
}

for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {

inputBytes := [16]byte(hexToBytes(t, tt.hexInput))
// verify that the conversion is correct
decStr := decimalBytesToString(inputBytes, tt.inputScale)
if tt.expected == "" {
tt.expected = strings.TrimPrefix(tt.input, "+")
}
assert.Equal(t, tt.input, decStr)

newBytes, newPrecision, newScale, err := modifyDecimalPrecisionAndScale(inputBytes, tt.inputScale, tt.targetPrecision, tt.targetScale)
if tt.expectError {
require.Error(t, err)
return
}
require.NoError(t, err)
assert.Equal(t, tt.targetPrecision, newPrecision)
assert.Equal(t, tt.targetScale, newScale)
if tt.hexWant2 == "" {
tt.hexWant2 = tt.hexInput
}
assert.Equal(t, hexToBytes(t, tt.hexWant2), newBytes[:])
decStr = decimalBytesToString(newBytes, newScale)
assert.Equal(t, tt.expected, decStr)
})
}
}
Loading

0 comments on commit d6e63d9

Please sign in to comment.