From a72264006b738c8aa3d49d7835a86bcce130d20d Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Sat, 27 Dec 2025 11:29:31 +0100 Subject: Add signed arithmetics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- libj2_j2i_divmod_j2i_to_j2i.c | 459 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 459 insertions(+) create mode 100644 libj2_j2i_divmod_j2i_to_j2i.c (limited to 'libj2_j2i_divmod_j2i_to_j2i.c') diff --git a/libj2_j2i_divmod_j2i_to_j2i.c b/libj2_j2i_divmod_j2i_to_j2i.c new file mode 100644 index 0000000..147fe0c --- /dev/null +++ b/libj2_j2i_divmod_j2i_to_j2i.c @@ -0,0 +1,459 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + +extern inline void libj2_j2i_divmod_j2i_to_j2i(struct libj2_j2i *a, const struct libj2_j2i *b, struct libj2_j2i *res_q); +/* TODO Add man page */ + + +#else + +static uintmax_t +random_ju(void) +{ + size_t n = LIBJ2_JU_BIT; + uintmax_t r = 0; + while (n--) + if (rand() < rand()) + r |= (uintmax_t)1 << n; + return r; +} + + +static void +validate_result(const struct libj2_j2i *a, const struct libj2_j2i *b, + const struct libj2_j2i *q, const struct libj2_j2i *r) +{ + struct libj2_j2i v; + libj2_j2i_mul_j2i_to_j2i(q, b, &v); + libj2_j2i_add_j2i(&v, r); + EXPECT(libj2_j2i_eq_j2i(&v, a)); + + if (!a->high && !b->high) { + EXPECT(!q->high); + EXPECT(!r->high); + EXPECT(q->low == a->low / b->low); + EXPECT(r->low == a->low % b->low); + } +} + + +static void +check_manual(const struct libj2_j2i *a, const struct libj2_j2i *b, + const struct libj2_j2i *expected_q, const struct libj2_j2i *expected_r) +{ + struct libj2_j2i a_saved = *a, b_saved = *b, q, r, eq, er; + intmax_t b_low_ji; + int underflow; + + EXPECT(!libj2_j2i_is_zero(b)); + + r = (struct libj2_j2i){111, 222}; + q = (struct libj2_j2i){333, 444}; + libj2_j2i_divmod_j2i_to_j2i_j2i(a, b, &q, &r); + EXPECT(libj2_j2i_eq_j2i(a, &a_saved)); + EXPECT(libj2_j2i_eq_j2i(b, &b_saved)); + if (expected_q) { + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + } else { + eq = q; + expected_q = &eq; + } + if (expected_r) { + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + } else { + er = r; + expected_r = &er; + } + underflow = !libj2_j2i_is_zero(expected_r); + + validate_result(a, b, expected_q, expected_r); + + r = (struct libj2_j2i){111, 222}; + q = *a; + libj2_j2i_divmod_j2i_to_j2i_j2i(&q, b, &q, &r); + EXPECT(libj2_j2i_eq_j2i(b, &b_saved)); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + r = *a; + q = (struct libj2_j2i){333, 444}; + libj2_j2i_divmod_j2i_to_j2i_j2i(&r, b, &q, &r); + EXPECT(libj2_j2i_eq_j2i(b, &b_saved)); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + r = (struct libj2_j2i){111, 222}; + q = *b; + libj2_j2i_divmod_j2i_to_j2i_j2i(a, &q, &q, &r); + EXPECT(libj2_j2i_eq_j2i(a, &a_saved)); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + r = *b; + q = (struct libj2_j2i){333, 444}; + libj2_j2i_divmod_j2i_to_j2i_j2i(a, &r, &q, &r); + EXPECT(libj2_j2i_eq_j2i(a, &a_saved)); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + r = *b; + q = *a; + libj2_j2i_divmod_j2i_to_j2i_j2i(&q, &r, &q, &r); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + r = *a; + q = *b; + libj2_j2i_divmod_j2i_to_j2i_j2i(&r, &q, &q, &r); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + q = (struct libj2_j2i){333, 444}; + libj2_j2i_div_j2i_to_j2i(a, b, &q); + EXPECT(libj2_j2i_eq_j2i(a, &a_saved)); + EXPECT(libj2_j2i_eq_j2i(b, &b_saved)); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + + q = *a; + libj2_j2i_div_j2i_to_j2i(&q, b, &q); + EXPECT(libj2_j2i_eq_j2i(b, &b_saved)); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + + q = *b; + libj2_j2i_div_j2i_to_j2i(a, &q, &q); + EXPECT(libj2_j2i_eq_j2i(a, &a_saved)); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + + q = (struct libj2_j2i){333, 444}; + EXPECT(libj2_j2i_div_j2i_to_j2i_underflow(a, b, &q) == underflow); + EXPECT(libj2_j2i_eq_j2i(a, &a_saved)); + EXPECT(libj2_j2i_eq_j2i(b, &b_saved)); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + + q = *a; + EXPECT(libj2_j2i_div_j2i_to_j2i_underflow(&q, b, &q) == underflow); + EXPECT(libj2_j2i_eq_j2i(b, &b_saved)); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + + q = *b; + EXPECT(libj2_j2i_div_j2i_to_j2i_underflow(a, &q, &q) == underflow); + EXPECT(libj2_j2i_eq_j2i(a, &a_saved)); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + + r = (struct libj2_j2i){111, 222}; + libj2_j2i_mod_j2i_to_j2i(a, b, &r); + EXPECT(libj2_j2i_eq_j2i(a, &a_saved)); + EXPECT(libj2_j2i_eq_j2i(b, &b_saved)); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + r = *a; + libj2_j2i_mod_j2i_to_j2i(&r, b, &r); + EXPECT(libj2_j2i_eq_j2i(b, &b_saved)); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + r = *b; + libj2_j2i_mod_j2i_to_j2i(a, &r, &r); + EXPECT(libj2_j2i_eq_j2i(a, &a_saved)); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + r = *a; + q = (struct libj2_j2i){333, 444}; + libj2_j2i_divmod_j2i_to_j2i(&r, b, &q); + EXPECT(libj2_j2i_eq_j2i(b, &b_saved)); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + r = *a; + q = *b; + libj2_j2i_divmod_j2i_to_j2i(&r, &q, &q); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + q = *a; + libj2_j2i_div_j2i(&q, b); + EXPECT(libj2_j2i_eq_j2i(b, &b_saved)); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + + q = *a; + EXPECT(libj2_j2i_div_j2i_underflow(&q, b) == underflow); + EXPECT(libj2_j2i_eq_j2i(b, &b_saved)); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + + r = *a; + libj2_j2i_mod_j2i(&r, b); + EXPECT(libj2_j2i_eq_j2i(b, &b_saved)); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + r = *b; + q = (struct libj2_j2i){333, 444}; + libj2_j2i_rdivmod_j2i_to_j2i(&r, a, &q); + EXPECT(libj2_j2i_eq_j2i(a, &a_saved)); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + r = *b; + q = *a; + libj2_j2i_rdivmod_j2i_to_j2i(&r, &q, &q); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + q = *b; + libj2_j2i_rdiv_j2i(&q, a); + EXPECT(libj2_j2i_eq_j2i(a, &a_saved)); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + + q = *b; + EXPECT(libj2_j2i_rdiv_j2i_underflow(&q, a) == underflow); + EXPECT(libj2_j2i_eq_j2i(a, &a_saved)); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + + r = *b; + libj2_j2i_rmod_j2i(&r, a); + EXPECT(libj2_j2i_eq_j2i(a, &a_saved)); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + r = *a; + EXPECT(libj2_j2i_divmod_j2i(&r, b) == (intmax_t)expected_q->low); + EXPECT(libj2_j2i_eq_j2i(b, &b_saved)); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + EXPECT(libj2_j2i_div_j2i_return(a, b) == (intmax_t)expected_q->low); + EXPECT(libj2_j2i_eq_j2i(a, &a_saved)); + EXPECT(libj2_j2i_eq_j2i(b, &b_saved)); + + if (!b->high && b->low <= (intmax_t)INTMAX_MAX) { + b_low_ji = (intmax_t)b->low; + + r = (struct libj2_j2i){111, 222}; + q = (struct libj2_j2i){333, 444}; + libj2_j2i_divmod_ji_to_j2i_j2i(a, b_low_ji, &q, &r); + EXPECT(libj2_j2i_eq_j2i(a, &a_saved)); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + r = (struct libj2_j2i){111, 222}; + q = *a; + libj2_j2i_divmod_ji_to_j2i_j2i(&q, b_low_ji, &q, &r); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + r = *a; + q = (struct libj2_j2i){333, 444}; + libj2_j2i_divmod_ji_to_j2i_j2i(&r, b_low_ji, &q, &r); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + q = (struct libj2_j2i){333, 444}; + libj2_j2i_div_ji_to_j2i(a, b_low_ji, &q); + EXPECT(libj2_j2i_eq_j2i(a, &a_saved)); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + + q = *a; + libj2_j2i_div_ji_to_j2i(&q, b_low_ji, &q); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + + q = (struct libj2_j2i){333, 444}; + EXPECT(libj2_j2i_div_ji_to_j2i_underflow(a, b_low_ji, &q) == underflow); + EXPECT(libj2_j2i_eq_j2i(a, &a_saved)); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + + q = *a; + EXPECT(libj2_j2i_div_ji_to_j2i_underflow(&q, b_low_ji, &q) == underflow); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + + r = (struct libj2_j2i){111, 222}; + libj2_j2i_mod_ji_to_j2i(a, b_low_ji, &r); + EXPECT(libj2_j2i_eq_j2i(a, &a_saved)); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + r = *a; + libj2_j2i_mod_ji_to_j2i(&r, b_low_ji, &r); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + r = *a; + q = (struct libj2_j2i){333, 444}; + libj2_j2i_divmod_ji_to_j2i(&r, b_low_ji, &q); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + q = *a; + libj2_j2i_div_ji(&q, b_low_ji); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + + q = *a; + EXPECT(libj2_j2i_div_ji_underflow(&q, b_low_ji) == underflow); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + + r = *a; + libj2_j2i_mod_ji(&r, b_low_ji); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + r = *b; + EXPECT(libj2_j2i_rdivmod_j2i(&r, a) == (intmax_t)expected_q->low); + EXPECT(libj2_j2i_eq_j2i(a, &a_saved)); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + r = *a; + EXPECT(libj2_j2i_divmod_ji(&r, b_low_ji) == (intmax_t)expected_q->low); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + EXPECT(libj2_j2i_div_ji_return(a, b_low_ji) == (intmax_t)expected_q->low); + EXPECT(libj2_j2i_eq_j2i(a, &a_saved)); + } + + if (a == b) { + r = (struct libj2_j2i){111, 222}; + q = *a; + libj2_j2i_divmod_j2i_to_j2i_j2i(&q, &q, &q, &r); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + r = *a; + q = (struct libj2_j2i){333, 444}; + libj2_j2i_divmod_j2i_to_j2i_j2i(&r, &r, &q, &r); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + q = *a; + libj2_j2i_div_j2i_to_j2i(&q, &q, &q); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + + q = *a; + EXPECT(libj2_j2i_div_j2i_to_j2i_underflow(&q, &q, &q) == underflow); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + + r = *a; + libj2_j2i_mod_j2i_to_j2i(&r, &r, &r); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + r = *a; + q = (struct libj2_j2i){333, 444}; + libj2_j2i_divmod_j2i_to_j2i(&r, &r, &q); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + q = *a; + libj2_j2i_div_j2i(&q, &q); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + + q = *a; + EXPECT(libj2_j2i_div_j2i_underflow(&q, &q) == underflow); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + + r = *a; + libj2_j2i_mod_j2i(&r, &r); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + r = *a; + q = (struct libj2_j2i){333, 444}; + libj2_j2i_rdivmod_j2i_to_j2i(&r, &r, &q); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + q = *a; + libj2_j2i_rdiv_j2i(&q, &q); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + + q = *a; + EXPECT(libj2_j2i_rdiv_j2i_underflow(&q, &q) == underflow); + EXPECT(libj2_j2i_eq_j2i(&q, expected_q)); + + r = *a; + libj2_j2i_rmod_j2i(&r, &r); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + + r = *a; + EXPECT(libj2_j2i_divmod_j2i(&r, &r) == (intmax_t)expected_q->low); + EXPECT(libj2_j2i_eq_j2i(&r, expected_r)); + } +} + + +static void +check(const struct libj2_j2i *a, const struct libj2_j2i *b) +{ + struct libj2_j2i u, v; + if (!a) { + u.high = random_ju(); + u.low = random_ju(); + a = &u; + } + if (!b) { + do { + v.high = random_ju(); + v.low = random_ju(); + } while (!v.high && !v.low); + b = &u; + } + check_manual(a, b, NULL, NULL); +} + + +int +main(void) +{ + struct libj2_j2i a = {0, 0}, b = {0, 0}, q = {0, 0}, r = {0, 0}; + struct libj2_j2i zero = {0, 0}, one = {0, 0}, two = {0, 0}, minus_one; + unsigned i; + + srand((unsigned)time(NULL)); + + one.low = 1; + two.low = 2; + libj2_ji_to_j2i(-1, &minus_one); + + check_manual(&one, &one, &one, &zero); + check_manual(&two, &one, &two, &zero); + check_manual(&one, &two, &zero, &one); + check_manual(&zero, &one, &zero, &zero); + + a.low = 35; + b.low = 4; + q.low = 8; + r.low = 3; + check_manual(&a, &b, &q, &r); + + a.low = UINTMAX_C(0x14385c048097d350); + b.low = UINTMAX_C(0x1212734e33d9df1e); + r.low = a.low % b.low; + check(&a, &b); + check_manual(&a, &b, &one, &r); + + for (i = 0; i < 2048; i++) { + a.high = b.high = 0; + a.low = random_ju(); + b.low = random_ju(); + check(NULL, NULL); + check(NULL, &a); + check(&a, NULL); + check(&a, &b); + a.high = b.low; + check(&a, &a); + if (!libj2_j2i_is_zero(&a)) + check_manual(&zero, &a, &zero, &zero); + check_manual(&a, &one, &a, &zero); + b.high = a.high >> 1; + b.low = a.low >> 1; + b.low |= (a.high & 1U) << (LIBJ2_JU_BIT - 1U); + if (a.high & ((uintmax_t)INTMAX_MAX + 1U)) { + b.high |= (uintmax_t)INTMAX_MAX + 1U; + if (a.low & 1U) { + b.low += 1U; + if (b.low == 0U) + b.high += 1U; + check_manual(&a, &two, &b, &minus_one); + } else { + check_manual(&a, &two, &b, &zero); + } + } else { + check_manual(&a, &two, &b, (a.low & 1U) ? &one : &zero); + } + } + + /* TODO test sign-issues in a better way, especially ji divisor */ + + return 0; +} + +#endif -- cgit v1.2.3-70-g09d2