#include "test_ckdint.hpp" extern "C" { #include "guf_math_ckdint.h" } /* CkdIntTest: */ void CkdIntTest::run() { push_check_name("test_ckd"); test_ckd(); pop_check_name(); push_check_name("test_ckd_uint"); test_ckd_uint(); pop_check_name(); } void CkdIntTest::test_ckd() { for (int32_t a = INT8_MIN; a <= INT8_MAX; ++a) { for (int32_t b = INT8_MIN; b <= INT8_MAX; ++b) { const int32_t add_res = a + b; const guf_math_ckd_result ckd_add = guf_ckd_add_i8((int8_t)a, (int8_t)b); TEST_CHECK(ckd_add == guf_ckd_add_i8((int8_t)b, (int8_t)a)); if (add_res > INT8_MAX) { TEST_CHECK(ckd_add == GUF_MATH_CKD_OVERFLOW); int8_t saturated, saturated2; TEST_CHECK(guf_saturating_add_i8(static_cast(a), static_cast(b), &saturated) == GUF_MATH_CKD_OVERFLOW); TEST_CHECK(saturated == INT8_MAX); TEST_CHECK(guf_saturating_add_i8(static_cast(b), static_cast(a), &saturated2) == GUF_MATH_CKD_OVERFLOW); TEST_CHECK(saturated == saturated2); int8_t wrapped, wrapped2; TEST_CHECK(guf_wrapping_add_i8(static_cast(a), static_cast(b), &wrapped) == GUF_MATH_CKD_OVERFLOW); TEST_CHECK(static_cast(wrapped) == INT8_MIN + (add_res % (INT8_MAX + 1))); TEST_CHECK(guf_wrapping_add_i8(static_cast(b), static_cast(a), &wrapped2) == GUF_MATH_CKD_OVERFLOW); TEST_CHECK(wrapped == wrapped2); } else if (add_res < INT8_MIN) { TEST_CHECK(ckd_add == GUF_MATH_CKD_UNDERFLOW); int8_t saturated, saturated2; TEST_CHECK(guf_saturating_add_i8(static_cast(a), static_cast(b), &saturated) == GUF_MATH_CKD_UNDERFLOW); TEST_CHECK(saturated == INT8_MIN); TEST_CHECK(guf_saturating_add_i8(static_cast(b), static_cast(a), &saturated2) == GUF_MATH_CKD_UNDERFLOW); TEST_CHECK(saturated == saturated2); int8_t wrapped, wrapped2; TEST_CHECK(guf_wrapping_add_i8(static_cast(a), static_cast(b), &wrapped) == GUF_MATH_CKD_UNDERFLOW); TEST_CHECK(static_cast(wrapped) == INT8_MAX - (-add_res % (-INT8_MIN + 1))); TEST_CHECK(guf_wrapping_add_i8(static_cast(b), static_cast(a), &wrapped2) == GUF_MATH_CKD_UNDERFLOW); TEST_CHECK(wrapped == wrapped2); } else { TEST_CHECK(ckd_add == GUF_MATH_CKD_SUCCESS); int8_t saturated, saturated2; TEST_CHECK(guf_saturating_add_i8(static_cast(a), static_cast(b), &saturated) == GUF_MATH_CKD_SUCCESS); TEST_CHECK(static_cast(saturated) == add_res); TEST_CHECK(guf_saturating_add_i8(static_cast(b), static_cast(a), &saturated2) == GUF_MATH_CKD_SUCCESS); TEST_CHECK(saturated == saturated2); int8_t wrapped, wrapped2; TEST_CHECK(guf_wrapping_add_i8(static_cast(a), static_cast(b), &wrapped) == GUF_MATH_CKD_SUCCESS); TEST_CHECK(static_cast(wrapped) == add_res); TEST_CHECK(guf_wrapping_add_i8(static_cast(b), static_cast(a), &wrapped2) == GUF_MATH_CKD_SUCCESS); TEST_CHECK(wrapped == wrapped2); } const int32_t sub_res = a - b; const guf_math_ckd_result ckd_sub = guf_ckd_sub_i8((int8_t)a, (int8_t)b); if (sub_res > INT8_MAX) { TEST_CHECK(ckd_sub == GUF_MATH_CKD_OVERFLOW); int8_t saturated; TEST_CHECK(guf_saturating_sub_i8(static_cast(a), static_cast(b), &saturated) == GUF_MATH_CKD_OVERFLOW); TEST_CHECK(saturated == INT8_MAX); int8_t wrapped; TEST_CHECK(guf_wrapping_sub_i8(static_cast(a), static_cast(b), &wrapped) == GUF_MATH_CKD_OVERFLOW); TEST_CHECK(static_cast(wrapped) == INT8_MIN + (sub_res % (INT8_MAX + 1))); } else if (sub_res < INT8_MIN) { TEST_CHECK(ckd_sub == GUF_MATH_CKD_UNDERFLOW); int8_t saturated; TEST_CHECK(guf_saturating_sub_i8(static_cast(a), static_cast(b), &saturated) == GUF_MATH_CKD_UNDERFLOW); TEST_CHECK(saturated == INT8_MIN); int8_t wrapped; TEST_CHECK(guf_wrapping_sub_i8(static_cast(a), static_cast(b), &wrapped) == GUF_MATH_CKD_UNDERFLOW); TEST_CHECK(static_cast(wrapped) == INT8_MAX - (-sub_res % (-INT8_MIN + 1))); } else { TEST_CHECK(ckd_sub == GUF_MATH_CKD_SUCCESS); int8_t saturated; TEST_CHECK(guf_saturating_sub_i8(static_cast(a), static_cast(b), &saturated) == GUF_MATH_CKD_SUCCESS); TEST_CHECK(static_cast(saturated) == sub_res); int8_t wrapped; TEST_CHECK(guf_wrapping_sub_i8(static_cast(a), static_cast(b), &wrapped) == GUF_MATH_CKD_SUCCESS); TEST_CHECK(static_cast(wrapped) == sub_res); } const int32_t mul_res = a * b; const guf_math_ckd_result ckd_mul = guf_ckd_mul_i8((int8_t)a, (int8_t)b); TEST_CHECK(ckd_mul == guf_ckd_mul_i8((int8_t)b, (int8_t)a)); if (mul_res > INT8_MAX) { TEST_CHECK(ckd_mul == GUF_MATH_CKD_OVERFLOW); int8_t saturated, saturated2; TEST_CHECK(guf_saturating_mul_i8(static_cast(a), static_cast(b), &saturated) == GUF_MATH_CKD_OVERFLOW); TEST_CHECK(saturated == INT8_MAX); TEST_CHECK(guf_saturating_mul_i8(static_cast(b), static_cast(a), &saturated2) == GUF_MATH_CKD_OVERFLOW); TEST_CHECK(saturated == saturated2); int8_t wrapped, wrapped2; TEST_CHECK(guf_wrapping_mul_i8(static_cast(a), static_cast(b), &wrapped) == GUF_MATH_CKD_OVERFLOW); TEST_CHECK(guf_wrapping_mul_i8(static_cast(b), static_cast(a), &wrapped2) == GUF_MATH_CKD_OVERFLOW); TEST_CHECK(wrapped == wrapped2); // TODO: check wrapped } else if (mul_res < INT8_MIN) { TEST_CHECK(ckd_mul == GUF_MATH_CKD_UNDERFLOW); int8_t saturated, saturated2; TEST_CHECK(guf_saturating_mul_i8(static_cast(a), static_cast(b), &saturated) == GUF_MATH_CKD_UNDERFLOW); TEST_CHECK(saturated == INT8_MIN); TEST_CHECK(guf_saturating_mul_i8(static_cast(b), static_cast(a), &saturated2) == GUF_MATH_CKD_UNDERFLOW); TEST_CHECK(saturated == saturated2); int8_t wrapped, wrapped2; TEST_CHECK(guf_wrapping_mul_i8(static_cast(a), static_cast(b), &wrapped) == GUF_MATH_CKD_UNDERFLOW); TEST_CHECK(guf_wrapping_mul_i8(static_cast(b), static_cast(a), &wrapped2) == GUF_MATH_CKD_UNDERFLOW); TEST_CHECK(wrapped == wrapped2); // TODO: check wrapped } else { TEST_CHECK(ckd_mul == GUF_MATH_CKD_SUCCESS); int8_t saturated, saturated2; TEST_CHECK(guf_saturating_mul_i8(static_cast(a), static_cast(b), &saturated) == GUF_MATH_CKD_SUCCESS); TEST_CHECK(static_cast(saturated) == mul_res); TEST_CHECK(guf_saturating_mul_i8(static_cast(b), static_cast(a), &saturated2) == GUF_MATH_CKD_SUCCESS); TEST_CHECK(saturated == saturated2); int8_t wrapped, wrapped2; TEST_CHECK(guf_wrapping_mul_i8(static_cast(a), static_cast(b), &wrapped) == GUF_MATH_CKD_SUCCESS); TEST_CHECK(static_cast(wrapped) == mul_res); TEST_CHECK(guf_wrapping_mul_i8(static_cast(b), static_cast(a), &wrapped2) == GUF_MATH_CKD_SUCCESS); TEST_CHECK(wrapped == wrapped2); } } } int8_t mul_i8_res = -1; TEST_CHECK(guf_wrapping_mul_i8(42, 5, &mul_i8_res) == GUF_MATH_CKD_OVERFLOW); TEST_CHECK(mul_i8_res == -46); mul_i8_res = -1; TEST_CHECK(guf_wrapping_mul_i8(5, 42, &mul_i8_res) == GUF_MATH_CKD_OVERFLOW); TEST_CHECK(mul_i8_res == -46); /* // https://play.rust-lang.org/?version=stable&mode=debug&edition=2024 use std::num::Wrapping; fn main() { let a = Wrapping(-314159265_i32); let b = Wrapping(4096_i32); println!("{}", a * b); } */ int32_t mul_i32_res = -1; TEST_CHECK(guf_wrapping_mul_i32(42002718, 314159265, &mul_i32_res) == GUF_MATH_CKD_OVERFLOW); TEST_CHECK(mul_i32_res == -972735522); mul_i32_res = -1; TEST_CHECK(guf_wrapping_mul_i32(314159265, 42002718, &mul_i32_res) == GUF_MATH_CKD_OVERFLOW); TEST_CHECK(mul_i32_res == -972735522); mul_i32_res = 12345; guf_wrapping_mul_i32(42002718, 314159265, &mul_i32_res); TEST_CHECK(mul_i32_res == -972735522); mul_i32_res = 12345; guf_wrapping_mul_i32(-42002718, 314159265, &mul_i32_res); TEST_CHECK(mul_i32_res == 972735522); mul_i32_res = 12345; guf_wrapping_mul_i32(-88888888, 99999999, &mul_i32_res); TEST_CHECK(mul_i32_res == 1374494264); mul_i32_res = 12345; guf_wrapping_mul_i32(INT32_MIN, -1, &mul_i32_res); TEST_CHECK(mul_i32_res == INT32_MIN); mul_i32_res = 12345; guf_wrapping_mul_i32(-2147483648, 2147483640, &mul_i32_res); TEST_CHECK(mul_i32_res == 0); mul_i32_res = 12345; guf_wrapping_mul_i32(-2048, -314159265, &mul_i32_res); TEST_CHECK(mul_i32_res == -846919680); mul_i32_res = 12345; guf_wrapping_mul_i32(4096, -314159265, &mul_i32_res); TEST_CHECK(mul_i32_res == 1693839360); ptrdiff_t ptrdiff_res = -1234; TEST_CHECK(guf_saturating_add_ptrdiff_t(PTRDIFF_MAX, 1, &ptrdiff_res) == GUF_MATH_CKD_OVERFLOW); TEST_CHECK(ptrdiff_res == PTRDIFF_MAX); TEST_CHECK(guf_saturating_add_ptrdiff_t(PTRDIFF_MIN, -1, &ptrdiff_res) == GUF_MATH_CKD_UNDERFLOW); TEST_CHECK(ptrdiff_res == PTRDIFF_MIN); TEST_CHECK(guf_saturating_mul_ptrdiff_t(PTRDIFF_MAX, 2, &ptrdiff_res) == GUF_MATH_CKD_OVERFLOW); TEST_CHECK(ptrdiff_res == PTRDIFF_MAX); TEST_CHECK(guf_saturating_mul_ptrdiff_t(PTRDIFF_MIN, 2, &ptrdiff_res) == GUF_MATH_CKD_UNDERFLOW); TEST_CHECK(ptrdiff_res == PTRDIFF_MIN); } void CkdIntTest::test_ckd_uint() { for (int32_t a = 0; a <= UINT8_MAX; ++a) { for (int32_t b = 0; b <= UINT8_MAX; ++b) { const int32_t add_res = a + b; const guf_math_ckd_result ckd_add = guf_ckd_add_u8((uint8_t)a, (uint8_t)b); if (add_res > UINT8_MAX) { TEST_CHECK(ckd_add == GUF_MATH_CKD_OVERFLOW); uint8_t saturated; TEST_CHECK(guf_saturating_add_u8(static_cast(a), static_cast(b), &saturated) == GUF_MATH_CKD_OVERFLOW); TEST_CHECK(saturated == UINT8_MAX); uint8_t wrapped; TEST_CHECK(guf_wrapping_add_u8(static_cast(a), static_cast(b), &wrapped) == GUF_MATH_CKD_OVERFLOW); TEST_CHECK(static_cast(wrapped) == 0 + (add_res % (UINT8_MAX + 1))); } else { TEST_CHECK(ckd_add == GUF_MATH_CKD_SUCCESS); uint8_t saturated; TEST_CHECK(guf_saturating_add_u8(static_cast(a), static_cast(b), &saturated) == GUF_MATH_CKD_SUCCESS); TEST_CHECK(static_cast(saturated) == add_res); uint8_t wrapped; TEST_CHECK(guf_wrapping_add_u8(static_cast(a), static_cast(b), &wrapped) == GUF_MATH_CKD_SUCCESS); TEST_CHECK(static_cast(wrapped) == add_res); } const int32_t sub_res = a - b; const guf_math_ckd_result ckd_sub = guf_ckd_sub_u8((uint8_t)a, (uint8_t)b); if (sub_res < 0) { TEST_CHECK(ckd_sub == GUF_MATH_CKD_UNDERFLOW); uint8_t saturated; TEST_CHECK(guf_saturating_sub_u8(static_cast(a), static_cast(b), &saturated) == GUF_MATH_CKD_UNDERFLOW); TEST_CHECK(saturated == 0); uint8_t wrapped; TEST_CHECK(guf_wrapping_sub_u8(static_cast(a), static_cast(b), &wrapped) == GUF_MATH_CKD_UNDERFLOW); TEST_CHECK(wrapped == static_cast(static_cast(a) - static_cast(b))); } else { TEST_CHECK(ckd_sub == GUF_MATH_CKD_SUCCESS); uint8_t saturated; TEST_CHECK(guf_saturating_sub_u8(static_cast(a), static_cast(b), &saturated) == GUF_MATH_CKD_SUCCESS); TEST_CHECK(static_cast(saturated) == sub_res); uint8_t wrapped; TEST_CHECK(guf_wrapping_sub_u8(static_cast(a), static_cast(b), &wrapped) == GUF_MATH_CKD_SUCCESS); TEST_CHECK(static_cast(wrapped) == sub_res); } const int32_t mul_res = a * b; const guf_math_ckd_result ckd_mul = guf_ckd_mul_u8((uint8_t)a, (uint8_t)b); if (mul_res > UINT8_MAX) { TEST_CHECK(ckd_mul == GUF_MATH_CKD_OVERFLOW); uint8_t saturated; TEST_CHECK(guf_saturating_mul_u8(static_cast(a), static_cast(b), &saturated) == GUF_MATH_CKD_OVERFLOW); TEST_CHECK(saturated == UINT8_MAX); uint8_t wrapped; TEST_CHECK(guf_wrapping_mul_u8(static_cast(a), static_cast(b), &wrapped) == GUF_MATH_CKD_OVERFLOW); TEST_CHECK(wrapped == static_cast(static_cast(a) * static_cast(b))); } else { TEST_CHECK(ckd_mul == GUF_MATH_CKD_SUCCESS); uint8_t saturated; TEST_CHECK(guf_saturating_mul_u8(static_cast(a), static_cast(b), &saturated) == GUF_MATH_CKD_SUCCESS); TEST_CHECK(static_cast(saturated) == mul_res); uint8_t wrapped; TEST_CHECK(guf_wrapping_mul_u8(static_cast(a), static_cast(b), &wrapped) == GUF_MATH_CKD_SUCCESS); TEST_CHECK(static_cast(wrapped) == mul_res); } } } }