diff options
Diffstat (limited to 'libc/test/src/__support/uint_test.cpp')
-rw-r--r-- | libc/test/src/__support/uint_test.cpp | 192 |
1 files changed, 191 insertions, 1 deletions
diff --git a/libc/test/src/__support/uint_test.cpp b/libc/test/src/__support/uint_test.cpp index 5764324ca288..5696e54c73f3 100644 --- a/libc/test/src/__support/uint_test.cpp +++ b/libc/test/src/__support/uint_test.cpp @@ -8,6 +8,7 @@ #include "src/__support/CPP/optional.h" #include "src/__support/UInt.h" +#include "src/__support/integer_literals.h" // parse_unsigned_bigint #include "src/__support/macros/properties/types.h" // LIBC_TYPES_HAS_INT128 #include "include/llvm-libc-macros/math-macros.h" // HUGE_VALF, HUGE_VALF @@ -15,6 +16,195 @@ namespace LIBC_NAMESPACE { +enum Value { ZERO, ONE, TWO, MIN, MAX }; + +template <typename T> auto create(Value value) { + switch (value) { + case ZERO: + return T(0); + case ONE: + return T(1); + case TWO: + return T(2); + case MIN: + return T::min(); + case MAX: + return T::max(); + } +} + +using Types = testing::TypeList< // +#ifdef LIBC_TYPES_HAS_INT64 + BigInt<64, false, uint64_t>, // 64-bits unsigned (1 x uint64_t) + BigInt<64, true, uint64_t>, // 64-bits signed (1 x uint64_t) +#endif +#ifdef LIBC_TYPES_HAS_INT128 + BigInt<128, false, __uint128_t>, // 128-bits unsigned (1 x __uint128_t) + BigInt<128, true, __uint128_t>, // 128-bits signed (1 x __uint128_t) +#endif + BigInt<16, false, uint16_t>, // 16-bits unsigned (1 x uint16_t) + BigInt<16, true, uint16_t>, // 16-bits signed (1 x uint16_t) + BigInt<64, false, uint16_t>, // 64-bits unsigned (4 x uint16_t) + BigInt<64, true, uint16_t> // 64-bits signed (4 x uint16_t) + >; + +#define ASSERT_SAME(A, B) ASSERT_TRUE((A) == (B)) + +TYPED_TEST(LlvmLibcUIntClassTest, Additions, Types) { + ASSERT_SAME(create<T>(ZERO) + create<T>(ZERO), create<T>(ZERO)); + ASSERT_SAME(create<T>(ONE) + create<T>(ZERO), create<T>(ONE)); + ASSERT_SAME(create<T>(ZERO) + create<T>(ONE), create<T>(ONE)); + ASSERT_SAME(create<T>(ONE) + create<T>(ONE), create<T>(TWO)); + // 2's complement addition works for signed and unsigned types. + // - unsigned : 0xff + 0x01 = 0x00 (255 + 1 = 0) + // - signed : 0xef + 0x01 = 0xf0 (127 + 1 = -128) + ASSERT_SAME(create<T>(MAX) + create<T>(ONE), create<T>(MIN)); +} + +TYPED_TEST(LlvmLibcUIntClassTest, Subtraction, Types) { + ASSERT_SAME(create<T>(ZERO) - create<T>(ZERO), create<T>(ZERO)); + ASSERT_SAME(create<T>(ONE) - create<T>(ONE), create<T>(ZERO)); + ASSERT_SAME(create<T>(ONE) - create<T>(ZERO), create<T>(ONE)); + // 2's complement subtraction works for signed and unsigned types. + // - unsigned : 0x00 - 0x01 = 0xff ( 0 - 1 = 255) + // - signed : 0xf0 - 0x01 = 0xef (-128 - 1 = 127) + ASSERT_SAME(create<T>(MIN) - create<T>(ONE), create<T>(MAX)); +} + +TYPED_TEST(LlvmLibcUIntClassTest, Multiplication, Types) { + ASSERT_SAME(create<T>(ZERO) * create<T>(ZERO), create<T>(ZERO)); + ASSERT_SAME(create<T>(ZERO) * create<T>(ONE), create<T>(ZERO)); + ASSERT_SAME(create<T>(ONE) * create<T>(ZERO), create<T>(ZERO)); + ASSERT_SAME(create<T>(ONE) * create<T>(ONE), create<T>(ONE)); + ASSERT_SAME(create<T>(ONE) * create<T>(TWO), create<T>(TWO)); + ASSERT_SAME(create<T>(TWO) * create<T>(ONE), create<T>(TWO)); + // - unsigned : 0xff x 0xff = 0x01 (mod 0xff) + // - signed : 0xef x 0xef = 0x01 (mod 0xff) + ASSERT_SAME(create<T>(MAX) * create<T>(MAX), create<T>(ONE)); +} + +template <typename T> void print(const char *msg, T value) { + testing::tlog << msg; + IntegerToString<T, radix::Hex> buffer(value); + testing::tlog << buffer.view() << "\n"; +} + +TEST(LlvmLibcUIntClassTest, SignedAddSub) { + // Computations performed by https://www.wolframalpha.com/ + using T = BigInt<128, true, uint32_t>; + const T a = parse_bigint<T>("1927508279017230597"); + const T b = parse_bigint<T>("278789278723478925"); + const T s = parse_bigint<T>("2206297557740709522"); + // Addition + ASSERT_SAME(a + b, s); + ASSERT_SAME(b + a, s); // commutative + // Subtraction + ASSERT_SAME(a - s, -b); + ASSERT_SAME(s - a, b); +} + +TEST(LlvmLibcUIntClassTest, SignedMulDiv) { + // Computations performed by https://www.wolframalpha.com/ + using T = BigInt<128, true, uint16_t>; + struct { + const char *a; + const char *b; + const char *mul; + } const test_cases[] = {{"-4", "3", "-12"}, + {"-3", "-3", "9"}, + {"1927508279017230597", "278789278723478925", + "537368642840747885329125014794668225"}}; + for (auto tc : test_cases) { + const T a = parse_bigint<T>(tc.a); + const T b = parse_bigint<T>(tc.b); + const T mul = parse_bigint<T>(tc.mul); + // Multiplication + ASSERT_SAME(a * b, mul); + ASSERT_SAME(b * a, mul); // commutative + ASSERT_SAME(a * -b, -mul); // sign + ASSERT_SAME(-a * b, -mul); // sign + ASSERT_SAME(-a * -b, mul); // sign + // Division + ASSERT_SAME(mul / a, b); + ASSERT_SAME(mul / b, a); + ASSERT_SAME(-mul / a, -b); // sign + ASSERT_SAME(mul / -a, -b); // sign + ASSERT_SAME(-mul / -a, b); // sign + } +} + +TYPED_TEST(LlvmLibcUIntClassTest, Division, Types) { + ASSERT_SAME(create<T>(ZERO) / create<T>(ONE), create<T>(ZERO)); + ASSERT_SAME(create<T>(MAX) / create<T>(ONE), create<T>(MAX)); + ASSERT_SAME(create<T>(MAX) / create<T>(MAX), create<T>(ONE)); + ASSERT_SAME(create<T>(ONE) / create<T>(ONE), create<T>(ONE)); + if constexpr (T::SIGNED) { + // Special case found by fuzzing. + ASSERT_SAME(create<T>(MIN) / create<T>(MIN), create<T>(ONE)); + } + // - unsigned : 0xff / 0x02 = 0x7f + // - signed : 0xef / 0x02 = 0x77 + ASSERT_SAME(create<T>(MAX) / create<T>(TWO), (create<T>(MAX) >> 1)); + + using word_type = typename T::word_type; + const T zero_one_repeated = T::all_ones() / T(0xff); + const word_type pattern = word_type(~0) / word_type(0xff); + for (const word_type part : zero_one_repeated.val) { + if constexpr (T::SIGNED == false) { + EXPECT_EQ(part, pattern); + } + } +} + +TYPED_TEST(LlvmLibcUIntClassTest, is_neg, Types) { + EXPECT_FALSE(create<T>(ZERO).is_neg()); + EXPECT_FALSE(create<T>(ONE).is_neg()); + EXPECT_FALSE(create<T>(TWO).is_neg()); + EXPECT_EQ(create<T>(MIN).is_neg(), T::SIGNED); + EXPECT_FALSE(create<T>(MAX).is_neg()); +} + +TYPED_TEST(LlvmLibcUIntClassTest, Masks, Types) { + if constexpr (!T::SIGNED) { + constexpr size_t BITS = T::BITS; + // mask_trailing_ones + ASSERT_SAME((mask_trailing_ones<T, 0>()), T::zero()); + ASSERT_SAME((mask_trailing_ones<T, 1>()), T::one()); + ASSERT_SAME((mask_trailing_ones<T, BITS - 1>()), T::all_ones() >> 1); + ASSERT_SAME((mask_trailing_ones<T, BITS>()), T::all_ones()); + // mask_leading_ones + ASSERT_SAME((mask_leading_ones<T, 0>()), T::zero()); + ASSERT_SAME((mask_leading_ones<T, 1>()), T::one() << (BITS - 1)); + ASSERT_SAME((mask_leading_ones<T, BITS - 1>()), T::all_ones() - T::one()); + ASSERT_SAME((mask_leading_ones<T, BITS>()), T::all_ones()); + // mask_trailing_zeros + ASSERT_SAME((mask_trailing_zeros<T, 0>()), T::all_ones()); + ASSERT_SAME((mask_trailing_zeros<T, 1>()), T::all_ones() - T::one()); + ASSERT_SAME((mask_trailing_zeros<T, BITS - 1>()), T::one() << (BITS - 1)); + ASSERT_SAME((mask_trailing_zeros<T, BITS>()), T::zero()); + // mask_trailing_zeros + ASSERT_SAME((mask_leading_zeros<T, 0>()), T::all_ones()); + ASSERT_SAME((mask_leading_zeros<T, 1>()), T::all_ones() >> 1); + ASSERT_SAME((mask_leading_zeros<T, BITS - 1>()), T::one()); + ASSERT_SAME((mask_leading_zeros<T, BITS>()), T::zero()); + } +} + +TYPED_TEST(LlvmLibcUIntClassTest, CountBits, Types) { + if constexpr (!T::SIGNED) { + for (size_t i = 0; i <= T::BITS; ++i) { + const auto l_one = T::all_ones() << i; // 0b111...000 + const auto r_one = T::all_ones() >> i; // 0b000...111 + const int zeros = i; + const int ones = T::BITS - zeros; + ASSERT_EQ(cpp::countr_one(r_one), ones); + ASSERT_EQ(cpp::countl_one(l_one), ones); + ASSERT_EQ(cpp::countr_zero(l_one), zeros); + ASSERT_EQ(cpp::countl_zero(r_one), zeros); + } + } +} + using LL_UInt64 = UInt<64>; // We want to test UInt<128> explicitly. So, for // convenience, we use a sugar which does not conflict with the UInt128 type @@ -561,7 +751,7 @@ TEST(LlvmLibcUIntClassTest, FullMulTests) { LL_UInt##Bits a = ~LL_UInt##Bits(0); \ LL_UInt##Bits hi = a.quick_mul_hi(a); \ LL_UInt##Bits trunc = static_cast<LL_UInt##Bits>(a.ful_mul(a) >> Bits); \ - uint64_t overflow = trunc.sub(hi); \ + uint64_t overflow = trunc.sub_overflow(hi); \ EXPECT_EQ(overflow, uint64_t(0)); \ EXPECT_LE(uint64_t(trunc), uint64_t(Error)); \ } while (0) |