aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvvvv <vvvv@yandex-team.com>2024-11-02 16:47:48 +0300
committervvvv <vvvv@yandex-team.com>2024-11-02 16:59:45 +0300
commitd9417f6a9bc844e4b6ac40255999684026433719 (patch)
treeb656fd425d7e15cadf5819e168b2eb74820ae0ba
parent4c6bd8b8a7bc697bf7131f9d91bfe3804732e406 (diff)
downloadydb-d9417f6a9bc844e4b6ac40255999684026433719.tar.gz
Moved yql/public/decimal YQL-19206
fresh init commit_hash:1bae0afa7e556cf64c1c39196765e52d82efdee1
-rw-r--r--yql/essentials/public/decimal/ut/ya.make5
-rw-r--r--yql/essentials/public/decimal/ut/yql_decimal_ut.cpp351
-rw-r--r--yql/essentials/public/decimal/ut/yql_wide_int_ut.cpp465
-rw-r--r--yql/essentials/public/decimal/ya.make14
-rw-r--r--yql/essentials/public/decimal/yql_decimal.cpp463
-rw-r--r--yql/essentials/public/decimal/yql_decimal.h346
-rw-r--r--yql/essentials/public/decimal/yql_decimal_serialize.cpp82
-rw-r--r--yql/essentials/public/decimal/yql_decimal_serialize.h13
-rw-r--r--yql/essentials/public/decimal/yql_wide_int.h344
-rw-r--r--yql/essentials/public/ya.make1
10 files changed, 2084 insertions, 0 deletions
diff --git a/yql/essentials/public/decimal/ut/ya.make b/yql/essentials/public/decimal/ut/ya.make
new file mode 100644
index 0000000000..aad0eb010b
--- /dev/null
+++ b/yql/essentials/public/decimal/ut/ya.make
@@ -0,0 +1,5 @@
+UNITTEST_FOR(yql/essentials/public/decimal)
+
+SRCS(yql_decimal_ut.cpp yql_wide_int_ut.cpp)
+
+END()
diff --git a/yql/essentials/public/decimal/ut/yql_decimal_ut.cpp b/yql/essentials/public/decimal/ut/yql_decimal_ut.cpp
new file mode 100644
index 0000000000..c93993190f
--- /dev/null
+++ b/yql/essentials/public/decimal/ut/yql_decimal_ut.cpp
@@ -0,0 +1,351 @@
+#include <yql/essentials/public/decimal/yql_decimal.h>
+#include <yql/essentials/public/decimal/yql_decimal_serialize.h>
+
+#include <library/cpp/testing/unittest/registar.h>
+
+namespace NYql {
+namespace NDecimal {
+Y_UNIT_TEST_SUITE(TYqlDecimalTest) {
+ void SimplePositiveTest(TInt128 v, ui8 precision, ui8 scale, const TString& expected) {
+ TString result = ToString(v, precision, scale);
+ UNIT_ASSERT_VALUES_EQUAL(result, expected);
+ TInt128 parsed = FromString(result, precision, scale);
+ UNIT_ASSERT(parsed == v);
+ }
+
+ void SimpleNegativeFormatTest(TInt128 v, ui8 precision, ui8 scale) {
+ TString result = ToString(v, precision, scale);
+ UNIT_ASSERT_VALUES_EQUAL(result, "");
+ }
+
+ void SimpleSerializeAndDeserialize(TInt128 v, size_t expectedSize) {
+ char buff[sizeof(TInt128)];
+ const auto s = Serialize(v, buff);
+ UNIT_ASSERT_VALUES_EQUAL(s, expectedSize);
+ const auto& des = Deserialize(buff, expectedSize);
+ UNIT_ASSERT_VALUES_EQUAL(des.second, expectedSize);
+ UNIT_ASSERT(des.first == v);
+ const auto& e = Deserialize(buff, expectedSize - 1);
+ UNIT_ASSERT(e.first == Err());
+ }
+
+ template<ui8 Precision, ui8 Scale>
+ void CheckMulAndRescale(const TStringBuf& lhs, const TStringBuf& rhs, const TStringBuf& expected) {
+ const auto l = FromString(lhs, Precision, Scale);
+ const auto r = FromString(rhs, Precision, Scale);
+ const auto m = MulAndDivNormalDivider(l, r, GetDivider<Scale>());
+ const auto result = ToString(m, Precision, Scale);
+ UNIT_ASSERT_VALUES_EQUAL(result, expected);
+ }
+
+ template<ui8 Precision, ui8 Scale>
+ void CheckDivAndRescale(const TStringBuf& lhs, const TStringBuf& rhs, const TStringBuf& expected) {
+ const auto l = FromString(lhs, Precision, Scale);
+ const auto r = FromString(rhs, Precision, Scale);
+ const auto m = MulAndDivNormalMultiplier(l, GetDivider<Scale>(), r);
+ const auto result = ToString(m, Precision, Scale);
+ UNIT_ASSERT_VALUES_EQUAL(result, expected);
+ }
+
+ template<ui8 Precision, ui8 Scale = 0>
+ void CheckMul(const TStringBuf& lhs, const TStringBuf& rhs, const TStringBuf& expected) {
+ const auto l = FromString(lhs, Precision, Scale);
+ const auto r = FromString(rhs, Precision, Scale);
+ const auto m = Mul(l, r);
+ const auto result = ToString(m, Precision, Scale);
+ UNIT_ASSERT_VALUES_EQUAL(result, expected);
+ }
+
+ Y_UNIT_TEST(TestZeroFormat) {
+ UNIT_ASSERT_VALUES_EQUAL(ToString(0, 1, 0), "0");
+ UNIT_ASSERT_VALUES_EQUAL(ToString(0, 15, 6), "0");
+ UNIT_ASSERT_VALUES_EQUAL(ToString(0, 15, 0), "0");
+ }
+
+ Y_UNIT_TEST(TestZeroScale) {
+ SimplePositiveTest(1, 5, 0, "1");
+ SimplePositiveTest(10, 5, 0, "10");
+ SimplePositiveTest(100, 5, 0, "100");
+ SimplePositiveTest(1000, 5, 0, "1000");
+ SimplePositiveTest(10000, 5, 0, "10000");
+ SimpleNegativeFormatTest(100000, 5, 0);
+ SimpleNegativeFormatTest(1000000, 5, 0);
+
+ // negative numbers
+ SimplePositiveTest(-1, 5, 0, "-1");
+ SimplePositiveTest(-10, 5, 0, "-10");
+ SimplePositiveTest(-100, 5, 0, "-100");
+ SimplePositiveTest(-1000, 5, 0, "-1000");
+ SimplePositiveTest(-10000, 5, 0, "-10000");
+ SimpleNegativeFormatTest(-100000, 5, 0);
+ }
+
+ Y_UNIT_TEST(TestFormats) {
+ // we have no trailing zeros
+ SimplePositiveTest(1, 15, 6, "0.000001");
+ SimplePositiveTest(10, 15, 6, "0.00001");
+ SimplePositiveTest(100, 15, 6, "0.0001");
+ SimplePositiveTest(1000, 15, 6, "0.001");
+ SimplePositiveTest(10000, 15, 6, "0.01");
+ SimplePositiveTest(100000, 15, 6, "0.1");
+ SimplePositiveTest(1000000, 15, 6, "1");
+ SimplePositiveTest(10000000, 15, 6, "10");
+ SimplePositiveTest(100000000, 15, 6, "100");
+
+ SimplePositiveTest(2020000, 15, 6, "2.02");
+ SimplePositiveTest(3003000, 15, 6, "3.003");
+
+ // negative numbers
+ SimplePositiveTest(-1, 15, 6, "-0.000001");
+ SimplePositiveTest(-10, 15, 6, "-0.00001");
+ SimplePositiveTest(-100, 15, 6, "-0.0001");
+ SimplePositiveTest(-1000, 15, 6, "-0.001");
+ SimplePositiveTest(-10000, 15, 6, "-0.01");
+ SimplePositiveTest(-100000, 15, 6, "-0.1");
+ SimplePositiveTest(-1000000, 15, 6, "-1");
+ SimplePositiveTest(-10000000, 15, 6, "-10");
+ SimplePositiveTest(-100000000, 15, 6, "-100");
+
+ SimplePositiveTest(-2020000, 15, 6, "-2.02");
+ SimplePositiveTest(-3003000, 15, 6, "-3.003");
+
+ SimplePositiveTest(1, 15, 6, "0.000001");
+ SimplePositiveTest(12, 15, 6, "0.000012");
+ SimplePositiveTest(123, 15, 6, "0.000123");
+ SimplePositiveTest(1234, 15, 6, "0.001234");
+ SimplePositiveTest(12345, 15, 6, "0.012345");
+ SimplePositiveTest(123456, 15, 6, "0.123456");
+ SimplePositiveTest(1234567, 15, 6, "1.234567");
+ SimplePositiveTest(12345678, 15, 6, "12.345678");
+ SimplePositiveTest(123456789, 15, 6, "123.456789");
+ SimplePositiveTest(1234567898, 15, 6, "1234.567898");
+ SimplePositiveTest(12345678987ll, 15, 6, "12345.678987");
+ SimplePositiveTest(123456789876ll, 15, 6, "123456.789876");
+ }
+
+ Y_UNIT_TEST(TestHugeNumberFormat) {
+ TInt128 max120 = Inf() - 1;
+ const char max120String[] = "99999999999999999999999999999999999"; // 35 digits
+ static_assert(sizeof(max120String) == 36, "sizeof(max120String) == 36");
+ SimplePositiveTest(max120, MaxPrecision, 0, max120String);
+ SimplePositiveTest(max120 + 1, MaxPrecision, 0, "inf");
+
+ TInt128 min120 = -Inf() + 1;
+ const char min120String[] = "-99999999999999999999999999999999999";
+ static_assert(sizeof(min120String) == 37, "sizeof(min120String) == 37");
+ SimplePositiveTest(min120, MaxPrecision, 0, min120String);
+ SimplePositiveTest(min120 - 1, MaxPrecision, 0, "-inf");
+
+ // take spot for sign and zero before dot
+ const char min120StringAfterDot[] = "-0.99999999999999999999999999999999999"; // 35 by nine + leading zero
+ static_assert(sizeof(min120StringAfterDot) == 39, "sizeof(min120StringAfterDot) == 39");
+ SimplePositiveTest(min120, MaxPrecision, MaxPrecision, min120StringAfterDot);
+
+ SimpleNegativeFormatTest(1, MaxPrecision + 1, MaxPrecision + 1);
+ SimpleNegativeFormatTest(1, MaxPrecision + 1, 0);
+ SimpleNegativeFormatTest(1, 2, 3);
+ }
+
+ Y_UNIT_TEST(TestFormStringRoundToEven) {
+ UNIT_ASSERT(FromString(".51", 1, 0) == 1);
+ UNIT_ASSERT(FromString("-0.51", 1, 0) == -1);
+
+ UNIT_ASSERT(FromString("+00000008.5", 1, 0) == 8);
+ UNIT_ASSERT(FromString("-8.5000000000000000000000000000000", 1, 0) == -8);
+
+ UNIT_ASSERT(FromString("00008.51", 1, 0) == 9);
+ UNIT_ASSERT(FromString("-8.5000000000000000000000000000001", 1, 0) == -9);
+
+ UNIT_ASSERT(FromString("09.499999999999999999999999999999999999999999999999999999999", 1, 0) == 9);
+ UNIT_ASSERT(FromString("-9.499999999999999999999999999999999999999999999999999999999", 1, 0) == -9);
+
+ UNIT_ASSERT(FromString("9.50", 2, 0) == 10);
+ UNIT_ASSERT(FromString("-9.5", 2, 0) == -10);
+
+ UNIT_ASSERT(FromString("+0.9949", 2, 2) == 99);
+ UNIT_ASSERT(FromString("-0.9949", 2, 2) == -99);
+ }
+
+ Y_UNIT_TEST(TestInfinityValues) {
+ UNIT_ASSERT(FromString("+1", 1, 1) == Inf());
+ UNIT_ASSERT(FromString("-1", 1, 1) == -Inf());
+
+ UNIT_ASSERT(FromString("10.000", 1, 0) == Inf());
+ UNIT_ASSERT(FromString("-10.000", 1, 0) == -Inf());
+
+ UNIT_ASSERT(FromString("9.500", 1, 0) == Inf());
+ UNIT_ASSERT(FromString("-9.500", 1, 0) == -Inf());
+
+ UNIT_ASSERT(FromString("+0.950", 1, 1) == Inf());
+ UNIT_ASSERT(FromString("-0.950", 1, 1) == -Inf());
+
+ UNIT_ASSERT(FromString("+0.9950", 2, 2) == Inf());
+ UNIT_ASSERT(FromString("-0.9950", 2, 2) == -Inf());
+
+ UNIT_ASSERT(FromString("9999999999999999999999999999999999999.5", 35, 0) == Inf());
+ UNIT_ASSERT(FromString("-9999999999999999999999999999999999999.5", 35, 0) == -Inf());
+ }
+
+ Y_UNIT_TEST(TestInvalidValues) {
+ UNIT_ASSERT(IsValid("+999999999999999991234567890.039493804903849038490312345678909999999999999999990"));
+
+ UNIT_ASSERT(!IsValid("")); // empty
+ UNIT_ASSERT(!IsValid("12.2.3")); // double dot
+ UNIT_ASSERT(!IsValid("+-12")); // extra sign
+ UNIT_ASSERT(!IsValid("463786378O74674")); // letter inside
+
+ UNIT_ASSERT(IsError(FromString("", 35, 15))); // empty
+ UNIT_ASSERT(IsError(FromString("12.2.3", 35, 15))); // double dot
+ UNIT_ASSERT(IsError(FromString("+-12", 35, 15))); // extra sign
+ UNIT_ASSERT(IsError(FromString("463786378O74674", 35, 15))); // letter inside
+ UNIT_ASSERT(IsError(FromString("+7.039493804E1", 35, 5))); // letter in tail after scale
+ }
+
+ Y_UNIT_TEST(TestFormStringEx) {
+ UNIT_ASSERT(FromStringEx("NAN", 13, 1) == Nan());
+ UNIT_ASSERT(FromStringEx("+inf", 11, 7) == Inf());
+ UNIT_ASSERT(FromStringEx("-inf", 7, 7) == -Inf());
+
+ UNIT_ASSERT(FromStringEx("0.1E3", 10, 1) == 1000);
+ UNIT_ASSERT(FromStringEx("0.51e-3", 10, 3) == 1);
+
+ UNIT_ASSERT(FromStringEx("1E30", 10, 0) == Inf());
+ UNIT_ASSERT(FromStringEx("1e-30", 10, 0) == 0);
+ UNIT_ASSERT(FromStringEx("-1E+99", 10, 2) == -Inf());
+ UNIT_ASSERT(FromStringEx("-1e-99", 10, 2) == 0);
+ UNIT_ASSERT(FromStringEx("-510e-3", 1, 0) == -1);
+ UNIT_ASSERT(FromStringEx("+99E3", 5, 0) == 99000);
+ UNIT_ASSERT(FromStringEx("2.1E-130", 35, 2) == 0);
+ UNIT_ASSERT(FromStringEx("2.1E0", 35, 2) == 210);
+ }
+
+ Y_UNIT_TEST(TestFormStringExInvalidValues) {
+ UNIT_ASSERT(IsError(FromStringEx("", 35, 15))); // empty
+ UNIT_ASSERT(IsError(FromStringEx("12.2.3", 35, 15))); // double dot
+ UNIT_ASSERT(IsError(FromStringEx("+-12", 35, 15))); // extra sign
+ UNIT_ASSERT(IsError(FromStringEx("463786378O74674", 35, 15))); // letter inside
+
+ UNIT_ASSERT(IsError(FromStringEx("E2", 35, 15))); // empty
+ UNIT_ASSERT(IsError(FromStringEx("E2E4", 35, 15))); // empty
+ UNIT_ASSERT(IsError(FromStringEx("NANE5", 35, 15))); // nan with exp
+ UNIT_ASSERT(IsError(FromStringEx("infE5", 35, 15))); // inf with exp
+ UNIT_ASSERT(IsError(FromStringEx("-infe-5", 35, 15))); // inf with exp
+ UNIT_ASSERT(IsError(FromStringEx("2.1E0X", 35, 2))); // not fully parsed exp
+ UNIT_ASSERT(IsError(FromStringEx("2.1E+-1", 35, 2))); // two signs
+ UNIT_ASSERT(IsError(FromStringEx("ae30", 10, 0))); // bad mantissa
+ }
+
+ Y_UNIT_TEST(TestSpecialAsString) {
+ UNIT_ASSERT(IsValid("Nan"));
+ UNIT_ASSERT(IsValid("INF"));
+ UNIT_ASSERT(IsValid("-inf"));
+
+ UNIT_ASSERT_VALUES_EQUAL(ToString(Nan(), 10, 2), "nan");
+
+ UNIT_ASSERT_VALUES_EQUAL(ToString(+Inf(), 10, 2), "inf");
+ UNIT_ASSERT_VALUES_EQUAL(ToString(-Inf(), 10, 2), "-inf");
+
+ UNIT_ASSERT(IsNan(FromString("nan", 10, 2)));
+ UNIT_ASSERT(IsInf(FromString("+INf", MaxPrecision, 6)));
+ UNIT_ASSERT(IsInf(FromString("-inF", 4, 2)));
+ }
+
+ Y_UNIT_TEST(TestToStringOfNonNormal) {
+ // above Inf
+ for (TInt128 i = Inf() + 2, end = Inf() + 100; i < end; i++) {
+ UNIT_ASSERT(!IsNormal(i));
+ UNIT_ASSERT(ToString(i, MaxPrecision, 0) == nullptr);
+ }
+
+ // below -Inf
+ for (TInt128 i = -Inf() - 2, end = -Inf() - 100; i < end; i--) {
+ UNIT_ASSERT(!IsNormal(i));
+ UNIT_ASSERT(ToString(i, MaxPrecision, 0) == nullptr);
+ }
+ }
+
+ Y_UNIT_TEST(TestSerializeAndDeserialize) {
+ SimpleSerializeAndDeserialize(-Nan(), 1U);
+ SimpleSerializeAndDeserialize(-Inf(), 1U);
+
+ SimpleSerializeAndDeserialize(-Inf() + 1, 16U);
+ SimpleSerializeAndDeserialize(-Inf() + 2, 16U);
+
+ SimpleSerializeAndDeserialize(-65537, 4U);
+ SimpleSerializeAndDeserialize(-65536, 3U);
+
+ SimpleSerializeAndDeserialize(-257, 3U);
+ SimpleSerializeAndDeserialize(-256, 2U);
+
+ SimpleSerializeAndDeserialize(-3, 2U);
+ SimpleSerializeAndDeserialize(-2, 2U);
+
+ SimpleSerializeAndDeserialize(-1, 1U);
+ SimpleSerializeAndDeserialize(0, 1U);
+
+ SimpleSerializeAndDeserialize(+1, 2U);
+ SimpleSerializeAndDeserialize(+2, 2U);
+
+ SimpleSerializeAndDeserialize(+255, 2U);
+ SimpleSerializeAndDeserialize(+256, 3U);
+
+ SimpleSerializeAndDeserialize(+65535, 3U);
+ SimpleSerializeAndDeserialize(+65536, 4U);
+
+ SimpleSerializeAndDeserialize(+Inf() - 2, 16U);
+ SimpleSerializeAndDeserialize(+Inf() - 1, 16U);
+
+ SimpleSerializeAndDeserialize(+Inf(), 1U);
+ SimpleSerializeAndDeserialize(+Nan(), 1U);
+ }
+
+ Y_UNIT_TEST(TestMulAndRescale) {
+ CheckMulAndRescale<35,35>("0.99999999999999999999999999999999999", "-0.99999999999999999999999999999999999", "-0.99999999999999999999999999999999998");
+ CheckMulAndRescale<35,35>("-0.99999999999999999999999999999999999", "0.33333333333333333333333333333333333", "-0.33333333333333333333333333333333333");
+ CheckMulAndRescale<35,35>("0.33333333333333333333333333333333333", "0.33333333333333333333333333333333333", "0.11111111111111111111111111111111111");
+ CheckMulAndRescale<35,35>("0.99999999999999999999999999999999999", "0.000000000000001", "0.000000000000001");
+ CheckMulAndRescale<35,35>("0.99999999999999999999999999999999999", "0.00000000000000101010101", "0.00000000000000101010101");
+ CheckMulAndRescale<35,35>("0.12345678901234567890123456789012345", "0.12345678901234567890123456789012345", "0.01524157875323883675049535156256668");
+
+ CheckMulAndRescale<35,34>("9.9999999999999999999999999999999999", "-1.9999999999999999999999999999999999", "-inf");
+ CheckMulAndRescale<35,34>("3.3333333333333333333333333333333333", "3.3333333333333333333333333333333333", "inf");
+ CheckMulAndRescale<35,34>("3.3333333333333333333333333333333333", "1.3333333333333333333333333333333333", "4.4444444444444444444444444444444443");
+ CheckMulAndRescale<35,34>("-1.3333333333333333333333333333333333", "1.3333333333333333333333333333333333", "-1.7777777777777777777777777777777777");
+
+ CheckMulAndRescale<35,34>("-7", "0", "0");
+ CheckMulAndRescale<35,34>("inf", "nan", "nan");
+ CheckMulAndRescale<35,34>("inf", "0", "nan");
+ CheckMulAndRescale<35,34>("-inf", "-inf", "inf");
+ }
+
+ Y_UNIT_TEST(TestDivAndRescale) {
+ CheckDivAndRescale<35,35>("-0.99999999999999999999999999999999999", "0.33333333333333333333333333333333333", "-inf");
+ CheckDivAndRescale<35,35>("0.33333333333333333333333333333333333", "-0.33333333333333333333333333333333333", "-inf");
+ CheckDivAndRescale<35,35>("0.12345678901234567890123456789012345", "0.12345678901234567890123456789012345", "inf");
+
+ CheckDivAndRescale<35,34>("9.9999999999999999999999999999999999", "-1.9999999999999999999999999999999999", "-5.0000000000000000000000000000000002");
+ CheckDivAndRescale<35,34>("3.3333333333333333333333333333333333", "3.3333333333333333333333333333333333", "1");
+ CheckDivAndRescale<35,34>("3.3333333333333333333333333333333333", "1.3333333333333333333333333333333333", "2.5");
+ CheckDivAndRescale<35,34>("-1.7777777777777777777777777777777777", "1.3333333333333333333333333333333333", "-1.3333333333333333333333333333333333");
+
+ CheckDivAndRescale<35,34>("-7", "0", "-inf");
+ CheckDivAndRescale<35,34>("inf", "0", "inf");
+ CheckDivAndRescale<35,34>("inf", "0", "inf");
+ CheckDivAndRescale<35,34>("-inf", "inf", "nan");
+ }
+
+ Y_UNIT_TEST(TestWideMul) {
+ CheckMul<35>("999999999999999", "99999999999999999999", "99999999999999899999000000000000001");
+ CheckMul<35>("9999999999999999", "99999999999999999999", "inf");
+ CheckMul<35>("-99999999999999999999999999999999999", "10000000000000000000000000000000000", "-inf");
+ CheckMul<35>("-99999999999999999999999999999999999", "-1", "99999999999999999999999999999999999");
+ CheckMul<35>("-99999999999999999999999999999999999", "-2", "inf");
+
+ CheckMul<35>("nan", "0", "nan");
+ CheckMul<35>("inf", "-inf", "-inf");
+ CheckMul<35>("inf", "nan", "nan");
+ }
+}
+
+}
+}
diff --git a/yql/essentials/public/decimal/ut/yql_wide_int_ut.cpp b/yql/essentials/public/decimal/ut/yql_wide_int_ut.cpp
new file mode 100644
index 0000000000..5238249da1
--- /dev/null
+++ b/yql/essentials/public/decimal/ut/yql_wide_int_ut.cpp
@@ -0,0 +1,465 @@
+#include <yql/essentials/public/decimal/yql_wide_int.h>
+#include <library/cpp/testing/unittest/registar.h>
+
+namespace NYql {
+Y_UNIT_TEST_SUITE(TYqlWideIntTest) {
+ template<typename T>
+ void TestUnary(const T aa) {
+ using Test = TWide<typename THalfOf<T>::Type>;
+ const Test at(aa);
+ static_assert(sizeof(at) == sizeof(aa), "Bad wide int size!");
+
+ UNIT_ASSERT_VALUES_EQUAL(static_cast<i8>(aa), static_cast<i8>(at));
+ UNIT_ASSERT_VALUES_EQUAL(static_cast<ui8>(aa), static_cast<ui8>(at));
+ UNIT_ASSERT_VALUES_EQUAL(static_cast<i16>(aa), static_cast<i16>(at));
+ UNIT_ASSERT_VALUES_EQUAL(static_cast<ui16>(aa), static_cast<ui16>(at));
+ UNIT_ASSERT_VALUES_EQUAL(static_cast<i32>(aa), static_cast<i32>(at));
+ UNIT_ASSERT_VALUES_EQUAL(static_cast<ui32>(aa), static_cast<ui32>(at));
+ UNIT_ASSERT_VALUES_EQUAL(static_cast<i64>(aa), static_cast<i64>(at));
+ UNIT_ASSERT_VALUES_EQUAL(static_cast<ui64>(aa), static_cast<ui64>(at));
+#ifndef _win_
+ UNIT_ASSERT(static_cast<i128_t>(aa) == static_cast<i128_t>(at));
+ UNIT_ASSERT(static_cast<ui128_t>(aa) == static_cast<ui128_t>(at));
+#endif
+
+ {
+ const auto exp = ~aa;
+ const auto tst = ~at;
+ UNIT_ASSERT(T(tst) == T(exp));
+ }
+
+ {
+ const auto exp = +aa;
+ const auto tst = +at;
+ UNIT_ASSERT(T(tst) == T(exp));
+ }
+
+ {
+ const auto exp = -aa;
+ const auto tst = -at;
+ UNIT_ASSERT(T(tst) == T(exp));
+ }
+
+ {
+ auto exp = aa;
+ auto tst = at;
+ ++exp;
+ ++tst;
+ UNIT_ASSERT(T(tst) == T(exp));
+ }
+
+ {
+ auto exp = aa;
+ auto tst = at;
+ --exp;
+ --tst;
+ UNIT_ASSERT(T(tst) == T(exp));
+ }
+
+ {
+ auto exp = aa;
+ auto tst = at;
+ exp++;
+ tst++;
+ UNIT_ASSERT(T(tst) == T(exp));
+ }
+
+ {
+ auto exp = aa;
+ auto tst = at;
+ exp--;
+ tst--;
+ UNIT_ASSERT(T(tst) == T(exp));
+ }
+ }
+
+ template<typename T>
+ void TestBinary(const T ll, const T rr) {
+ using Test = TWide<typename THalfOf<T>::Type>;
+ const Test lt(ll), rt(rr);
+
+ {
+ const auto exp = ll & rr;
+ const auto tst = lt & rt;
+ UNIT_ASSERT(T(tst) == T(exp));
+ }
+
+ {
+ const auto exp = ll | rr;
+ const auto tst = lt | rt;
+ UNIT_ASSERT(T(tst) == T(exp));
+ }
+
+ {
+ const auto exp = ll ^ rr;
+ const auto tst = lt ^ rt;
+ UNIT_ASSERT(T(tst) == T(exp));
+ }
+
+ {
+ const auto exp = ll + rr;
+ const auto tst = lt + rt;
+ UNIT_ASSERT(T(tst) == T(exp));
+ }
+
+ {
+ const auto exp = ll - rr;
+ const auto tst = lt - rt;
+ UNIT_ASSERT(T(tst) == T(exp));
+ }
+
+ if (rr > 0 && rr < T(sizeof(T) << 3U))
+ {
+ const auto exp = ll >> rr;
+ const auto tst = lt >> rt;
+ UNIT_ASSERT(T(tst) == T(exp));
+ }
+
+ if (rr > 0 && rr < T(sizeof(T) << 3U))
+ {
+ const auto exp = ll << rr;
+ const auto tst = lt << rt;
+ UNIT_ASSERT(T(tst) == T(exp));
+ }
+
+ {
+ const auto exp = ll * rr;
+ const auto tst = lt * rt;
+ UNIT_ASSERT(T(tst) == T(exp));
+ }
+
+ if (rr)
+ {
+ const auto exp = ll / rr;
+ const auto tst = lt / rt;
+ UNIT_ASSERT(T(tst) == T(exp));
+ }
+
+ if (rr)
+ {
+ const auto exp = ll % rr;
+ const auto tst = lt % rt;
+ UNIT_ASSERT(T(tst) == T(exp));
+ }
+
+ {
+ const auto exp = ll == rr;
+ const auto tst = lt == rt;
+ UNIT_ASSERT_VALUES_EQUAL(tst, exp);
+ }
+
+ {
+ const auto exp = ll != rr;
+ const auto tst = lt != rt;
+ UNIT_ASSERT_VALUES_EQUAL(tst, exp);
+ }
+
+ {
+ const auto exp = ll > rr;
+ const auto tst = lt > rt;
+ UNIT_ASSERT_VALUES_EQUAL(tst, exp);
+ }
+
+ {
+ const auto exp = ll < rr;
+ const auto tst = lt < rt;
+ UNIT_ASSERT_VALUES_EQUAL(tst, exp);
+ }
+
+ {
+ const auto exp = ll >= rr;
+ const auto tst = lt >= rt;
+ UNIT_ASSERT_VALUES_EQUAL(tst, exp);
+ }
+
+ {
+ const auto exp = ll <= rr;
+ const auto tst = lt <= rt;
+ UNIT_ASSERT_VALUES_EQUAL(tst, exp);
+ }
+ }
+
+ template<typename T>
+ void TestsForUnsignedType() {
+ static_assert(std::is_unsigned<T>::value, "Tests for unsigned type.");
+ TestUnary<T>(2U);
+ TestUnary<T>(4U);
+ TestUnary<T>(17U);
+ TestUnary<T>(42U);
+ TestUnary<T>(127U);
+ TestUnary<T>(128U);
+ TestUnary<T>(129U);
+ TestUnary<T>(200U);
+ TestUnary<T>(255U);
+ TestUnary<T>(256U);
+ TestUnary<T>(257U);
+
+ TestUnary<T>(std::numeric_limits<T>::min());
+ TestUnary<T>(std::numeric_limits<T>::max());
+ TestUnary<T>(std::numeric_limits<T>::max() - 1U);
+ TestUnary<T>(std::numeric_limits<T>::max() >> 1U);
+ TestUnary<T>(std::numeric_limits<T>::max() >> 3U);
+ TestUnary<T>(std::numeric_limits<T>::max() >> 7U);
+
+
+ TestUnary<T>(std::numeric_limits<T>::min() + 1U);
+ TestUnary<T>(std::numeric_limits<T>::max() - 1U);
+
+ TestUnary<T>(std::numeric_limits<T>::min() + 3U);
+ TestUnary<T>(std::numeric_limits<T>::max() - 3U);
+
+ TestUnary<T>(std::numeric_limits<T>::min() + 7U);
+ TestUnary<T>(std::numeric_limits<T>::max() - 7U);
+
+
+ TestBinary<T>(1U, 1U);
+ TestBinary<T>(7U, 31U);
+ TestBinary<T>(30000U, 13U);
+ TestBinary<T>(127U, 13U);
+ TestBinary<T>(128U, 19U);
+ TestBinary<T>(129U, 17U);
+
+
+ TestBinary<T>(std::numeric_limits<T>::min(), 7U);
+ TestBinary<T>(std::numeric_limits<T>::max(), 7U);
+
+ TestBinary<T>(std::numeric_limits<T>::min(), 8U);
+ TestBinary<T>(std::numeric_limits<T>::max(), 8U);
+
+ TestBinary<T>(std::numeric_limits<T>::min(), 9U);
+ TestBinary<T>(std::numeric_limits<T>::max(), 9U);
+
+ TestBinary<T>(std::numeric_limits<T>::max() - 1U, std::numeric_limits<T>::min());
+ TestBinary<T>(std::numeric_limits<T>::max() - 1U, std::numeric_limits<T>::min() + 1U);
+
+
+ TestBinary<T>(std::numeric_limits<T>::max(), std::numeric_limits<T>::max());
+ TestBinary<T>(std::numeric_limits<T>::max(), std::numeric_limits<T>::min());
+ TestBinary<T>(std::numeric_limits<T>::min(), std::numeric_limits<T>::min());
+ TestBinary<T>(std::numeric_limits<T>::max(), std::numeric_limits<T>::min());
+
+ TestBinary<T>(std::numeric_limits<T>::max(), std::numeric_limits<T>::min() + 1U);
+ TestBinary<T>(std::numeric_limits<T>::max() - 1U, std::numeric_limits<T>::min());
+ TestBinary<T>(std::numeric_limits<T>::max() - 1U, std::numeric_limits<T>::min() + 1U);
+
+ TestBinary<T>(std::numeric_limits<T>::max() - 1U, std::numeric_limits<T>::min());
+ TestBinary<T>(std::numeric_limits<T>::min() + 1U, std::numeric_limits<T>::min());
+
+ TestBinary<T>(std::numeric_limits<T>::max(), 1U);
+ TestBinary<T>(std::numeric_limits<T>::min(), 1U);
+ TestBinary<T>(std::numeric_limits<T>::max() - 1U, 1U);
+ TestBinary<T>(std::numeric_limits<T>::min() + 1U, 1U);
+
+ TestBinary<T>(std::numeric_limits<T>::max(), 7U);
+ TestBinary<T>(std::numeric_limits<T>::min(), 7U);
+ TestBinary<T>(std::numeric_limits<T>::max() - 1U, 7U);
+ TestBinary<T>(std::numeric_limits<T>::min() + 1U, 7U);
+
+ TestBinary<T>(std::numeric_limits<T>::max() >> 1U, std::numeric_limits<T>::min() >> 1U);
+ TestBinary<T>(std::numeric_limits<T>::max() >> 1U, std::numeric_limits<T>::min() + 1U);
+ TestBinary<T>(std::numeric_limits<T>::max() - 1U, std::numeric_limits<T>::min() >> 1U);
+ TestBinary<T>(std::numeric_limits<T>::max() >> 1U, std::numeric_limits<T>::min());
+ TestBinary<T>(std::numeric_limits<T>::max(), std::numeric_limits<T>::min() >> 1U);
+
+ TestBinary<T>(std::numeric_limits<T>::max() >> 3U, std::numeric_limits<T>::min() >> 3U);
+ TestBinary<T>(std::numeric_limits<T>::max() >> 3U, std::numeric_limits<T>::min() + 3U);
+ TestBinary<T>(std::numeric_limits<T>::max() - 3U, std::numeric_limits<T>::min() >> 3U);
+ TestBinary<T>(std::numeric_limits<T>::max() >> 3U, std::numeric_limits<T>::min());
+ TestBinary<T>(std::numeric_limits<T>::max(), std::numeric_limits<T>::min() >> 3U);
+ }
+
+ template<typename T>
+ void TestsForSignedType() {
+ static_assert(std::is_signed<T>::value, "Tests for signed type.");
+ TestUnary<T>(0);
+
+ TestUnary<T>(1);
+ TestUnary<T>(-1);
+
+ TestUnary<T>(2);
+ TestUnary<T>(-2);
+
+ TestUnary<T>(3);
+ TestUnary<T>(-3);
+
+ TestUnary<T>(4);
+ TestUnary<T>(-4);
+
+ TestUnary<T>(17);
+ TestUnary<T>(-17);
+
+ TestUnary<T>(42);
+ TestUnary<T>(-42);
+
+ TestUnary<T>(127);
+ TestUnary<T>(-127);
+
+ TestUnary<T>(128);
+ TestUnary<T>(-128);
+
+ TestUnary<T>(129);
+ TestUnary<T>(-129);
+
+ TestUnary<T>(200);
+ TestUnary<T>(-200);
+
+ TestUnary<T>(255);
+ TestUnary<T>(-255);
+
+ TestUnary<T>(256);
+ TestUnary<T>(-256);
+
+ TestUnary<T>(257);
+ TestUnary<T>(-257);
+
+ TestUnary<T>(258);
+ TestUnary<T>(-258);
+
+ TestUnary<T>(std::numeric_limits<T>::min());
+ TestUnary<T>(std::numeric_limits<T>::max());
+
+ TestUnary<T>(std::numeric_limits<T>::min() + 1);
+ TestUnary<T>(std::numeric_limits<T>::max() - 1);
+
+ TestUnary<T>(std::numeric_limits<T>::min() + 2);
+ TestUnary<T>(std::numeric_limits<T>::max() - 2);
+
+ TestUnary<T>(std::numeric_limits<T>::min() + 3);
+ TestUnary<T>(std::numeric_limits<T>::max() - 3);
+
+ TestUnary<T>(std::numeric_limits<T>::min() + 7);
+ TestUnary<T>(std::numeric_limits<T>::max() - 7);
+
+ TestUnary<T>(std::numeric_limits<T>::min() >> 1);
+ TestUnary<T>(std::numeric_limits<T>::max() >> 1);
+
+ TestUnary<T>(std::numeric_limits<T>::min() >> 3);
+ TestUnary<T>(std::numeric_limits<T>::max() >> 3);
+
+ TestUnary<T>(std::numeric_limits<T>::min() >> 7);
+ TestUnary<T>(std::numeric_limits<T>::max() >> 7);
+
+
+ TestUnary<T>(std::numeric_limits<T>::min() + 1);
+ TestUnary<T>(std::numeric_limits<T>::max() - 1);
+
+ TestUnary<T>(std::numeric_limits<T>::min() + 3);
+ TestUnary<T>(std::numeric_limits<T>::max() - 3);
+
+ TestUnary<T>(std::numeric_limits<T>::min() + 7);
+ TestUnary<T>(std::numeric_limits<T>::max() - 7);
+
+
+ TestBinary<T>(0, 0);
+ TestBinary<T>(1, 0);
+ TestBinary<T>(0, -1);
+
+ TestBinary<T>(1, 1);
+ TestBinary<T>(1, -1);
+ TestBinary<T>(-1, 1);
+ TestBinary<T>(-1, -1);
+
+ TestBinary<T>(7, 42);
+ TestBinary<T>(-7, 42);
+
+ TestBinary<T>(0, -43);
+
+ TestBinary<T>(-30000, 64);
+ TestBinary<T>(30000, -64);
+ TestBinary<T>(30000, 64);
+
+ TestBinary<T>(21, 0);
+ TestBinary<T>(13, -127);
+ TestBinary<T>(-19, 128);
+ TestBinary<T>(-77, -129);
+ TestBinary<T>(13, 127);
+ TestBinary<T>(19, 128);
+ TestBinary<T>(77, 129);
+
+ TestBinary<T>(std::numeric_limits<T>::max(), -1);
+
+ TestBinary<T>(std::numeric_limits<T>::min(), -7);
+ TestBinary<T>(std::numeric_limits<T>::max(), -7);
+
+ TestBinary<T>(std::numeric_limits<T>::min(), -8);
+ TestBinary<T>(std::numeric_limits<T>::max(), -8);
+
+ TestBinary<T>(std::numeric_limits<T>::min(), -9);
+ TestBinary<T>(std::numeric_limits<T>::max(), -9);
+
+ TestBinary<T>(std::numeric_limits<T>::min(), std::numeric_limits<T>::min() >> 5);
+ TestBinary<T>(std::numeric_limits<T>::max(), std::numeric_limits<T>::min() >> 5);
+
+
+ TestBinary<T>(std::numeric_limits<T>::min(), 7);
+ TestBinary<T>(std::numeric_limits<T>::max(), 7);
+
+ TestBinary<T>(std::numeric_limits<T>::min(), 8);
+ TestBinary<T>(std::numeric_limits<T>::max(), 8);
+
+ TestBinary<T>(std::numeric_limits<T>::min(), 9);
+ TestBinary<T>(std::numeric_limits<T>::max(), 9);
+
+ TestBinary<T>(std::numeric_limits<T>::max() - 1, std::numeric_limits<T>::min());
+ TestBinary<T>(std::numeric_limits<T>::max() - 1, std::numeric_limits<T>::min() + 1);
+
+
+ TestBinary<T>(std::numeric_limits<T>::max(), std::numeric_limits<T>::max());
+ TestBinary<T>(std::numeric_limits<T>::max(), std::numeric_limits<T>::min());
+ TestBinary<T>(std::numeric_limits<T>::min(), std::numeric_limits<T>::min());
+ TestBinary<T>(std::numeric_limits<T>::max(), std::numeric_limits<T>::min());
+
+ TestBinary<T>(std::numeric_limits<T>::max(), std::numeric_limits<T>::min() + 1);
+ TestBinary<T>(std::numeric_limits<T>::max() - 1, std::numeric_limits<T>::min());
+ TestBinary<T>(std::numeric_limits<T>::max() - 1, std::numeric_limits<T>::min() + 1);
+
+ TestBinary<T>(std::numeric_limits<T>::max(), 0);
+ TestBinary<T>(std::numeric_limits<T>::min(), 0);
+ TestBinary<T>(std::numeric_limits<T>::max() - 1, 0);
+ TestBinary<T>(std::numeric_limits<T>::min() + 1, 0);
+
+ TestBinary<T>(std::numeric_limits<T>::max(), 1);
+ TestBinary<T>(std::numeric_limits<T>::min(), 1);
+ TestBinary<T>(std::numeric_limits<T>::max() - 1, 1);
+ TestBinary<T>(std::numeric_limits<T>::min() + 1, 1);
+
+ TestBinary<T>(std::numeric_limits<T>::max(), 7);
+ TestBinary<T>(std::numeric_limits<T>::min(), 7);
+ TestBinary<T>(std::numeric_limits<T>::max() - 1, 7);
+ TestBinary<T>(std::numeric_limits<T>::min() + 1, 7);
+
+ TestBinary<T>(std::numeric_limits<T>::max() >> 1, std::numeric_limits<T>::min() >> 1);
+ TestBinary<T>(std::numeric_limits<T>::max() >> 1, std::numeric_limits<T>::min() + 1);
+ TestBinary<T>(std::numeric_limits<T>::max() - 1, std::numeric_limits<T>::min() >> 1);
+ TestBinary<T>(std::numeric_limits<T>::max() >> 1, std::numeric_limits<T>::min());
+ TestBinary<T>(std::numeric_limits<T>::max(), std::numeric_limits<T>::min() >> 1);
+
+ TestBinary<T>(std::numeric_limits<T>::max() >> 3, std::numeric_limits<T>::min() >> 3);
+ TestBinary<T>(std::numeric_limits<T>::max() >> 3, std::numeric_limits<T>::min() + 3);
+ TestBinary<T>(std::numeric_limits<T>::max() - 3, std::numeric_limits<T>::min() >> 3);
+ TestBinary<T>(std::numeric_limits<T>::max() >> 3, std::numeric_limits<T>::min());
+ TestBinary<T>(std::numeric_limits<T>::max(), std::numeric_limits<T>::min() >> 3);
+ }
+
+ Y_UNIT_TEST(CheckUnsignedByCompilerIntegrals) {
+ TestsForUnsignedType<ui32>();
+ TestsForUnsignedType<ui64>();
+#ifndef _win_
+ TestsForUnsignedType<ui128_t>();
+#endif
+ }
+
+#ifndef _ubsan_enabled_
+#ifndef _msan_enabled_
+ Y_UNIT_TEST(CheckSignedByCompilerIntegrals) {
+ TestsForSignedType<i32>();
+ TestsForSignedType<i64>();
+#ifndef _win_
+ TestsForSignedType<i128_t>();
+#endif
+ }
+#endif
+#endif
+}
+
+}
diff --git a/yql/essentials/public/decimal/ya.make b/yql/essentials/public/decimal/ya.make
new file mode 100644
index 0000000000..a9c23995b3
--- /dev/null
+++ b/yql/essentials/public/decimal/ya.make
@@ -0,0 +1,14 @@
+LIBRARY()
+
+SRCS(
+ yql_decimal.h
+ yql_decimal.cpp
+ yql_decimal_serialize.h
+ yql_decimal_serialize.cpp
+)
+
+END()
+
+RECURSE_FOR_TESTS(
+ ut
+)
diff --git a/yql/essentials/public/decimal/yql_decimal.cpp b/yql/essentials/public/decimal/yql_decimal.cpp
new file mode 100644
index 0000000000..91564a1a5f
--- /dev/null
+++ b/yql/essentials/public/decimal/yql_decimal.cpp
@@ -0,0 +1,463 @@
+#include "yql_decimal.h"
+
+#include <cstring>
+#include <ostream>
+#include <string>
+#include <charconv>
+
+namespace NYql {
+namespace NDecimal {
+
+static const TUint128 Ten(10U);
+
+TUint128 GetDivider(ui8 scale) {
+ if (scale > MaxPrecision) {
+ return Inf();
+ }
+
+ TUint128 d(1U);
+ while (scale--)
+ d *= Ten;
+ return d;
+}
+
+bool IsError(TInt128 v) {
+ return v > Nan() || v < -Inf();
+}
+
+bool IsNan(TInt128 v) {
+ return v == Nan();
+}
+
+bool IsInf(TInt128 v) {
+ return v == Inf() || v == -Inf();
+}
+
+bool IsNormal(TInt128 v) {
+ return v < Inf() && v > -Inf();
+}
+
+bool IsComparable(TInt128 v) {
+ return v <= Inf() && v >= -Inf();
+}
+
+const char* ToString(TInt128 val, ui8 precision, ui8 scale) {
+ if (!precision || precision > MaxPrecision || scale > precision) {
+ return nullptr;
+ }
+
+ if (val == Inf())
+ return "inf";
+ if (val == -Inf())
+ return "-inf";
+ if (val == Nan())
+ return "nan";
+
+ if (!IsNormal(val)) {
+ return nullptr;
+ }
+
+ if (!val) {
+ return "0";
+ }
+
+ const bool neg = val < 0;
+ TUint128 v = neg ? -val : val;
+
+ // log_{10}(2^120) ~= 36.12, 37 decimal places
+ // plus dot, zero before dot, sign and zero byte at the end
+ static thread_local char str[40];
+ auto end = str + sizeof(str);
+ *--end = 0;
+
+ auto s = end;
+
+ do {
+ if (!precision--) {
+ return nullptr;
+ }
+
+
+ const auto digit = ui8(v % Ten);
+ if (digit || !scale || s != end) {
+ *--s = "0123456789"[digit];
+ }
+
+ if (scale && !--scale && s != end) {
+ *--s = '.';
+ }
+ } while (v /= Ten);
+
+ if (scale) {
+ do {
+ if (!precision--) {
+ return nullptr;
+ }
+
+ *--s = '0';
+ } while (--scale);
+
+ *--s = '.';
+ }
+
+ if (*s == '.') {
+ *--s = '0';
+ }
+
+ if (neg) {
+ *--s = '-';
+ }
+
+ return s;
+}
+
+namespace {
+ bool IsNan(const char* s) {
+ return (s[0] == 'N' || s[0] == 'n') && (s[1] == 'A' || s[1] == 'a') && (s[2] == 'N' || s[2] == 'n');
+ }
+
+ bool IsInf(const char* s) {
+ return (s[0] == 'I' || s[0] == 'i') && (s[1] == 'N' || s[1] == 'n') && (s[2] == 'F' || s[2] == 'f');
+ }
+}
+
+
+TInt128 FromString(const TStringBuf& str, ui8 precision, ui8 scale) {
+ if (scale > precision)
+ return Err();
+
+ auto s = str.data();
+ auto l = str.size();
+
+ if (!s || !l)
+ return Err();
+
+ const bool neg = '-' == *s;
+ if (neg || '+' == *s) {
+ ++s;
+ --l;
+ }
+
+ if (3U == l) {
+ if (IsInf(s))
+ return neg ? -Inf() : Inf();
+ if (IsNan(s))
+ return Nan();
+ }
+
+ TUint128 v = 0U;
+ auto integral = precision - scale;
+
+ for (bool dot = false; l; --l) {
+ if (*s == '.') {
+ if (dot)
+ return Err();
+
+ ++s;
+ dot = true;
+ continue;
+ }
+
+ if (dot) {
+ if (scale)
+ --scale;
+ else
+ break;
+ }
+
+ const char c = *s++;
+ if (!std::isdigit(c))
+ return Err();
+
+ v *= Ten;
+ v += c - '0';
+
+ if (!dot && v && !integral--) {
+ return neg ? -Inf() : Inf();
+ }
+ }
+
+ if (l--) {
+ const char c = *s++;
+ if (!std::isdigit(c))
+ return Err();
+
+ bool plus = c > '5';
+ if (!plus && c == '5') {
+ for (plus = v & 1; !plus && l; --l) {
+ const char c = *s++;
+ if (!std::isdigit(c))
+ return Err();
+
+ plus = c != '0';
+ }
+ }
+
+ while (l--)
+ if (!std::isdigit(*s++))
+ return Err();
+
+ if (plus)
+ if (++v >= GetDivider(precision))
+ v = Inf();
+ }
+
+ while (scale--)
+ v *= Ten;
+
+ return neg ? -v : v;
+}
+
+TInt128 FromStringEx(const TStringBuf& str, ui8 precision, ui8 scale) {
+ if (scale > precision)
+ return Err();
+
+ const auto s = str.data();
+
+ for (auto ptr = s + str.size() - 1U; ptr > s; --ptr) {
+ if (*ptr == 'E' || *ptr == 'e') {
+ const auto len = ptr - s;
+ if (!len)
+ return Err();
+
+ ++ptr;
+ if (ptr != s + str.size() && *ptr == '+') {
+ ++ptr;
+ if (ptr != s + str.size() && *ptr == '-')
+ return Err();
+ }
+
+ int exp;
+ auto [finish, ec] = std::from_chars(ptr, s + str.size(), exp);
+ if (ec != std::errc() || finish != s + str.size())
+ return Err();
+
+ const int p = precision, s = int(scale) + exp;
+
+ const auto r = exp > 0 ?
+ FromString(str.Head(len), precision, std::min(s, p)):
+ FromString(str.Head(len), std::min(p - exp, int(MaxPrecision)), std::max(s, 0));
+
+ if (IsError(r) || IsNan(r)) {
+ return Err();
+ }
+
+ if (IsInf(r)) {
+ auto p = str.data();
+ if (*p == '+' || *p == '-')
+ ++p;
+
+ if (!std::isdigit(*p))
+ return Err();
+
+ return r;
+ }
+
+ if (const auto e = exp > 0 ? std::max(0, s - p) : std::min(0, s)) {
+ if (r) {
+ if (exp > 0)
+ return Mul(r, GetDivider(+e));
+ if (exp < 0)
+ return Div(r, GetDivider(-e));
+ }
+ }
+
+ return r;
+ }
+ }
+
+ return FromString(str, precision, scale);
+}
+
+bool IsValid(const TStringBuf& str) {
+ auto s = str.data();
+ auto l = str.size();
+
+ if (!s || !l)
+ return false;
+
+ if ('-' == *s || '+' == *s) {
+ ++s;
+ --l;
+ }
+
+ if (3U == l && (IsInf(s) || IsNan(s))) {
+ return true;
+ }
+
+ for (bool dot = false; l--;) {
+ const char c = *s++;
+ if (c == '.') {
+ if (dot)
+ return false;
+
+ dot = true;
+ continue;
+ }
+
+ if (!std::isdigit(c))
+ return false;
+ }
+
+ return true;
+}
+
+TInt128 Mod(TInt128 a, TInt128 b) {
+ if (!b || !(IsNormal(a) && IsNormal(b)))
+ return Nan();
+
+ return a % b;
+}
+
+TInt128 Div(TInt128 a, TInt128 b) {
+ if (IsNan(a) || IsNan(b))
+ return Nan();
+
+ if (!b) {
+ if (a > 0)
+ return Inf();
+ else if (a < 0)
+ return -Inf();
+ else
+ return Nan();
+ } else if (IsInf(b)) {
+ return IsInf(a) ? Nan() : TInt128(0);
+ } else if (IsInf(a)) {
+ return b > 0 ? a : -a;
+ }
+
+ if (b & 1)
+ a = TUint128(a) << 1U;
+ else
+ b >>= 1;
+
+ auto d = a / b;
+
+ if (d & 1) {
+ if (const auto m = a % b) {
+ if (m > 0) ++d;
+ // else --d;
+ } else {
+ if (d & 2) ++d;
+ }
+ }
+
+ return d >>= 1;
+}
+
+namespace {
+
+using TInt256 = TWide<TInt128, TInt128, TUint128>;
+
+TInt128 Normalize(const TInt256& v) {
+ static const TInt256 PInf256(+Inf()), NInf256(-Inf());
+
+ if (v > PInf256)
+ return +Inf();
+ if (v < NInf256)
+ return -Inf();
+ return *reinterpret_cast<const TInt128*>(&v);
+}
+
+constexpr auto HalfBitSize = sizeof(TUint128) << 2U;
+
+TUint128 GetUpperHalf(const TUint128& v) {
+ return v >> HalfBitSize;
+}
+
+TUint128 GetLowerHalf(const TUint128& v) {
+ return v & TUint128(0xFFFFFFFFFFFFFFFFULL);
+}
+
+TInt256 WidenMul(const TInt128& lhs, const TInt128& rhs) {
+ const bool nl = lhs < 0;
+ const bool nr = rhs < 0;
+
+ const TUint128 l = nl ? -lhs : +lhs;
+ const TUint128 r = nr ? -rhs : +rhs;
+
+ const TUint128 lh[] = {GetLowerHalf(l), GetUpperHalf(l)};
+ const TUint128 rh[] = {GetLowerHalf(r), GetUpperHalf(r)};
+
+ const TUint128 prods[] = {lh[0] * rh[0], lh[0] * rh[1], lh[1] * rh[0], lh[1] * rh[1]};
+
+ const TUint128 fourthQ = GetLowerHalf(prods[0]);
+ const TUint128 thirdQ = GetUpperHalf(prods[0]) + GetLowerHalf(prods[1]) + GetLowerHalf(prods[2]);
+ const TUint128 secondQ = GetUpperHalf(thirdQ) + GetUpperHalf(prods[1]) + GetUpperHalf(prods[2]) + GetLowerHalf(prods[3]);
+ const TUint128 firstQ = GetUpperHalf(secondQ) + GetUpperHalf(prods[3]);
+
+ const TInt256 combine((firstQ << HalfBitSize) | GetLowerHalf(secondQ), (thirdQ << HalfBitSize) | fourthQ);
+ return nl == nr ? +combine : -combine;
+}
+
+template<bool MayOddDivider>
+TInt256 Div(TInt256&& a, TInt256&& b) {
+ if (MayOddDivider && b & 1)
+ a <<= 1;
+ else
+ b >>= 1;
+
+ auto d = a / b;
+
+ if (d & 1) {
+ if (const auto m = a % b) {
+ if (m > 0) ++d;
+ // else --d;
+ } else {
+ if (d & 2) ++d;
+ }
+ }
+
+ return d >>= 1;
+}
+
+}
+
+TInt128 Mul(TInt128 a, TInt128 b) {
+ if (IsNan(a) || IsNan(b))
+ return Nan();
+
+ if (IsInf(a))
+ return !b ? Nan() : (b > 0 ? a : -a);
+
+ if (IsInf(b))
+ return !a ? Nan() : (a > 0 ? b : -b);
+
+ return Normalize(WidenMul(a, b));
+}
+
+TInt128 MulAndDivNormalMultiplier(TInt128 a, TInt128 b, TInt128 c) {
+ if (IsNan(a) || IsNan(c))
+ return Nan();
+
+ if (!c) {
+ if (a > 0)
+ return Inf();
+ else if (a < 0)
+ return -Inf();
+ else
+ return Nan();
+ } else if (IsInf(c)) {
+ return IsInf(a) ? Nan() : TInt128(0);
+ } else if (IsInf(a)) {
+ return c > 0 ? a : -a;
+ }
+
+ return Normalize(Div<true>(WidenMul(a, b), TInt256(c)));
+}
+
+TInt128 MulAndDivNormalDivider(TInt128 a, TInt128 b, TInt128 c) {
+ if (IsNan(a) || IsNan(b))
+ return Nan();
+
+ if (IsInf(a))
+ return !b ? Nan() : (b > 0 ? a : -a);
+
+ if (IsInf(b))
+ return !a ? Nan() : (a > 0 ? b : -b);
+
+ return Normalize(Div<false>(WidenMul(a, b), TInt256(c)));
+}
+
+}
+}
diff --git a/yql/essentials/public/decimal/yql_decimal.h b/yql/essentials/public/decimal/yql_decimal.h
new file mode 100644
index 0000000000..a53ed3ffa4
--- /dev/null
+++ b/yql/essentials/public/decimal/yql_decimal.h
@@ -0,0 +1,346 @@
+#pragma once
+
+#include <util/generic/strbuf.h>
+#include "yql_wide_int.h"
+
+#include <type_traits>
+#include <limits>
+
+namespace NYql {
+namespace NDecimal {
+
+#ifdef _win_
+#ifndef DONT_USE_NATIVE_INT128
+#define DONT_USE_NATIVE_INT128
+#endif
+#endif
+
+#ifdef DONT_USE_NATIVE_INT128
+using TInt128 = TWide<i64>;
+using TUint128 = TWide<ui64>;
+#else
+using TInt128 = signed __int128;
+using TUint128 = unsigned __int128;
+#endif
+
+template<ui8 Scale> struct TDivider;
+#if defined(__clang__) && defined(DONT_USE_NATIVE_INT128)
+template<> struct TDivider<0> { static inline constexpr TUint128 Value = 1U; };
+template<ui8 Scale> struct TDivider { static inline constexpr TInt128 Value = TDivider<Scale - 1U>::Value * 10U; };
+#else
+template<> struct TDivider<0> { static constexpr TUint128 Value = 1U; };
+template<ui8 Scale> struct TDivider { static constexpr TUint128 Value = TDivider<Scale - 1U>::Value * 10U; };
+#endif
+
+constexpr ui8 MaxPrecision = 35;
+
+static_assert(sizeof(TInt128) == 16, "Wrong size of TInt128, expected 16");
+
+inline constexpr TInt128 Inf() {
+ return TInt128(100000000000000000ULL) * TInt128(1000000000000000000ULL);
+}
+
+inline constexpr TInt128 Nan() {
+ return Inf() + TInt128(1);
+}
+
+inline constexpr TInt128 Err() {
+ return Nan() + TInt128(1);
+}
+
+TUint128 GetDivider(ui8 scale);
+
+template<ui8 Precision>
+inline constexpr TUint128 GetDivider() {
+ return TDivider<Precision>::Value;
+}
+
+template<ui8 Precision, bool IncLow = false, bool DecHigh = false>
+inline constexpr std::pair<TInt128, TInt128> GetBounds() {
+ return std::make_pair(-GetDivider<Precision>() + (IncLow ? 1 : 0), +GetDivider<Precision>() - (DecHigh ? 1 : 0));
+}
+
+bool IsError(TInt128 v);
+bool IsNan(TInt128 v);
+bool IsInf(TInt128 v);
+
+bool IsNormal(TInt128 v);
+bool IsComparable(TInt128 v);
+
+template<ui8 Precision>
+inline bool IsNormal(TInt128 v) {
+ const auto& b = GetBounds<Precision>();
+ return v > b.first && v < b.second;
+}
+
+const char* ToString(TInt128 v, ui8 precision, ui8 scale = 0);
+TInt128 FromString(const TStringBuf& str, ui8 precision, ui8 scale = 0);
+
+// Accept string representation with exponent.
+TInt128 FromStringEx(const TStringBuf& str, ui8 precision, ui8 scale);
+
+template<typename TMkqlProto>
+inline TInt128 FromProto(const TMkqlProto& val) {
+ ui64 half[2] = {val.GetLow128(), val.GetHi128()};
+ TInt128 val128;
+ std::memcpy(&val128, half, sizeof(val128));
+ return val128;
+}
+
+template<typename TValue>
+inline constexpr TValue YtDecimalNan() {
+ return std::numeric_limits<TValue>::max();
+}
+
+template<>
+inline constexpr TInt128 YtDecimalNan<TInt128>() {
+ return ~(TInt128(1) << 127);
+}
+
+template<typename TValue>
+inline constexpr TValue YtDecimalInf() {
+ return YtDecimalNan<TValue>() - 1;
+}
+
+template<typename TValue>
+inline TInt128 FromYtDecimal(TValue val) {
+ static_assert(std::is_same<TInt128, TValue>::value || std::is_signed<TValue>::value, "Expected signed value");
+ if (YtDecimalNan<TValue>() == val) {
+ return Nan();
+ } else if (YtDecimalInf<TValue>() == val) {
+ return Inf();
+ } else if (-YtDecimalInf<TValue>() == val) {
+ return -Inf();
+ } else {
+ return TInt128(val);
+ }
+}
+
+template<typename TValue>
+inline TValue ToYtDecimal(TInt128 val) {
+ static_assert(std::is_same<TInt128, TValue>::value || std::is_signed<TValue>::value, "Expected signed value");
+ if (IsNormal(val)) {
+ return (TValue)val;
+ } else if (val == Inf()) {
+ return YtDecimalInf<TValue>();
+ } else if (val == -Inf()) {
+ return -YtDecimalInf<TValue>();
+ }
+ return YtDecimalNan<TValue>();
+}
+
+inline TInt128 FromHalfs(ui64 lo, i64 hi) {
+ ui64 half[2] = {lo, static_cast<ui64>(hi)};
+ TInt128 val128;
+ std::memcpy(&val128, half, sizeof(val128));
+ return val128;
+}
+
+inline std::pair<ui64, ui64> MakePair(const TInt128 v) {
+ std::pair<ui64, ui64> r;
+ std::memcpy(&r, &v, sizeof(v));
+ return r;
+ static_assert(sizeof(r) == sizeof(v), "Bad pair size.");
+}
+
+bool IsValid(const TStringBuf& str);
+
+// Round to nearest, ties to even.
+TInt128 Div(TInt128 a, TInt128 b); // a/b
+TInt128 Mul(TInt128 a, TInt128 b); // a*b
+TInt128 Mod(TInt128 a, TInt128 b); // a%b
+
+// a*b/c Only for non zero even normal positive divider.
+TInt128 MulAndDivNormalDivider(TInt128 a, TInt128 b, TInt128 c);
+// a*b/c Only for non zero normal positive multiplier.
+TInt128 MulAndDivNormalMultiplier(TInt128 a, TInt128 b, TInt128 c);
+
+struct TDecimal {
+ TInt128 Value = 0;
+
+ TDecimal() = default;
+
+ template<typename T>
+ TDecimal(T t): Value(t) { }
+
+ explicit operator TInt128() const {
+ return Value;
+ }
+
+ TDecimal& operator+=(TDecimal right) {
+ const auto l = Value;
+ const auto r = right.Value;
+ const auto a = l + r;
+ if (IsNormal(l) && IsNormal(r) && IsNormal(a)) {
+ Value = a;
+ } else if (IsNan(l) || IsNan(r) || !a /* inf - inf*/) {
+ Value = Nan();
+ } else {
+ Value = a > 0
+ ? +Inf()
+ : -Inf();
+ }
+ return *this;
+ }
+
+ TDecimal& operator*=(TDecimal right) {
+ Value = Mul(Value, right.Value);
+ return *this;
+ }
+
+ TDecimal& operator/=(TDecimal right) {
+ Value = Div(Value, right.Value);
+ return *this;
+ }
+
+ friend TDecimal operator+(TDecimal left, TDecimal right) {
+ left += right;
+ return left;
+ }
+
+ friend TDecimal operator*(TDecimal left, TDecimal right) {
+ left *= right;
+ return left;
+ }
+
+ friend TDecimal operator/(TDecimal left, TDecimal right) {
+ left /= right;
+ return left;
+ }
+};
+
+template<typename TRight>
+class TDecimalMultiplicator {
+protected:
+ const TInt128 Bound;
+
+public:
+ TDecimalMultiplicator(
+ ui8 precision,
+ ui8 scale = 0 /* unused */)
+ : Bound(GetDivider(precision))
+ {
+ Y_UNUSED(scale);
+ }
+
+ TInt128 Do(TInt128 left, TRight right) const {
+ TInt128 mul = Mul(left, right);
+
+ if (mul > -Bound && mul < +Bound)
+ return mul;
+
+ return IsNan(mul) ? Nan() : (mul > 0 ? +Inf() : -Inf());
+ }
+};
+
+template<>
+class TDecimalMultiplicator<TInt128> {
+protected:
+ const TInt128 Bound;
+ const TInt128 Divider;
+
+public:
+ TDecimalMultiplicator(
+ ui8 precision,
+ ui8 scale)
+ : Bound(GetDivider(precision))
+ , Divider(GetDivider(scale))
+ { }
+
+ TInt128 Do(TInt128 left, TInt128 right) const {
+ TInt128 mul = Divider > 1 ?
+ MulAndDivNormalDivider(left, right, Divider):
+ Mul(left, right);
+
+ if (mul > -Bound && mul < +Bound)
+ return mul;
+
+ return IsNan(mul) ? Nan() : (mul > 0 ? +Inf() : -Inf());
+ }
+};
+
+template<typename TRight>
+class TDecimalDivisor {
+public:
+ TDecimalDivisor(
+ ui8 precision = 0 /* unused */,
+ ui8 scale = 0 /* unused */)
+ {
+ Y_UNUSED(precision);
+ Y_UNUSED(scale);
+ }
+
+ TInt128 Do(TInt128 left, TRight right) const {
+ return Div(left, right);
+ }
+};
+
+template<>
+class TDecimalDivisor<TInt128> {
+protected:
+ const TInt128 Bound;
+ const TInt128 Divider;
+
+public:
+ TDecimalDivisor(
+ ui8 precision,
+ ui8 scale)
+ : Bound(GetDivider(precision))
+ , Divider(GetDivider(scale))
+ { }
+
+ TInt128 Do(TInt128 left, TInt128 right) const {
+ TInt128 div = MulAndDivNormalMultiplier(left, Divider, right);
+ if (div > -Bound && div < +Bound) {
+ return div;
+ }
+
+ return IsNan(div) ? Nan() : (div > 0 ? +Inf() : -Inf());
+ }
+};
+
+template<typename TRight>
+class TDecimalRemainder {
+protected:
+ const TInt128 Bound;
+ const TInt128 Divider;
+
+public:
+ TDecimalRemainder(
+ ui8 precision,
+ ui8 scale)
+ : Bound(NYql::NDecimal::GetDivider(precision - scale))
+ , Divider(NYql::NDecimal::GetDivider(scale))
+ { }
+
+ TInt128 Do(TInt128 left, TRight right) const {
+ if constexpr (std::is_signed<TRight>::value) {
+ if (TInt128(right) >= +Bound || TInt128(right) <= -Bound)
+ return left;
+ } else {
+ if (TInt128(right) >= Bound)
+ return left;
+ }
+
+ return Mod(left, Mul(Divider, right));
+ }
+};
+
+template<>
+class TDecimalRemainder<TInt128> {
+public:
+ TDecimalRemainder(
+ ui8 precision = 0 /*unused*/,
+ ui8 scale = 0 /*unused*/)
+ {
+ Y_UNUSED(precision);
+ Y_UNUSED(scale);
+ }
+
+ TInt128 Do(TInt128 left, TInt128 right) const {
+ return NYql::NDecimal::Mod(left, right);
+ }
+};
+
+}
+}
diff --git a/yql/essentials/public/decimal/yql_decimal_serialize.cpp b/yql/essentials/public/decimal/yql_decimal_serialize.cpp
new file mode 100644
index 0000000000..5fb844f5f8
--- /dev/null
+++ b/yql/essentials/public/decimal/yql_decimal_serialize.cpp
@@ -0,0 +1,82 @@
+#include "yql_decimal_serialize.h"
+
+#include <utility>
+
+namespace NYql {
+namespace NDecimal {
+
+size_t Serialize(TInt128 value, char* buf) {
+ if (value == -Nan()) {
+ *buf = 0x00;
+ return 1U;
+ }
+
+ if (value == -Inf()) {
+ *buf = 0x01;
+ return 1U;
+ }
+
+ if (value == +Inf()) {
+ *buf = 0xFE;
+ return 1U;
+ }
+
+ if (value == +Nan()) {
+ *buf = 0xFF;
+ return 1U;
+ }
+
+ auto size = sizeof(value);
+ auto p = reinterpret_cast<const char*>(&value) + size - 1U;
+
+ if (*(p - 1U) & 0x80) {
+ while (size > 1U && ~0 == *--p)
+ --size;
+ *buf = 0x80 - size;
+ } else {
+ while (size > 1U && 0 == *--p)
+ --size;
+ *buf = 0x7F + size;
+ }
+
+ for (auto i = 1U; i < size; ++i) {
+ *++buf = *p--;
+ }
+
+ return size;
+}
+
+std::pair<TInt128, size_t> Deserialize(const char* b, size_t len) {
+ if (!b || len == 0U)
+ return std::make_pair(Err(), 0U);
+
+ const auto mark = ui8(*b);
+ const bool neg = mark < 0x80u;
+ if (mark == 0x00u || mark == 0xFFu)
+ return std::make_pair(neg ? -Nan() : +Nan(), 1U);
+ if (mark == 0x01u || mark == 0xFEu)
+ return std::make_pair(neg ? -Inf() : +Inf(), 1U);
+
+ if (mark < 0x70u || mark > 0x8Fu) {
+ return std::make_pair(Err(), 0U);
+ }
+
+ const auto used = neg ? 0x80u - mark : mark - 0x7Fu;
+ if (len < used)
+ return std::make_pair(Err(), 0U);
+
+ TInt128 v;
+ const auto size = sizeof(v);
+ auto p = reinterpret_cast<char*>(&v) + size;
+
+ for (auto fill = size - used + 2U; --fill;)
+ *--p = neg ? ~0 : 0;
+
+ for (auto copy = used; --copy;)
+ *--p = *++b;
+
+ return std::make_pair(v, used);
+}
+
+}
+}
diff --git a/yql/essentials/public/decimal/yql_decimal_serialize.h b/yql/essentials/public/decimal/yql_decimal_serialize.h
new file mode 100644
index 0000000000..6fdb32e6f2
--- /dev/null
+++ b/yql/essentials/public/decimal/yql_decimal_serialize.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <yql/essentials/public/decimal/yql_decimal.h>
+
+namespace NYql {
+namespace NDecimal {
+
+// big-endian 16 bytes buffer.
+size_t Serialize(TInt128 v, char* buff);
+std::pair<TInt128, size_t> Deserialize(const char* buff, size_t len);
+
+}
+}
diff --git a/yql/essentials/public/decimal/yql_wide_int.h b/yql/essentials/public/decimal/yql_wide_int.h
new file mode 100644
index 0000000000..0f650213b9
--- /dev/null
+++ b/yql/essentials/public/decimal/yql_wide_int.h
@@ -0,0 +1,344 @@
+#pragma once
+
+#include <util/system/types.h>
+
+#include <type_traits>
+#include <tuple>
+#include <cmath>
+
+namespace NYql {
+
+#ifndef _win_
+typedef __int128 i128_t;
+typedef unsigned __int128 ui128_t;
+#endif
+
+template<typename TOneHalf, typename TSignedPart = std::make_signed_t<TOneHalf>, typename TUnsignedPart = std::make_unsigned_t<TOneHalf>>
+class TWide {
+private:
+ using THalf = TOneHalf;
+ using TPart = TUnsignedPart;
+
+ using TIsSigned = std::is_same<THalf, TSignedPart>;
+ using TIsUnsigned = std::is_same<THalf, TUnsignedPart>;
+
+ static_assert(TIsSigned::value || TIsUnsigned::value, "Invalid using of TWide.");
+ static_assert(sizeof(TSignedPart) == sizeof(TUnsignedPart), "Different sizes of TWide parts.");
+
+ TPart Lo;
+ THalf Hi;
+
+ static constexpr auto FullBitSize = sizeof(TPart) << 4U;
+ static constexpr auto PartBitSize = sizeof(TPart) << 3U;
+ static constexpr auto QuarterBitSize = sizeof(TPart) << 2U;
+
+ static constexpr TPart GetUpperQuarter(TPart h) {
+ return h >> QuarterBitSize;
+ }
+
+ static constexpr TPart GetLowerQuarter(TPart h) {
+ const auto mask = TPart(~TPart(0U)) >> QuarterBitSize;
+ return h & mask;
+ }
+
+ template<typename T>
+ constexpr std::enable_if_t<sizeof(T) <= sizeof(THalf), T> CastImpl() const {
+ return static_cast<T>(Lo);
+ }
+
+ template<typename T>
+ constexpr std::enable_if_t<sizeof(T) == sizeof(THalf) << 1U, T> CastImpl() const {
+ return *reinterpret_cast<const T*>(this);
+ }
+
+ template<typename T>
+ constexpr std::enable_if_t<sizeof(THalf) << 1U < sizeof(T), T> CastImpl() const {
+ return (T(std::make_unsigned_t<T>(Hi) << PartBitSize)) | Lo;
+ }
+
+ constexpr size_t GetBits() const {
+ size_t out = Hi ? PartBitSize : 0U;
+ for (auto up = TPart(out ? Hi : Lo); up; up >>= 1U) {
+ ++out;
+ }
+ return out;
+ }
+
+ using TUnsigned = TWide<TUnsignedPart, TSignedPart, TUnsignedPart>;
+
+ using TSibling = std::conditional_t<std::is_same<THalf, TPart>::value,
+ TWide<TSignedPart, TSignedPart, TUnsignedPart>, TWide<TUnsignedPart, TSignedPart, TUnsignedPart>>;
+ friend TSibling;
+public:
+ constexpr TWide() = default;
+ constexpr TWide(const TWide& rhs) = default;
+ constexpr TWide(TWide&& rhs) = default;
+
+ constexpr TWide& operator=(const TWide& rhs) = default;
+ constexpr TWide& operator=(TWide&& rhs) = default;
+
+ constexpr TWide(const TSibling& rhs)
+ : Lo(rhs.Lo), Hi(rhs.Hi)
+ {}
+
+ template<typename U, typename L>
+ constexpr TWide(const U upper_rhs, const L lower_rhs)
+ : Lo(lower_rhs), Hi(upper_rhs)
+ {}
+
+ template <typename T, std::enable_if_t<std::is_integral<T>::value && sizeof(THalf) < sizeof(T), size_t> Shift = PartBitSize>
+ constexpr TWide(const T rhs)
+ : Lo(rhs), Hi(rhs >> Shift)
+ {}
+
+ template <typename T, std::enable_if_t<std::is_integral<T>::value && sizeof(T) <= sizeof(THalf), bool> Signed = std::is_signed<T>::value>
+ constexpr TWide(const T rhs)
+ : Lo(rhs), Hi(Signed && rhs < 0 ? ~0 : 0)
+ {}
+
+ template <typename T, typename TArg = std::enable_if_t<std::is_class<T>::value && std::is_same<T, THalf>::value, THalf>>
+ constexpr explicit TWide(const T& rhs)
+ : Lo(rhs), Hi(TIsSigned::value && rhs < 0 ? ~0 : 0)
+ {}
+
+ template <typename T, std::enable_if_t<std::is_integral<T>::value && sizeof(THalf) < sizeof(T), size_t> Shift = PartBitSize>
+ constexpr TWide& operator=(const T rhs) {
+ Hi = rhs >> Shift;
+ Lo = rhs;
+ return *this;
+ }
+
+ template <typename T, std::enable_if_t<std::is_integral<T>::value && sizeof(T) <= sizeof(THalf), bool> Signed = std::is_signed<T>::value>
+ constexpr TWide& operator=(const T rhs) {
+ Hi = Signed && rhs < 0 ? ~0 : 0;
+ Lo = rhs;
+ return *this;
+ }
+
+ constexpr explicit operator bool() const { return bool(Hi) || bool(Lo); }
+
+ constexpr explicit operator i8() const { return CastImpl<i8>(); }
+ constexpr explicit operator ui8() const { return CastImpl<ui8>(); }
+ constexpr explicit operator i16() const { return CastImpl<i16>(); }
+ constexpr explicit operator ui16() const { return CastImpl<ui16>(); }
+ constexpr explicit operator i32() const { return CastImpl<i32>(); }
+ constexpr explicit operator ui32() const { return CastImpl<ui32>(); }
+ constexpr explicit operator i64() const { return CastImpl<i64>(); }
+ constexpr explicit operator ui64() const { return CastImpl<ui64>(); }
+#ifndef _win_
+ constexpr explicit operator i128_t() const { return CastImpl<i128_t>(); }
+ constexpr explicit operator ui128_t() const { return CastImpl<ui128_t>(); }
+#endif
+ constexpr explicit operator float() const {
+ return TIsSigned::value && Hi < 0 ? -float(-*this) : std::fma(float(Hi), std::exp2(float(PartBitSize)), float(Lo));
+ }
+
+ constexpr explicit operator double() const {
+ return TIsSigned::value && Hi < 0 ? -double(-*this) : std::fma(double(Hi), std::exp2(double(PartBitSize)), double(Lo));
+ }
+
+ constexpr TWide operator~() const {
+ return TWide(~Hi, ~Lo);
+ }
+
+ constexpr TWide operator+() const {
+ return TWide(Hi, Lo);
+ }
+
+ constexpr TWide operator-() const {
+ const auto sign = THalf(1) << PartBitSize - 1U;
+ if (TIsSigned::value && !Lo && Hi == sign)
+ return *this;
+ return ++~*this;
+ }
+
+ constexpr TWide& operator++() {
+ if (!++Lo) ++Hi;
+ return *this;
+ }
+
+ constexpr TWide& operator--() {
+ if (!Lo--) --Hi;
+ return *this;
+ }
+
+ constexpr TWide operator++(int) {
+ const TWide r(*this);
+ ++*this;
+ return r;
+ }
+
+ constexpr TWide operator--(int) {
+ const TWide r(*this);
+ --*this;
+ return r;
+ }
+
+ constexpr TWide& operator&=(const TWide& rhs) {
+ Hi &= rhs.Hi;
+ Lo &= rhs.Lo;
+ return *this;
+ }
+
+ constexpr TWide& operator|=(const TWide& rhs) {
+ Hi |= rhs.Hi;
+ Lo |= rhs.Lo;
+ return *this;
+ }
+
+ constexpr TWide& operator^=(const TWide& rhs) {
+ Hi ^= rhs.Hi;
+ Lo ^= rhs.Lo;
+ return *this;
+ }
+
+ constexpr TWide& operator+=(const TWide& rhs) {
+ const auto l = Lo;
+ Lo += rhs.Lo;
+ Hi += rhs.Hi;
+ if (l > Lo) ++Hi;
+ return *this;
+ }
+
+ constexpr TWide& operator-=(const TWide& rhs) {
+ const auto l = Lo;
+ Lo -= rhs.Lo;
+ Hi -= rhs.Hi;
+ if (l < Lo) --Hi;
+ return *this;
+ }
+
+ constexpr TWide& operator<<=(const TWide& rhs) {
+ if (const auto shift = size_t(rhs.Lo) % FullBitSize) {
+ if (shift < PartBitSize) {
+ Hi = TPart(Hi) << shift;
+ Hi |= Lo >> (PartBitSize - shift);
+ Lo <<= shift;
+ } else {
+ Hi = Lo << (shift - PartBitSize);
+ Lo = 0;
+ }
+ }
+
+ return *this;
+ }
+
+ constexpr TWide& operator>>=(const TWide& rhs) {
+ if (const auto shift = size_t(rhs.Lo) % FullBitSize) {
+ if (shift < PartBitSize) {
+ Lo >>= shift;
+ Lo |= TPart(Hi) << (PartBitSize - shift);
+ Hi >>= shift;
+ } else {
+ Lo = Hi >> (shift - PartBitSize);
+ Hi = TIsSigned::value && Hi < 0 ? ~0 : 0;
+ }
+ }
+
+ return *this;
+ }
+
+ constexpr TWide& operator*=(const TWide& rhs) { return *this = Mul(*this, rhs); }
+ constexpr TWide& operator/=(const TWide& rhs) { return *this = DivMod(*this, rhs).first; }
+ constexpr TWide& operator%=(const TWide& rhs) { return *this = DivMod(*this, rhs).second; }
+
+ constexpr TWide operator&(const TWide& rhs) const { return TWide(*this) &= rhs; }
+ constexpr TWide operator|(const TWide& rhs) const { return TWide(*this) |= rhs; }
+ constexpr TWide operator^(const TWide& rhs) const { return TWide(*this) ^= rhs; }
+ constexpr TWide operator+(const TWide& rhs) const { return TWide(*this) += rhs; }
+ constexpr TWide operator-(const TWide& rhs) const { return TWide(*this) -= rhs; }
+ constexpr TWide operator*(const TWide& rhs) const { return Mul(*this, rhs); }
+ constexpr TWide operator/(const TWide& rhs) const { return DivMod(*this, rhs).first; }
+ constexpr TWide operator%(const TWide& rhs) const { return DivMod(*this, rhs).second; }
+ constexpr TWide operator<<(const TWide& rhs) const { return TWide(*this) <<= rhs; }
+ constexpr TWide operator>>(const TWide& rhs) const { return TWide(*this) >>= rhs; }
+
+ template <typename T>
+ constexpr std::enable_if_t<std::is_integral<T>::value, T> operator&(const T rhs) const { return T(*this) & rhs; }
+
+ constexpr bool operator==(const TWide& rhs) const { return std::tie(Hi, Lo) == std::tie(rhs.Hi, rhs.Lo); }
+ constexpr bool operator!=(const TWide& rhs) const { return std::tie(Hi, Lo) != std::tie(rhs.Hi, rhs.Lo); }
+ constexpr bool operator>=(const TWide& rhs) const { return std::tie(Hi, Lo) >= std::tie(rhs.Hi, rhs.Lo); }
+ constexpr bool operator<=(const TWide& rhs) const { return std::tie(Hi, Lo) <= std::tie(rhs.Hi, rhs.Lo); }
+ constexpr bool operator>(const TWide& rhs) const { return std::tie(Hi, Lo) > std::tie(rhs.Hi, rhs.Lo); }
+ constexpr bool operator<(const TWide& rhs) const { return std::tie(Hi, Lo) < std::tie(rhs.Hi, rhs.Lo); }
+
+private:
+ static constexpr TWide Mul(const TWide& lhs, const TWide& rhs) {
+ const TPart lq[] = {GetLowerQuarter(lhs.Lo), GetUpperQuarter(lhs.Lo), GetLowerQuarter(lhs.Hi), GetUpperQuarter(lhs.Hi)};
+ const TPart rq[] = {GetLowerQuarter(rhs.Lo), GetUpperQuarter(rhs.Lo), GetLowerQuarter(rhs.Hi), GetUpperQuarter(rhs.Hi)};
+
+ const TPart prod0[] = {TPart(lq[0] * rq[0])};
+ const TPart prod1[] = {TPart(lq[0] * rq[1]), TPart(lq[1] * rq[0])};
+ const TPart prod2[] = {TPart(lq[0] * rq[2]), TPart(lq[1] * rq[1]), TPart(lq[2] * rq[0])};
+ const TPart prod3[] = {TPart(lq[0] * rq[3]), TPart(lq[1] * rq[2]), TPart(lq[2] * rq[1]), TPart(lq[3] * rq[0])};
+
+ const TPart fourthQ = GetLowerQuarter(prod0[0]);
+ const TPart thirdQ = GetUpperQuarter(prod0[0])
+ + GetLowerQuarter(prod1[0]) + GetLowerQuarter(prod1[1]);
+ const TPart secondQ = GetUpperQuarter(thirdQ)
+ + GetUpperQuarter(prod1[0]) + GetUpperQuarter(prod1[1])
+ + GetLowerQuarter(prod2[0]) + GetLowerQuarter(prod2[1]) + GetLowerQuarter(prod2[2]);
+ const TPart firstQ = GetUpperQuarter(secondQ)
+ + GetUpperQuarter(prod2[0]) + GetUpperQuarter(prod2[1]) + GetUpperQuarter(prod2[2])
+ + GetLowerQuarter(prod3[0]) + GetLowerQuarter(prod3[1]) + GetLowerQuarter(prod3[2]) + GetLowerQuarter(prod3[3]);
+
+ return TWide((firstQ << QuarterBitSize) | GetLowerQuarter(secondQ), (thirdQ << QuarterBitSize) | fourthQ);
+ }
+
+ static constexpr std::pair<TWide, TWide> DivMod(const TWide& lhs, const TWide& rhs) {
+ const bool nl = TIsSigned::value && lhs.Hi < 0;
+ const bool nr = TIsSigned::value && rhs.Hi < 0;
+
+ const TUnsigned l = nl ? -lhs : +lhs, r = nr ? -rhs : +rhs;
+
+ TUnsigned div = 0, mod = 0;
+
+ for (auto x = l.GetBits(); x;) {
+ mod <<= 1;
+ div <<= 1;
+
+ if (--x < PartBitSize ? l.Lo & (TPart(1U) << x) : l.Hi & (TPart(1U) << x - PartBitSize)) {
+ ++mod;
+ }
+
+ if (mod >= r) {
+ mod -= r;
+ ++div;
+ }
+ }
+
+ if (nl) mod = -mod;
+ if (nr != nl) div = -div;
+
+ return {div, mod};
+ }
+};
+
+template<typename T> struct THalfOf;
+template<> struct THalfOf<i16> { typedef i8 Type; };
+template<> struct THalfOf<ui16> { typedef ui8 Type; };
+template<> struct THalfOf<i32> { typedef i16 Type; };
+template<> struct THalfOf<ui32> { typedef ui16 Type; };
+template<> struct THalfOf<i64> { typedef i32 Type; };
+template<> struct THalfOf<ui64> { typedef ui32 Type; };
+#ifndef _win_
+template<> struct THalfOf<i128_t> { typedef i64 Type; };
+template<> struct THalfOf<ui128_t> { typedef ui64 Type; };
+#endif
+template<typename T> struct THalfOf {};
+
+template<typename T> struct TPairOf;
+template<> struct TPairOf<i8> { typedef i16 Type; };
+template<> struct TPairOf<ui8> { typedef ui16 Type; };
+template<> struct TPairOf<i16> { typedef i32 Type; };
+template<> struct TPairOf<ui16> { typedef ui32 Type; };
+template<> struct TPairOf<i32> { typedef i64 Type; };
+template<> struct TPairOf<ui32> { typedef ui64 Type; };
+#ifndef _win_
+template<> struct TPairOf<i64> { typedef i128_t Type; };
+template<> struct TPairOf<ui64> { typedef ui128_t Type; };
+#endif
+template<typename T> struct TPairOf {};
+
+}
diff --git a/yql/essentials/public/ya.make b/yql/essentials/public/ya.make
index 57308f1d25..182e389b07 100644
--- a/yql/essentials/public/ya.make
+++ b/yql/essentials/public/ya.make
@@ -1,4 +1,5 @@
RECURSE(
+ decimal
issue
result_format
types