blob: 7853aecc0e34a84617bc536aeb4841df2b324d5a [file]
# 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.
import unittest
from decimal import Decimal as BigDecimal
from pypaimon.data.decimal import Decimal
class DecimalTest(unittest.TestCase):
def test_from_big_decimal(self):
d = Decimal.from_big_decimal(BigDecimal("12.34"), precision=10, scale=2)
self.assertIsNotNone(d)
self.assertEqual(d.precision, 10)
self.assertEqual(d.scale, 2)
self.assertEqual(d.to_big_decimal(), BigDecimal("12.34"))
self.assertEqual(d.to_unscaled_long(), 1234)
def test_from_big_decimal_overflow(self):
d = Decimal.from_big_decimal(BigDecimal("12345678901.23"), precision=10, scale=2)
self.assertIsNone(d)
def test_from_unscaled_long(self):
d = Decimal.from_unscaled_long(1234, precision=10, scale=2)
self.assertEqual(d.precision, 10)
self.assertEqual(d.scale, 2)
self.assertEqual(d.to_big_decimal(), BigDecimal("12.34"))
def test_from_unscaled_bytes(self):
d = Decimal.from_big_decimal(BigDecimal("12.34"), 10, 2)
d2 = Decimal.from_unscaled_bytes(
d.to_unscaled_bytes(),
precision=10,
scale=2,
)
self.assertEqual(d2, d)
def test_zero(self):
d = Decimal.zero(precision=10, scale=2)
self.assertEqual(d.to_big_decimal(), BigDecimal("0.00"))
def test_copy(self):
d = Decimal.from_big_decimal(BigDecimal("12.34"), 10, 2)
copy = d.copy()
self.assertEqual(copy, d)
self.assertIsNot(copy, d)
def test_compare(self):
d1 = Decimal.from_big_decimal(BigDecimal("1.23"), 10, 2)
d2 = Decimal.from_big_decimal(BigDecimal("2.34"), 10, 2)
self.assertLess(d1.compare_to(d2), 0)
self.assertGreater(d2.compare_to(d1), 0)
self.assertEqual(d1.compare_to(d1.copy()), 0)
def test_hash(self):
d1 = Decimal.from_big_decimal(BigDecimal("12.34"), 10, 2)
d2 = Decimal.from_big_decimal(BigDecimal("12.34"), 10, 2)
self.assertEqual(hash(d1), hash(d2))
def test_to_big_decimal(self):
d = Decimal.from_unscaled_long(1234, 10, 2)
self.assertEqual(d.to_big_decimal(), BigDecimal("12.34"))
def test_to_unscaled_long(self):
d = Decimal.from_big_decimal(BigDecimal("12.34"), 10, 2)
self.assertEqual(d.to_unscaled_long(), 1234)
def test_to_unscaled_bytes(self):
d = Decimal.from_big_decimal(BigDecimal("12.34"), 10, 2)
self.assertEqual(
int.from_bytes(d.to_unscaled_bytes(), "big", signed=True),
1234,
)
def test_to_string(self):
self.assertEqual(
str(Decimal.from_big_decimal(BigDecimal("12.34"), 10, 2)),
"12.34",
)
self.assertEqual(
str(Decimal.from_big_decimal(BigDecimal("0.0000000000000000001"), 39, 19)),
"0.0000000000000000001",
)
def test_is_compact(self):
self.assertTrue(Decimal.is_compact_precision(18))
self.assertFalse(Decimal.is_compact_precision(19))
self.assertTrue(Decimal.from_big_decimal(BigDecimal("1"), 18, 0).is_compact())
self.assertFalse(Decimal.from_big_decimal(BigDecimal("1"), 19, 0).is_compact())
def test_extract_decimal_precision_scale(self):
self.assertEqual(Decimal.extract_decimal_precision_scale("DECIMAL"), (10, 0))
self.assertEqual(Decimal.extract_decimal_precision_scale("DECIMAL(20)"), (20, 0))
self.assertEqual(Decimal.extract_decimal_precision_scale("DECIMAL(20,5)"), (20, 5))
self.assertEqual(Decimal.extract_decimal_precision_scale("NUMERIC(18,2)"), (18, 2))
self.assertEqual(Decimal.extract_decimal_precision_scale("DEC(8,3)"), (8, 3))
def test_extract_decimal_precision_scale_invalid(self):
with self.assertRaises(ValueError):
Decimal.extract_decimal_precision_scale("INT")
def test_high_precision_unscaled_bytes_round_trip(self):
value = BigDecimal("12345678901234567890123456789012345678")
d = Decimal.from_big_decimal(value, precision=38, scale=0)
self.assertIsNotNone(d)
encoded = d.to_unscaled_bytes()
d2 = Decimal.from_unscaled_bytes(
encoded,
precision=38,
scale=0,
)
self.assertEqual(d2, d)
self.assertEqual(d2.to_big_decimal(), value)
def test_high_precision_scaled_round_trip(self):
value = BigDecimal("12345678901234567890.123456789012345678")
d = Decimal.from_big_decimal(
value,
precision=38,
scale=18,
)
self.assertIsNotNone(d)
d2 = Decimal.from_unscaled_bytes(
d.to_unscaled_bytes(),
precision=38,
scale=18,
)
self.assertEqual(d2.to_big_decimal(), value)
def test_to_unscaled_bytes_java_compatible(self):
cases = [
(-32769, "ff7fff"),
(-32768, "8000"),
(-129, "ff7f"),
(-128, "80"),
(-127, "81"),
(-2, "fe"),
(-1, "ff"),
(0, "00"),
(1, "01"),
(127, "7f"),
(128, "0080"),
(255, "00ff"),
(256, "0100"),
(32767, "7fff"),
(32768, "008000"),
]
for unscaled, expected in cases:
with self.subTest(unscaled=unscaled):
d = Decimal.from_unscaled_long(
unscaled,
precision=18,
scale=0,
)
self.assertEqual(
d.to_unscaled_bytes().hex(),
expected,
)
def test_from_unscaled_bytes_java_compatible(self):
cases = [
("80", -128),
("ff7f", -129),
("ff", -1),
("00", 0),
("7f", 127),
("0080", 128),
("00ff", 255),
]
for encoded, expected in cases:
with self.subTest(encoded=encoded):
d = Decimal.from_unscaled_bytes(
bytes.fromhex(encoded),
precision=18,
scale=0,
)
self.assertEqual(
d.to_unscaled_long(),
expected,
)
def test_unscaled_bytes_round_trip(self):
values = [
-123456789012345678,
-1000,
-129,
-128,
-127,
-1,
0,
1,
127,
128,
255,
256,
123456789012345678,
]
for value in values:
with self.subTest(value=value):
d = Decimal.from_unscaled_long(
value,
precision=18,
scale=0,
)
d2 = Decimal.from_unscaled_bytes(
d.to_unscaled_bytes(),
precision=18,
scale=0,
)
self.assertEqual(d2, d)
if __name__ == '__main__':
unittest.main()