/* * Copyright (c) 2026-present, the Ladybird developers. * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include // Normal signed add (nowhere near saturation) TEST_CASE(signed_add_normal) { EXPECT_EQ(saturating_add(0, 0), 0); EXPECT_EQ(saturating_add(1, 1), 2); EXPECT_EQ(saturating_add(3, 4), 7); EXPECT_EQ(saturating_add(-3, -4), -7); EXPECT_EQ(saturating_add(3, -4), -1); EXPECT_EQ(saturating_add(-3, 4), 1); EXPECT_EQ(saturating_add(100, 200), 300); EXPECT_EQ(saturating_add(-100, -200), -300); EXPECT_EQ(saturating_add(100, -200), -100); EXPECT_EQ(saturating_add(1000000, 2000000), 3000000); } // Signed add: cross-boundary (large values that cancel out, should NOT saturate) TEST_CASE(signed_add_cross_boundary) { EXPECT_EQ(saturating_add(NumericLimits::max(), NumericLimits::min()), -1); EXPECT_EQ(saturating_add(NumericLimits::min(), NumericLimits::max()), -1); EXPECT_EQ(saturating_add(NumericLimits::max(), -NumericLimits::max()), 0); EXPECT_EQ(saturating_add(NumericLimits::max() / 2, -(NumericLimits::max() / 2)), 0); } // Signed add: off-by-one at saturation point TEST_CASE(signed_add_off_by_one) { // Exactly at max: no saturation EXPECT_EQ(saturating_add(NumericLimits::max() - 1, 1), NumericLimits::max()); EXPECT_EQ(saturating_add(NumericLimits::max() - 10, 10), NumericLimits::max()); // One past max: saturates EXPECT_EQ(saturating_add(NumericLimits::max(), 1), NumericLimits::max()); EXPECT_EQ(saturating_add(NumericLimits::max() - 9, 10), NumericLimits::max()); // Exactly at min: no saturation EXPECT_EQ(saturating_add(NumericLimits::min() + 1, -1), NumericLimits::min()); EXPECT_EQ(saturating_add(NumericLimits::min() + 10, -10), NumericLimits::min()); // One past min: saturates EXPECT_EQ(saturating_add(NumericLimits::min(), -1), NumericLimits::min()); EXPECT_EQ(saturating_add(NumericLimits::min() + 9, -10), NumericLimits::min()); } // Signed add: extreme overflow TEST_CASE(signed_add_extreme_overflow) { EXPECT_EQ(saturating_add(NumericLimits::max(), NumericLimits::max()), NumericLimits::max()); EXPECT_EQ(saturating_add(NumericLimits::min(), NumericLimits::min()), NumericLimits::min()); } // Signed add: identity TEST_CASE(signed_add_identity) { EXPECT_EQ(saturating_add(NumericLimits::max(), 0), NumericLimits::max()); EXPECT_EQ(saturating_add(NumericLimits::min(), 0), NumericLimits::min()); EXPECT_EQ(saturating_add(0, NumericLimits::max()), NumericLimits::max()); EXPECT_EQ(saturating_add(0, NumericLimits::min()), NumericLimits::min()); } // Normal signed sub (nowhere near saturation) TEST_CASE(signed_sub_normal) { EXPECT_EQ(saturating_sub(0, 0), 0); EXPECT_EQ(saturating_sub(7, 4), 3); EXPECT_EQ(saturating_sub(4, 7), -3); EXPECT_EQ(saturating_sub(-7, -4), -3); EXPECT_EQ(saturating_sub(3, -4), 7); EXPECT_EQ(saturating_sub(-3, 4), -7); EXPECT_EQ(saturating_sub(300, 200), 100); EXPECT_EQ(saturating_sub(-300, -200), -100); EXPECT_EQ(saturating_sub(1000000, 2000000), -1000000); } // Signed sub: cross-boundary (should NOT saturate) TEST_CASE(signed_sub_cross_boundary) { EXPECT_EQ(saturating_sub(NumericLimits::max(), NumericLimits::max()), 0); EXPECT_EQ(saturating_sub(NumericLimits::min(), NumericLimits::min()), 0); EXPECT_EQ(saturating_sub(0, NumericLimits::max()), NumericLimits::min() + 1); } // Signed sub: off-by-one at saturation point TEST_CASE(signed_sub_off_by_one) { // Exactly at max: no saturation EXPECT_EQ(saturating_sub(NumericLimits::max() - 1, -1), NumericLimits::max()); EXPECT_EQ(saturating_sub(NumericLimits::max() - 10, -10), NumericLimits::max()); // One past max: saturates EXPECT_EQ(saturating_sub(NumericLimits::max(), -1), NumericLimits::max()); EXPECT_EQ(saturating_sub(NumericLimits::max() - 9, -10), NumericLimits::max()); // Exactly at min: no saturation EXPECT_EQ(saturating_sub(NumericLimits::min() + 1, 1), NumericLimits::min()); EXPECT_EQ(saturating_sub(NumericLimits::min() + 10, 10), NumericLimits::min()); // One past min: saturates EXPECT_EQ(saturating_sub(NumericLimits::min(), 1), NumericLimits::min()); EXPECT_EQ(saturating_sub(NumericLimits::min() + 9, 10), NumericLimits::min()); } // Signed sub: extreme overflow TEST_CASE(signed_sub_extreme_overflow) { EXPECT_EQ(saturating_sub(NumericLimits::max(), NumericLimits::min()), NumericLimits::max()); EXPECT_EQ(saturating_sub(NumericLimits::min(), NumericLimits::max()), NumericLimits::min()); } // Signed sub: identity TEST_CASE(signed_sub_identity) { EXPECT_EQ(saturating_sub(NumericLimits::max(), 0), NumericLimits::max()); EXPECT_EQ(saturating_sub(NumericLimits::min(), 0), NumericLimits::min()); } // Normal unsigned add (nowhere near saturation) TEST_CASE(unsigned_add_normal) { EXPECT_EQ(saturating_add(0u, 0u), 0u); EXPECT_EQ(saturating_add(1u, 1u), 2u); EXPECT_EQ(saturating_add(3u, 4u), 7u); EXPECT_EQ(saturating_add(100u, 200u), 300u); EXPECT_EQ(saturating_add(1000000u, 2000000u), 3000000u); } // Unsigned add: off-by-one at saturation point TEST_CASE(unsigned_add_off_by_one) { EXPECT_EQ(saturating_add(NumericLimits::max() - 1, 1u), NumericLimits::max()); EXPECT_EQ(saturating_add(NumericLimits::max() - 10, 10u), NumericLimits::max()); EXPECT_EQ(saturating_add(NumericLimits::max(), 1u), NumericLimits::max()); EXPECT_EQ(saturating_add(NumericLimits::max() - 9, 10u), NumericLimits::max()); } // Unsigned add: extreme overflow and identity TEST_CASE(unsigned_add_extreme_and_identity) { EXPECT_EQ(saturating_add(NumericLimits::max(), NumericLimits::max()), NumericLimits::max()); EXPECT_EQ(saturating_add(NumericLimits::max(), 0u), NumericLimits::max()); EXPECT_EQ(saturating_add(0u, NumericLimits::max()), NumericLimits::max()); } // Normal unsigned sub (nowhere near saturation) TEST_CASE(unsigned_sub_normal) { EXPECT_EQ(saturating_sub(0u, 0u), 0u); EXPECT_EQ(saturating_sub(7u, 4u), 3u); EXPECT_EQ(saturating_sub(4u, 4u), 0u); EXPECT_EQ(saturating_sub(300u, 200u), 100u); EXPECT_EQ(saturating_sub(NumericLimits::max(), NumericLimits::max()), 0u); } // Unsigned sub: off-by-one at saturation point TEST_CASE(unsigned_sub_off_by_one) { EXPECT_EQ(saturating_sub(1u, 1u), 0u); EXPECT_EQ(saturating_sub(10u, 10u), 0u); EXPECT_EQ(saturating_sub(0u, 1u), 0u); EXPECT_EQ(saturating_sub(9u, 10u), 0u); } // Unsigned sub: extreme underflow and identity TEST_CASE(unsigned_sub_extreme_and_identity) { EXPECT_EQ(saturating_sub(0u, NumericLimits::max()), 0u); EXPECT_EQ(saturating_sub(5u, 10u), 0u); EXPECT_EQ(saturating_sub(NumericLimits::max(), 0u), NumericLimits::max()); } // Normal signed mul (nowhere near saturation) TEST_CASE(signed_mul_normal) { EXPECT_EQ(saturating_mul(0, 0), 0); EXPECT_EQ(saturating_mul(1, 1), 1); EXPECT_EQ(saturating_mul(3, 4), 12); EXPECT_EQ(saturating_mul(-3, 4), -12); EXPECT_EQ(saturating_mul(3, -4), -12); EXPECT_EQ(saturating_mul(-3, -4), 12); EXPECT_EQ(saturating_mul(100, 200), 20000); EXPECT_EQ(saturating_mul(0, NumericLimits::max()), 0); EXPECT_EQ(saturating_mul(NumericLimits::max(), 0), 0); EXPECT_EQ(saturating_mul(1, NumericLimits::max()), NumericLimits::max()); EXPECT_EQ(saturating_mul(NumericLimits::max(), 1), NumericLimits::max()); EXPECT_EQ(saturating_mul(-1, NumericLimits::max()), -NumericLimits::max()); } TEST_CASE(signed_mul_overflow) { EXPECT_EQ(saturating_mul(NumericLimits::max(), 2), NumericLimits::max()); EXPECT_EQ(saturating_mul(NumericLimits::max(), NumericLimits::max()), NumericLimits::max()); EXPECT_EQ(saturating_mul(NumericLimits::min(), 2), NumericLimits::min()); EXPECT_EQ(saturating_mul(NumericLimits::min(), NumericLimits::min()), NumericLimits::max()); EXPECT_EQ(saturating_mul(NumericLimits::max(), -2), NumericLimits::min()); EXPECT_EQ(saturating_mul(NumericLimits::max(), NumericLimits::min()), NumericLimits::min()); } // Normal unsigned mul (nowhere near saturation) TEST_CASE(unsigned_mul_normal) { EXPECT_EQ(saturating_mul(0u, 0u), 0u); EXPECT_EQ(saturating_mul(3u, 4u), 12u); EXPECT_EQ(saturating_mul(100u, 200u), 20000u); EXPECT_EQ(saturating_mul(0u, NumericLimits::max()), 0u); EXPECT_EQ(saturating_mul(1u, NumericLimits::max()), NumericLimits::max()); } TEST_CASE(unsigned_mul_overflow) { EXPECT_EQ(saturating_mul(NumericLimits::max(), 2u), NumericLimits::max()); EXPECT_EQ(saturating_mul(NumericLimits::max(), NumericLimits::max()), NumericLimits::max()); } // All integer sizes TEST_CASE(i8_saturation) { EXPECT_EQ(saturating_add(static_cast(50), static_cast(30)), static_cast(80)); EXPECT_EQ(saturating_add(static_cast(100), static_cast(100)), NumericLimits::max()); EXPECT_EQ(saturating_add(static_cast(-100), static_cast(-100)), NumericLimits::min()); EXPECT_EQ(saturating_add(NumericLimits::max(), NumericLimits::min()), static_cast(-1)); EXPECT_EQ(saturating_sub(static_cast(50), static_cast(30)), static_cast(20)); EXPECT_EQ(saturating_sub(static_cast(-100), static_cast(100)), NumericLimits::min()); EXPECT_EQ(saturating_sub(static_cast(100), static_cast(-100)), NumericLimits::max()); } TEST_CASE(i16_saturation) { EXPECT_EQ(saturating_add(static_cast(1000), static_cast(2000)), static_cast(3000)); EXPECT_EQ(saturating_add(NumericLimits::max(), static_cast(1)), NumericLimits::max()); EXPECT_EQ(saturating_add(NumericLimits::min(), static_cast(-1)), NumericLimits::min()); EXPECT_EQ(saturating_add(NumericLimits::max(), NumericLimits::min()), static_cast(-1)); EXPECT_EQ(saturating_sub(static_cast(3000), static_cast(1000)), static_cast(2000)); EXPECT_EQ(saturating_sub(NumericLimits::max(), static_cast(-1)), NumericLimits::max()); EXPECT_EQ(saturating_sub(NumericLimits::min(), static_cast(1)), NumericLimits::min()); } TEST_CASE(i64_saturation) { EXPECT_EQ(saturating_add(static_cast(1000000000), static_cast(2000000000)), static_cast(3000000000)); EXPECT_EQ(saturating_add(NumericLimits::max(), static_cast(1)), NumericLimits::max()); EXPECT_EQ(saturating_add(NumericLimits::min(), static_cast(-1)), NumericLimits::min()); EXPECT_EQ(saturating_add(NumericLimits::max(), NumericLimits::min()), static_cast(-1)); EXPECT_EQ(saturating_sub(NumericLimits::min(), static_cast(1)), NumericLimits::min()); EXPECT_EQ(saturating_sub(NumericLimits::max(), static_cast(-1)), NumericLimits::max()); EXPECT_EQ(saturating_sub(NumericLimits::max(), NumericLimits::max()), static_cast(0)); } TEST_CASE(u8_saturation) { EXPECT_EQ(saturating_add(static_cast(50), static_cast(30)), static_cast(80)); EXPECT_EQ(saturating_add(static_cast(200), static_cast(200)), NumericLimits::max()); EXPECT_EQ(saturating_sub(static_cast(80), static_cast(30)), static_cast(50)); EXPECT_EQ(saturating_sub(static_cast(0), static_cast(1)), static_cast(0)); } TEST_CASE(u16_saturation) { EXPECT_EQ(saturating_add(static_cast(1000), static_cast(2000)), static_cast(3000)); EXPECT_EQ(saturating_add(NumericLimits::max(), static_cast(1)), NumericLimits::max()); EXPECT_EQ(saturating_sub(static_cast(3000), static_cast(1000)), static_cast(2000)); EXPECT_EQ(saturating_sub(static_cast(0), static_cast(1)), static_cast(0)); } TEST_CASE(u64_saturation) { EXPECT_EQ(saturating_add(static_cast(1000000000), static_cast(2000000000)), static_cast(3000000000)); EXPECT_EQ(saturating_add(NumericLimits::max(), static_cast(1)), NumericLimits::max()); EXPECT_EQ(saturating_sub(static_cast(3000000000), static_cast(1000000000)), static_cast(2000000000)); EXPECT_EQ(saturating_sub(static_cast(0), static_cast(1)), static_cast(0)); }