From d8abd04a488a88a10aa9ef202f5cb687488a05b7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Feb 2026 14:57:02 +0000 Subject: [PATCH 01/41] Initial plan From ac1bef0053fd9946c71118d0cd1ed96fe5c6793f Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Mon, 9 Feb 2026 14:54:27 +0000 Subject: [PATCH 02/41] mpz: use pointer tagging to save space (#8447) Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> --- src/util/mpz.cpp | 475 +++++++++++++++++++++++------------------------ src/util/mpz.h | 187 ++++++++++++------- 2 files changed, 360 insertions(+), 302 deletions(-) diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 10c345b0c..d74ce46ee 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -214,8 +214,8 @@ void mpz_manager::deallocate(bool is_heap, mpz_cell * ptr) { template mpz_manager::sign_cell::sign_cell(mpz_manager& m, mpz const& a): m_local(reinterpret_cast(m_bytes)), m_a(a) { - m_local.m_ptr->m_capacity = capacity; - m.get_sign_cell(a, m_sign, m_cell, m_local.m_ptr); + m_local.ptr()->m_capacity = capacity; + m.get_sign_cell(a, m_sign, m_cell, m_local.ptr()); } @@ -224,12 +224,11 @@ mpz_manager::sign_cell::sign_cell(mpz_manager& m, mpz const& a): template void mpz_manager::del(mpz_manager* m, mpz & a) { - if (a.m_ptr) { + if (!a.is_small()) { SASSERT(m); - m->deallocate(a.m_owner == mpz_self, a.m_ptr); - a.m_ptr = nullptr; - a.m_kind = mpz_small; - a.m_owner = mpz_self; + mpz::mpz_type* p = a.ptr(); + m->deallocate(!a.is_external(), p); + a.m_value = 0; // reset to small } } @@ -260,43 +259,44 @@ void mpz_manager::sub(mpz const & a, mpz const & b, mpz & c) { template void mpz_manager::set_big_i64(mpz & c, int64_t v) { #ifndef _MP_GMP - if (c.m_ptr == nullptr) { - c.m_ptr = allocate(m_init_cell_capacity); - c.m_owner = mpz_self; + mpz_cell* cell = c.is_small() ? nullptr : c.ptr(); + if (cell == nullptr) { + cell = allocate(m_init_cell_capacity); + c.set_ptr(cell, false, false); // Will update sign below } - c.m_kind = mpz_large; SASSERT(capacity(c) >= m_init_cell_capacity); uint64_t _v; + bool is_negative = false; if (v == std::numeric_limits::min()) { // min-int is even _v = -(v/2); - c.m_val = -1; + is_negative = true; } else if (v < 0) { _v = -v; - c.m_val = -1; + is_negative = true; } else { _v = v; - c.m_val = 1; } + c.set_sign(is_negative ? -1 : 1); if (sizeof(digit_t) == sizeof(uint64_t)) { // 64-bit machine digits(c)[0] = static_cast(_v); - c.m_ptr->m_size = 1; + c.ptr()->m_size = 1; } else { // 32-bit machine digits(c)[0] = static_cast(_v); digits(c)[1] = static_cast(_v >> 32); - c.m_ptr->m_size = digits(c)[1] == 0 ? 1 : 2; + c.ptr()->m_size = digits(c)[1] == 0 ? 1 : 2; } #else - if (c.m_ptr == nullptr) { - c.m_ptr = allocate(); - c.m_owner = mpz_self; + mpz_t* cell = c.is_small() ? nullptr : c.ptr(); + if (cell == nullptr) { + cell = allocate(); + c.set_ptr(cell, false, false); } - c.m_kind = mpz_large; uint64_t _v; bool sign = v < 0; if (v == std::numeric_limits::min()) { @@ -308,14 +308,14 @@ void mpz_manager::set_big_i64(mpz & c, int64_t v) { else { _v = v; } - mpz_set_ui(*c.m_ptr, static_cast(_v)); + mpz_set_ui(*c.ptr(), static_cast(_v)); MPZ_BEGIN_CRITICAL(); mpz_set_ui(m_tmp, static_cast(_v >> 32)); mpz_mul(m_tmp, m_tmp, m_two32); - mpz_add(*c.m_ptr, *c.m_ptr, m_tmp); + mpz_add(*c.ptr(), *c.ptr(), m_tmp); MPZ_END_CRITICAL(); if (sign) - mpz_neg(*c.m_ptr, *c.m_ptr); + mpz_neg(*c.ptr(), *c.ptr()); #endif if (v == std::numeric_limits::min()) { big_add(c, c, c); @@ -325,35 +325,35 @@ void mpz_manager::set_big_i64(mpz & c, int64_t v) { template void mpz_manager::set_big_ui64(mpz & c, uint64_t v) { #ifndef _MP_GMP - if (c.m_ptr == nullptr) { - c.m_ptr = allocate(m_init_cell_capacity); - c.m_owner = mpz_self; + mpz_cell* cell = c.is_small() ? nullptr : c.ptr(); + if (cell == nullptr) { + cell = allocate(m_init_cell_capacity); + c.set_ptr(cell, false, false); // positive, owned } - c.m_kind = mpz_large; SASSERT(capacity(c) >= m_init_cell_capacity); - c.m_val = 1; + c.set_sign(1); // positive if (sizeof(digit_t) == sizeof(uint64_t)) { // 64-bit machine digits(c)[0] = static_cast(v); - c.m_ptr->m_size = 1; + c.ptr()->m_size = 1; } else { // 32-bit machine digits(c)[0] = static_cast(v); digits(c)[1] = static_cast(v >> 32); - c.m_ptr->m_size = digits(c)[1] == 0 ? 1 : 2; + c.ptr()->m_size = digits(c)[1] == 0 ? 1 : 2; } #else - if (c.m_ptr == nullptr) { - c.m_ptr = allocate(); - c.m_owner = mpz_self; + mpz_t* cell = c.is_small() ? nullptr : c.ptr(); + if (cell == nullptr) { + cell = allocate(); + c.set_ptr(cell, false, false); // positive, owned } - c.m_kind = mpz_large; - mpz_set_ui(*c.m_ptr, static_cast(v)); + mpz_set_ui(*c.ptr(), static_cast(v)); MPZ_BEGIN_CRITICAL(); mpz_set_ui(m_tmp, static_cast(v >> 32)); mpz_mul(m_tmp, m_tmp, m_two32); - mpz_add(*c.m_ptr, *c.m_ptr, m_tmp); + mpz_add(*c.ptr(), *c.ptr(), m_tmp); MPZ_END_CRITICAL(); #endif } @@ -365,10 +365,10 @@ mpz_manager::ensure_mpz_t::ensure_mpz_t(mpz const& a) { if (is_small(a)) { m_result = &m_local; mpz_init(m_local); - mpz_set_si(m_local, a.m_val); + mpz_set_si(m_local, a.value()); } else { - m_result = a.m_ptr; + m_result = a.ptr(); } } @@ -396,15 +396,14 @@ void mpz_manager::set(mpz_cell& src, mpz & a, int sign, unsigned sz) { unsigned d = src.m_digits[0]; if (i == 1 && d <= INT_MAX) { // src fits is a fixnum - a.m_val = sign < 0 ? -static_cast(d) : static_cast(d); - a.m_kind = mpz_small; + a.set(sign < 0 ? -static_cast(d) : static_cast(d)); return; } set_digits(a, i, src.m_digits); - a.m_val = sign; + a.set_sign(sign); - SASSERT(a.m_kind == mpz_large); + SASSERT(!a.is_small()); } #endif @@ -443,15 +442,14 @@ void mpz_manager::set_digits(mpz & target, unsigned sz, digit_t const * d set(target, digits[0]); else { #ifndef _MP_GMP - target.m_val = 1; // number is positive. - if (target.m_ptr == nullptr) { + mpz_cell* cell = target.is_small() ? nullptr : target.ptr(); + if (cell == nullptr) { unsigned c = sz < m_init_cell_capacity ? m_init_cell_capacity : sz; - target.m_ptr = allocate(c); - target.m_ptr->m_size = sz; - target.m_ptr->m_capacity = c; - target.m_kind = mpz_large; - target.m_owner = mpz_self; - memcpy(target.m_ptr->m_digits, digits, sizeof(digit_t) * sz); + cell = allocate(c); + cell->m_size = sz; + cell->m_capacity = c; + target.set_ptr(cell, false, false); // positive, owned + memcpy(cell->m_digits, digits, sizeof(digit_t) * sz); } else if (capacity(target) < sz) { SASSERT(sz > m_init_cell_capacity); @@ -460,29 +458,26 @@ void mpz_manager::set_digits(mpz & target, unsigned sz, digit_t const * d ptr->m_size = sz; ptr->m_capacity = sz; deallocate(target); - target.m_val = 1; - target.m_ptr = ptr; - target.m_kind = mpz_large; - target.m_owner = mpz_self; + target.set_ptr(ptr, false, false); // positive, owned } else { - target.m_ptr->m_size = sz; - if (target.m_ptr->m_digits != digits) - memcpy(target.m_ptr->m_digits, digits, sizeof(digit_t) * sz); - target.m_kind = mpz_large; + target.ptr()->m_size = sz; + if (target.ptr()->m_digits != digits) + memcpy(target.ptr()->m_digits, digits, sizeof(digit_t) * sz); + // already large } #else mk_big(target); // reset - mpz_set_ui(*target.m_ptr, digits[sz - 1]); + mpz_set_ui(*target.ptr(), digits[sz - 1]); SASSERT(sz > 0); unsigned i = sz - 1; MPZ_BEGIN_CRITICAL(); while (i > 0) { --i; - mpz_mul_2exp(*target.m_ptr, *target.m_ptr, 32); + mpz_mul_2exp(*target.ptr(), *target.ptr(), 32); mpz_set_ui(m_tmp, digits[i]); - mpz_add(*target.m_ptr, *target.m_ptr, m_tmp); + mpz_add(*target.ptr(), *target.ptr(), m_tmp); } MPZ_END_CRITICAL(); #endif @@ -568,7 +563,7 @@ void mpz_manager::machine_div(mpz const & a, mpz const & b, mpz & c) { template void mpz_manager::reset(mpz & a) { deallocate(a); - set(a, 0); + a.m_value = 0; // reset to small } template @@ -673,7 +668,7 @@ mpz mpz_manager::mod2k(mpz const & a, unsigned k) { if (rem_bits > 0 && digit_count < ca.cell()->m_size) { is_zero &= (digits(result)[digit_count] = ca.cell()->m_digits[digit_count] & mask) == 0; } - result.m_ptr->m_size = total_digits; + result.ptr()->m_size = total_digits; if (ca.sign() < 0 && !is_zero) { // Negative case: if non-zero, result = 2^k - (|a| mod 2^k) @@ -700,7 +695,7 @@ mpz mpz_manager::mod2k(mpz const & a, unsigned k) { ensure_mpz_t a1(a); mk_big(result); MPZ_BEGIN_CRITICAL(); - mpz_tdiv_r_2exp(*result.m_ptr, a1(), k); + mpz_tdiv_r_2exp(*result.ptr(), a1(), k); MPZ_END_CRITICAL(); #endif return result; @@ -709,19 +704,24 @@ mpz mpz_manager::mod2k(mpz const & a, unsigned k) { template void mpz_manager::neg(mpz & a) { STRACE(mpz, tout << "[mpz] 0 - " << to_string(a) << " == ";); - if (is_small(a) && a.m_val == INT_MIN) { + if (is_small(a) && a.value() == INT_MIN) { // neg(INT_MIN) is not a small int set_big_i64(a, - static_cast(INT_MIN)); return; } #ifndef _MP_GMP - a.m_val = -a.m_val; -#else if (is_small(a)) { - a.m_val = -a.m_val; + a.set(-a.value()); } else { - mpz_neg(*a.m_ptr, *a.m_ptr); + a.set_sign(-a.sign()); + } +#else + if (is_small(a)) { + a.set(-a.value()); + } + else { + mpz_neg(*a.ptr(), *a.ptr()); } #endif STRACE(mpz, tout << to_string(a) << "\n";); @@ -730,20 +730,21 @@ void mpz_manager::neg(mpz & a) { template void mpz_manager::abs(mpz & a) { if (is_small(a)) { - if (a.m_val < 0) { - if (a.m_val == INT_MIN) { + int v = a.value(); + if (v < 0) { + if (v == INT_MIN) { // abs(INT_MIN) is not a small int set_big_i64(a, - static_cast(INT_MIN)); } else - a.m_val = -a.m_val; + a.set(-v); } } else { #ifndef _MP_GMP - a.m_val = 1; + a.set_sign(1); #else - mpz_abs(*a.m_ptr, *a.m_ptr); + mpz_abs(*a.ptr(), *a.ptr()); #endif } } @@ -765,9 +766,9 @@ void mpz_manager::big_add_sub(mpz const & a, mpz const & b, mpz & c) { allocate_if_needed(tmp, sz); m_mpn_manager.add(ca.cell()->m_digits, ca.cell()->m_size, cb.cell()->m_digits, cb.cell()->m_size, - tmp.m_ptr->m_digits, sz, &real_sz); + tmp.ptr()->m_digits, sz, &real_sz); SASSERT(real_sz <= sz); - set(*tmp.m_ptr, c, ca.sign(), real_sz); + set(*tmp.ptr(), c, ca.sign(), real_sz); } else { digit_t borrow; @@ -784,10 +785,10 @@ void mpz_manager::big_add_sub(mpz const & a, mpz const & b, mpz & c) { cb.cell()->m_size, ca.cell()->m_digits, ca.cell()->m_size, - tmp.m_ptr->m_digits, + tmp.ptr()->m_digits, &borrow); SASSERT(borrow == 0); - set(*tmp.m_ptr, c, sign_b, sz); + set(*tmp.ptr(), c, sign_b, sz); } else { // a > b @@ -797,10 +798,10 @@ void mpz_manager::big_add_sub(mpz const & a, mpz const & b, mpz & c) { ca.cell()->m_size, cb.cell()->m_digits, cb.cell()->m_size, - tmp.m_ptr->m_digits, + tmp.ptr()->m_digits, &borrow); SASSERT(borrow == 0); - set(*tmp.m_ptr, c, ca.sign(), sz); + set(*tmp.ptr(), c, ca.sign(), sz); } } del(tmp); @@ -817,7 +818,7 @@ void mpz_manager::big_add(mpz const & a, mpz const & b, mpz & c) { // GMP version ensure_mpz_t a1(a), b1(b); mk_big(c); - mpz_add(*c.m_ptr, a1(), b1()); + mpz_add(*c.ptr(), a1(), b1()); #endif } @@ -829,7 +830,7 @@ void mpz_manager::big_sub(mpz const & a, mpz const & b, mpz & c) { // GMP version ensure_mpz_t a1(a), b1(b); mk_big(c); - mpz_sub(*c.m_ptr, a1(), b1()); + mpz_sub(*c.ptr(), a1(), b1()); #endif } @@ -845,14 +846,14 @@ void mpz_manager::big_mul(mpz const & a, mpz const & b, mpz & c) { ca.cell()->m_size, cb.cell()->m_digits, cb.cell()->m_size, - tmp.m_ptr->m_digits); - set(*tmp.m_ptr, c, ca.sign() == cb.sign() ? 1 : -1, sz); + tmp.ptr()->m_digits); + set(*tmp.ptr(), c, ca.sign() == cb.sign() ? 1 : -1, sz); del(tmp); #else // GMP version ensure_mpz_t a1(a), b1(b); mk_big(c); - mpz_mul(*c.m_ptr, a1(), b1()); + mpz_mul(*c.ptr(), a1(), b1()); #endif } @@ -866,7 +867,7 @@ void mpz_manager::big_div_rem(mpz const & a, mpz const & b, mpz & q, mpz ensure_mpz_t a1(a), b1(b); mk_big(q); mk_big(r); - mpz_tdiv_qr(*q.m_ptr, *r.m_ptr, a1(), b1()); + mpz_tdiv_qr(*q.ptr(), *r.ptr(), a1(), b1()); #endif } @@ -897,12 +898,12 @@ void mpz_manager::quot_rem_core(mpz const & a, mpz const & b, mpz & q, mp allocate_if_needed(r1, r_sz); m_mpn_manager.div(ca.cell()->m_digits, ca.cell()->m_size, cb.cell()->m_digits, cb.cell()->m_size, - q1.m_ptr->m_digits, - r1.m_ptr->m_digits); + q1.ptr()->m_digits, + r1.ptr()->m_digits); if (MODE == QUOT_ONLY || MODE == QUOT_AND_REM) - set(*q1.m_ptr, q, ca.sign() == cb.sign() ? 1 : -1, q_sz); + set(*q1.ptr(), q, ca.sign() == cb.sign() ? 1 : -1, q_sz); if (MODE == REM_ONLY || MODE == QUOT_AND_REM) - set(*r1.m_ptr, r, ca.sign(), r_sz); + set(*r1.ptr(), r, ca.sign(), r_sz); del(q1); del(r1); } @@ -919,7 +920,7 @@ void mpz_manager::big_div(mpz const & a, mpz const & b, mpz & c) { // GMP version ensure_mpz_t a1(a), b1(b); mk_big(c); - mpz_tdiv_q(*c.m_ptr, a1(), b1()); + mpz_tdiv_q(*c.ptr(), a1(), b1()); #endif } @@ -934,17 +935,17 @@ void mpz_manager::big_rem(mpz const & a, mpz const & b, mpz & c) { // GMP version ensure_mpz_t a1(a), b1(b); mk_big(c); - mpz_tdiv_r(*c.m_ptr, a1(), b1()); + mpz_tdiv_r(*c.ptr(), a1(), b1()); #endif } template void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { - static_assert(sizeof(a.m_val) == sizeof(int), "size mismatch"); + static_assert(sizeof(int) == sizeof(int), "size mismatch"); static_assert(sizeof(mpz) <= 16, "mpz size overflow"); - if (is_small(a) && is_small(b) && a.m_val != INT_MIN && b.m_val != INT_MIN) { - int _a = a.m_val; - int _b = b.m_val; + if (is_small(a) && is_small(b) && a.value() != INT_MIN && b.value() != INT_MIN) { + int _a = a.value(); + int _b = b.value(); if (_a < 0) _a = -_a; if (_b < 0) _b = -_b; unsigned r = u_gcd(_a, _b); @@ -954,7 +955,7 @@ void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { #ifdef _MP_GMP ensure_mpz_t a1(a), b1(b); mk_big(c); - mpz_gcd(*c.m_ptr, a1(), b1()); + mpz_gcd(*c.ptr(), a1(), b1()); return; #endif if (is_zero(a)) { @@ -1001,9 +1002,9 @@ void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { // reset least significant bit if (is_small(v)) - v.m_val &= ~1; + v.set(v.value() & ~1); else - v.m_ptr->m_digits[0] &= ~static_cast(1); + v.ptr()->m_digits[0] &= ~static_cast(1); k_v = power_of_two_multiple(v); } @@ -1118,7 +1119,7 @@ void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { SASSERT(ge(a1, b1)); if (is_small(b1)) { if (is_small(a1)) { - unsigned r = u_gcd(a1.m_val, b1.m_val); + unsigned r = u_gcd(a1.value(), b1.value()); set(c, r); break; } @@ -1135,11 +1136,11 @@ void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { } SASSERT(!is_small(a1)); SASSERT(!is_small(b1)); - a_sz = a1.m_ptr->m_size; - b_sz = b1.m_ptr->m_size; + a_sz = a1.ptr()->m_size; + b_sz = b1.ptr()->m_size; SASSERT(b_sz <= a_sz); - a_hat = a1.m_ptr->m_digits[a_sz - 1]; - b_hat = (b_sz == a_sz) ? b1.m_ptr->m_digits[b_sz - 1] : 0; + a_hat = a1.ptr()->m_digits[a_sz - 1]; + b_hat = (b_sz == a_sz) ? b1.ptr()->m_digits[b_sz - 1] : 0; A = 1; B = 0; C = 0; @@ -1203,9 +1204,9 @@ unsigned mpz_manager::size_info(mpz const & a) { if (is_small(a)) return 1; #ifndef _MP_GMP - return a.m_ptr->m_size + 1; + return a.ptr()->m_size + 1; #else - return mpz_size(*a.m_ptr); + return mpz_size(*a.ptr()); #endif } @@ -1398,8 +1399,7 @@ void mpz_manager::bitwise_or(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_nonneg(b)); TRACE(mpz, tout << "is_small(a): " << is_small(a) << ", is_small(b): " << is_small(b) << "\n";); if (is_small(a) && is_small(b)) { - c.m_val = a.m_val | b.m_val; - c.m_kind = mpz_small; + c.set(a.value() | b.value()); } else { #ifndef _MP_GMP @@ -1434,7 +1434,7 @@ void mpz_manager::bitwise_or(mpz const & a, mpz const & b, mpz & c) { #else ensure_mpz_t a1(a), b1(b); mk_big(c); - mpz_ior(*c.m_ptr, a1(), b1()); + mpz_ior(*c.ptr(), a1(), b1()); #endif } } @@ -1444,8 +1444,7 @@ void mpz_manager::bitwise_and(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_nonneg(a)); SASSERT(is_nonneg(b)); if (is_small(a) && is_small(b)) { - c.m_val = a.m_val & b.m_val; - c.m_kind = mpz_small; + c.set(a.value() & b.value()); } else { #ifndef _MP_GMP @@ -1469,7 +1468,7 @@ void mpz_manager::bitwise_and(mpz const & a, mpz const & b, mpz & c) { #else ensure_mpz_t a1(a), b1(b); mk_big(c); - mpz_and(*c.m_ptr, a1(), b1()); + mpz_and(*c.ptr(), a1(), b1()); #endif } } @@ -1511,7 +1510,7 @@ void mpz_manager::bitwise_xor(mpz const & a, mpz const & b, mpz & c) { #else ensure_mpz_t a1(a), b1(b); mk_big(c); - mpz_xor(*c.m_ptr, a1(), b1()); + mpz_xor(*c.ptr(), a1(), b1()); #endif } } @@ -1558,33 +1557,32 @@ void mpz_manager::big_set(mpz & target, mpz const & source) { #ifndef _MP_GMP if (&target == &source) return; - target.m_val = source.m_val; - if (target.m_ptr == nullptr) { - target.m_ptr = allocate(capacity(source)); - target.m_ptr->m_size = size(source); - target.m_ptr->m_capacity = capacity(source); - target.m_kind = mpz_large; - target.m_owner = mpz_self; - memcpy(target.m_ptr->m_digits, source.m_ptr->m_digits, sizeof(digit_t) * size(source)); + int src_sign = source.sign(); + mpz_cell* target_cell = target.is_small() ? nullptr : target.ptr(); + if (target_cell == nullptr) { + mpz_cell* new_cell = allocate(capacity(source)); + new_cell->m_size = size(source); + new_cell->m_capacity = capacity(source); + memcpy(new_cell->m_digits, source.ptr()->m_digits, sizeof(digit_t) * size(source)); + target.set_ptr(new_cell, src_sign < 0, false); } else if (capacity(target) < size(source)) { deallocate(target); - target.m_ptr = allocate(capacity(source)); - target.m_ptr->m_size = size(source); - target.m_ptr->m_capacity = capacity(source); - target.m_kind = mpz_large; - target.m_owner = mpz_self; - memcpy(target.m_ptr->m_digits, source.m_ptr->m_digits, sizeof(digit_t) * size(source)); + mpz_cell* new_cell = allocate(capacity(source)); + new_cell->m_size = size(source); + new_cell->m_capacity = capacity(source); + memcpy(new_cell->m_digits, source.ptr()->m_digits, sizeof(digit_t) * size(source)); + target.set_ptr(new_cell, src_sign < 0, false); } else { - target.m_ptr->m_size = size(source); - memcpy(target.m_ptr->m_digits, source.m_ptr->m_digits, sizeof(digit_t) * size(source)); - target.m_kind = mpz_large; + target.ptr()->m_size = size(source); + memcpy(target.ptr()->m_digits, source.ptr()->m_digits, sizeof(digit_t) * size(source)); + target.set_sign(src_sign); } #else // GMP version mk_big(target); - mpz_set(*target.m_ptr, *source.m_ptr); + mpz_set(*target.ptr(), *source.ptr()); #endif } @@ -1628,10 +1626,10 @@ int mpz_manager::big_compare(mpz const & a, mpz const & b) { template bool mpz_manager::is_uint64(mpz const & a) const { #ifndef _MP_GMP - if (a.m_val < 0) - return false; if (is_small(a)) - return true; + return a.value() >= 0; + if (a.sign() < 0) + return false; if (sizeof(digit_t) == sizeof(uint64_t)) { return size(a) <= 1; } @@ -1641,8 +1639,8 @@ bool mpz_manager::is_uint64(mpz const & a) const { #else // GMP version if (is_small(a)) - return a.m_val >= 0; - return is_nonneg(a) && mpz_cmp(*a.m_ptr, m_uint64_max) <= 0; + return a.value() >= 0; + return is_nonneg(a) && mpz_cmp(*a.ptr(), m_uint64_max) <= 0; #endif } @@ -1656,7 +1654,7 @@ bool mpz_manager::is_int64(mpz const & a) const { uint64_t num = big_abs_to_uint64(a); uint64_t msb = static_cast(1) << 63; uint64_t msb_val = msb & num; - if (a.m_val >= 0) { + if (a.sign() >= 0) { // non-negative number. return (0 == msb_val); } @@ -1669,29 +1667,29 @@ bool mpz_manager::is_int64(mpz const & a) const { } #else // GMP version - return mpz_cmp(m_int64_min, *a.m_ptr) <= 0 && mpz_cmp(*a.m_ptr, m_int64_max) <= 0; + return mpz_cmp(m_int64_min, *a.ptr()) <= 0 && mpz_cmp(*a.ptr(), m_int64_max) <= 0; #endif } template uint64_t mpz_manager::get_uint64(mpz const & a) const { if (is_small(a)) - return static_cast(a.m_val); + return static_cast(a.value()); #ifndef _MP_GMP - SASSERT(a.m_ptr->m_size > 0); + SASSERT(a.ptr()->m_size > 0); return big_abs_to_uint64(a); #else // GMP version if (sizeof(uint64_t) == sizeof(unsigned long)) { - return mpz_get_ui(*a.m_ptr); + return mpz_get_ui(*a.ptr()); } else { MPZ_BEGIN_CRITICAL(); mpz_manager * _this = const_cast(this); - mpz_set(_this->m_tmp, *a.m_ptr); + mpz_set(_this->m_tmp, *a.ptr()); mpz_mod(_this->m_tmp, m_tmp, m_two32); uint64_t r = static_cast(mpz_get_ui(m_tmp)); - mpz_set(_this->m_tmp, *a.m_ptr); + mpz_set(_this->m_tmp, *a.ptr()); mpz_div(_this->m_tmp, m_tmp, m_two32); r += static_cast(mpz_get_ui(m_tmp)) << static_cast(32); MPZ_END_CRITICAL(); @@ -1703,11 +1701,11 @@ uint64_t mpz_manager::get_uint64(mpz const & a) const { template int64_t mpz_manager::get_int64(mpz const & a) const { if (is_small(a)) - return static_cast(a.m_val); + return static_cast(a.value()); #ifndef _MP_GMP SASSERT(is_int64(a)); uint64_t num = big_abs_to_uint64(a); - if (a.m_val < 0) { + if (a.sign() < 0) { if (num != 0 && (num << 1) == 0) return INT64_MIN; return -static_cast(num); @@ -1715,15 +1713,15 @@ int64_t mpz_manager::get_int64(mpz const & a) const { return static_cast(num); #else // GMP - if (sizeof(int64_t) == sizeof(long) || mpz_fits_slong_p(*a.m_ptr)) { - return mpz_get_si(*a.m_ptr); + if (sizeof(int64_t) == sizeof(long) || mpz_fits_slong_p(*a.ptr())) { + return mpz_get_si(*a.ptr()); } else { MPZ_BEGIN_CRITICAL(); mpz_manager * _this = const_cast(this); - mpz_mod(_this->m_tmp, *a.m_ptr, m_two32); + mpz_mod(_this->m_tmp, *a.ptr(), m_two32); int64_t r = static_cast(mpz_get_ui(m_tmp)); - mpz_div(_this->m_tmp, *a.m_ptr, m_two32); + mpz_div(_this->m_tmp, *a.ptr(), m_two32); r += static_cast(mpz_get_si(m_tmp)) << static_cast(32); MPZ_END_CRITICAL(); return r; @@ -1734,7 +1732,7 @@ int64_t mpz_manager::get_int64(mpz const & a) const { template double mpz_manager::get_double(mpz const & a) const { if (is_small(a)) - return static_cast(a.m_val); + return static_cast(a.value()); #ifndef _MP_GMP double r = 0.0; double d = 1.0; @@ -1749,30 +1747,34 @@ double mpz_manager::get_double(mpz const & a) const { if (!(r >= 0.0)) { r = static_cast(UINT64_MAX); // some large number } - return a.m_val < 0 ? -r : r; + return a.sign() < 0 ? -r : r; #else - return mpz_get_d(*a.m_ptr); + return mpz_get_d(*a.ptr()); #endif } template void mpz_manager::display(std::ostream & out, mpz const & a) const { if (is_small(a)) { - out << a.m_val; + out << a.value(); } else { #ifndef _MP_GMP - if (a.m_val < 0) - out << '-'; - - auto sz = sizeof(digit_t) == 4 ? 11 : 21; - sbuffer buffer(sz * size(a), 0); - out << m_mpn_manager.to_string(digits(a), size(a), buffer.begin(), buffer.size()); + if (a.sign() < 0) + out << "-"; + if (sizeof(digit_t) == 4) { + sbuffer buffer(11*size(a), 0); + out << m_mpn_manager.to_string(digits(a), size(a), buffer.begin(), buffer.size()); + } + else { + sbuffer buffer(21*size(a), 0); + out << m_mpn_manager.to_string(digits(a), size(a), buffer.begin(), buffer.size()); + } #else // GMP version - size_t sz = mpz_sizeinbase(*a.m_ptr, 10) + 2; + size_t sz = mpz_sizeinbase(*a.ptr(), 10) + 2; sbuffer buffer(sz, 0); - mpz_get_str(buffer.data(), 10, *a.m_ptr); + mpz_get_str(buffer.data(), 10, *a.ptr()); out << buffer.data(); #endif } @@ -1828,11 +1830,11 @@ void mpz_manager::display_hex(std::ostream & out, mpz const & a, unsigned } #else // GMP version - size_t sz = mpz_sizeinbase(*(a.m_ptr), 16); + size_t sz = mpz_sizeinbase(*(a.ptr()), 16); unsigned requiredLength = num_bits / 4; unsigned padding = requiredLength > sz ? requiredLength - sz : 0; sbuffer buffer(sz, 0); - mpz_get_str(buffer.data(), 16, *(a.m_ptr)); + mpz_get_str(buffer.data(), 16, *(a.ptr())); for (unsigned i = 0; i < padding; ++i) { out << "0"; } @@ -1883,10 +1885,10 @@ void mpz_manager::display_bin(std::ostream & out, mpz const & a, unsigned } #else // GMP version - size_t sz = mpz_sizeinbase(*(a.m_ptr), 2); + size_t sz = mpz_sizeinbase(*(a.ptr()), 2); unsigned padding = num_bits > sz ? num_bits - sz : 0; sbuffer buffer(sz, 0); - mpz_get_str(buffer.data(), 2, *(a.m_ptr)); + mpz_get_str(buffer.data(), 2, *(a.ptr())); for (unsigned i = 0; i < padding; ++i) { out << "0"; } @@ -1905,14 +1907,14 @@ std::string mpz_manager::to_string(mpz const & a) const { template unsigned mpz_manager::hash(mpz const & a) { if (is_small(a)) - return ::abs(a.m_val); + return ::abs(a.value()); #ifndef _MP_GMP unsigned sz = size(a); if (sz == 1) return static_cast(digits(a)[0]); return string_hash(std::string_view(reinterpret_cast(digits(a)), sz * sizeof(digit_t)), 17); #else - return mpz_get_si(*a.m_ptr); + return mpz_get_si(*a.ptr()); #endif } @@ -1921,38 +1923,37 @@ void mpz_manager::power(mpz const & a, unsigned p, mpz & b) { #ifdef _MP_GMP if (!is_small(a)) { mk_big(b); - mpz_pow_ui(*b.m_ptr, *a.m_ptr, p); + mpz_pow_ui(*b.ptr(), *a.ptr(), p); return; } #endif #ifndef _MP_GMP if (is_small(a)) { - if (a.m_val == 2) { + if (a.value() == 2) { if (p < 8 * sizeof(int) - 1) { - b.m_val = 1 << p; - b.m_kind = mpz_small; + b.set(1 << p); } else { unsigned sz = p/(8 * sizeof(digit_t)) + 1; unsigned shift = p%(8 * sizeof(digit_t)); SASSERT(sz > 0); allocate_if_needed(b, sz); - SASSERT(b.m_ptr->m_capacity >= sz); - b.m_ptr->m_size = sz; + SASSERT(b.ptr()->m_capacity >= sz); + b.ptr()->m_size = sz; for (unsigned i = 0; i < sz - 1; ++i) - b.m_ptr->m_digits[i] = 0; - b.m_ptr->m_digits[sz-1] = 1 << shift; - b.m_val = 1; - b.m_kind = mpz_large; + b.ptr()->m_digits[i] = 0; + b.ptr()->m_digits[sz-1] = 1 << shift; + // b is already large after allocate_if_needed, just ensure sign is positive + b.set_sign(1); } return; } - if (a.m_val == 0) { + if (a.value() == 0) { SASSERT(p != 0); set(b, 0); return; } - if (a.m_val == 1) { + if (a.value() == 1) { set(b, 1); return; } @@ -1983,8 +1984,8 @@ bool mpz_manager::is_power_of_two(mpz const & a, unsigned & shift) { if (is_nonpos(a)) return false; if (is_small(a)) { - if (::is_power_of_two(a.m_val)) { - shift = ::log2((unsigned)a.m_val); + if (::is_power_of_two(a.value())) { + shift = ::log2((unsigned)a.value()); return true; } else { @@ -1992,7 +1993,7 @@ bool mpz_manager::is_power_of_two(mpz const & a, unsigned & shift) { } } #ifndef _MP_GMP - mpz_cell * c = a.m_ptr; + mpz_cell * c = a.ptr(); unsigned sz = c->m_size; digit_t * ds = c->m_digits; for (unsigned i = 0; i < sz - 1; ++i) { @@ -2008,7 +2009,7 @@ bool mpz_manager::is_power_of_two(mpz const & a, unsigned & shift) { return false; } #else - if (mpz_popcount(*a.m_ptr) == 1) { + if (mpz_popcount(*a.ptr()) == 1) { shift = log2(a); return true; } @@ -2028,45 +2029,43 @@ void mpz_manager::ensure_capacity(mpz & a, unsigned capacity) { capacity = m_init_cell_capacity; if (is_small(a)) { - int val = a.m_val; + int val = a.value(); allocate_if_needed(a, capacity); - a.m_kind = mpz_large; - SASSERT(a.m_ptr->m_capacity >= capacity); + SASSERT(a.ptr()->m_capacity >= capacity); if (val == INT_MIN) { - unsigned intmin_sz = m_int_min.m_ptr->m_size; + unsigned intmin_sz = m_int_min.ptr()->m_size; for (unsigned i = 0; i < intmin_sz; ++i) - a.m_ptr->m_digits[i] = m_int_min.m_ptr->m_digits[i]; - a.m_val = -1; - a.m_ptr->m_size = m_int_min.m_ptr->m_size; + a.ptr()->m_digits[i] = m_int_min.ptr()->m_digits[i]; + a.set_sign(-1); + a.ptr()->m_size = m_int_min.ptr()->m_size; } else if (val < 0) { - a.m_ptr->m_digits[0] = -val; - a.m_val = -1; - a.m_ptr->m_size = 1; + a.ptr()->m_digits[0] = -val; + a.set_sign(-1); + a.ptr()->m_size = 1; } else { - a.m_ptr->m_digits[0] = val; - a.m_val = 1; - a.m_ptr->m_size = 1; + a.ptr()->m_digits[0] = val; + a.set_sign(1); + a.ptr()->m_size = 1; } } - else if (a.m_ptr->m_capacity < capacity) { + else if (a.ptr()->m_capacity < capacity) { mpz_cell * new_cell = allocate(capacity); SASSERT(new_cell->m_capacity == capacity); - unsigned old_sz = a.m_ptr->m_size; + unsigned old_sz = a.ptr()->m_size; new_cell->m_size = old_sz; for (unsigned i = 0; i < old_sz; ++i) - new_cell->m_digits[i] = a.m_ptr->m_digits[i]; + new_cell->m_digits[i] = a.ptr()->m_digits[i]; + bool is_neg = a.sign() < 0; deallocate(a); - a.m_ptr = new_cell; - a.m_owner = mpz_self; - a.m_kind = mpz_large; + a.set_ptr(new_cell, is_neg, false); } } template void mpz_manager::normalize(mpz & a) { - mpz_cell * c = a.m_ptr; + mpz_cell * c = a.ptr(); digit_t * ds = c->m_digits; unsigned i = c->m_size; for (; i > 0; --i) { @@ -2082,9 +2081,8 @@ void mpz_manager::normalize(mpz & a) { if (i == 1 && ds[0] <= INT_MAX) { // a is small - int val = a.m_val < 0 ? -static_cast(ds[0]) : static_cast(ds[0]); - a.m_val = val; - a.m_kind = mpz_small; + int val = a.sign() < 0 ? -static_cast(ds[0]) : static_cast(ds[0]); + a.set(val); return; } // adjust size @@ -2099,17 +2097,17 @@ void mpz_manager::machine_div2k(mpz & a, unsigned k) { if (is_small(a)) { if (k < 32) { int64_t twok = 1ull << ((int64_t)k); - int64_t val = a.m_val; - a.m_val = (int)(val/twok); + int64_t val = a.value(); + a.set((int)(val/twok)); } else { - a.m_val = 0; + a.set(0); } return; } #ifndef _MP_GMP unsigned digit_shift = k / (8 * sizeof(digit_t)); - mpz_cell * c = a.m_ptr; + mpz_cell * c = a.ptr(); unsigned sz = c->m_size; if (digit_shift >= sz) { set(a, 0); @@ -2157,7 +2155,7 @@ void mpz_manager::machine_div2k(mpz & a, unsigned k) { MPZ_BEGIN_CRITICAL(); mpz_tdiv_q_2exp(m_tmp, a1(), k); mk_big(a); - mpz_swap(*a.m_ptr, m_tmp); + mpz_swap(*a.ptr(), m_tmp); MPZ_END_CRITICAL(); #endif } @@ -2174,14 +2172,13 @@ void mpz_manager::mul2k(mpz & a, unsigned k) { TRACE(mpz_mul2k, tout << "mul2k\na: " << to_string(a) << "\nk: " << k << "\n";); unsigned word_shift = k / (8 * sizeof(digit_t)); unsigned bit_shift = k % (8 * sizeof(digit_t)); - unsigned old_sz = is_small(a) ? 1 : a.m_ptr->m_size; + unsigned old_sz = is_small(a) ? 1 : a.ptr()->m_size; unsigned new_sz = old_sz + word_shift + 1; ensure_capacity(a, new_sz); TRACE(mpz_mul2k, tout << "word_shift: " << word_shift << "\nbit_shift: " << bit_shift << "\nold_sz: " << old_sz << "\nnew_sz: " << new_sz - << "\na after ensure capacity:\n" << to_string(a) << "\n"; - tout << a.m_kind << "\n";); + << "\na after ensure capacity:\n" << to_string(a) << "\n";); SASSERT(!is_small(a)); - mpz_cell * cell_a = a.m_ptr; + mpz_cell * cell_a = a.ptr(); old_sz = cell_a->m_size; digit_t * ds = cell_a->m_digits; for (unsigned i = old_sz; i < new_sz; ++i) @@ -2220,7 +2217,7 @@ void mpz_manager::mul2k(mpz & a, unsigned k) { #else ensure_mpz_t a1(a); mk_big(a); - mpz_mul_2exp(*a.m_ptr, a1(), k); + mpz_mul_2exp(*a.ptr(), a1(), k); #endif } @@ -2234,7 +2231,7 @@ unsigned mpz_manager::power_of_two_multiple(mpz const & a) { return 0; if (is_small(a)) { unsigned r = 0; - int v = a.m_val; + int v = a.value(); #define COUNT_DIGIT_RIGHT_ZEROS() \ if (v % (1 << 16) == 0) { \ r += 16; \ @@ -2259,7 +2256,7 @@ unsigned mpz_manager::power_of_two_multiple(mpz const & a) { return r; } #ifndef _MP_GMP - mpz_cell * c = a.m_ptr; + mpz_cell * c = a.ptr(); unsigned sz = c->m_size; unsigned r = 0; digit_t * source = c->m_digits; @@ -2281,7 +2278,7 @@ unsigned mpz_manager::power_of_two_multiple(mpz const & a) { } return r; #else - return mpz_scan1(*a.m_ptr, 0); + return mpz_scan1(*a.ptr(), 0); #endif } @@ -2290,10 +2287,10 @@ unsigned mpz_manager::log2(mpz const & a) { if (is_nonpos(a)) return 0; if (is_small(a)) - return ::log2((unsigned)a.m_val); + return ::log2((unsigned)a.value()); #ifndef _MP_GMP static_assert(sizeof(digit_t) == 8 || sizeof(digit_t) == 4, ""); - mpz_cell * c = a.m_ptr; + mpz_cell * c = a.ptr(); unsigned sz = c->m_size; digit_t * ds = c->m_digits; if (sizeof(digit_t) == 8) @@ -2301,7 +2298,7 @@ unsigned mpz_manager::log2(mpz const & a) { else return (sz - 1)*32 + ::log2(static_cast(ds[sz-1])); #else - unsigned r = mpz_sizeinbase(*a.m_ptr, 2); + unsigned r = mpz_sizeinbase(*a.ptr(), 2); SASSERT(r > 0); return r - 1; #endif @@ -2311,14 +2308,14 @@ template unsigned mpz_manager::mlog2(mpz const & a) { if (is_nonneg(a)) return 0; - if (is_small(a) && a.m_val == INT_MIN) - return ::log2((unsigned)a.m_val); + if (is_small(a) && a.value() == INT_MIN) + return ::log2((unsigned)a.value()); if (is_small(a)) - return ::log2((unsigned)-a.m_val); + return ::log2((unsigned)-a.value()); #ifndef _MP_GMP static_assert(sizeof(digit_t) == 8 || sizeof(digit_t) == 4, ""); - mpz_cell * c = a.m_ptr; + mpz_cell * c = a.ptr(); unsigned sz = c->m_size; digit_t * ds = c->m_digits; if (sizeof(digit_t) == 8) @@ -2327,7 +2324,7 @@ unsigned mpz_manager::mlog2(mpz const & a) { return (sz - 1)*32 + ::log2(static_cast(ds[sz-1])); #else MPZ_BEGIN_CRITICAL(); - mpz_neg(m_tmp, *a.m_ptr); + mpz_neg(m_tmp, *a.ptr()); unsigned r = mpz_sizeinbase(m_tmp, 2); MPZ_END_CRITICAL(); SASSERT(r > 0); @@ -2534,15 +2531,15 @@ template digit_t mpz_manager::get_least_significant(mpz const& a) { SASSERT(!is_neg(a)); if (is_small(a)) - return std::abs(a.m_val); + return std::abs(a.value()); #ifndef _MP_GMP - mpz_cell* cell_a = a.m_ptr; + mpz_cell* cell_a = a.ptr(); unsigned sz = cell_a->m_size; if (sz == 0) return 0; return cell_a->m_digits[0]; #else - return mpz_get_ui(*a.m_ptr); + return mpz_get_ui(*a.ptr()); #endif } @@ -2550,27 +2547,27 @@ template bool mpz_manager::decompose(mpz const & a, svector & digits) { digits.reset(); if (is_small(a)) { - if (a.m_val < 0) { - digits.push_back(-a.m_val); + if (a.value() < 0) { + digits.push_back(-a.value()); return true; } else { - digits.push_back(a.m_val); + digits.push_back(a.value()); return false; } } else { #ifndef _MP_GMP - mpz_cell * cell_a = a.m_ptr; + mpz_cell * cell_a = a.ptr(); unsigned sz = cell_a->m_size; for (unsigned i = 0; i < sz; ++i) { digits.push_back(cell_a->m_digits[i]); } - return a.m_val < 0; + return a.sign() < 0; #else bool r = is_neg(a); MPZ_BEGIN_CRITICAL(); - mpz_set(m_tmp, *a.m_ptr); + mpz_set(m_tmp, *a.ptr()); mpz_abs(m_tmp, m_tmp); while (mpz_sgn(m_tmp) != 0) { mpz_tdiv_r_2exp(m_tmp2, m_tmp, 32); @@ -2587,16 +2584,16 @@ bool mpz_manager::decompose(mpz const & a, svector & digits) { template bool mpz_manager::get_bit(mpz const & a, unsigned index) { if (is_small(a)) { - SASSERT(a.m_val >= 0); + SASSERT(a.value() >= 0); if (index >= 8*sizeof(digit_t)) return false; - return 0 != (a.m_val & (1ull << (digit_t)index)); + return 0 != (a.value() & (1ull << (digit_t)index)); } unsigned i = index / (sizeof(digit_t)*8); unsigned o = index % (sizeof(digit_t)*8); #ifndef _MP_GMP - mpz_cell * cell_a = a.m_ptr; + mpz_cell * cell_a = a.ptr(); unsigned sz = cell_a->m_size; if (sz*sizeof(digit_t)*8 <= index) return false; diff --git a/src/util/mpz.h b/src/util/mpz.h index 505bb177e..758ebc3a4 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -68,15 +68,15 @@ class mpz_cell { /** \brief Multi-precision integer. - - If m_kind == mpz_small, it is a small number and the value is stored in m_val. - If m_kind == mpz_large, the value is stored in m_ptr and m_ptr != nullptr. - m_val contains the sign (-1 negative, 1 positive) - under winodws, m_ptr points to a mpz_cell that store the value. -*/ -enum mpz_kind { mpz_small = 0, mpz_large = 1}; -enum mpz_owner { mpz_self = 0, mpz_ext = 1}; + m_value encodes either a small integer (if the least significant bit is 1) + or a pointer to a mpz_cell structure (if the least significant bit is 0). + The last 3 bits of pointers are always 0 due to alignment, so we use them + to store additional information: + - bit 0: small/large info (0 = small, 1 = large) + - bit 1: sign bit (0 = non-negative, 1 = negative) + - bit 2: owner info (0 = owned, 1 = external) +*/ class mpz { #ifndef _MP_GMP @@ -84,11 +84,48 @@ class mpz { #else typedef mpz_t mpz_type; #endif +private: + uintptr_t m_value = 0; + + static constexpr uintptr_t LARGE_BIT = 0x1; + static constexpr uintptr_t SIGN_BIT = 0x2; + static constexpr uintptr_t OWNER_BIT = 0x4; + static constexpr uintptr_t MPZ_PTR_MASK = ~static_cast(0x7); + + mpz_type * ptr() const { + SASSERT(!is_small()); + return reinterpret_cast(m_value & MPZ_PTR_MASK); + } + + void set_ptr(mpz_type* p, bool is_negative, bool is_external) { + SASSERT(is_small()); + SASSERT((reinterpret_cast(p) & 0x7) == 0); // Check alignment + m_value = reinterpret_cast(p) | LARGE_BIT; + if (is_negative) + m_value |= SIGN_BIT; + if (is_external) + m_value |= OWNER_BIT; + } + + int get_sign() const { + SASSERT(!is_small()); + return (m_value & SIGN_BIT) ? -1 : 1; + } + + void set_sign(int s) { + SASSERT(!is_small()); + if (s < 0) + m_value |= SIGN_BIT; + else + m_value &= ~SIGN_BIT; + } + + bool is_external() const { + SASSERT(!is_small()); + return (m_value & OWNER_BIT) != 0; + } + protected: - int m_val; - unsigned m_kind:1; - unsigned m_owner:1; - mpz_type * m_ptr; friend class mpz_manager; friend class mpz_manager; friend class mpq_manager; @@ -98,42 +135,60 @@ protected: friend class mpbq_manager; friend class mpz_stack; public: - mpz(int v = 0) noexcept : m_val(v), m_kind(mpz_small), m_owner(mpz_self), m_ptr(nullptr) {} - mpz(mpz_type* ptr) noexcept : m_val(0), m_kind(mpz_small), m_owner(mpz_ext), m_ptr(ptr) { SASSERT(ptr); } - mpz(mpz && other) noexcept : mpz() { swap(other); } + mpz(int v = 0) noexcept : m_value(static_cast(static_cast(v)) << 1) {} + } + + mpz(mpz_type* ptr) noexcept { + SASSERT(ptr); + set_ptr(ptr, false, true); // external pointer, non-negative + } + + mpz(mpz && other) noexcept : m_value(other.m_value) { + other.m_value = 0; // reset other to small + } mpz& operator=(mpz const& other) = delete; + mpz& operator=(mpz &&other) noexcept { - swap(other); + std::swap(m_value, other.m_value); return *this; } - void swap(mpz & other) noexcept { - std::swap(m_val, other.m_val); - std::swap(m_ptr, other.m_ptr); - unsigned o = m_owner; m_owner = other.m_owner; other.m_owner = o; - unsigned k = m_kind; m_kind = other.m_kind; other.m_kind = k; - } - void set(int v) { - m_val = v; - m_kind = mpz_small; + if (is_small()) { + m_value = static_cast(static_cast(v)) << 1; + } else { + bool is_negative = v < 0; + c.set_sign(is_negative ? -1 : 1); + auto *p = ptr(); + p->m_digits[0] = static_cast(is_negative ? -v : v); + p->m_size = 1; + } } - inline bool is_small() const { return m_kind == mpz_small; } + inline bool is_small() const { + return (m_value & LARGE_BIT) == 0; + } - inline int value() const { SASSERT(is_small()); return m_val; } + inline int value() const { + SASSERT(is_small()); + // Decode small integer: shift right by 1 (arithmetic shift to preserve sign) + return static_cast(static_cast(m_value) >> 1); + } - inline int sign() const { SASSERT(!is_small()); return m_val; } + inline int sign() const { + SASSERT(!is_small()); + return get_sign(); + } }; #ifndef _MP_GMP class mpz_stack : public mpz { static const unsigned capacity = 8; - unsigned char m_bytes[sizeof(mpz_cell) + sizeof(digit_t) * capacity]; + alignas(8) unsigned char m_bytes[sizeof(mpz_cell) + sizeof(digit_t) * capacity]; public: mpz_stack():mpz(reinterpret_cast(m_bytes)) { - m_ptr->m_capacity = capacity; + ptr()->m_capacity = capacity; } }; #else @@ -169,16 +224,11 @@ class mpz_manager { // make sure that n is a big number and has capacity equal to at least c. void allocate_if_needed(mpz & n, unsigned c) { if (m_init_cell_capacity > c) c = m_init_cell_capacity; - if (n.m_ptr == nullptr || capacity(n) < c) { + if (n.is_small() || n.ptr() == nullptr || capacity(n) < c) { deallocate(n); - n.m_val = 1; - n.m_kind = mpz_large; - n.m_owner = mpz_self; - n.m_ptr = allocate(c); - } - else { - n.m_kind = mpz_large; + n.set_ptr(allocate(c), false, false); // positive, owned } + // else already has enough capacity, keep as large } void deallocate(bool is_heap, mpz_cell * ptr); @@ -230,14 +280,16 @@ class mpz_manager { } } - void clear(mpz& n) { if (n.m_ptr) { mpz_clear(*n.m_ptr); }} + void clear(mpz& n) { if (!n.is_small() && n.ptr()) { mpz_clear(*n.ptr()); }} #endif void deallocate(mpz& n) { - if (n.m_ptr) { - deallocate(n.m_owner == mpz_self, n.m_ptr); - n.m_ptr = nullptr; - n.m_kind = mpz_small; + if (!n.is_small()) { + auto* p = n.ptr(); + if (p) { + deallocate(!n.is_external(), p); + n.m_value = 0; // reset to small + } } } @@ -262,11 +314,20 @@ class mpz_manager { #ifndef _MP_GMP - static unsigned capacity(mpz const & c) { return c.m_ptr->m_capacity; } + static unsigned capacity(mpz const & c) { + SASSERT(!c.is_small()); + return c.ptr()->m_capacity; + } - static unsigned size(mpz const & c) { return c.m_ptr->m_size; } + static unsigned size(mpz const & c) { + SASSERT(!c.is_small()); + return c.ptr()->m_size; + } - static digit_t * digits(mpz const & c) { return c.m_ptr->m_digits; } + static digit_t * digits(mpz const & c) { + SASSERT(!c.is_small()); + return c.ptr()->m_digits; + } // Return true if the absolute value fits in a UINT64 static bool is_abs_uint64(mpz const & a) { @@ -282,7 +343,7 @@ class mpz_manager { static uint64_t big_abs_to_uint64(mpz const & a) { SASSERT(is_abs_uint64(a)); SASSERT(!is_small(a)); - if (a.m_ptr->m_size == 1) + if (a.ptr()->m_size == 1) return digits(a)[0]; if (sizeof(digit_t) == sizeof(uint64_t)) // 64-bit machine @@ -309,7 +370,7 @@ class mpz_manager { if (is_small(a)) { if (a.value() == INT_MIN) { sign = -1; - cell = m_int_min.m_ptr; + cell = m_int_min.ptr(); } else { cell = reserve; @@ -326,7 +387,7 @@ class mpz_manager { } else { sign = a.sign(); - cell = a.m_ptr; + cell = a.ptr(); } } @@ -343,12 +404,10 @@ class mpz_manager { }; void mk_big(mpz & a) { - if (a.m_ptr == nullptr) { - a.m_val = 0; - a.m_ptr = allocate(); - a.m_owner = mpz_self; + if (a.is_small()) { + a.set_ptr(allocate(), false, false); // positive, owned } - a.m_kind = mpz_large; + // else already large with valid pointer } @@ -448,13 +507,15 @@ public: static bool is_zero(mpz const & a) { return sign(a) == 0; } static int sign(mpz const & a) { -#ifndef _MP_GMP - return a.m_val; -#else - if (is_small(a)) - return a.m_val; + if (is_small(a)) { + int v = a.value(); + return (v > 0) - (v < 0); // Returns -1, 0, or 1 + } else - return mpz_sgn(*a.m_ptr); +#ifndef _MP_GMP + return a.sign(); +#else + return mpz_sgn(*a.ptr()); #endif } @@ -625,7 +686,7 @@ public: #else if (is_small(a)) return a.value() == 1; - return mpz_cmp_si(*a.m_ptr, 1) == 0; + return mpz_cmp_si(*a.ptr(), 1) == 0; #endif } @@ -635,7 +696,7 @@ public: #else if (is_small(a)) return a.value() == -1; - return mpz_cmp_si(*a.m_ptr, -1) == 0; + return mpz_cmp_si(*a.ptr(), -1) == 0; #endif } @@ -713,7 +774,7 @@ public: #ifndef _MP_GMP return !(0x1 & digits(a)[0]); #else - return mpz_even_p(*a.m_ptr); + return mpz_even_p(*a.ptr()); #endif } From cb3b8603eb531b9ef965d513f06cfdbb2660cede Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Feb 2026 15:16:45 +0000 Subject: [PATCH 03/41] Fix syntax errors in mpz.h Co-authored-by: nunoplopes <2998477+nunoplopes@users.noreply.github.com> --- src/util/mpz.h | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/util/mpz.h b/src/util/mpz.h index 758ebc3a4..64e1587cb 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -136,7 +136,6 @@ protected: friend class mpz_stack; public: mpz(int v = 0) noexcept : m_value(static_cast(static_cast(v)) << 1) {} - } mpz(mpz_type* ptr) noexcept { SASSERT(ptr); @@ -155,15 +154,11 @@ public: } void set(int v) { - if (is_small()) { - m_value = static_cast(static_cast(v)) << 1; - } else { - bool is_negative = v < 0; - c.set_sign(is_negative ? -1 : 1); - auto *p = ptr(); - p->m_digits[0] = static_cast(is_negative ? -v : v); - p->m_size = 1; - } + m_value = static_cast(static_cast(v)) << 1; + } + + void swap(mpz & other) noexcept { + std::swap(m_value, other.m_value); } inline bool is_small() const { @@ -598,6 +593,9 @@ public: } void set(mpz & a, int val) { + if (!is_small(a)) { + deallocate(a); + } a.set(val); } From 6faaf86dca01526b7b7eccd365a0d946b5f50742 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Feb 2026 15:38:31 +0000 Subject: [PATCH 04/41] Implement proper bounds checking for small integers based on platform pointer size Co-authored-by: nunoplopes <2998477+nunoplopes@users.noreply.github.com> --- src/util/mpz.cpp | 545 +++++++++++++++++++++++++---------------------- src/util/mpz.h | 131 +++++++++--- 2 files changed, 393 insertions(+), 283 deletions(-) diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index d74ce46ee..136a95132 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -394,9 +394,9 @@ void mpz_manager::set(mpz_cell& src, mpz & a, int sign, unsigned sz) { } unsigned d = src.m_digits[0]; - if (i == 1 && d <= INT_MAX) { - // src fits is a fixnum - a.set(sign < 0 ? -static_cast(d) : static_cast(d)); + if (i == 1 && d <= static_cast(mpz::SMALL_INT_MAX)) { + // src fits in small integer range + a.set64(sign < 0 ? -static_cast(d) : static_cast(d)); return; } @@ -704,22 +704,20 @@ mpz mpz_manager::mod2k(mpz const & a, unsigned k) { template void mpz_manager::neg(mpz & a) { STRACE(mpz, tout << "[mpz] 0 - " << to_string(a) << " == ";); - if (is_small(a) && a.value() == INT_MIN) { - // neg(INT_MIN) is not a small int - set_big_i64(a, - static_cast(INT_MIN)); - return; + if (is_small(a)) { + int64_t v = a.value64(); + if (v == mpz::SMALL_INT_MIN) { + // neg(SMALL_INT_MIN) overflows small range + set_big_i64(a, -v); + return; + } + a.set64(-v); } #ifndef _MP_GMP - if (is_small(a)) { - a.set(-a.value()); - } else { a.set_sign(-a.sign()); } #else - if (is_small(a)) { - a.set(-a.value()); - } else { mpz_neg(*a.ptr(), *a.ptr()); } @@ -730,14 +728,14 @@ void mpz_manager::neg(mpz & a) { template void mpz_manager::abs(mpz & a) { if (is_small(a)) { - int v = a.value(); + int64_t v = a.value64(); if (v < 0) { - if (v == INT_MIN) { - // abs(INT_MIN) is not a small int - set_big_i64(a, - static_cast(INT_MIN)); + if (v == mpz::SMALL_INT_MIN) { + // abs(SMALL_INT_MIN) overflows small range + set_big_i64(a, -v); } else - a.set(-v); + a.set64(-v); } } else { @@ -943,260 +941,263 @@ template void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { static_assert(sizeof(int) == sizeof(int), "size mismatch"); static_assert(sizeof(mpz) <= 16, "mpz size overflow"); - if (is_small(a) && is_small(b) && a.value() != INT_MIN && b.value() != INT_MIN) { - int _a = a.value(); - int _b = b.value(); - if (_a < 0) _a = -_a; - if (_b < 0) _b = -_b; - unsigned r = u_gcd(_a, _b); - set(c, r); + if (is_small(a) && is_small(b)) { + int64_t _a = a.value64(); + int64_t _b = b.value64(); + // Check if absolute values fit in uint64 (they always do for small integers) + // and won't overflow when negating + if (_a != mpz::SMALL_INT_MIN && _b != mpz::SMALL_INT_MIN) { + if (_a < 0) _a = -_a; + if (_b < 0) _b = -_b; + uint64_t r = u64_gcd(static_cast(_a), static_cast(_b)); + set(c, r); + return; + } } - else { #ifdef _MP_GMP - ensure_mpz_t a1(a), b1(b); - mk_big(c); - mpz_gcd(*c.ptr(), a1(), b1()); - return; + ensure_mpz_t a1(a), b1(b); + mk_big(c); + mpz_gcd(*c.ptr(), a1(), b1()); + return; #endif - if (is_zero(a)) { - set(c, b); - abs(c); - return; - } - if (is_zero(b)) { - set(c, a); - abs(c); - return; - } + if (is_zero(a)) { + set(c, b); + abs(c); + return; + } + if (is_zero(b)) { + set(c, a); + abs(c); + return; + } #ifdef BINARY_GCD - // Binary GCD for big numbers - // - It doesn't use division - // - The initial experiments, don't show any performance improvement - // - It only works with _MP_INTERNAL - mpz u, v, diff; - set(u, a); - set(v, b); - abs(u); - abs(v); + // Binary GCD for big numbers + // - It doesn't use division + // - The initial experiments, don't show any performance improvement + // - It only works with _MP_INTERNAL + mpz u, v, diff; + set(u, a); + set(v, b); + abs(u); + abs(v); - unsigned k_u = power_of_two_multiple(u); - unsigned k_v = power_of_two_multiple(v); - unsigned k = k_u < k_v ? k_u : k_v; + unsigned k_u = power_of_two_multiple(u); + unsigned k_v = power_of_two_multiple(v); + unsigned k = k_u < k_v ? k_u : k_v; - machine_div2k(u, k_u); + machine_div2k(u, k_u); - while (true) { - machine_div2k(v, k_v); + while (true) { + machine_div2k(v, k_v); - if (lt(u, v)) { - sub(v, u, v); - } - else { - sub(u, v, diff); - swap(u, v); - swap(v, diff); - } - - if (is_zero(v) || is_one(v)) - break; - - // reset least significant bit - if (is_small(v)) - v.set(v.value() & ~1); - else - v.ptr()->m_digits[0] &= ~static_cast(1); - k_v = power_of_two_multiple(v); + if (lt(u, v)) { + sub(v, u, v); + } + else { + sub(u, v, diff); + swap(u, v); + swap(v, diff); } + + if (is_zero(v) || is_one(v)) + break; + + // reset least significant bit + if (is_small(v)) + v.set(v.value() & ~1); + else + v.ptr()->m_digits[0] &= ~static_cast(1); + k_v = power_of_two_multiple(v); + } - mul2k(u, k, c); - del(u); del(v); del(diff); + mul2k(u, k, c); + del(u); del(v); del(diff); #endif // BINARY_GCD #ifdef EUCLID_GCD - mpz tmp1; - mpz tmp2; - mpz aux; - set(tmp1, a); - set(tmp2, b); - abs(tmp1); - abs(tmp2); - if (lt(tmp1, tmp2)) - swap(tmp1, tmp2); - if (is_zero(tmp2)) { - swap(c, tmp1); - } - else { - while (true) { - if (is_uint64(tmp1) && is_uint64(tmp2)) { - set(c, u64_gcd(get_uint64(tmp1), get_uint64(tmp2))); - break; - } - rem(tmp1, tmp2, aux); - if (is_zero(aux)) { - swap(c, tmp2); - break; - } - swap(tmp1, tmp2); - swap(tmp2, aux); + mpz tmp1; + mpz tmp2; + mpz aux; + set(tmp1, a); + set(tmp2, b); + abs(tmp1); + abs(tmp2); + if (lt(tmp1, tmp2)) + swap(tmp1, tmp2); + if (is_zero(tmp2)) { + swap(c, tmp1); + } + else { + while (true) { + if (is_uint64(tmp1) && is_uint64(tmp2)) { + set(c, u64_gcd(get_uint64(tmp1), get_uint64(tmp2))); + break; } + rem(tmp1, tmp2, aux); + if (is_zero(aux)) { + swap(c, tmp2); + break; + } + swap(tmp1, tmp2); + swap(tmp2, aux); } - del(tmp1); del(tmp2); del(aux); + } + del(tmp1); del(tmp2); del(aux); #endif // EUCLID_GCD #ifdef LS_BINARY_GCD - mpz u, v, t, u1, u2; - set(u, a); - set(v, b); - abs(u); - abs(v); - if (lt(u, v)) - swap(u, v); - while (!is_zero(v)) { - // Basic idea: - // compute t = 2^e*v such that t <= u < 2t - // u := min{u - t, 2t - u} - // - // The assignment u := min{u - t, 2t - u} - // can be replaced with u := u - t - // - // Since u and v are positive, we have: - // 2^{log2(u)} <= u < 2^{(log2(u) + 1)} - // 2^{log2(v)} <= v < 2^{(log2(v) + 1)} - // --> - // 2^{log2(v)}*2^{log2(u)-log2(v)} <= v*2^{log2(u)-log2(v)} < 2^{log2(v) + 1}*2^{log2(u)-log2(v)} - // --> - // 2^{log2(u)} <= v*2^{log2(u)-log2(v)} < 2^{log2(u) + 1} - // - // Now, let t be v*2^{log2(u)-log2(v)} - // If t <= u, then we found t - // Otherwise t = t div 2 - unsigned k_u = log2(u); - unsigned k_v = log2(v); - SASSERT(k_v <= k_u); - unsigned e = k_u - k_v; - mul2k(v, e, t); - sub(u, t, u1); - if (is_neg(u1)) { - // t is too big - machine_div2k(t, 1); - // Now, u1 contains u - 2t - neg(u1); - // Now, u1 contains 2t - u - sub(u, t, u2); // u2 := u - t - } - else { - // u1 contains u - t - mul2k(t, 1); - sub(t, u, u2); - // u2 contains 2t - u - } - SASSERT(is_nonneg(u1)); - SASSERT(is_nonneg(u2)); - if (lt(u1, u2)) - swap(u, u1); - else - swap(u, u2); - if (lt(u, v)) - swap(u,v); + mpz u, v, t, u1, u2; + set(u, a); + set(v, b); + abs(u); + abs(v); + if (lt(u, v)) + swap(u, v); + while (!is_zero(v)) { + // Basic idea: + // compute t = 2^e*v such that t <= u < 2t + // u := min{u - t, 2t - u} + // + // The assignment u := min{u - t, 2t - u} + // can be replaced with u := u - t + // + // Since u and v are positive, we have: + // 2^{log2(u)} <= u < 2^{(log2(u) + 1)} + // 2^{log2(v)} <= v < 2^{(log2(v) + 1)} + // --> + // 2^{log2(v)}*2^{log2(u)-log2(v)} <= v*2^{log2(u)-log2(v)} < 2^{log2(v) + 1}*2^{log2(u)-log2(v)} + // --> + // 2^{log2(u)} <= v*2^{log2(u)-log2(v)} < 2^{log2(u) + 1} + // + // Now, let t be v*2^{log2(u)-log2(v)} + // If t <= u, then we found t + // Otherwise t = t div 2 + unsigned k_u = log2(u); + unsigned k_v = log2(v); + SASSERT(k_v <= k_u); + unsigned e = k_u - k_v; + mul2k(v, e, t); + sub(u, t, u1); + if (is_neg(u1)) { + // t is too big + machine_div2k(t, 1); + // Now, u1 contains u - 2t + neg(u1); + // Now, u1 contains 2t - u + sub(u, t, u2); // u2 := u - t } - swap(u, c); - del(u); del(v); del(t); del(u1); del(u2); + else { + // u1 contains u - t + mul2k(t, 1); + sub(t, u, u2); + // u2 contains 2t - u + } + SASSERT(is_nonneg(u1)); + SASSERT(is_nonneg(u2)); + if (lt(u1, u2)) + swap(u, u1); + else + swap(u, u2); + if (lt(u, v)) + swap(u,v); + } + swap(u, c); + del(u); del(v); del(t); del(u1); del(u2); #endif // LS_BINARY_GCD #ifdef LEHMER_GCD - // For now, it only works if sizeof(digit_t) == sizeof(unsigned) - static_assert(sizeof(digit_t) == sizeof(unsigned), ""); - - int64_t a_hat, b_hat, A, B, C, D, T, q, a_sz, b_sz; - mpz a1, b1, t, r, tmp; - set(a1, a); - set(b1, b); - abs(a1); - abs(b1); - if (lt(a1, b1)) - swap(a1, b1); - while (true) { - SASSERT(ge(a1, b1)); - if (is_small(b1)) { - if (is_small(a1)) { - unsigned r = u_gcd(a1.value(), b1.value()); - set(c, r); - break; - } - else { - while (!is_zero(b1)) { - SASSERT(ge(a1, b1)); - rem(a1, b1, tmp); - swap(a1, b1); - swap(b1, tmp); - } - swap(c, a1); - break; - } - } - SASSERT(!is_small(a1)); - SASSERT(!is_small(b1)); - a_sz = a1.ptr()->m_size; - b_sz = b1.ptr()->m_size; - SASSERT(b_sz <= a_sz); - a_hat = a1.ptr()->m_digits[a_sz - 1]; - b_hat = (b_sz == a_sz) ? b1.ptr()->m_digits[b_sz - 1] : 0; - A = 1; - B = 0; - C = 0; - D = 1; - while (true) { - // Loop invariants - SASSERT(a_hat + A <= static_cast(UINT_MAX) + 1); - SASSERT(a_hat + B < static_cast(UINT_MAX) + 1); - SASSERT(b_hat + C < static_cast(UINT_MAX) + 1); - SASSERT(b_hat + D <= static_cast(UINT_MAX) + 1); - // overflows can't happen since I'm using int64 - if (b_hat + C == 0 || b_hat + D == 0) - break; - q = (a_hat + A)/(b_hat + C); - if (q != (a_hat + B)/(b_hat + D)) - break; - T = A - q*C; - A = C; - C = T; - T = B - q*D; - B = D; - D = T; - T = a_hat - q*b_hat; - a_hat = b_hat; - b_hat = T; - } - SASSERT(ge(a1, b1)); - if (B == 0) { - rem(a1, b1, t); - swap(a1, b1); - swap(b1, t); - SASSERT(ge(a1, b1)); + // For now, it only works if sizeof(digit_t) == sizeof(unsigned) + static_assert(sizeof(digit_t) == sizeof(unsigned), ""); + + int64_t a_hat, b_hat, A, B, C, D, T, q, a_sz, b_sz; + mpz a1, b1, t, r, tmp; + set(a1, a); + set(b1, b); + abs(a1); + abs(b1); + if (lt(a1, b1)) + swap(a1, b1); + while (true) { + SASSERT(ge(a1, b1)); + if (is_small(b1)) { + if (is_small(a1)) { + unsigned r = u_gcd(a1.value(), b1.value()); + set(c, r); + break; } else { - // t <- A*a1 - set(tmp, A); - mul(a1, tmp, t); - // t <- t + B*b1 - set(tmp, B); - addmul(t, tmp, b1, t); - // r <- C*a1 - set(tmp, C); - mul(a1, tmp, r); - // r <- r + D*b1 - set(tmp, D); - addmul(r, tmp, b1, r); - // a <- t - swap(a1, t); - // b <- r - swap(b1, r); - SASSERT(ge(a1, b1)); + while (!is_zero(b1)) { + SASSERT(ge(a1, b1)); + rem(a1, b1, tmp); + swap(a1, b1); + swap(b1, tmp); + } + swap(c, a1); + break; } } - del(a1); del(b1); del(r); del(t); del(tmp); -#endif // LEHMER_GCD + SASSERT(!is_small(a1)); + SASSERT(!is_small(b1)); + a_sz = a1.ptr()->m_size; + b_sz = b1.ptr()->m_size; + SASSERT(b_sz <= a_sz); + a_hat = a1.ptr()->m_digits[a_sz - 1]; + b_hat = (b_sz == a_sz) ? b1.ptr()->m_digits[b_sz - 1] : 0; + A = 1; + B = 0; + C = 0; + D = 1; + while (true) { + // Loop invariants + SASSERT(a_hat + A <= static_cast(UINT_MAX) + 1); + SASSERT(a_hat + B < static_cast(UINT_MAX) + 1); + SASSERT(b_hat + C < static_cast(UINT_MAX) + 1); + SASSERT(b_hat + D <= static_cast(UINT_MAX) + 1); + // overflows can't happen since I'm using int64 + if (b_hat + C == 0 || b_hat + D == 0) + break; + q = (a_hat + A)/(b_hat + C); + if (q != (a_hat + B)/(b_hat + D)) + break; + T = A - q*C; + A = C; + C = T; + T = B - q*D; + B = D; + D = T; + T = a_hat - q*b_hat; + a_hat = b_hat; + b_hat = T; + } + SASSERT(ge(a1, b1)); + if (B == 0) { + rem(a1, b1, t); + swap(a1, b1); + swap(b1, t); + SASSERT(ge(a1, b1)); + } + else { + // t <- A*a1 + set(tmp, A); + mul(a1, tmp, t); + // t <- t + B*b1 + set(tmp, B); + addmul(t, tmp, b1, t); + // r <- C*a1 + set(tmp, C); + mul(a1, tmp, r); + // r <- r + D*b1 + set(tmp, D); + addmul(r, tmp, b1, r); + // a <- t + swap(a1, t); + // b <- r + swap(b1, r); + SASSERT(ge(a1, b1)); + } } + del(a1); del(b1); del(r); del(t); del(tmp); +#endif // LEHMER_GCD } template @@ -2029,25 +2030,51 @@ void mpz_manager::ensure_capacity(mpz & a, unsigned capacity) { capacity = m_init_cell_capacity; if (is_small(a)) { - int val = a.value(); + int64_t val = a.value64(); allocate_if_needed(a, capacity); SASSERT(a.ptr()->m_capacity >= capacity); - if (val == INT_MIN) { - unsigned intmin_sz = m_int_min.ptr()->m_size; - for (unsigned i = 0; i < intmin_sz; ++i) - a.ptr()->m_digits[i] = m_int_min.ptr()->m_digits[i]; + // Check if this is SMALL_INT_MIN which needs special handling + if (val == mpz::SMALL_INT_MIN) { + // For 32-bit: SMALL_INT_MIN = -2^30, so -val = 2^30 fits in unsigned + // For 64-bit: SMALL_INT_MIN = -2^62, so -val = 2^62 fits in uint64_t + uint64_t abs_val = static_cast(-val); + if (sizeof(digit_t) == sizeof(uint64_t)) { + // 64-bit machine + a.ptr()->m_digits[0] = static_cast(abs_val); + a.ptr()->m_size = 1; + } + else { + // 32-bit machine + a.ptr()->m_digits[0] = static_cast(abs_val); + a.ptr()->m_digits[1] = static_cast(abs_val >> 32); + a.ptr()->m_size = (abs_val >> 32) == 0 ? 1 : 2; + } a.set_sign(-1); - a.ptr()->m_size = m_int_min.ptr()->m_size; } else if (val < 0) { - a.ptr()->m_digits[0] = -val; + uint64_t abs_val = static_cast(-val); + if (sizeof(digit_t) == sizeof(uint64_t)) { + a.ptr()->m_digits[0] = static_cast(abs_val); + a.ptr()->m_size = 1; + } + else { + a.ptr()->m_digits[0] = static_cast(abs_val); + a.ptr()->m_digits[1] = static_cast(abs_val >> 32); + a.ptr()->m_size = (abs_val >> 32) == 0 ? 1 : 2; + } a.set_sign(-1); - a.ptr()->m_size = 1; } else { - a.ptr()->m_digits[0] = val; + if (sizeof(digit_t) == sizeof(uint64_t)) { + a.ptr()->m_digits[0] = static_cast(val); + a.ptr()->m_size = 1; + } + else { + a.ptr()->m_digits[0] = static_cast(val); + a.ptr()->m_digits[1] = static_cast(val >> 32); + a.ptr()->m_size = (val >> 32) == 0 ? 1 : 2; + } a.set_sign(1); - a.ptr()->m_size = 1; } } else if (a.ptr()->m_capacity < capacity) { @@ -2079,10 +2106,10 @@ void mpz_manager::normalize(mpz & a) { return; } - if (i == 1 && ds[0] <= INT_MAX) { - // a is small - int val = a.sign() < 0 ? -static_cast(ds[0]) : static_cast(ds[0]); - a.set(val); + if (i == 1 && ds[0] <= static_cast(mpz::SMALL_INT_MAX)) { + // a fits in small integer range + int64_t val = a.sign() < 0 ? -static_cast(ds[0]) : static_cast(ds[0]); + a.set64(val); return; } // adjust size diff --git a/src/util/mpz.h b/src/util/mpz.h index 64e1587cb..490c85993 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -92,6 +92,32 @@ private: static constexpr uintptr_t OWNER_BIT = 0x4; static constexpr uintptr_t MPZ_PTR_MASK = ~static_cast(0x7); + // Small integers are stored shifted left by 1, so we have (sizeof(uintptr_t)*8 - 1) bits available + // This gives us: + // - On 32-bit platforms: 31 bits, range [-2^30, 2^30-1] + // - On 64-bit platforms: 63 bits, range [-2^62, 2^62-1] + static constexpr int SMALL_BITS = sizeof(uintptr_t) * 8 - 1; + + // Maximum and minimum values that can be stored as small integers + static constexpr int64_t SMALL_INT_MAX = (static_cast(1) << (SMALL_BITS - 1)) - 1; + static constexpr int64_t SMALL_INT_MIN = -(static_cast(1) << (SMALL_BITS - 1)); + + static bool fits_in_small(int64_t v) { + return v >= SMALL_INT_MIN && v <= SMALL_INT_MAX; + } + + static bool fits_in_small(uint64_t v) { + return v <= static_cast(SMALL_INT_MAX); + } + + static bool fits_in_small(int v) { + return fits_in_small(static_cast(v)); + } + + static bool fits_in_small(unsigned int v) { + return fits_in_small(static_cast(v)); + } + mpz_type * ptr() const { SASSERT(!is_small()); return reinterpret_cast(m_value & MPZ_PTR_MASK); @@ -135,7 +161,11 @@ protected: friend class mpbq_manager; friend class mpz_stack; public: - mpz(int v = 0) noexcept : m_value(static_cast(static_cast(v)) << 1) {} + mpz(int v = 0) noexcept : m_value(static_cast(static_cast(v)) << 1) { + // On 32-bit platforms, INT_MIN doesn't fit in 31 bits. This constructor should only be used + // with values that fit, or the caller should use set_big_i64. + SASSERT(fits_in_small(v)); + } mpz(mpz_type* ptr) noexcept { SASSERT(ptr); @@ -157,6 +187,11 @@ public: m_value = static_cast(static_cast(v)) << 1; } + void set64(int64_t v) { + SASSERT(fits_in_small(v)); + m_value = static_cast(static_cast(v)) << 1; + } + void swap(mpz & other) noexcept { std::swap(m_value, other.m_value); } @@ -168,9 +203,16 @@ public: inline int value() const { SASSERT(is_small()); // Decode small integer: shift right by 1 (arithmetic shift to preserve sign) + // Note: On 64-bit platforms, this may truncate if the value doesn't fit in int return static_cast(static_cast(m_value) >> 1); } + inline int64_t value64() const { + SASSERT(is_small()); + // Decode small integer: shift right by 1 (arithmetic shift to preserve sign) + return static_cast(static_cast(m_value) >> 1); + } + inline int sign() const { SASSERT(!is_small()); return get_sign(); @@ -291,13 +333,16 @@ class mpz_manager { mpz m_two64; - static int64_t i64(mpz const & a) { return static_cast(a.value()); } + static int64_t i64(mpz const & a) { return a.value64(); } void set_big_i64(mpz & c, int64_t v); void set_i64(mpz & c, int64_t v) { - if (v >= INT_MIN && v <= INT_MAX) { - c.set(static_cast(v)); + if (mpz::fits_in_small(v)) { + if (!is_small(c)) { + deallocate(c); + } + c.set64(v); } else { set_big_i64(c, v); @@ -363,20 +408,44 @@ class mpz_manager { void get_sign_cell(mpz const & a, int & sign, mpz_cell * & cell, mpz_cell* reserve) { if (is_small(a)) { - if (a.value() == INT_MIN) { + int64_t val = a.value64(); + cell = reserve; + if (val == mpz::SMALL_INT_MIN) { sign = -1; - cell = m_int_min.ptr(); - } - else { - cell = reserve; - cell->m_size = 1; - if (a.value() < 0) { - sign = -1; - cell->m_digits[0] = -a.value(); + uint64_t abs_val = static_cast(-val); + if (sizeof(digit_t) == sizeof(uint64_t)) { + cell->m_size = 1; + cell->m_digits[0] = static_cast(abs_val); } else { - sign = 1; - cell->m_digits[0] = a.value(); + cell->m_digits[0] = static_cast(abs_val); + cell->m_digits[1] = static_cast(abs_val >> 32); + cell->m_size = (abs_val >> 32) == 0 ? 1 : 2; + } + } + else if (val < 0) { + sign = -1; + uint64_t abs_val = static_cast(-val); + if (sizeof(digit_t) == sizeof(uint64_t)) { + cell->m_size = 1; + cell->m_digits[0] = static_cast(abs_val); + } + else { + cell->m_digits[0] = static_cast(abs_val); + cell->m_digits[1] = static_cast(abs_val >> 32); + cell->m_size = (abs_val >> 32) == 0 ? 1 : 2; + } + } + else { + sign = 1; + if (sizeof(digit_t) == sizeof(uint64_t)) { + cell->m_size = 1; + cell->m_digits[0] = static_cast(val); + } + else { + cell->m_digits[0] = static_cast(val); + cell->m_digits[1] = static_cast(val >> 32); + cell->m_size = (val >> 32) == 0 ? 1 : 2; } } } @@ -593,17 +662,28 @@ public: } void set(mpz & a, int val) { - if (!is_small(a)) { - deallocate(a); + // On 32-bit platforms, int can be outside small range + if (mpz::fits_in_small(val)) { + if (!is_small(a)) { + deallocate(a); + } + a.set(val); + } + else { + set_i64(a, val); } - a.set(val); } void set(mpz & a, unsigned val) { - if (val <= INT_MAX) - set(a, static_cast(val)); - else - set(a, static_cast(static_cast(val))); + if (mpz::fits_in_small(val)) { + if (!is_small(a)) { + deallocate(a); + } + a.set(static_cast(val)); + } + else { + set_i64(a, static_cast(val)); + } } void set(mpz & a, char const * val); @@ -613,8 +693,11 @@ public: } void set(mpz & a, uint64_t val) { - if (val < INT_MAX) { - a.set(static_cast(val)); + if (mpz::fits_in_small(val)) { + if (!is_small(a)) { + deallocate(a); + } + a.set64(static_cast(val)); } else { set_big_ui64(a, val); From b8ed45bfac9fdc4c69a450ce75929b6c8df99fd2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Feb 2026 16:08:58 +0000 Subject: [PATCH 05/41] Revert deallocation in set methods and restore indentation in gcd function Co-authored-by: nunoplopes <2998477+nunoplopes@users.noreply.github.com> --- src/util/mpz.cpp | 446 ++++++++++++++++++++++++----------------------- src/util/mpz.h | 20 +-- 2 files changed, 228 insertions(+), 238 deletions(-) diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 136a95132..2d9768758 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -954,249 +954,251 @@ void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { return; } } + else { #ifdef _MP_GMP - ensure_mpz_t a1(a), b1(b); - mk_big(c); - mpz_gcd(*c.ptr(), a1(), b1()); - return; + ensure_mpz_t a1(a), b1(b); + mk_big(c); + mpz_gcd(*c.ptr(), a1(), b1()); + return; #endif - if (is_zero(a)) { - set(c, b); - abs(c); - return; - } - if (is_zero(b)) { - set(c, a); - abs(c); - return; - } -#ifdef BINARY_GCD - // Binary GCD for big numbers - // - It doesn't use division - // - The initial experiments, don't show any performance improvement - // - It only works with _MP_INTERNAL - mpz u, v, diff; - set(u, a); - set(v, b); - abs(u); - abs(v); - - unsigned k_u = power_of_two_multiple(u); - unsigned k_v = power_of_two_multiple(v); - unsigned k = k_u < k_v ? k_u : k_v; - - machine_div2k(u, k_u); - - while (true) { - machine_div2k(v, k_v); - - if (lt(u, v)) { - sub(v, u, v); - } - else { - sub(u, v, diff); - swap(u, v); - swap(v, diff); + if (is_zero(a)) { + set(c, b); + abs(c); + return; } - - if (is_zero(v) || is_one(v)) - break; - - // reset least significant bit - if (is_small(v)) - v.set(v.value() & ~1); - else - v.ptr()->m_digits[0] &= ~static_cast(1); - k_v = power_of_two_multiple(v); - } + if (is_zero(b)) { + set(c, a); + abs(c); + return; + } +#ifdef BINARY_GCD + // Binary GCD for big numbers + // - It doesn't use division + // - The initial experiments, don't show any performance improvement + // - It only works with _MP_INTERNAL + mpz u, v, diff; + set(u, a); + set(v, b); + abs(u); + abs(v); - mul2k(u, k, c); - del(u); del(v); del(diff); + unsigned k_u = power_of_two_multiple(u); + unsigned k_v = power_of_two_multiple(v); + unsigned k = k_u < k_v ? k_u : k_v; + + machine_div2k(u, k_u); + + while (true) { + machine_div2k(v, k_v); + + if (lt(u, v)) { + sub(v, u, v); + } + else { + sub(u, v, diff); + swap(u, v); + swap(v, diff); + } + + if (is_zero(v) || is_one(v)) + break; + + // reset least significant bit + if (is_small(v)) + v.set(v.value() & ~1); + else + v.ptr()->m_digits[0] &= ~static_cast(1); + k_v = power_of_two_multiple(v); + } + + mul2k(u, k, c); + del(u); del(v); del(diff); #endif // BINARY_GCD #ifdef EUCLID_GCD - mpz tmp1; - mpz tmp2; - mpz aux; - set(tmp1, a); - set(tmp2, b); - abs(tmp1); - abs(tmp2); - if (lt(tmp1, tmp2)) - swap(tmp1, tmp2); - if (is_zero(tmp2)) { - swap(c, tmp1); - } - else { - while (true) { - if (is_uint64(tmp1) && is_uint64(tmp2)) { - set(c, u64_gcd(get_uint64(tmp1), get_uint64(tmp2))); - break; - } - rem(tmp1, tmp2, aux); - if (is_zero(aux)) { - swap(c, tmp2); - break; - } + mpz tmp1; + mpz tmp2; + mpz aux; + set(tmp1, a); + set(tmp2, b); + abs(tmp1); + abs(tmp2); + if (lt(tmp1, tmp2)) swap(tmp1, tmp2); - swap(tmp2, aux); + if (is_zero(tmp2)) { + swap(c, tmp1); } - } - del(tmp1); del(tmp2); del(aux); + else { + while (true) { + if (is_uint64(tmp1) && is_uint64(tmp2)) { + set(c, u64_gcd(get_uint64(tmp1), get_uint64(tmp2))); + break; + } + rem(tmp1, tmp2, aux); + if (is_zero(aux)) { + swap(c, tmp2); + break; + } + swap(tmp1, tmp2); + swap(tmp2, aux); + } + } + del(tmp1); del(tmp2); del(aux); #endif // EUCLID_GCD #ifdef LS_BINARY_GCD - mpz u, v, t, u1, u2; - set(u, a); - set(v, b); - abs(u); - abs(v); - if (lt(u, v)) - swap(u, v); - while (!is_zero(v)) { - // Basic idea: - // compute t = 2^e*v such that t <= u < 2t - // u := min{u - t, 2t - u} - // - // The assignment u := min{u - t, 2t - u} - // can be replaced with u := u - t - // - // Since u and v are positive, we have: - // 2^{log2(u)} <= u < 2^{(log2(u) + 1)} - // 2^{log2(v)} <= v < 2^{(log2(v) + 1)} - // --> - // 2^{log2(v)}*2^{log2(u)-log2(v)} <= v*2^{log2(u)-log2(v)} < 2^{log2(v) + 1}*2^{log2(u)-log2(v)} - // --> - // 2^{log2(u)} <= v*2^{log2(u)-log2(v)} < 2^{log2(u) + 1} - // - // Now, let t be v*2^{log2(u)-log2(v)} - // If t <= u, then we found t - // Otherwise t = t div 2 - unsigned k_u = log2(u); - unsigned k_v = log2(v); - SASSERT(k_v <= k_u); - unsigned e = k_u - k_v; - mul2k(v, e, t); - sub(u, t, u1); - if (is_neg(u1)) { - // t is too big - machine_div2k(t, 1); - // Now, u1 contains u - 2t - neg(u1); - // Now, u1 contains 2t - u - sub(u, t, u2); // u2 := u - t - } - else { - // u1 contains u - t - mul2k(t, 1); - sub(t, u, u2); - // u2 contains 2t - u - } - SASSERT(is_nonneg(u1)); - SASSERT(is_nonneg(u2)); - if (lt(u1, u2)) - swap(u, u1); - else - swap(u, u2); + mpz u, v, t, u1, u2; + set(u, a); + set(v, b); + abs(u); + abs(v); if (lt(u, v)) - swap(u,v); - } - swap(u, c); - del(u); del(v); del(t); del(u1); del(u2); + swap(u, v); + while (!is_zero(v)) { + // Basic idea: + // compute t = 2^e*v such that t <= u < 2t + // u := min{u - t, 2t - u} + // + // The assignment u := min{u - t, 2t - u} + // can be replaced with u := u - t + // + // Since u and v are positive, we have: + // 2^{log2(u)} <= u < 2^{(log2(u) + 1)} + // 2^{log2(v)} <= v < 2^{(log2(v) + 1)} + // --> + // 2^{log2(v)}*2^{log2(u)-log2(v)} <= v*2^{log2(u)-log2(v)} < 2^{log2(v) + 1}*2^{log2(u)-log2(v)} + // --> + // 2^{log2(u)} <= v*2^{log2(u)-log2(v)} < 2^{log2(u) + 1} + // + // Now, let t be v*2^{log2(u)-log2(v)} + // If t <= u, then we found t + // Otherwise t = t div 2 + unsigned k_u = log2(u); + unsigned k_v = log2(v); + SASSERT(k_v <= k_u); + unsigned e = k_u - k_v; + mul2k(v, e, t); + sub(u, t, u1); + if (is_neg(u1)) { + // t is too big + machine_div2k(t, 1); + // Now, u1 contains u - 2t + neg(u1); + // Now, u1 contains 2t - u + sub(u, t, u2); // u2 := u - t + } + else { + // u1 contains u - t + mul2k(t, 1); + sub(t, u, u2); + // u2 contains 2t - u + } + SASSERT(is_nonneg(u1)); + SASSERT(is_nonneg(u2)); + if (lt(u1, u2)) + swap(u, u1); + else + swap(u, u2); + if (lt(u, v)) + swap(u,v); + } + swap(u, c); + del(u); del(v); del(t); del(u1); del(u2); #endif // LS_BINARY_GCD #ifdef LEHMER_GCD - // For now, it only works if sizeof(digit_t) == sizeof(unsigned) - static_assert(sizeof(digit_t) == sizeof(unsigned), ""); - - int64_t a_hat, b_hat, A, B, C, D, T, q, a_sz, b_sz; - mpz a1, b1, t, r, tmp; - set(a1, a); - set(b1, b); - abs(a1); - abs(b1); - if (lt(a1, b1)) - swap(a1, b1); - while (true) { - SASSERT(ge(a1, b1)); - if (is_small(b1)) { - if (is_small(a1)) { - unsigned r = u_gcd(a1.value(), b1.value()); - set(c, r); - break; + // For now, it only works if sizeof(digit_t) == sizeof(unsigned) + static_assert(sizeof(digit_t) == sizeof(unsigned), ""); + + int64_t a_hat, b_hat, A, B, C, D, T, q, a_sz, b_sz; + mpz a1, b1, t, r, tmp; + set(a1, a); + set(b1, b); + abs(a1); + abs(b1); + if (lt(a1, b1)) + swap(a1, b1); + while (true) { + SASSERT(ge(a1, b1)); + if (is_small(b1)) { + if (is_small(a1)) { + unsigned r = u_gcd(a1.value(), b1.value()); + set(c, r); + break; + } + else { + while (!is_zero(b1)) { + SASSERT(ge(a1, b1)); + rem(a1, b1, tmp); + swap(a1, b1); + swap(b1, tmp); + } + swap(c, a1); + break; + } + } + SASSERT(!is_small(a1)); + SASSERT(!is_small(b1)); + a_sz = a1.ptr()->m_size; + b_sz = b1.ptr()->m_size; + SASSERT(b_sz <= a_sz); + a_hat = a1.ptr()->m_digits[a_sz - 1]; + b_hat = (b_sz == a_sz) ? b1.ptr()->m_digits[b_sz - 1] : 0; + A = 1; + B = 0; + C = 0; + D = 1; + while (true) { + // Loop invariants + SASSERT(a_hat + A <= static_cast(UINT_MAX) + 1); + SASSERT(a_hat + B < static_cast(UINT_MAX) + 1); + SASSERT(b_hat + C < static_cast(UINT_MAX) + 1); + SASSERT(b_hat + D <= static_cast(UINT_MAX) + 1); + // overflows can't happen since I'm using int64 + if (b_hat + C == 0 || b_hat + D == 0) + break; + q = (a_hat + A)/(b_hat + C); + if (q != (a_hat + B)/(b_hat + D)) + break; + T = A - q*C; + A = C; + C = T; + T = B - q*D; + B = D; + D = T; + T = a_hat - q*b_hat; + a_hat = b_hat; + b_hat = T; + } + SASSERT(ge(a1, b1)); + if (B == 0) { + rem(a1, b1, t); + swap(a1, b1); + swap(b1, t); + SASSERT(ge(a1, b1)); } else { - while (!is_zero(b1)) { - SASSERT(ge(a1, b1)); - rem(a1, b1, tmp); - swap(a1, b1); - swap(b1, tmp); - } - swap(c, a1); - break; + // t <- A*a1 + set(tmp, A); + mul(a1, tmp, t); + // t <- t + B*b1 + set(tmp, B); + addmul(t, tmp, b1, t); + // r <- C*a1 + set(tmp, C); + mul(a1, tmp, r); + // r <- r + D*b1 + set(tmp, D); + addmul(r, tmp, b1, r); + // a <- t + swap(a1, t); + // b <- r + swap(b1, r); + SASSERT(ge(a1, b1)); } } - SASSERT(!is_small(a1)); - SASSERT(!is_small(b1)); - a_sz = a1.ptr()->m_size; - b_sz = b1.ptr()->m_size; - SASSERT(b_sz <= a_sz); - a_hat = a1.ptr()->m_digits[a_sz - 1]; - b_hat = (b_sz == a_sz) ? b1.ptr()->m_digits[b_sz - 1] : 0; - A = 1; - B = 0; - C = 0; - D = 1; - while (true) { - // Loop invariants - SASSERT(a_hat + A <= static_cast(UINT_MAX) + 1); - SASSERT(a_hat + B < static_cast(UINT_MAX) + 1); - SASSERT(b_hat + C < static_cast(UINT_MAX) + 1); - SASSERT(b_hat + D <= static_cast(UINT_MAX) + 1); - // overflows can't happen since I'm using int64 - if (b_hat + C == 0 || b_hat + D == 0) - break; - q = (a_hat + A)/(b_hat + C); - if (q != (a_hat + B)/(b_hat + D)) - break; - T = A - q*C; - A = C; - C = T; - T = B - q*D; - B = D; - D = T; - T = a_hat - q*b_hat; - a_hat = b_hat; - b_hat = T; + del(a1); del(b1); del(r); del(t); del(tmp); } - SASSERT(ge(a1, b1)); - if (B == 0) { - rem(a1, b1, t); - swap(a1, b1); - swap(b1, t); - SASSERT(ge(a1, b1)); - } - else { - // t <- A*a1 - set(tmp, A); - mul(a1, tmp, t); - // t <- t + B*b1 - set(tmp, B); - addmul(t, tmp, b1, t); - // r <- C*a1 - set(tmp, C); - mul(a1, tmp, r); - // r <- r + D*b1 - set(tmp, D); - addmul(r, tmp, b1, r); - // a <- t - swap(a1, t); - // b <- r - swap(b1, r); - SASSERT(ge(a1, b1)); - } - } - del(a1); del(b1); del(r); del(t); del(tmp); #endif // LEHMER_GCD } diff --git a/src/util/mpz.h b/src/util/mpz.h index 490c85993..555b5e1de 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -338,10 +338,7 @@ class mpz_manager { void set_big_i64(mpz & c, int64_t v); void set_i64(mpz & c, int64_t v) { - if (mpz::fits_in_small(v)) { - if (!is_small(c)) { - deallocate(c); - } + if (mpz::fits_in_small(v) && is_small(c)) { c.set64(v); } else { @@ -663,10 +660,7 @@ public: void set(mpz & a, int val) { // On 32-bit platforms, int can be outside small range - if (mpz::fits_in_small(val)) { - if (!is_small(a)) { - deallocate(a); - } + if (mpz::fits_in_small(val) && is_small(a)) { a.set(val); } else { @@ -675,10 +669,7 @@ public: } void set(mpz & a, unsigned val) { - if (mpz::fits_in_small(val)) { - if (!is_small(a)) { - deallocate(a); - } + if (mpz::fits_in_small(val) && is_small(a)) { a.set(static_cast(val)); } else { @@ -693,10 +684,7 @@ public: } void set(mpz & a, uint64_t val) { - if (mpz::fits_in_small(val)) { - if (!is_small(a)) { - deallocate(a); - } + if (mpz::fits_in_small(val) && is_small(a)) { a.set64(static_cast(val)); } else { From 4dc74e2e30e967086ac8585b83b3583bc98534b5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Feb 2026 18:11:53 +0000 Subject: [PATCH 06/41] Fix undefined behavior in SMALL_INT_MAX/MIN constants by using unsigned arithmetic Co-authored-by: nunoplopes <2998477+nunoplopes@users.noreply.github.com> --- src/util/mpz.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/util/mpz.h b/src/util/mpz.h index 555b5e1de..51f1e0d68 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -99,8 +99,9 @@ private: static constexpr int SMALL_BITS = sizeof(uintptr_t) * 8 - 1; // Maximum and minimum values that can be stored as small integers - static constexpr int64_t SMALL_INT_MAX = (static_cast(1) << (SMALL_BITS - 1)) - 1; - static constexpr int64_t SMALL_INT_MIN = -(static_cast(1) << (SMALL_BITS - 1)); + // Use unsigned arithmetic to avoid undefined behavior on left shift + static constexpr int64_t SMALL_INT_MAX = (static_cast(1) << (SMALL_BITS - 1)) - 1; + static constexpr int64_t SMALL_INT_MIN = -(static_cast(static_cast(1) << (SMALL_BITS - 1))); static bool fits_in_small(int64_t v) { return v >= SMALL_INT_MIN && v <= SMALL_INT_MAX; From dcfeb559fe791b0edfc13220e6f3dfe83c885903 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Feb 2026 18:30:29 +0000 Subject: [PATCH 07/41] Add assertion to set() method and simplify get_sign_cell by removing redundant SMALL_INT_MIN case Co-authored-by: nunoplopes <2998477+nunoplopes@users.noreply.github.com> --- src/util/mpz.h | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/util/mpz.h b/src/util/mpz.h index 51f1e0d68..68bae1914 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -185,6 +185,7 @@ public: } void set(int v) { + SASSERT(is_small()); m_value = static_cast(static_cast(v)) << 1; } @@ -408,20 +409,7 @@ class mpz_manager { if (is_small(a)) { int64_t val = a.value64(); cell = reserve; - if (val == mpz::SMALL_INT_MIN) { - sign = -1; - uint64_t abs_val = static_cast(-val); - if (sizeof(digit_t) == sizeof(uint64_t)) { - cell->m_size = 1; - cell->m_digits[0] = static_cast(abs_val); - } - else { - cell->m_digits[0] = static_cast(abs_val); - cell->m_digits[1] = static_cast(abs_val >> 32); - cell->m_size = (abs_val >> 32) == 0 ? 1 : 2; - } - } - else if (val < 0) { + if (val < 0) { sign = -1; uint64_t abs_val = static_cast(-val); if (sizeof(digit_t) == sizeof(uint64_t)) { From 5edef9e9a0d123f626f6aec43cf4c6764f30940a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Feb 2026 18:49:20 +0000 Subject: [PATCH 08/41] Remove nonsensical assertion and revert to simpler shift for SMALL_INT constants Co-authored-by: nunoplopes <2998477+nunoplopes@users.noreply.github.com> --- src/util/mpz.cpp | 1 - src/util/mpz.h | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 2d9768758..b877c700b 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -939,7 +939,6 @@ void mpz_manager::big_rem(mpz const & a, mpz const & b, mpz & c) { template void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { - static_assert(sizeof(int) == sizeof(int), "size mismatch"); static_assert(sizeof(mpz) <= 16, "mpz size overflow"); if (is_small(a) && is_small(b)) { int64_t _a = a.value64(); diff --git a/src/util/mpz.h b/src/util/mpz.h index 68bae1914..5d1f30c7d 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -99,9 +99,8 @@ private: static constexpr int SMALL_BITS = sizeof(uintptr_t) * 8 - 1; // Maximum and minimum values that can be stored as small integers - // Use unsigned arithmetic to avoid undefined behavior on left shift - static constexpr int64_t SMALL_INT_MAX = (static_cast(1) << (SMALL_BITS - 1)) - 1; - static constexpr int64_t SMALL_INT_MIN = -(static_cast(static_cast(1) << (SMALL_BITS - 1))); + static constexpr int64_t SMALL_INT_MAX = (static_cast(1) << (SMALL_BITS - 1)) - 1; + static constexpr int64_t SMALL_INT_MIN = -(static_cast(1) << (SMALL_BITS - 1)); static bool fits_in_small(int64_t v) { return v >= SMALL_INT_MIN && v <= SMALL_INT_MAX; From 76dde47c4e3ead452b6ecd022ea393ab288a5651 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Feb 2026 22:47:15 +0000 Subject: [PATCH 09/41] Fix uses of value() that should be value64() for 64-bit small integers Co-authored-by: nunoplopes <2998477+nunoplopes@users.noreply.github.com> --- src/util/mpz.cpp | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index b877c700b..b807c5886 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -1121,7 +1121,7 @@ void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { SASSERT(ge(a1, b1)); if (is_small(b1)) { if (is_small(a1)) { - unsigned r = u_gcd(a1.value(), b1.value()); + uint64_t r = u64_gcd(static_cast(a1.value64()), static_cast(b1.value64())); set(c, r); break; } @@ -1401,7 +1401,7 @@ void mpz_manager::bitwise_or(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_nonneg(b)); TRACE(mpz, tout << "is_small(a): " << is_small(a) << ", is_small(b): " << is_small(b) << "\n";); if (is_small(a) && is_small(b)) { - c.set(a.value() | b.value()); + c.set64(a.value64() | b.value64()); } else { #ifndef _MP_GMP @@ -1446,7 +1446,7 @@ void mpz_manager::bitwise_and(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_nonneg(a)); SASSERT(is_nonneg(b)); if (is_small(a) && is_small(b)) { - c.set(a.value() & b.value()); + c.set64(a.value64() & b.value64()); } else { #ifndef _MP_GMP @@ -2336,11 +2336,16 @@ template unsigned mpz_manager::mlog2(mpz const & a) { if (is_nonneg(a)) return 0; - if (is_small(a) && a.value() == INT_MIN) - return ::log2((unsigned)a.value()); - - if (is_small(a)) - return ::log2((unsigned)-a.value()); + if (is_small(a)) { + int64_t v = a.value64(); + if (v == mpz::SMALL_INT_MIN) { + // Special case: negating SMALL_INT_MIN would overflow + // For 32-bit: SMALL_INT_MIN = -2^30, so log2(2^30) = 30 + // For 64-bit: SMALL_INT_MIN = -2^62, so log2(2^62) = 62 + return mpz::SMALL_BITS - 1; + } + return uint64_log2(static_cast(-v)); + } #ifndef _MP_GMP static_assert(sizeof(digit_t) == 8 || sizeof(digit_t) == 4, ""); mpz_cell * c = a.ptr(); From a5e1fa993073d1b634694fff5b36d32f5a74d480 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 12:35:47 +0000 Subject: [PATCH 10/41] Fix more uses of value() that should use value64() for 64-bit correctness Co-authored-by: nunoplopes <2998477+nunoplopes@users.noreply.github.com> --- src/util/mpz.cpp | 53 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index b807c5886..76a89c09a 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -1004,7 +1004,7 @@ void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { // reset least significant bit if (is_small(v)) - v.set(v.value() & ~1); + v.set64(v.value64() & ~1); else v.ptr()->m_digits[0] &= ~static_cast(1); k_v = power_of_two_multiple(v); @@ -1676,7 +1676,7 @@ bool mpz_manager::is_int64(mpz const & a) const { template uint64_t mpz_manager::get_uint64(mpz const & a) const { if (is_small(a)) - return static_cast(a.value()); + return static_cast(a.value64()); #ifndef _MP_GMP SASSERT(a.ptr()->m_size > 0); return big_abs_to_uint64(a); @@ -1703,7 +1703,7 @@ uint64_t mpz_manager::get_uint64(mpz const & a) const { template int64_t mpz_manager::get_int64(mpz const & a) const { if (is_small(a)) - return static_cast(a.value()); + return a.value64(); #ifndef _MP_GMP SASSERT(is_int64(a)); uint64_t num = big_abs_to_uint64(a); @@ -2125,8 +2125,17 @@ void mpz_manager::machine_div2k(mpz & a, unsigned k) { if (is_small(a)) { if (k < 32) { int64_t twok = 1ull << ((int64_t)k); - int64_t val = a.value(); - a.set((int)(val/twok)); + int64_t val = a.value64(); + if (mpz::fits_in_small(val/twok)) { + a.set64(val/twok); + } else { + a.set((int)(val/twok)); + } + } + else if (k < 64) { + int64_t twok = 1ull << ((int64_t)k); + int64_t val = a.value64(); + a.set64(val/twok); } else { a.set(0); @@ -2580,12 +2589,33 @@ template bool mpz_manager::decompose(mpz const & a, svector & digits) { digits.reset(); if (is_small(a)) { - if (a.value() < 0) { - digits.push_back(-a.value()); + int64_t v = a.value64(); + if (v < 0) { + // Decompose negative value into digits + uint64_t abs_v = static_cast(-v); + if (sizeof(digit_t) == sizeof(uint64_t)) { + digits.push_back(static_cast(abs_v)); + } else { + // digit_t is 32-bit, need to split 64-bit value + digits.push_back(static_cast(abs_v)); + if (abs_v >> 32) { + digits.push_back(static_cast(abs_v >> 32)); + } + } return true; } else { - digits.push_back(a.value()); + // Decompose positive value into digits + uint64_t abs_v = static_cast(v); + if (sizeof(digit_t) == sizeof(uint64_t)) { + digits.push_back(static_cast(abs_v)); + } else { + // digit_t is 32-bit, need to split 64-bit value + digits.push_back(static_cast(abs_v)); + if (abs_v >> 32) { + digits.push_back(static_cast(abs_v >> 32)); + } + } return false; } } @@ -2617,10 +2647,11 @@ bool mpz_manager::decompose(mpz const & a, svector & digits) { template bool mpz_manager::get_bit(mpz const & a, unsigned index) { if (is_small(a)) { - SASSERT(a.value() >= 0); - if (index >= 8*sizeof(digit_t)) + int64_t v = a.value64(); + SASSERT(v >= 0); + if (index >= 64) return false; - return 0 != (a.value() & (1ull << (digit_t)index)); + return 0 != (v & (1ull << index)); } unsigned i = index / (sizeof(digit_t)*8); unsigned o = index % (sizeof(digit_t)*8); From 10c82a3b8403f524a2c31bec87fb5b191b18e57d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 13:48:38 +0000 Subject: [PATCH 11/41] Fix machine_div2k to not truncate when result doesn't fit in small (add assertion instead) Co-authored-by: nunoplopes <2998477+nunoplopes@users.noreply.github.com> --- src/util/mpz.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 76a89c09a..48c8229f9 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -2126,11 +2126,10 @@ void mpz_manager::machine_div2k(mpz & a, unsigned k) { if (k < 32) { int64_t twok = 1ull << ((int64_t)k); int64_t val = a.value64(); - if (mpz::fits_in_small(val/twok)) { - a.set64(val/twok); - } else { - a.set((int)(val/twok)); - } + int64_t result = val / twok; + // Division by power of 2 should keep us in small range + SASSERT(mpz::fits_in_small(result)); + a.set64(result); } else if (k < 64) { int64_t twok = 1ull << ((int64_t)k); From 9bf691f8bd5740c4feaa576c2c99acb63ed21608 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 14:10:48 +0000 Subject: [PATCH 12/41] Refactor decompose function and consolidate value()/value64() into single value() returning int64_t Co-authored-by: nunoplopes <2998477+nunoplopes@users.noreply.github.com> --- src/util/mpz.cpp | 70 +++++++++++++++++++----------------------------- src/util/mpz.h | 13 +++------ 2 files changed, 31 insertions(+), 52 deletions(-) diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 48c8229f9..7a36adeb1 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -705,7 +705,7 @@ template void mpz_manager::neg(mpz & a) { STRACE(mpz, tout << "[mpz] 0 - " << to_string(a) << " == ";); if (is_small(a)) { - int64_t v = a.value64(); + int64_t v = a.value(); if (v == mpz::SMALL_INT_MIN) { // neg(SMALL_INT_MIN) overflows small range set_big_i64(a, -v); @@ -728,7 +728,7 @@ void mpz_manager::neg(mpz & a) { template void mpz_manager::abs(mpz & a) { if (is_small(a)) { - int64_t v = a.value64(); + int64_t v = a.value(); if (v < 0) { if (v == mpz::SMALL_INT_MIN) { // abs(SMALL_INT_MIN) overflows small range @@ -941,8 +941,8 @@ template void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { static_assert(sizeof(mpz) <= 16, "mpz size overflow"); if (is_small(a) && is_small(b)) { - int64_t _a = a.value64(); - int64_t _b = b.value64(); + int64_t _a = a.value(); + int64_t _b = b.value(); // Check if absolute values fit in uint64 (they always do for small integers) // and won't overflow when negating if (_a != mpz::SMALL_INT_MIN && _b != mpz::SMALL_INT_MIN) { @@ -1004,7 +1004,7 @@ void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { // reset least significant bit if (is_small(v)) - v.set64(v.value64() & ~1); + v.set64(v.value() & ~1); else v.ptr()->m_digits[0] &= ~static_cast(1); k_v = power_of_two_multiple(v); @@ -1121,7 +1121,7 @@ void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { SASSERT(ge(a1, b1)); if (is_small(b1)) { if (is_small(a1)) { - uint64_t r = u64_gcd(static_cast(a1.value64()), static_cast(b1.value64())); + uint64_t r = u64_gcd(static_cast(a1.value()), static_cast(b1.value())); set(c, r); break; } @@ -1401,7 +1401,7 @@ void mpz_manager::bitwise_or(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_nonneg(b)); TRACE(mpz, tout << "is_small(a): " << is_small(a) << ", is_small(b): " << is_small(b) << "\n";); if (is_small(a) && is_small(b)) { - c.set64(a.value64() | b.value64()); + c.set64(a.value() | b.value()); } else { #ifndef _MP_GMP @@ -1446,7 +1446,7 @@ void mpz_manager::bitwise_and(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_nonneg(a)); SASSERT(is_nonneg(b)); if (is_small(a) && is_small(b)) { - c.set64(a.value64() & b.value64()); + c.set64(a.value() & b.value()); } else { #ifndef _MP_GMP @@ -1676,7 +1676,7 @@ bool mpz_manager::is_int64(mpz const & a) const { template uint64_t mpz_manager::get_uint64(mpz const & a) const { if (is_small(a)) - return static_cast(a.value64()); + return static_cast(a.value()); #ifndef _MP_GMP SASSERT(a.ptr()->m_size > 0); return big_abs_to_uint64(a); @@ -1703,7 +1703,7 @@ uint64_t mpz_manager::get_uint64(mpz const & a) const { template int64_t mpz_manager::get_int64(mpz const & a) const { if (is_small(a)) - return a.value64(); + return a.value(); #ifndef _MP_GMP SASSERT(is_int64(a)); uint64_t num = big_abs_to_uint64(a); @@ -2031,7 +2031,7 @@ void mpz_manager::ensure_capacity(mpz & a, unsigned capacity) { capacity = m_init_cell_capacity; if (is_small(a)) { - int64_t val = a.value64(); + int64_t val = a.value(); allocate_if_needed(a, capacity); SASSERT(a.ptr()->m_capacity >= capacity); // Check if this is SMALL_INT_MIN which needs special handling @@ -2125,7 +2125,7 @@ void mpz_manager::machine_div2k(mpz & a, unsigned k) { if (is_small(a)) { if (k < 32) { int64_t twok = 1ull << ((int64_t)k); - int64_t val = a.value64(); + int64_t val = a.value(); int64_t result = val / twok; // Division by power of 2 should keep us in small range SASSERT(mpz::fits_in_small(result)); @@ -2133,7 +2133,7 @@ void mpz_manager::machine_div2k(mpz & a, unsigned k) { } else if (k < 64) { int64_t twok = 1ull << ((int64_t)k); - int64_t val = a.value64(); + int64_t val = a.value(); a.set64(val/twok); } else { @@ -2345,7 +2345,7 @@ unsigned mpz_manager::mlog2(mpz const & a) { if (is_nonneg(a)) return 0; if (is_small(a)) { - int64_t v = a.value64(); + int64_t v = a.value(); if (v == mpz::SMALL_INT_MIN) { // Special case: negating SMALL_INT_MIN would overflow // For 32-bit: SMALL_INT_MIN = -2^30, so log2(2^30) = 30 @@ -2588,35 +2588,21 @@ template bool mpz_manager::decompose(mpz const & a, svector & digits) { digits.reset(); if (is_small(a)) { - int64_t v = a.value64(); - if (v < 0) { - // Decompose negative value into digits - uint64_t abs_v = static_cast(-v); - if (sizeof(digit_t) == sizeof(uint64_t)) { - digits.push_back(static_cast(abs_v)); - } else { - // digit_t is 32-bit, need to split 64-bit value - digits.push_back(static_cast(abs_v)); - if (abs_v >> 32) { - digits.push_back(static_cast(abs_v >> 32)); - } + int64_t v = a.value(); + bool is_neg = v < 0; + uint64_t abs_v = is_neg ? static_cast(-v) : static_cast(v); + + // Decompose absolute value into digits + if (sizeof(digit_t) == sizeof(uint64_t)) { + digits.push_back(static_cast(abs_v)); + } else { + // digit_t is 32-bit, need to split 64-bit value + digits.push_back(static_cast(abs_v)); + if (abs_v >> 32) { + digits.push_back(static_cast(abs_v >> 32)); } - return true; - } - else { - // Decompose positive value into digits - uint64_t abs_v = static_cast(v); - if (sizeof(digit_t) == sizeof(uint64_t)) { - digits.push_back(static_cast(abs_v)); - } else { - // digit_t is 32-bit, need to split 64-bit value - digits.push_back(static_cast(abs_v)); - if (abs_v >> 32) { - digits.push_back(static_cast(abs_v >> 32)); - } - } - return false; } + return is_neg; } else { #ifndef _MP_GMP @@ -2646,7 +2632,7 @@ bool mpz_manager::decompose(mpz const & a, svector & digits) { template bool mpz_manager::get_bit(mpz const & a, unsigned index) { if (is_small(a)) { - int64_t v = a.value64(); + int64_t v = a.value(); SASSERT(v >= 0); if (index >= 64) return false; diff --git a/src/util/mpz.h b/src/util/mpz.h index 5d1f30c7d..55896bbdc 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -201,14 +201,7 @@ public: return (m_value & LARGE_BIT) == 0; } - inline int value() const { - SASSERT(is_small()); - // Decode small integer: shift right by 1 (arithmetic shift to preserve sign) - // Note: On 64-bit platforms, this may truncate if the value doesn't fit in int - return static_cast(static_cast(m_value) >> 1); - } - - inline int64_t value64() const { + inline int64_t value() const { SASSERT(is_small()); // Decode small integer: shift right by 1 (arithmetic shift to preserve sign) return static_cast(static_cast(m_value) >> 1); @@ -334,7 +327,7 @@ class mpz_manager { mpz m_two64; - static int64_t i64(mpz const & a) { return a.value64(); } + static int64_t i64(mpz const & a) { return a.value(); } void set_big_i64(mpz & c, int64_t v); @@ -406,7 +399,7 @@ class mpz_manager { void get_sign_cell(mpz const & a, int & sign, mpz_cell * & cell, mpz_cell* reserve) { if (is_small(a)) { - int64_t val = a.value64(); + int64_t val = a.value(); cell = reserve; if (val < 0) { sign = -1; From cd699ca58260261c28ccdcb603e46a0804bc941b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 15:47:46 +0000 Subject: [PATCH 13/41] Fix remaining int truncation issues in is_power_of_two, power_of_two_multiple, and log2 for 64-bit platforms Co-authored-by: nunoplopes <2998477+nunoplopes@users.noreply.github.com> --- src/util/mpz.cpp | 82 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 56 insertions(+), 26 deletions(-) diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 7a36adeb1..49469b4ce 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -1986,8 +1986,9 @@ bool mpz_manager::is_power_of_two(mpz const & a, unsigned & shift) { if (is_nonpos(a)) return false; if (is_small(a)) { - if (::is_power_of_two(a.value())) { - shift = ::log2((unsigned)a.value()); + int64_t v = a.value(); + if (v > 0 && (v & (v - 1)) == 0) { // Check if power of 2 + shift = uint64_log2(static_cast(v)); return true; } else { @@ -2267,28 +2268,36 @@ unsigned mpz_manager::power_of_two_multiple(mpz const & a) { return 0; if (is_small(a)) { unsigned r = 0; - int v = a.value(); -#define COUNT_DIGIT_RIGHT_ZEROS() \ - if (v % (1 << 16) == 0) { \ - r += 16; \ - v /= (1 << 16); \ - } \ - if (v % (1 << 8) == 0) { \ - r += 8; \ - v /= (1 << 8); \ - } \ - if (v % (1 << 4) == 0) { \ - r += 4; \ - v /= (1 << 4); \ - } \ - if (v % (1 << 2) == 0) { \ - r += 2; \ - v /= (1 << 2); \ - } \ - if (v % 2 == 0) { \ - r++; \ + int64_t val = a.value(); + // Count trailing zeros in 64-bit value + if (val == 0) return 0; + + // Work with absolute value for counting trailing zeros + uint64_t v = (val < 0) ? static_cast(-val) : static_cast(val); + + if ((v & 0xFFFFFFFF) == 0) { + r += 32; + v >>= 32; + } + if ((v & 0xFFFF) == 0) { + r += 16; + v >>= 16; + } + if ((v & 0xFF) == 0) { + r += 8; + v >>= 8; + } + if ((v & 0xF) == 0) { + r += 4; + v >>= 4; + } + if ((v & 0x3) == 0) { + r += 2; + v >>= 2; + } + if ((v & 0x1) == 0) { + r++; } - COUNT_DIGIT_RIGHT_ZEROS(); return r; } #ifndef _MP_GMP @@ -2307,7 +2316,26 @@ unsigned mpz_manager::power_of_two_multiple(mpz const & a) { v = static_cast(static_cast(v) / (static_cast(1) << 32)); } } - COUNT_DIGIT_RIGHT_ZEROS(); + // Count trailing zeros in digit_t + if (v % (1 << 16) == 0) { + r += 16; + v /= (1 << 16); + } + if (v % (1 << 8) == 0) { + r += 8; + v /= (1 << 8); + } + if (v % (1 << 4) == 0) { + r += 4; + v /= (1 << 4); + } + if (v % (1 << 2) == 0) { + r += 2; + v /= (1 << 2); + } + if (v % 2 == 0) { + r++; + } return r; } r += (8 * sizeof(digit_t)); @@ -2322,8 +2350,10 @@ template unsigned mpz_manager::log2(mpz const & a) { if (is_nonpos(a)) return 0; - if (is_small(a)) - return ::log2((unsigned)a.value()); + if (is_small(a)) { + int64_t v = a.value(); + return uint64_log2(static_cast(v)); + } #ifndef _MP_GMP static_assert(sizeof(digit_t) == 8 || sizeof(digit_t) == 4, ""); mpz_cell * c = a.ptr(); From 4941ef138b5d037045af7e8a8a1d85a922ab46ae Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 16:48:50 +0000 Subject: [PATCH 14/41] Fix undefined behavior when negating SMALL_INT_MIN in power_of_two_multiple Co-authored-by: nunoplopes <2998477+nunoplopes@users.noreply.github.com> --- src/util/mpz.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 49469b4ce..5a1e77caf 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -2273,7 +2273,16 @@ unsigned mpz_manager::power_of_two_multiple(mpz const & a) { if (val == 0) return 0; // Work with absolute value for counting trailing zeros - uint64_t v = (val < 0) ? static_cast(-val) : static_cast(val); + // Handle SMALL_INT_MIN specially to avoid overflow + uint64_t v; + if (val == mpz::SMALL_INT_MIN) { + // SMALL_INT_MIN = -2^(SMALL_BITS-1), so it has (SMALL_BITS-1) trailing zeros + return mpz::SMALL_BITS - 1; + } else if (val < 0) { + v = static_cast(-val); + } else { + v = static_cast(val); + } if ((v & 0xFFFFFFFF) == 0) { r += 32; From 227e48c0b15240d4aaf8b5692e5b5644bbf41c51 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 18:24:50 +0000 Subject: [PATCH 15/41] Fix SMALL_BITS constant reference to use direct calculation instead Co-authored-by: nunoplopes <2998477+nunoplopes@users.noreply.github.com> --- src/util/mpz.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 5a1e77caf..5835a86cc 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -2277,7 +2277,8 @@ unsigned mpz_manager::power_of_two_multiple(mpz const & a) { uint64_t v; if (val == mpz::SMALL_INT_MIN) { // SMALL_INT_MIN = -2^(SMALL_BITS-1), so it has (SMALL_BITS-1) trailing zeros - return mpz::SMALL_BITS - 1; + // On 32-bit: return 30, on 64-bit: return 62 + return (sizeof(uintptr_t) * 8 - 1) - 1; } else if (val < 0) { v = static_cast(-val); } else { @@ -2389,7 +2390,7 @@ unsigned mpz_manager::mlog2(mpz const & a) { // Special case: negating SMALL_INT_MIN would overflow // For 32-bit: SMALL_INT_MIN = -2^30, so log2(2^30) = 30 // For 64-bit: SMALL_INT_MIN = -2^62, so log2(2^62) = 62 - return mpz::SMALL_BITS - 1; + return (sizeof(uintptr_t) * 8 - 1) - 1; } return uint64_log2(static_cast(-v)); } From 45f5209c23c622688d519f579e20e2478b3d411c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 19:34:13 +0000 Subject: [PATCH 16/41] Add out-of-line definitions for static constexpr members to fix ODR violations Co-authored-by: nunoplopes <2998477+nunoplopes@users.noreply.github.com> --- src/util/mpz.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 5835a86cc..d83b74039 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -33,6 +33,11 @@ Revision History: #error No multi-precision library selected. #endif +// Out-of-line definitions for static constexpr members (required for ODR-use in C++14 and earlier) +constexpr int mpz::SMALL_BITS; +constexpr int64_t mpz::SMALL_INT_MAX; +constexpr int64_t mpz::SMALL_INT_MIN; + // Available GCD algorithms // #define EUCLID_GCD // #define BINARY_GCD From 8562788ad88aaf4b154fb2185d6131960bc64ae7 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 15 Feb 2026 16:11:57 +0000 Subject: [PATCH 17/41] fixes --- src/util/mpz.cpp | 352 +++++++++------------------------------------- src/util/mpz.h | 139 ++++++------------ src/util/util.cpp | 54 ------- src/util/util.h | 8 +- 4 files changed, 111 insertions(+), 442 deletions(-) diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index d83b74039..276b264f6 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -33,11 +33,6 @@ Revision History: #error No multi-precision library selected. #endif -// Out-of-line definitions for static constexpr members (required for ODR-use in C++14 and earlier) -constexpr int mpz::SMALL_BITS; -constexpr int64_t mpz::SMALL_INT_MAX; -constexpr int64_t mpz::SMALL_INT_MIN; - // Available GCD algorithms // #define EUCLID_GCD // #define BINARY_GCD @@ -263,28 +258,25 @@ void mpz_manager::sub(mpz const & a, mpz const & b, mpz & c) { template void mpz_manager::set_big_i64(mpz & c, int64_t v) { -#ifndef _MP_GMP - mpz_cell* cell = c.is_small() ? nullptr : c.ptr(); - if (cell == nullptr) { - cell = allocate(m_init_cell_capacity); - c.set_ptr(cell, false, false); // Will update sign below - } - SASSERT(capacity(c) >= m_init_cell_capacity); uint64_t _v; - bool is_negative = false; + bool sign = v < 0; if (v == std::numeric_limits::min()) { // min-int is even _v = -(v/2); - is_negative = true; } else if (v < 0) { _v = -v; - is_negative = true; } else { _v = v; } - c.set_sign(is_negative ? -1 : 1); +#ifndef _MP_GMP + if (c.is_small()) { + c.set_ptr(allocate(m_init_cell_capacity), sign, false); + } else { + c.set_sign(sign ? -1 : 1); + } + SASSERT(capacity(c) >= m_init_cell_capacity); if (sizeof(digit_t) == sizeof(uint64_t)) { // 64-bit machine digits(c)[0] = static_cast(_v); @@ -297,21 +289,8 @@ void mpz_manager::set_big_i64(mpz & c, int64_t v) { c.ptr()->m_size = digits(c)[1] == 0 ? 1 : 2; } #else - mpz_t* cell = c.is_small() ? nullptr : c.ptr(); - if (cell == nullptr) { - cell = allocate(); - c.set_ptr(cell, false, false); - } - uint64_t _v; - bool sign = v < 0; - if (v == std::numeric_limits::min()) { - _v = -(v/2); - } - else if (v < 0) { - _v = -v; - } - else { - _v = v; + if (c.is_small()) { + c.set_ptr(allocate(), false, false); } mpz_set_ui(*c.ptr(), static_cast(_v)); MPZ_BEGIN_CRITICAL(); @@ -330,13 +309,12 @@ void mpz_manager::set_big_i64(mpz & c, int64_t v) { template void mpz_manager::set_big_ui64(mpz & c, uint64_t v) { #ifndef _MP_GMP - mpz_cell* cell = c.is_small() ? nullptr : c.ptr(); - if (cell == nullptr) { - cell = allocate(m_init_cell_capacity); - c.set_ptr(cell, false, false); // positive, owned + if (c.is_small()) { + c.set_ptr(allocate(m_init_cell_capacity), false, false); // positive, owned + } else { + c.set_sign(1); // positive } SASSERT(capacity(c) >= m_init_cell_capacity); - c.set_sign(1); // positive if (sizeof(digit_t) == sizeof(uint64_t)) { // 64-bit machine digits(c)[0] = static_cast(v); @@ -349,10 +327,8 @@ void mpz_manager::set_big_ui64(mpz & c, uint64_t v) { c.ptr()->m_size = digits(c)[1] == 0 ? 1 : 2; } #else - mpz_t* cell = c.is_small() ? nullptr : c.ptr(); - if (cell == nullptr) { - cell = allocate(); - c.set_ptr(cell, false, false); // positive, owned + if (c.is_small()) { + c.set_ptr(allocate(), false, false); // positive, owned } mpz_set_ui(*c.ptr(), static_cast(v)); MPZ_BEGIN_CRITICAL(); @@ -399,7 +375,7 @@ void mpz_manager::set(mpz_cell& src, mpz & a, int sign, unsigned sz) { } unsigned d = src.m_digits[0]; - if (i == 1 && d <= static_cast(mpz::SMALL_INT_MAX)) { + if (i == 1 && d <= mpz::SMALL_INT_MAX) { // src fits in small integer range a.set64(sign < 0 ? -static_cast(d) : static_cast(d)); return; @@ -447,30 +423,9 @@ void mpz_manager::set_digits(mpz & target, unsigned sz, digit_t const * d set(target, digits[0]); else { #ifndef _MP_GMP - mpz_cell* cell = target.is_small() ? nullptr : target.ptr(); - if (cell == nullptr) { - unsigned c = sz < m_init_cell_capacity ? m_init_cell_capacity : sz; - cell = allocate(c); - cell->m_size = sz; - cell->m_capacity = c; - target.set_ptr(cell, false, false); // positive, owned - memcpy(cell->m_digits, digits, sizeof(digit_t) * sz); - } - else if (capacity(target) < sz) { - SASSERT(sz > m_init_cell_capacity); - mpz_cell* ptr = allocate(sz); - memcpy(ptr->m_digits, digits, sizeof(digit_t) * sz); - ptr->m_size = sz; - ptr->m_capacity = sz; - deallocate(target); - target.set_ptr(ptr, false, false); // positive, owned - } - else { - target.ptr()->m_size = sz; - if (target.ptr()->m_digits != digits) - memcpy(target.ptr()->m_digits, digits, sizeof(digit_t) * sz); - // already large - } + allocate_if_needed(target, sz); + memcpy(target.ptr()->m_digits, digits, sizeof(digit_t) * sz); + target.set_sign(1): #else mk_big(target); // reset @@ -710,13 +665,7 @@ template void mpz_manager::neg(mpz & a) { STRACE(mpz, tout << "[mpz] 0 - " << to_string(a) << " == ";); if (is_small(a)) { - int64_t v = a.value(); - if (v == mpz::SMALL_INT_MIN) { - // neg(SMALL_INT_MIN) overflows small range - set_big_i64(a, -v); - return; - } - a.set64(-v); + a.set64(-a.value()); } #ifndef _MP_GMP else { @@ -735,12 +684,7 @@ void mpz_manager::abs(mpz & a) { if (is_small(a)) { int64_t v = a.value(); if (v < 0) { - if (v == mpz::SMALL_INT_MIN) { - // abs(SMALL_INT_MIN) overflows small range - set_big_i64(a, -v); - } - else - a.set64(-v); + a.set64(-v); } } else { @@ -948,15 +892,11 @@ void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { if (is_small(a) && is_small(b)) { int64_t _a = a.value(); int64_t _b = b.value(); - // Check if absolute values fit in uint64 (they always do for small integers) - // and won't overflow when negating - if (_a != mpz::SMALL_INT_MIN && _b != mpz::SMALL_INT_MIN) { - if (_a < 0) _a = -_a; - if (_b < 0) _b = -_b; - uint64_t r = u64_gcd(static_cast(_a), static_cast(_b)); - set(c, r); - return; - } + if (_a < 0) _a = -_a; + if (_b < 0) _b = -_b; + uint64_t r = u64_gcd(static_cast(_a), static_cast(_b)); + set(c, r); + return; } else { #ifdef _MP_GMP @@ -1202,8 +1142,8 @@ void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { } } del(a1); del(b1); del(r); del(t); del(tmp); - } #endif // LEHMER_GCD + } } template @@ -1564,28 +1504,9 @@ void mpz_manager::big_set(mpz & target, mpz const & source) { #ifndef _MP_GMP if (&target == &source) return; - int src_sign = source.sign(); - mpz_cell* target_cell = target.is_small() ? nullptr : target.ptr(); - if (target_cell == nullptr) { - mpz_cell* new_cell = allocate(capacity(source)); - new_cell->m_size = size(source); - new_cell->m_capacity = capacity(source); - memcpy(new_cell->m_digits, source.ptr()->m_digits, sizeof(digit_t) * size(source)); - target.set_ptr(new_cell, src_sign < 0, false); - } - else if (capacity(target) < size(source)) { - deallocate(target); - mpz_cell* new_cell = allocate(capacity(source)); - new_cell->m_size = size(source); - new_cell->m_capacity = capacity(source); - memcpy(new_cell->m_digits, source.ptr()->m_digits, sizeof(digit_t) * size(source)); - target.set_ptr(new_cell, src_sign < 0, false); - } - else { - target.ptr()->m_size = size(source); - memcpy(target.ptr()->m_digits, source.ptr()->m_digits, sizeof(digit_t) * size(source)); - target.set_sign(src_sign); - } + allocate_if_needed(target, capacity(source)); + memcpy(digits(target), digits(source), sizeof(digit_t) * size(source)); + target.set_sign(source.sign()); #else // GMP version mk_big(target); @@ -1637,12 +1558,7 @@ bool mpz_manager::is_uint64(mpz const & a) const { return a.value() >= 0; if (a.sign() < 0) return false; - if (sizeof(digit_t) == sizeof(uint64_t)) { - return size(a) <= 1; - } - else { - return size(a) <= 2; - } + return size(a) <= (sizeof(digit_t) == sizeof(uint64_t) ? 1 : 2); #else // GMP version if (is_small(a)) @@ -1768,15 +1684,11 @@ void mpz_manager::display(std::ostream & out, mpz const & a) const { else { #ifndef _MP_GMP if (a.sign() < 0) - out << "-"; - if (sizeof(digit_t) == 4) { - sbuffer buffer(11*size(a), 0); - out << m_mpn_manager.to_string(digits(a), size(a), buffer.begin(), buffer.size()); - } - else { - sbuffer buffer(21*size(a), 0); - out << m_mpn_manager.to_string(digits(a), size(a), buffer.begin(), buffer.size()); - } + out << '-'; + + auto sz = sizeof(digit_t) == 4 ? 11 : 21; + sbuffer buffer(sz * size(a), 0); + out << m_mpn_manager.to_string(digits(a), size(a), buffer.begin(), buffer.size()); #else // GMP version size_t sz = mpz_sizeinbase(*a.ptr(), 10) + 2; @@ -1933,8 +1845,7 @@ void mpz_manager::power(mpz const & a, unsigned p, mpz & b) { mpz_pow_ui(*b.ptr(), *a.ptr(), p); return; } -#endif -#ifndef _MP_GMP +#else if (is_small(a)) { if (a.value() == 2) { if (p < 8 * sizeof(int) - 1) { @@ -1992,8 +1903,8 @@ bool mpz_manager::is_power_of_two(mpz const & a, unsigned & shift) { return false; if (is_small(a)) { int64_t v = a.value(); - if (v > 0 && (v & (v - 1)) == 0) { // Check if power of 2 - shift = uint64_log2(static_cast(v)); + if (::is_power_of_two(v)) { + shift = log2(static_cast(v)); return true; } else { @@ -2009,7 +1920,7 @@ bool mpz_manager::is_power_of_two(mpz const & a, unsigned & shift) { return false; } digit_t v = ds[sz-1]; - if (!(v & (v - 1)) && v) { + if (::is_power_of_two(v)) { shift = log2(a); return true; } @@ -2038,59 +1949,26 @@ void mpz_manager::ensure_capacity(mpz & a, unsigned capacity) { if (is_small(a)) { int64_t val = a.value(); + uint64_t abs_val = static_cast(-val); allocate_if_needed(a, capacity); SASSERT(a.ptr()->m_capacity >= capacity); - // Check if this is SMALL_INT_MIN which needs special handling - if (val == mpz::SMALL_INT_MIN) { - // For 32-bit: SMALL_INT_MIN = -2^30, so -val = 2^30 fits in unsigned - // For 64-bit: SMALL_INT_MIN = -2^62, so -val = 2^62 fits in uint64_t - uint64_t abs_val = static_cast(-val); - if (sizeof(digit_t) == sizeof(uint64_t)) { - // 64-bit machine - a.ptr()->m_digits[0] = static_cast(abs_val); - a.ptr()->m_size = 1; - } - else { - // 32-bit machine - a.ptr()->m_digits[0] = static_cast(abs_val); - a.ptr()->m_digits[1] = static_cast(abs_val >> 32); - a.ptr()->m_size = (abs_val >> 32) == 0 ? 1 : 2; - } - a.set_sign(-1); - } - else if (val < 0) { - uint64_t abs_val = static_cast(-val); - if (sizeof(digit_t) == sizeof(uint64_t)) { - a.ptr()->m_digits[0] = static_cast(abs_val); - a.ptr()->m_size = 1; - } - else { - a.ptr()->m_digits[0] = static_cast(abs_val); - a.ptr()->m_digits[1] = static_cast(abs_val >> 32); - a.ptr()->m_size = (abs_val >> 32) == 0 ? 1 : 2; - } - a.set_sign(-1); + if (sizeof(digit_t) == sizeof(uint64_t)) { + a.ptr()->m_digits[0] = static_cast(abs_val); + a.ptr()->m_size = 1; } else { - if (sizeof(digit_t) == sizeof(uint64_t)) { - a.ptr()->m_digits[0] = static_cast(val); - a.ptr()->m_size = 1; - } - else { - a.ptr()->m_digits[0] = static_cast(val); - a.ptr()->m_digits[1] = static_cast(val >> 32); - a.ptr()->m_size = (val >> 32) == 0 ? 1 : 2; - } - a.set_sign(1); + a.ptr()->m_digits[0] = static_cast(abs_val); + a.ptr()->m_digits[1] = static_cast(abs_val >> 32); + a.ptr()->m_size = (abs_val >> 32) == 0 ? 1 : 2; } + a.set_sign(val < 0 ? -1 : 1); } else if (a.ptr()->m_capacity < capacity) { mpz_cell * new_cell = allocate(capacity); - SASSERT(new_cell->m_capacity == capacity); unsigned old_sz = a.ptr()->m_size; + SASSERT(capacity >= old_sz); new_cell->m_size = old_sz; - for (unsigned i = 0; i < old_sz; ++i) - new_cell->m_digits[i] = a.ptr()->m_digits[i]; + memcpy(new_cell->m_digits, digits(a), sizeof(digit_t) * old_sz) bool is_neg = a.sign() < 0; deallocate(a); a.set_ptr(new_cell, is_neg, false); @@ -2106,21 +1984,7 @@ void mpz_manager::normalize(mpz & a) { if (ds[i-1] != 0) break; } - - if (i == 0) { - // a is zero... - set(a, 0); - return; - } - - if (i == 1 && ds[0] <= static_cast(mpz::SMALL_INT_MAX)) { - // a fits in small integer range - int64_t val = a.sign() < 0 ? -static_cast(ds[0]) : static_cast(ds[0]); - a.set64(val); - return; - } - // adjust size - c->m_size = i; + c->m_size = std::max(1u, i); } #endif @@ -2129,21 +1993,13 @@ void mpz_manager::machine_div2k(mpz & a, unsigned k) { if (k == 0 || is_zero(a)) return; if (is_small(a)) { - if (k < 32) { - int64_t twok = 1ull << ((int64_t)k); - int64_t val = a.value(); - int64_t result = val / twok; - // Division by power of 2 should keep us in small range - SASSERT(mpz::fits_in_small(result)); - a.set64(result); - } - else if (k < 64) { + if (k < 64) { int64_t twok = 1ull << ((int64_t)k); int64_t val = a.value(); a.set64(val/twok); } else { - a.set(0); + a.set64(0); } return; } @@ -2272,48 +2128,8 @@ unsigned mpz_manager::power_of_two_multiple(mpz const & a) { if (is_zero(a)) return 0; if (is_small(a)) { - unsigned r = 0; int64_t val = a.value(); - // Count trailing zeros in 64-bit value - if (val == 0) return 0; - - // Work with absolute value for counting trailing zeros - // Handle SMALL_INT_MIN specially to avoid overflow - uint64_t v; - if (val == mpz::SMALL_INT_MIN) { - // SMALL_INT_MIN = -2^(SMALL_BITS-1), so it has (SMALL_BITS-1) trailing zeros - // On 32-bit: return 30, on 64-bit: return 62 - return (sizeof(uintptr_t) * 8 - 1) - 1; - } else if (val < 0) { - v = static_cast(-val); - } else { - v = static_cast(val); - } - - if ((v & 0xFFFFFFFF) == 0) { - r += 32; - v >>= 32; - } - if ((v & 0xFFFF) == 0) { - r += 16; - v >>= 16; - } - if ((v & 0xFF) == 0) { - r += 8; - v >>= 8; - } - if ((v & 0xF) == 0) { - r += 4; - v >>= 4; - } - if ((v & 0x3) == 0) { - r += 2; - v >>= 2; - } - if ((v & 0x1) == 0) { - r++; - } - return r; + return std::countr_zero(static_cast(val < 0 ? -val : val)); } #ifndef _MP_GMP mpz_cell * c = a.ptr(); @@ -2322,35 +2138,7 @@ unsigned mpz_manager::power_of_two_multiple(mpz const & a) { digit_t * source = c->m_digits; for (unsigned i = 0; i < sz; ++i) { if (source[i] != 0) { - digit_t v = source[i]; - if (sizeof(digit_t) == 8) { - // TODO: we can remove this if after we move to MPN - // In MPN the digit_t is always an unsigned integer - if (static_cast(v) % (static_cast(1) << 32) == 0) { - r += 32; - v = static_cast(static_cast(v) / (static_cast(1) << 32)); - } - } - // Count trailing zeros in digit_t - if (v % (1 << 16) == 0) { - r += 16; - v /= (1 << 16); - } - if (v % (1 << 8) == 0) { - r += 8; - v /= (1 << 8); - } - if (v % (1 << 4) == 0) { - r += 4; - v /= (1 << 4); - } - if (v % (1 << 2) == 0) { - r += 2; - v /= (1 << 2); - } - if (v % 2 == 0) { - r++; - } + r += std::countr_zero(source[i]); return r; } r += (8 * sizeof(digit_t)); @@ -2366,8 +2154,7 @@ unsigned mpz_manager::log2(mpz const & a) { if (is_nonpos(a)) return 0; if (is_small(a)) { - int64_t v = a.value(); - return uint64_log2(static_cast(v)); + return ::log2(static_cast(a.value())); } #ifndef _MP_GMP static_assert(sizeof(digit_t) == 8 || sizeof(digit_t) == 4, ""); @@ -2375,9 +2162,9 @@ unsigned mpz_manager::log2(mpz const & a) { unsigned sz = c->m_size; digit_t * ds = c->m_digits; if (sizeof(digit_t) == 8) - return (sz - 1)*64 + uint64_log2(ds[sz-1]); + return (sz - 1)*64 + ::log2(ds[sz-1]); else - return (sz - 1)*32 + ::log2(static_cast(ds[sz-1])); + return (sz - 1)*32 + ::log2(ds[sz-1]); #else unsigned r = mpz_sizeinbase(*a.ptr(), 2); SASSERT(r > 0); @@ -2390,14 +2177,7 @@ unsigned mpz_manager::mlog2(mpz const & a) { if (is_nonneg(a)) return 0; if (is_small(a)) { - int64_t v = a.value(); - if (v == mpz::SMALL_INT_MIN) { - // Special case: negating SMALL_INT_MIN would overflow - // For 32-bit: SMALL_INT_MIN = -2^30, so log2(2^30) = 30 - // For 64-bit: SMALL_INT_MIN = -2^62, so log2(2^62) = 62 - return (sizeof(uintptr_t) * 8 - 1) - 1; - } - return uint64_log2(static_cast(-v)); + return ::log2(static_cast(-va.value())); } #ifndef _MP_GMP static_assert(sizeof(digit_t) == 8 || sizeof(digit_t) == 4, ""); @@ -2405,9 +2185,9 @@ unsigned mpz_manager::mlog2(mpz const & a) { unsigned sz = c->m_size; digit_t * ds = c->m_digits; if (sizeof(digit_t) == 8) - return (sz - 1)*64 + uint64_log2(ds[sz-1]); + return (sz - 1)*64 + ::log2(ds[sz-1]); else - return (sz - 1)*32 + ::log2(static_cast(ds[sz-1])); + return (sz - 1)*32 + ::log2(ds[sz-1]); #else MPZ_BEGIN_CRITICAL(); mpz_neg(m_tmp, *a.ptr()); @@ -2619,7 +2399,7 @@ digit_t mpz_manager::get_least_significant(mpz const& a) { if (is_small(a)) return std::abs(a.value()); #ifndef _MP_GMP - mpz_cell* cell_a = a.ptr(); + mpz_cell* cell_a = a.ptr(); unsigned sz = cell_a->m_size; if (sz == 0) return 0; @@ -2635,11 +2415,11 @@ bool mpz_manager::decompose(mpz const & a, svector & digits) { if (is_small(a)) { int64_t v = a.value(); bool is_neg = v < 0; - uint64_t abs_v = is_neg ? static_cast(-v) : static_cast(v); - + uint64_t abs_v = static_cast(is_neg ? -v : v); + // Decompose absolute value into digits if (sizeof(digit_t) == sizeof(uint64_t)) { - digits.push_back(static_cast(abs_v)); + digits.push_back(abs_v); } else { // digit_t is 32-bit, need to split 64-bit value digits.push_back(static_cast(abs_v)); diff --git a/src/util/mpz.h b/src/util/mpz.h index 55896bbdc..c48d7e708 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -69,8 +69,8 @@ class mpz_cell { /** \brief Multi-precision integer. - m_value encodes either a small integer (if the least significant bit is 1) - or a pointer to a mpz_cell structure (if the least significant bit is 0). + m_value encodes either a small integer (if the least significant bit is 0) + or a pointer to a mpz_cell structure (if the least significant bit is 1). The last 3 bits of pointers are always 0 due to alignment, so we use them to store additional information: - bit 0: small/large info (0 = small, 1 = large) @@ -89,15 +89,15 @@ private: static constexpr uintptr_t LARGE_BIT = 0x1; static constexpr uintptr_t SIGN_BIT = 0x2; - static constexpr uintptr_t OWNER_BIT = 0x4; + static constexpr uintptr_t EXTERNAL_BIT = 0x4; static constexpr uintptr_t MPZ_PTR_MASK = ~static_cast(0x7); - // Small integers are stored shifted left by 1, so we have (sizeof(uintptr_t)*8 - 1) bits available + // Small integers are stored shifted left by 1 // This gives us: // - On 32-bit platforms: 31 bits, range [-2^30, 2^30-1] // - On 64-bit platforms: 63 bits, range [-2^62, 2^62-1] - static constexpr int SMALL_BITS = sizeof(uintptr_t) * 8 - 1; - + static constexpr unaigned SMALL_BITS = sizeof(uintptr_t) * 8 - 1; + // Maximum and minimum values that can be stored as small integers static constexpr int64_t SMALL_INT_MAX = (static_cast(1) << (SMALL_BITS - 1)) - 1; static constexpr int64_t SMALL_INT_MIN = -(static_cast(1) << (SMALL_BITS - 1)); @@ -110,14 +110,6 @@ private: return v <= static_cast(SMALL_INT_MAX); } - static bool fits_in_small(int v) { - return fits_in_small(static_cast(v)); - } - - static bool fits_in_small(unsigned int v) { - return fits_in_small(static_cast(v)); - } - mpz_type * ptr() const { SASSERT(!is_small()); return reinterpret_cast(m_value & MPZ_PTR_MASK); @@ -130,7 +122,7 @@ private: if (is_negative) m_value |= SIGN_BIT; if (is_external) - m_value |= OWNER_BIT; + m_value |= EXTERNAL_BIT; } int get_sign() const { @@ -148,7 +140,7 @@ private: bool is_external() const { SASSERT(!is_small()); - return (m_value & OWNER_BIT) != 0; + return (m_value & EXTERNAL_BIT) != 0; } protected: @@ -161,54 +153,45 @@ protected: friend class mpbq_manager; friend class mpz_stack; public: - mpz(int v = 0) noexcept : m_value(static_cast(static_cast(v)) << 1) { + mpz(int64_t v = 0) noexcept : m_value(static_cast(v) << 1) { // On 32-bit platforms, INT_MIN doesn't fit in 31 bits. This constructor should only be used // with values that fit, or the caller should use set_big_i64. SASSERT(fits_in_small(v)); } - + mpz(mpz_type* ptr) noexcept { - SASSERT(ptr); set_ptr(ptr, false, true); // external pointer, non-negative } - + mpz(mpz && other) noexcept : m_value(other.m_value) { other.m_value = 0; // reset other to small } mpz& operator=(mpz const& other) = delete; - + mpz& operator=(mpz &&other) noexcept { std::swap(m_value, other.m_value); return *this; } - void set(int v) { - SASSERT(is_small()); - m_value = static_cast(static_cast(v)) << 1; - } - void set64(int64_t v) { + SASSERT(is_small()); SASSERT(fits_in_small(v)); - m_value = static_cast(static_cast(v)) << 1; + m_value = static_cast(v) << 1; } - void swap(mpz & other) noexcept { - std::swap(m_value, other.m_value); + inline bool is_small() const { + return (m_value & LARGE_BIT) == 0; } - inline bool is_small() const { - return (m_value & LARGE_BIT) == 0; - } - - inline int64_t value() const { + inline int64_t value() const { SASSERT(is_small()); // Decode small integer: shift right by 1 (arithmetic shift to preserve sign) return static_cast(static_cast(m_value) >> 1); } - inline int sign() const { - SASSERT(!is_small()); + inline int sign() const { + SASSERT(!is_small()); return get_sign(); } }; @@ -255,11 +238,10 @@ class mpz_manager { // make sure that n is a big number and has capacity equal to at least c. void allocate_if_needed(mpz & n, unsigned c) { if (m_init_cell_capacity > c) c = m_init_cell_capacity; - if (n.is_small() || n.ptr() == nullptr || capacity(n) < c) { + if (n.is_small() || capacity(n) < c) { deallocate(n); n.set_ptr(allocate(c), false, false); // positive, owned } - // else already has enough capacity, keep as large } void deallocate(bool is_heap, mpz_cell * ptr); @@ -311,35 +293,21 @@ class mpz_manager { } } - void clear(mpz& n) { if (!n.is_small() && n.ptr()) { mpz_clear(*n.ptr()); }} + void clear(mpz& n) { if (!n.is_small()) { mpz_clear(*n.ptr()); }} #endif void deallocate(mpz& n) { if (!n.is_small()) { - auto* p = n.ptr(); - if (p) { - deallocate(!n.is_external(), p); - n.m_value = 0; // reset to small - } + deallocate(!n.is_external(), n.ptr()); + n.m_value = 0; // reset to small } } - mpz m_two64; - + mpz m_two64; static int64_t i64(mpz const & a) { return a.value(); } void set_big_i64(mpz & c, int64_t v); - - void set_i64(mpz & c, int64_t v) { - if (mpz::fits_in_small(v) && is_small(c)) { - c.set64(v); - } - else { - set_big_i64(c, v); - } - } - void set_big_ui64(mpz & c, uint64_t v); @@ -400,31 +368,19 @@ class mpz_manager { void get_sign_cell(mpz const & a, int & sign, mpz_cell * & cell, mpz_cell* reserve) { if (is_small(a)) { int64_t val = a.value(); + bool neg = val < 0; + uint64_t abs_val = static_cast(neg ? -val : val); cell = reserve; - if (val < 0) { - sign = -1; - uint64_t abs_val = static_cast(-val); - if (sizeof(digit_t) == sizeof(uint64_t)) { - cell->m_size = 1; - cell->m_digits[0] = static_cast(abs_val); - } - else { - cell->m_digits[0] = static_cast(abs_val); - cell->m_digits[1] = static_cast(abs_val >> 32); - cell->m_size = (abs_val >> 32) == 0 ? 1 : 2; - } + sign = neg ? -1 : 1; + + if (sizeof(digit_t) == sizeof(uint64_t)) { + cell->m_size = 1; + cell->m_digits[0] = static_cast(abs_val); } else { - sign = 1; - if (sizeof(digit_t) == sizeof(uint64_t)) { - cell->m_size = 1; - cell->m_digits[0] = static_cast(val); - } - else { - cell->m_digits[0] = static_cast(val); - cell->m_digits[1] = static_cast(val >> 32); - cell->m_size = (val >> 32) == 0 ? 1 : 2; - } + cell->m_digits[0] = static_cast(abs_val); + cell->m_digits[1] = static_cast(abs_val >> 32); + cell->m_size = (abs_val >> 32) == 0 ? 1 : 2; } } else { @@ -449,7 +405,6 @@ class mpz_manager { if (a.is_small()) { a.set_ptr(allocate(), false, false); // positive, owned } - // else already large with valid pointer } @@ -639,29 +594,15 @@ public: } } - void set(mpz & a, int val) { - // On 32-bit platforms, int can be outside small range - if (mpz::fits_in_small(val) && is_small(a)) { - a.set(val); - } - else { - set_i64(a, val); - } - } - - void set(mpz & a, unsigned val) { - if (mpz::fits_in_small(val) && is_small(a)) { - a.set(static_cast(val)); - } - else { - set_i64(a, static_cast(val)); - } - } - void set(mpz & a, char const * val); void set(mpz & a, int64_t val) { - set_i64(a, val); + if (mpz::fits_in_small(v) && is_small(c)) { + c.set64(v); + } + else { + set_big_i64(c, v); + } } void set(mpz & a, uint64_t val) { diff --git a/src/util/util.cpp b/src/util/util.cpp index d60f8467a..857ea71a7 100644 --- a/src/util/util.cpp +++ b/src/util/util.cpp @@ -57,60 +57,6 @@ void set_fatal_error_handler(void (*pfn)(int error_code)) { g_fatal_error_handler = pfn; } -unsigned log2(unsigned v) { - unsigned r = 0; - if (v & 0xFFFF0000) { - v >>= 16; - r |= 16; - } - if (v & 0xFF00) { - v >>= 8; - r |= 8; - } - if (v & 0xF0) { - v >>= 4; - r |= 4; - } - if (v & 0xC) { - v >>= 2; - r |= 2; - } - if (v & 0x2) { - v >>= 1; - r |= 1; - } - return r; -} - -unsigned uint64_log2(uint64_t v) { - unsigned r = 0; - if (v & 0xFFFFFFFF00000000ull) { - v >>= 32; - r |= 32; - } - if (v & 0xFFFF0000) { - v >>= 16; - r |= 16; - } - if (v & 0xFF00) { - v >>= 8; - r |= 8; - } - if (v & 0xF0) { - v >>= 4; - r |= 4; - } - if (v & 0xC) { - v >>= 2; - r |= 2; - } - if (v & 0x2) { - v >>= 1; - r |= 1; - } - return r; -} - bool product_iterator_next(unsigned n, unsigned const * sz, unsigned * it) { for (unsigned i = 0; i < n; ++i) { it[i]++; diff --git a/src/util/util.h b/src/util/util.h index d580209e9..8146ac0d2 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -21,6 +21,7 @@ Revision History: #include "util/debug.h" #include "util/memory_manager.h" #include +#include #include #include #include @@ -80,7 +81,8 @@ static_assert(sizeof(int64_t) == 8, "64 bits"); # define Z3_fallthrough #endif -static inline bool is_power_of_two(unsigned v) { return !(v & (v - 1)) && v; } +static inline bool is_power_of_two(unsigned v) { return std::has_single_bit(v); } +static inline bool is_power_of_two(uint64_t v) { return std::has_single_bit(v); } /** \brief Return the next power of two that is greater than or equal to v. @@ -101,8 +103,8 @@ static inline unsigned next_power_of_two(unsigned v) { /** \brief Return the position of the most significant bit. */ -unsigned log2(unsigned v); -unsigned uint64_log2(uint64_t v); +static inline unsigned log2(unsigned v) { return std::bit_width(x) - 1; } +static inline unsigned log2(uint64_t v) { return std::bit_width(x) - 1; } static_assert(sizeof(unsigned) == 4, "unsigned are 32 bits"); From 10f986ee1a540a24094215836f0f53f7bfd8fac8 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 15 Feb 2026 16:15:06 +0000 Subject: [PATCH 18/41] fix --- src/util/mpz.h | 6 +++++- src/util/util.h | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/util/mpz.h b/src/util/mpz.h index c48d7e708..8c68a42b6 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -96,7 +96,7 @@ private: // This gives us: // - On 32-bit platforms: 31 bits, range [-2^30, 2^30-1] // - On 64-bit platforms: 63 bits, range [-2^62, 2^62-1] - static constexpr unaigned SMALL_BITS = sizeof(uintptr_t) * 8 - 1; + static constexpr unsigned SMALL_BITS = sizeof(uintptr_t) * 8 - 1; // Maximum and minimum values that can be stored as small integers static constexpr int64_t SMALL_INT_MAX = (static_cast(1) << (SMALL_BITS - 1)) - 1; @@ -174,6 +174,10 @@ public: return *this; } + void swap(mpz & other) noexcept { + std::swap(m_value, other.m_value); + } + void set64(int64_t v) { SASSERT(is_small()); SASSERT(fits_in_small(v)); diff --git a/src/util/util.h b/src/util/util.h index 8146ac0d2..aec42116b 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -103,8 +103,8 @@ static inline unsigned next_power_of_two(unsigned v) { /** \brief Return the position of the most significant bit. */ -static inline unsigned log2(unsigned v) { return std::bit_width(x) - 1; } -static inline unsigned log2(uint64_t v) { return std::bit_width(x) - 1; } +static inline unsigned log2(unsigned v) { return std::bit_width(v) - 1; } +static inline unsigned log2(uint64_t v) { return std::bit_width(v) - 1; } static_assert(sizeof(unsigned) == 4, "unsigned are 32 bits"); From 67ce05c1f54e502388c7a7f6d7aadf97ac61421e Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 15 Feb 2026 16:16:06 +0000 Subject: [PATCH 19/41] fix --- src/util/mpz.cpp | 16 ++++++++-------- src/util/mpz.h | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 276b264f6..52441ae38 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -377,7 +377,7 @@ void mpz_manager::set(mpz_cell& src, mpz & a, int sign, unsigned sz) { unsigned d = src.m_digits[0]; if (i == 1 && d <= mpz::SMALL_INT_MAX) { // src fits in small integer range - a.set64(sign < 0 ? -static_cast(d) : static_cast(d)); + a.set(sign < 0 ? -static_cast(d) : static_cast(d)); return; } @@ -665,7 +665,7 @@ template void mpz_manager::neg(mpz & a) { STRACE(mpz, tout << "[mpz] 0 - " << to_string(a) << " == ";); if (is_small(a)) { - a.set64(-a.value()); + a.set(-a.value()); } #ifndef _MP_GMP else { @@ -684,7 +684,7 @@ void mpz_manager::abs(mpz & a) { if (is_small(a)) { int64_t v = a.value(); if (v < 0) { - a.set64(-v); + a.set(-v); } } else { @@ -949,7 +949,7 @@ void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { // reset least significant bit if (is_small(v)) - v.set64(v.value() & ~1); + v.set(v.value() & ~1); else v.ptr()->m_digits[0] &= ~static_cast(1); k_v = power_of_two_multiple(v); @@ -1346,7 +1346,7 @@ void mpz_manager::bitwise_or(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_nonneg(b)); TRACE(mpz, tout << "is_small(a): " << is_small(a) << ", is_small(b): " << is_small(b) << "\n";); if (is_small(a) && is_small(b)) { - c.set64(a.value() | b.value()); + c.set(a.value() | b.value()); } else { #ifndef _MP_GMP @@ -1391,7 +1391,7 @@ void mpz_manager::bitwise_and(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_nonneg(a)); SASSERT(is_nonneg(b)); if (is_small(a) && is_small(b)) { - c.set64(a.value() & b.value()); + c.set(a.value() & b.value()); } else { #ifndef _MP_GMP @@ -1996,10 +1996,10 @@ void mpz_manager::machine_div2k(mpz & a, unsigned k) { if (k < 64) { int64_t twok = 1ull << ((int64_t)k); int64_t val = a.value(); - a.set64(val/twok); + a.set(val/twok); } else { - a.set64(0); + a.set(0); } return; } diff --git a/src/util/mpz.h b/src/util/mpz.h index 8c68a42b6..243189bef 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -178,7 +178,7 @@ public: std::swap(m_value, other.m_value); } - void set64(int64_t v) { + void set(int64_t v) { SASSERT(is_small()); SASSERT(fits_in_small(v)); m_value = static_cast(v) << 1; @@ -602,7 +602,7 @@ public: void set(mpz & a, int64_t val) { if (mpz::fits_in_small(v) && is_small(c)) { - c.set64(v); + c.set(v); } else { set_big_i64(c, v); @@ -611,7 +611,7 @@ public: void set(mpz & a, uint64_t val) { if (mpz::fits_in_small(val) && is_small(a)) { - a.set64(static_cast(val)); + a.set(static_cast(val)); } else { set_big_ui64(a, val); From 1c5cbcbe926254f2a27043592a840d3ee0b27e54 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 15 Feb 2026 16:17:57 +0000 Subject: [PATCH 20/41] fix --- src/util/mpq.h | 2 +- src/util/mpz.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/util/mpq.h b/src/util/mpq.h index 03a51d7d1..85285ecdd 100644 --- a/src/util/mpq.h +++ b/src/util/mpq.h @@ -672,7 +672,7 @@ public: set(target.m_den, source.m_den); } - void set(mpz & a, int val) { mpz_manager::set(a, val); } + void set(mpz & a, int64_t val) { mpz_manager::set(a, val); } void set(mpq & a, int val) { set(a.m_num, val); diff --git a/src/util/mpz.h b/src/util/mpz.h index 243189bef..384acc7f2 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -601,11 +601,11 @@ public: void set(mpz & a, char const * val); void set(mpz & a, int64_t val) { - if (mpz::fits_in_small(v) && is_small(c)) { - c.set(v); + if (mpz::fits_in_small(val) && is_small(a)) { + a.set(v); } else { - set_big_i64(c, v); + set_big_i64(a, v); } } From 9448f67d21304daf71750d43537c0a0fefd2f95f Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 15 Feb 2026 16:19:51 +0000 Subject: [PATCH 21/41] fix --- src/util/mpq.h | 9 +-------- src/util/mpz.h | 4 ++-- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/util/mpq.h b/src/util/mpq.h index 85285ecdd..83992111f 100644 --- a/src/util/mpq.h +++ b/src/util/mpq.h @@ -674,7 +674,7 @@ public: void set(mpz & a, int64_t val) { mpz_manager::set(a, val); } - void set(mpq & a, int val) { + void set(mpq & a, int64_t val) { set(a.m_num, val); reset_denominator(a); } @@ -724,13 +724,6 @@ public: void set(mpq & a, char const * val); - void set(mpz & a, int64_t val) { mpz_manager::set(a, val); } - - void set(mpq & a, int64_t val) { - set(a.m_num, val); - reset_denominator(a); - } - void set(mpz & a, uint64_t val) { mpz_manager::set(a, val); } void set(mpq & a, uint64_t val) { diff --git a/src/util/mpz.h b/src/util/mpz.h index 384acc7f2..c4ac37d1d 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -602,10 +602,10 @@ public: void set(mpz & a, int64_t val) { if (mpz::fits_in_small(val) && is_small(a)) { - a.set(v); + a.set(val); } else { - set_big_i64(a, v); + set_big_i64(a, val); } } From 88242fb4cea02fe13f432eb72907e155e12b3185 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 15 Feb 2026 16:20:44 +0000 Subject: [PATCH 22/41] fix --- src/util/rational.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/rational.h b/src/util/rational.h index 3649a9848..ba99f308e 100644 --- a/src/util/rational.h +++ b/src/util/rational.h @@ -50,7 +50,7 @@ public: explicit rational(unsigned n) { m().set(m_val, n); } - rational(int n, int d) { m().set(m_val, n, d); } + rational(int64_t n, int d) { m().set(m_val, n, d); } rational(mpq const & q) { m().set(m_val, q); } rational(mpq && q) noexcept : m_val(std::move(q)) {} rational(mpz const & z) { m().set(m_val, z); } From 5f429d1223afc590b0fceb48f6c24cc7be8c69ed Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 15 Feb 2026 16:21:19 +0000 Subject: [PATCH 23/41] fix --- src/util/rational.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/rational.h b/src/util/rational.h index ba99f308e..214c1c8e1 100644 --- a/src/util/rational.h +++ b/src/util/rational.h @@ -46,11 +46,11 @@ public: rational(rational const & r) { m().set(m_val, r.m_val); } rational(rational&&) = default; - explicit rational(int n) { m().set(m_val, n); } + explicit rational(int64_t n) { m().set(m_val, n); } explicit rational(unsigned n) { m().set(m_val, n); } - rational(int64_t n, int d) { m().set(m_val, n, d); } + rational(int n, int d) { m().set(m_val, n, d); } rational(mpq const & q) { m().set(m_val, q); } rational(mpq && q) noexcept : m_val(std::move(q)) {} rational(mpz const & z) { m().set(m_val, z); } From 8ce362abda880cbd292f8e86e9c92adbfd2384aa Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 15 Feb 2026 16:22:17 +0000 Subject: [PATCH 24/41] fix --- src/util/rational.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/rational.h b/src/util/rational.h index 214c1c8e1..303211d49 100644 --- a/src/util/rational.h +++ b/src/util/rational.h @@ -48,7 +48,7 @@ public: explicit rational(int64_t n) { m().set(m_val, n); } - explicit rational(unsigned n) { m().set(m_val, n); } + explicit rational(uint64_t n) { m().set(m_val, n); } rational(int n, int d) { m().set(m_val, n, d); } rational(mpq const & q) { m().set(m_val, q); } From e9556c498bb056c2fcee25fa78a006fde523375f Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 15 Feb 2026 16:23:59 +0000 Subject: [PATCH 25/41] fix --- src/util/rational.h | 48 ++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/util/rational.h b/src/util/rational.h index 303211d49..888386678 100644 --- a/src/util/rational.h +++ b/src/util/rational.h @@ -172,7 +172,7 @@ public: rational & operator=(bool) = delete; rational operator*(bool r1) const = delete; - rational & operator=(int v) { + rational & operator=(int64_t v) { m().set(m_val, v); return *this; } @@ -193,7 +193,7 @@ public: return *this; } - rational & operator+=(int r) { + rational & operator+=(int64_t r) { (*this) += rational(r); return *this; } @@ -203,7 +203,7 @@ public: return *this; } - rational& operator-=(int r) { + rational& operator-=(int64_t r) { (*this) -= rational(r); return *this; } @@ -223,15 +223,15 @@ public: return *this; } - rational & operator%=(int v) { + rational & operator%=(int64_t v) { return *this %= rational(v); } - rational & operator/=(int v) { + rational & operator/=(int64_t v) { return *this /= rational(v); } - rational & operator*=(int v) { + rational & operator*=(int64_t v) { return *this *= rational(v); } @@ -561,11 +561,11 @@ inline bool operator>(rational const & r1, rational const & r2) { return operator<(r2, r1); } -inline bool operator<(int r1, rational const & r2) { +inline bool operator<(int64_t r1, rational const & r2) { return rational(r1) < r2; } -inline bool operator<(rational const & r1, int r2) { +inline bool operator<(rational const & r1, int64_t r2) { return r1 < rational(r2); } @@ -577,35 +577,35 @@ inline bool operator>=(rational const & r1, rational const & r2) { return !operator<(r1, r2); } -inline bool operator>(rational const & a, int b) { +inline bool operator>(rational const & a, int64_t b) { return a > rational(b); } -inline bool operator>(int a, rational const & b) { +inline bool operator>(int64_t a, rational const & b) { return rational(a) > b; } -inline bool operator>=(rational const& a, int b) { +inline bool operator>=(rational const& a, int64_t b) { return a >= rational(b); } -inline bool operator>=(int a, rational const& b) { +inline bool operator>=(int64_t a, rational const& b) { return rational(a) >= b; } -inline bool operator<=(rational const& a, int b) { +inline bool operator<=(rational const& a, int64_t b) { return a <= rational(b); } -inline bool operator<=(int a, rational const& b) { +inline bool operator<=(int64_t a, rational const& b) { return rational(a) <= b; } -inline bool operator!=(rational const& a, int b) { +inline bool operator!=(rational const& a, int64_t b) { return !(a == rational(b)); } -inline bool operator==(rational const & a, int b) { +inline bool operator==(rational const & a, int64_t b) { return a == rational(b); } @@ -613,11 +613,11 @@ inline rational operator+(rational const & r1, rational const & r2) { return rational(r1) += r2; } -inline rational operator+(int r1, rational const & r2) { +inline rational operator+(int64_t r1, rational const & r2) { return rational(r1) + r2; } -inline rational operator+(rational const & r1, int r2) { +inline rational operator+(rational const & r1, int64_t r2) { return r1 + rational(r2); } @@ -626,11 +626,11 @@ inline rational operator-(rational const & r1, rational const & r2) { return rational(r1) -= r2; } -inline rational operator-(rational const & r1, int r2) { +inline rational operator-(rational const & r1, int64_t r2) { return r1 - rational(r2); } -inline rational operator-(int r1, rational const & r2) { +inline rational operator-(int64_t r1, rational const & r2) { return rational(r1) - r2; } @@ -648,7 +648,7 @@ inline rational operator*(rational const & r1, bool r2) { UNREACHABLE(); return r1 * rational(r2); } -inline rational operator*(rational const & r1, int r2) { +inline rational operator*(rational const & r1, int64_t r2) { return r1 * rational(r2); } inline rational operator*(bool r1, rational const & r2) { @@ -656,7 +656,7 @@ inline rational operator*(bool r1, rational const & r2) { return rational(r1) * r2; } -inline rational operator*(int r1, rational const & r2) { +inline rational operator*(int64_t r1, rational const & r2) { return rational(r1) * r2; } @@ -664,7 +664,7 @@ inline rational operator/(rational const & r1, rational const & r2) { return rational(r1) /= r2; } -inline rational operator/(rational const & r1, int r2) { +inline rational operator/(rational const & r1, int64_t r2) { return r1 / rational(r2); } @@ -673,7 +673,7 @@ inline rational operator/(rational const & r1, bool r2) { return r1 / rational(r2); } -inline rational operator/(int r1, rational const & r2) { +inline rational operator/(int64_t r1, rational const & r2) { return rational(r1) / r2; } From 92158ac6075fbaedeb1521d0aef24261b3b71b7c Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 15 Feb 2026 16:27:51 +0000 Subject: [PATCH 26/41] fix --- src/util/mpq.h | 6 +++--- src/util/rational.h | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/util/mpq.h b/src/util/mpq.h index 83992111f..cef964a9e 100644 --- a/src/util/mpq.h +++ b/src/util/mpq.h @@ -679,11 +679,11 @@ public: reset_denominator(a); } - void set(mpq & a, int n, int d) { + void set(mpq & a, int64_t n, int64_t d) { SASSERT(d != 0); if (d < 0) { - SASSERT(d != INT_MIN); - SASSERT(n != INT_MIN); + SASSERT(d != std::numeric_limits()); + SASSERT(n != std::numeric_limits()); n = -n; d = -d; } diff --git a/src/util/rational.h b/src/util/rational.h index 888386678..3b5485d59 100644 --- a/src/util/rational.h +++ b/src/util/rational.h @@ -50,7 +50,7 @@ public: explicit rational(uint64_t n) { m().set(m_val, n); } - rational(int n, int d) { m().set(m_val, n, d); } + rational(int64_t n, int64_t d) { m().set(m_val, n, d); } rational(mpq const & q) { m().set(m_val, q); } rational(mpq && q) noexcept : m_val(std::move(q)) {} rational(mpz const & z) { m().set(m_val, z); } @@ -290,7 +290,7 @@ public: rational r = mod(a,b); SASSERT(r.is_nonneg()); rational r2 = r; - r2 *= rational(2); + r2 *= rational(2ull); if (operator<(b, r2)) { r -= b; } @@ -510,11 +510,11 @@ public: } unsigned get_num_bits() const { - return get_num_digits(rational(2)); + return get_num_digits(rational(2ull)); } unsigned get_num_decimal() const { - return get_num_digits(rational(10)); + return get_num_digits(rational(10ull)); } /** @@ -646,14 +646,14 @@ inline rational operator*(rational const & r1, rational const & r2) { inline rational operator*(rational const & r1, bool r2) { UNREACHABLE(); - return r1 * rational(r2); + return r1 * rational((uint64_t)r2); } inline rational operator*(rational const & r1, int64_t r2) { return r1 * rational(r2); } -inline rational operator*(bool r1, rational const & r2) { +inline rational operator*(bool r1, rational const & r2) { UNREACHABLE(); - return rational(r1) * r2; + return rational((uint64_t)r1) * r2; } inline rational operator*(int64_t r1, rational const & r2) { From 4cdbe221cf9721e30f0d9913ca42c2246e683702 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 15 Feb 2026 16:30:28 +0000 Subject: [PATCH 27/41] fix --- src/util/rational.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/util/rational.h b/src/util/rational.h index 3b5485d59..514726c82 100644 --- a/src/util/rational.h +++ b/src/util/rational.h @@ -47,9 +47,8 @@ public: rational(rational&&) = default; explicit rational(int64_t n) { m().set(m_val, n); } - explicit rational(uint64_t n) { m().set(m_val, n); } - + rational(int64_t n, int64_t d) { m().set(m_val, n, d); } rational(mpq const & q) { m().set(m_val, q); } rational(mpq && q) noexcept : m_val(std::move(q)) {} @@ -290,7 +289,7 @@ public: rational r = mod(a,b); SASSERT(r.is_nonneg()); rational r2 = r; - r2 *= rational(2ull); + r2 *= rational(uint64_t(2)); if (operator<(b, r2)) { r -= b; } @@ -510,11 +509,11 @@ public: } unsigned get_num_bits() const { - return get_num_digits(rational(2ull)); + return get_num_digits(uint64_t(2)); } unsigned get_num_decimal() const { - return get_num_digits(rational(10ull)); + return get_num_digits(uint64_t(10)); } /** From dad012ad5d1d23148edb5e437d483eaa883059bb Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 15 Feb 2026 16:31:14 +0000 Subject: [PATCH 28/41] fix --- src/util/rational.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/rational.h b/src/util/rational.h index 514726c82..af90017a5 100644 --- a/src/util/rational.h +++ b/src/util/rational.h @@ -509,11 +509,11 @@ public: } unsigned get_num_bits() const { - return get_num_digits(uint64_t(2)); + return get_num_digits(rational(uint64_t(2))); } unsigned get_num_decimal() const { - return get_num_digits(uint64_t(10)); + return get_num_digits(rational(uint64_t(10))); } /** From 0a2233219667991375249fe0b1829096c6326add Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 15 Feb 2026 16:31:38 +0000 Subject: [PATCH 29/41] fix --- src/util/rational.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/rational.h b/src/util/rational.h index af90017a5..d13b691e1 100644 --- a/src/util/rational.h +++ b/src/util/rational.h @@ -669,7 +669,7 @@ inline rational operator/(rational const & r1, int64_t r2) { inline rational operator/(rational const & r1, bool r2) { UNREACHABLE(); - return r1 / rational(r2); + return r1 / rational((uint64_t)r2); } inline rational operator/(int64_t r1, rational const & r2) { From bd993ca9630d7ff1b264f6200e691e3cabe6acce Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 15 Feb 2026 16:45:38 +0000 Subject: [PATCH 30/41] fix --- src/util/mpq.h | 3 +++ src/util/mpz.h | 11 +++++++++++ src/util/rational.h | 8 +++++--- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/util/mpq.h b/src/util/mpq.h index cef964a9e..ff92915a6 100644 --- a/src/util/mpq.h +++ b/src/util/mpq.h @@ -672,8 +672,11 @@ public: set(target.m_den, source.m_den); } + void set(mpz & a, int val) { mpz_manager::set(a, val); } void set(mpz & a, int64_t val) { mpz_manager::set(a, val); } + void set(mpq & a, int val) { set(a, (int64_t)val); } + void set(mpq & a, int64_t val) { set(a.m_num, val); reset_denominator(a); diff --git a/src/util/mpz.h b/src/util/mpz.h index c4ac37d1d..4f12b848b 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -159,6 +159,14 @@ public: SASSERT(fits_in_small(v)); } + mpz(int v) noexcept : m_value(static_cast(v) << 1) { + SASSERT(fits_in_small(v)); + } + + mpz(unsigned v) noexcept : m_value(static_cast(v) << 1) { + SASSERT(fits_in_small(v)); + } + mpz(mpz_type* ptr) noexcept { set_ptr(ptr, false, true); // external pointer, non-negative } @@ -600,6 +608,9 @@ public: void set(mpz & a, char const * val); + void set(mpz & a, int val) { set(a, (int64_t)val); } + void set(mpz & a, unsigned val) { set(a, (uint64_t)val); } + void set(mpz & a, int64_t val) { if (mpz::fits_in_small(val) && is_small(a)) { a.set(val); diff --git a/src/util/rational.h b/src/util/rational.h index d13b691e1..b74901b0e 100644 --- a/src/util/rational.h +++ b/src/util/rational.h @@ -46,6 +46,8 @@ public: rational(rational const & r) { m().set(m_val, r.m_val); } rational(rational&&) = default; + explicit rational(int n) { m().set(m_val, n); } + explicit rational(unsigned n) { m().set(m_val, n); } explicit rational(int64_t n) { m().set(m_val, n); } explicit rational(uint64_t n) { m().set(m_val, n); } @@ -289,7 +291,7 @@ public: rational r = mod(a,b); SASSERT(r.is_nonneg()); rational r2 = r; - r2 *= rational(uint64_t(2)); + r2 *= rational(2); if (operator<(b, r2)) { r -= b; } @@ -509,11 +511,11 @@ public: } unsigned get_num_bits() const { - return get_num_digits(rational(uint64_t(2))); + return get_num_digits(rational(2)); } unsigned get_num_decimal() const { - return get_num_digits(rational(uint64_t(10))); + return get_num_digits(rational(10)); } /** From ac748d973c1f17b2fd785cd5a1d1caeaa2c86c1c Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 15 Feb 2026 17:10:08 +0000 Subject: [PATCH 31/41] fix --- src/util/mpq.h | 18 ++++++++------ src/util/mpz.cpp | 38 ++++++++++++++--------------- src/util/mpz.h | 2 -- src/util/rational.h | 59 +++++++++++++++++++++++---------------------- 4 files changed, 60 insertions(+), 57 deletions(-) diff --git a/src/util/mpq.h b/src/util/mpq.h index ff92915a6..03a51d7d1 100644 --- a/src/util/mpq.h +++ b/src/util/mpq.h @@ -673,20 +673,17 @@ public: } void set(mpz & a, int val) { mpz_manager::set(a, val); } - void set(mpz & a, int64_t val) { mpz_manager::set(a, val); } - void set(mpq & a, int val) { set(a, (int64_t)val); } - - void set(mpq & a, int64_t val) { + void set(mpq & a, int val) { set(a.m_num, val); reset_denominator(a); } - void set(mpq & a, int64_t n, int64_t d) { + void set(mpq & a, int n, int d) { SASSERT(d != 0); if (d < 0) { - SASSERT(d != std::numeric_limits()); - SASSERT(n != std::numeric_limits()); + SASSERT(d != INT_MIN); + SASSERT(n != INT_MIN); n = -n; d = -d; } @@ -727,6 +724,13 @@ public: void set(mpq & a, char const * val); + void set(mpz & a, int64_t val) { mpz_manager::set(a, val); } + + void set(mpq & a, int64_t val) { + set(a.m_num, val); + reset_denominator(a); + } + void set(mpz & a, uint64_t val) { mpz_manager::set(a, val); } void set(mpq & a, uint64_t val) { diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 52441ae38..ef6f5a3c6 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -236,7 +236,7 @@ template void mpz_manager::add(mpz const & a, mpz const & b, mpz & c) { STRACE(mpz, tout << "[mpz] " << to_string(a) << " + " << to_string(b) << " == ";); if (is_small(a) && is_small(b)) { - set_i64(c, i64(a) + i64(b)); + set(c, a.value() + b.value()); } else { big_add(a, b, c); @@ -248,7 +248,7 @@ template void mpz_manager::sub(mpz const & a, mpz const & b, mpz & c) { STRACE(mpz, tout << "[mpz] " << to_string(a) << " - " << to_string(b) << " == ";); if (is_small(a) && is_small(b)) { - set_i64(c, i64(a) - i64(b)); + set(c, a.value() - b.value()); } else { big_sub(a, b, c); @@ -425,7 +425,7 @@ void mpz_manager::set_digits(mpz & target, unsigned sz, digit_t const * d #ifndef _MP_GMP allocate_if_needed(target, sz); memcpy(target.ptr()->m_digits, digits, sizeof(digit_t) * sz); - target.set_sign(1): + target.set_sign(1); #else mk_big(target); // reset @@ -448,7 +448,7 @@ template void mpz_manager::mul(mpz const & a, mpz const & b, mpz & c) { STRACE(mpz, tout << "[mpz] " << to_string(a) << " * " << to_string(b) << " == ";); if (is_small(a) && is_small(b)) { - set_i64(c, i64(a) * i64(b)); + set(c, a.value() * b.value()); } else { big_mul(a, b, c); @@ -496,10 +496,10 @@ template void mpz_manager::machine_div_rem(mpz const & a, mpz const & b, mpz & q, mpz & r) { STRACE(mpz, tout << "[mpz-ext] divrem(" << to_string(a) << ", " << to_string(b) << ") == ";); if (is_small(a) && is_small(b)) { - int64_t _a = i64(a); - int64_t _b = i64(b); - set_i64(q, _a / _b); - set_i64(r, _a % _b); + int64_t _a = a.value(); + int64_t _b = b.value(); + set(q, _a / _b); + set(r, _a % _b); } else { big_div_rem(a, b, q, r); @@ -510,11 +510,11 @@ void mpz_manager::machine_div_rem(mpz const & a, mpz const & b, mpz & q, template void mpz_manager::machine_div(mpz const & a, mpz const & b, mpz & c) { STRACE(mpz, tout << "[mpz-ext] machine-div(" << to_string(a) << ", " << to_string(b) << ") == ";); - if (is_small(b) && i64(b) == 0) + if (is_small(b) && b.value() == 0) throw default_exception("division by 0"); if (is_small(a) && is_small(b)) - set_i64(c, i64(a) / i64(b)); + set(c, a.value() / b.value()); else big_div(a, b, c); STRACE(mpz, tout << to_string(c) << "\n";); @@ -530,7 +530,7 @@ template void mpz_manager::rem(mpz const & a, mpz const & b, mpz & c) { STRACE(mpz, tout << "[mpz-ext] rem(" << to_string(a) << ", " << to_string(b) << ") == ";); if (is_small(a) && is_small(b)) { - set_i64(c, i64(a) % i64(b)); + set(c, a.value() % b.value()); } else { big_rem(a, b, c); @@ -597,8 +597,8 @@ mpz mpz_manager::mod2k(mpz const & a, unsigned k) { if (is_small(a) && k < 64) { uint64_t mask = ((1ULL << k) - 1); - uint64_t uval = static_cast(i64(a)); - set_i64(result, static_cast(uval & mask)); + uint64_t uval = static_cast(a.value()); + set(result, static_cast(uval & mask)); return result; } @@ -1425,7 +1425,7 @@ void mpz_manager::bitwise_xor(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_nonneg(a)); SASSERT(is_nonneg(b)); if (is_small(a) && is_small(b)) { - set_i64(c, i64(a) ^ i64(b)); + set(c, a.value() ^ b.value()); } else { #ifndef _MP_GMP @@ -1902,9 +1902,9 @@ bool mpz_manager::is_power_of_two(mpz const & a, unsigned & shift) { if (is_nonpos(a)) return false; if (is_small(a)) { - int64_t v = a.value(); + auto v = static_cast(a.value()); if (::is_power_of_two(v)) { - shift = log2(static_cast(v)); + shift = ::log2(v); return true; } else { @@ -1968,7 +1968,7 @@ void mpz_manager::ensure_capacity(mpz & a, unsigned capacity) { unsigned old_sz = a.ptr()->m_size; SASSERT(capacity >= old_sz); new_cell->m_size = old_sz; - memcpy(new_cell->m_digits, digits(a), sizeof(digit_t) * old_sz) + memcpy(new_cell->m_digits, digits(a), sizeof(digit_t) * old_sz); bool is_neg = a.sign() < 0; deallocate(a); a.set_ptr(new_cell, is_neg, false); @@ -2063,7 +2063,7 @@ void mpz_manager::mul2k(mpz & a, unsigned k) { if (k == 0 || is_zero(a)) return; if (is_small(a) && k < 32) { - set_i64(a, i64(a) * (static_cast(1) << k)); + set(a, a.value() * (static_cast(1) << k)); return; } #ifndef _MP_GMP @@ -2177,7 +2177,7 @@ unsigned mpz_manager::mlog2(mpz const & a) { if (is_nonneg(a)) return 0; if (is_small(a)) { - return ::log2(static_cast(-va.value())); + return ::log2(static_cast(-a.value())); } #ifndef _MP_GMP static_assert(sizeof(digit_t) == 8 || sizeof(digit_t) == 4, ""); diff --git a/src/util/mpz.h b/src/util/mpz.h index 4f12b848b..1e87f29a9 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -317,8 +317,6 @@ class mpz_manager { mpz m_two64; - static int64_t i64(mpz const & a) { return a.value(); } - void set_big_i64(mpz & c, int64_t v); void set_big_ui64(mpz & c, uint64_t v); diff --git a/src/util/rational.h b/src/util/rational.h index b74901b0e..a2354736e 100644 --- a/src/util/rational.h +++ b/src/util/rational.h @@ -51,7 +51,7 @@ public: explicit rational(int64_t n) { m().set(m_val, n); } explicit rational(uint64_t n) { m().set(m_val, n); } - rational(int64_t n, int64_t d) { m().set(m_val, n, d); } + rational(int n, int d) { m().set(m_val, n, d); } rational(mpq const & q) { m().set(m_val, q); } rational(mpq && q) noexcept : m_val(std::move(q)) {} rational(mpz const & z) { m().set(m_val, z); } @@ -173,6 +173,7 @@ public: rational & operator=(bool) = delete; rational operator*(bool r1) const = delete; + rational & operator=(int v) { return *this = (int64_t)v; } rational & operator=(int64_t v) { m().set(m_val, v); return *this; @@ -194,7 +195,7 @@ public: return *this; } - rational & operator+=(int64_t r) { + rational & operator+=(int r) { (*this) += rational(r); return *this; } @@ -204,7 +205,7 @@ public: return *this; } - rational& operator-=(int64_t r) { + rational& operator-=(int r) { (*this) -= rational(r); return *this; } @@ -224,15 +225,15 @@ public: return *this; } - rational & operator%=(int64_t v) { + rational & operator%=(int v) { return *this %= rational(v); } - rational & operator/=(int64_t v) { + rational & operator/=(int v) { return *this /= rational(v); } - rational & operator*=(int64_t v) { + rational & operator*=(int v) { return *this *= rational(v); } @@ -562,11 +563,11 @@ inline bool operator>(rational const & r1, rational const & r2) { return operator<(r2, r1); } -inline bool operator<(int64_t r1, rational const & r2) { +inline bool operator<(int r1, rational const & r2) { return rational(r1) < r2; } -inline bool operator<(rational const & r1, int64_t r2) { +inline bool operator<(rational const & r1, int r2) { return r1 < rational(r2); } @@ -578,35 +579,35 @@ inline bool operator>=(rational const & r1, rational const & r2) { return !operator<(r1, r2); } -inline bool operator>(rational const & a, int64_t b) { +inline bool operator>(rational const & a, int b) { return a > rational(b); } -inline bool operator>(int64_t a, rational const & b) { +inline bool operator>(int a, rational const & b) { return rational(a) > b; } -inline bool operator>=(rational const& a, int64_t b) { +inline bool operator>=(rational const& a, int b) { return a >= rational(b); } -inline bool operator>=(int64_t a, rational const& b) { +inline bool operator>=(int a, rational const& b) { return rational(a) >= b; } -inline bool operator<=(rational const& a, int64_t b) { +inline bool operator<=(rational const& a, int b) { return a <= rational(b); } -inline bool operator<=(int64_t a, rational const& b) { +inline bool operator<=(int a, rational const& b) { return rational(a) <= b; } -inline bool operator!=(rational const& a, int64_t b) { +inline bool operator!=(rational const& a, int b) { return !(a == rational(b)); } -inline bool operator==(rational const & a, int64_t b) { +inline bool operator==(rational const & a, int b) { return a == rational(b); } @@ -614,11 +615,11 @@ inline rational operator+(rational const & r1, rational const & r2) { return rational(r1) += r2; } -inline rational operator+(int64_t r1, rational const & r2) { +inline rational operator+(int r1, rational const & r2) { return rational(r1) + r2; } -inline rational operator+(rational const & r1, int64_t r2) { +inline rational operator+(rational const & r1, int r2) { return r1 + rational(r2); } @@ -627,11 +628,11 @@ inline rational operator-(rational const & r1, rational const & r2) { return rational(r1) -= r2; } -inline rational operator-(rational const & r1, int64_t r2) { +inline rational operator-(rational const & r1, int r2) { return r1 - rational(r2); } -inline rational operator-(int64_t r1, rational const & r2) { +inline rational operator-(int r1, rational const & r2) { return rational(r1) - r2; } @@ -647,17 +648,17 @@ inline rational operator*(rational const & r1, rational const & r2) { inline rational operator*(rational const & r1, bool r2) { UNREACHABLE(); - return r1 * rational((uint64_t)r2); -} -inline rational operator*(rational const & r1, int64_t r2) { return r1 * rational(r2); } -inline rational operator*(bool r1, rational const & r2) { +inline rational operator*(rational const & r1, int r2) { + return r1 * rational(r2); +} +inline rational operator*(bool r1, rational const & r2) { UNREACHABLE(); - return rational((uint64_t)r1) * r2; + return rational(r1) * r2; } -inline rational operator*(int64_t r1, rational const & r2) { +inline rational operator*(int r1, rational const & r2) { return rational(r1) * r2; } @@ -665,16 +666,16 @@ inline rational operator/(rational const & r1, rational const & r2) { return rational(r1) /= r2; } -inline rational operator/(rational const & r1, int64_t r2) { +inline rational operator/(rational const & r1, int r2) { return r1 / rational(r2); } inline rational operator/(rational const & r1, bool r2) { UNREACHABLE(); - return r1 / rational((uint64_t)r2); + return r1 / rational(r2); } -inline rational operator/(int64_t r1, rational const & r2) { +inline rational operator/(int r1, rational const & r2) { return rational(r1) / r2; } From 1a189ebdd737f6890267a7e870590db48641bebb Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 15 Feb 2026 17:11:17 +0000 Subject: [PATCH 32/41] fix --- src/util/util.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/util.h b/src/util/util.h index aec42116b..7b69ec921 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -103,6 +103,7 @@ static inline unsigned next_power_of_two(unsigned v) { /** \brief Return the position of the most significant bit. */ +static inline unsigned log2(int v) { return std::bit_width((unsigned)v) - 1; } static inline unsigned log2(unsigned v) { return std::bit_width(v) - 1; } static inline unsigned log2(uint64_t v) { return std::bit_width(v) - 1; } From fa73827d9840905ac37745cae06ea82f90302fb8 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 15 Feb 2026 22:09:11 +0000 Subject: [PATCH 33/41] fixes --- src/test/memory.cpp | 2 +- src/util/mpz.cpp | 88 +++++++++++++++++---------------------------- src/util/mpz.h | 39 +++++++++----------- 3 files changed, 51 insertions(+), 78 deletions(-) diff --git a/src/test/memory.cpp b/src/test/memory.cpp index 9a8d12b5d..f6c010380 100644 --- a/src/test/memory.cpp +++ b/src/test/memory.cpp @@ -39,7 +39,7 @@ static void hit_me(char const* wm) { Z3_mk_bv_sort(ctx,i); } - catch (std::bad_alloc) { + catch (std::bad_alloc&) { std::cout << "caught\n"; } } diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index ef6f5a3c6..8445ec134 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -17,6 +17,7 @@ Revision History: --*/ #include +#include #include #include #include "util/mpz.h" @@ -25,6 +26,22 @@ Revision History: #include "util/hash.h" #include "util/bit_util.h" +static bool mul_overflows(int64_t a, int64_t b, int64_t & result) { +#if __STDC_VERSION_STDCKDINT_H__ >= 202311L + return std::ckd_mul(&result, a, b); +#elif defined(__GNUC__) + return __builtin_mul_overflow(a, b, &result); +#elif defined(_MSC_VER) + // MSVC _mul128 intrinsic + __int64 high; + result = _mul128(a, b, &high); + // Overflow if high bits are not the sign extension of result + return high != 0 && high != -1; +#else + static_assert(false); +#endif +} + #if defined(_MP_INTERNAL) #include "util/mpn.h" #elif defined(_MP_GMP) @@ -111,22 +128,6 @@ unsigned u_gcd(unsigned u, unsigned v) { return u << shift; } -uint64_t u64_gcd(uint64_t u, uint64_t v) { - if (u == 0) return v; - if (v == 0) return u; - if (u == 1 || v == 1) return 1; - auto shift = _trailing_zeros64(u | v); - u >>= _trailing_zeros64(u); - do { - v >>= _trailing_zeros64(v); - if (u > v) std::swap(u, v); - v -= u; - } - while (v != 0); - return u << shift; -} - - template mpz_manager::mpz_manager(): @@ -375,9 +376,9 @@ void mpz_manager::set(mpz_cell& src, mpz & a, int sign, unsigned sz) { } unsigned d = src.m_digits[0]; - if (i == 1 && d <= mpz::SMALL_INT_MAX) { - // src fits in small integer range - a.set(sign < 0 ? -static_cast(d) : static_cast(d)); + int64_t val = sign < 0 ? -static_cast(d) : static_cast(d); + if (i == 1 && mpz::fits_in_small(val) && a.is_small()) { + a.set(val); return; } @@ -425,6 +426,7 @@ void mpz_manager::set_digits(mpz & target, unsigned sz, digit_t const * d #ifndef _MP_GMP allocate_if_needed(target, sz); memcpy(target.ptr()->m_digits, digits, sizeof(digit_t) * sz); + target.ptr()->m_size = sz; target.set_sign(1); #else mk_big(target); @@ -446,9 +448,10 @@ void mpz_manager::set_digits(mpz & target, unsigned sz, digit_t const * d template void mpz_manager::mul(mpz const & a, mpz const & b, mpz & c) { - STRACE(mpz, tout << "[mpz] " << to_string(a) << " * " << to_string(b) << " == ";); - if (is_small(a) && is_small(b)) { - set(c, a.value() * b.value()); + STRACE(mpz, tout << "[mpz] " << to_string(a) << " * " << to_string(b) << " == ";); + int64_t result; + if (is_small(a) && is_small(b) && !mul_overflows(a.value(), b.value(), result)) { + set(c, result); } else { big_mul(a, b, c); @@ -510,7 +513,7 @@ void mpz_manager::machine_div_rem(mpz const & a, mpz const & b, mpz & q, template void mpz_manager::machine_div(mpz const & a, mpz const & b, mpz & c) { STRACE(mpz, tout << "[mpz-ext] machine-div(" << to_string(a) << ", " << to_string(b) << ") == ";); - if (is_small(b) && b.value() == 0) + if (is_zero(b)) throw default_exception("division by 0"); if (is_small(a) && is_small(b)) @@ -890,12 +893,7 @@ template void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { static_assert(sizeof(mpz) <= 16, "mpz size overflow"); if (is_small(a) && is_small(b)) { - int64_t _a = a.value(); - int64_t _b = b.value(); - if (_a < 0) _a = -_a; - if (_b < 0) _b = -_b; - uint64_t r = u64_gcd(static_cast(_a), static_cast(_b)); - set(c, r); + set(c, std::gcd(a.value(), b.value())); return; } else { @@ -975,7 +973,7 @@ void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { else { while (true) { if (is_uint64(tmp1) && is_uint64(tmp2)) { - set(c, u64_gcd(get_uint64(tmp1), get_uint64(tmp2))); + set(c, std::gcd(get_uint64(tmp1), get_uint64(tmp2))); break; } rem(tmp1, tmp2, aux); @@ -1066,8 +1064,7 @@ void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { SASSERT(ge(a1, b1)); if (is_small(b1)) { if (is_small(a1)) { - uint64_t r = u64_gcd(static_cast(a1.value()), static_cast(b1.value())); - set(c, r); + set(c, std::gcd(a1.value(), b1.value())); break; } else { @@ -1081,12 +1078,12 @@ void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { break; } } - SASSERT(!is_small(a1)); + sign_cell ca(*this, a1); SASSERT(!is_small(b1)); - a_sz = a1.ptr()->m_size; + a_sz = ca.cell()->m_size; b_sz = b1.ptr()->m_size; SASSERT(b_sz <= a_sz); - a_hat = a1.ptr()->m_digits[a_sz - 1]; + a_hat = ca.cell()->m_digits[a_sz - 1]; b_hat = (b_sz == a_sz) ? b1.ptr()->m_digits[b_sz - 1] : 0; A = 1; B = 0; @@ -1499,21 +1496,6 @@ void mpz_manager::bitwise_not(unsigned sz, mpz const & a, mpz & c) { } } -template -void mpz_manager::big_set(mpz & target, mpz const & source) { -#ifndef _MP_GMP - if (&target == &source) - return; - allocate_if_needed(target, capacity(source)); - memcpy(digits(target), digits(source), sizeof(digit_t) * size(source)); - target.set_sign(source.sign()); -#else - // GMP version - mk_big(target); - mpz_set(*target.ptr(), *source.ptr()); -#endif -} - template int mpz_manager::big_compare(mpz const & a, mpz const & b) { #ifndef _MP_GMP @@ -1856,12 +1838,10 @@ void mpz_manager::power(mpz const & a, unsigned p, mpz & b) { unsigned shift = p%(8 * sizeof(digit_t)); SASSERT(sz > 0); allocate_if_needed(b, sz); - SASSERT(b.ptr()->m_capacity >= sz); - b.ptr()->m_size = sz; + b.ptr()->m_size = sz; for (unsigned i = 0; i < sz - 1; ++i) b.ptr()->m_digits[i] = 0; b.ptr()->m_digits[sz-1] = 1 << shift; - // b is already large after allocate_if_needed, just ensure sign is positive b.set_sign(1); } return; @@ -1951,7 +1931,6 @@ void mpz_manager::ensure_capacity(mpz & a, unsigned capacity) { int64_t val = a.value(); uint64_t abs_val = static_cast(-val); allocate_if_needed(a, capacity); - SASSERT(a.ptr()->m_capacity >= capacity); if (sizeof(digit_t) == sizeof(uint64_t)) { a.ptr()->m_digits[0] = static_cast(abs_val); a.ptr()->m_size = 1; @@ -1966,7 +1945,6 @@ void mpz_manager::ensure_capacity(mpz & a, unsigned capacity) { else if (a.ptr()->m_capacity < capacity) { mpz_cell * new_cell = allocate(capacity); unsigned old_sz = a.ptr()->m_size; - SASSERT(capacity >= old_sz); new_cell->m_size = old_sz; memcpy(new_cell->m_digits, digits(a), sizeof(digit_t) * old_sz); bool is_neg = a.sign() < 0; diff --git a/src/util/mpz.h b/src/util/mpz.h index 1e87f29a9..e74da812a 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -28,7 +28,6 @@ Revision History: #include "util/mpn.h" unsigned u_gcd(unsigned u, unsigned v); -uint64_t u64_gcd(uint64_t u, uint64_t v); unsigned trailing_zeros(uint64_t); unsigned trailing_zeros(uint32_t); @@ -158,14 +157,8 @@ public: // with values that fit, or the caller should use set_big_i64. SASSERT(fits_in_small(v)); } - - mpz(int v) noexcept : m_value(static_cast(v) << 1) { - SASSERT(fits_in_small(v)); - } - - mpz(unsigned v) noexcept : m_value(static_cast(v) << 1) { - SASSERT(fits_in_small(v)); - } + mpz(int v) : mpz(int64_t(v)) {} + mpz(unsigned v) : mpz(int64_t(v)) {} mpz(mpz_type* ptr) noexcept { set_ptr(ptr, false, true); // external pointer, non-negative @@ -364,7 +357,7 @@ class mpz_manager { class sign_cell { static const unsigned capacity = 2; - unsigned char m_bytes[sizeof(mpz_cell) + sizeof(digit_t) * capacity]; + alignas(8) unsigned char m_bytes[sizeof(mpz_cell) + sizeof(digit_t) * capacity]; mpz m_local; mpz const& m_a; int m_sign; @@ -431,8 +424,6 @@ class mpz_manager { void big_mul(mpz const & a, mpz const & b, mpz & c); - void big_set(mpz & target, mpz const & source); - #ifndef _MP_GMP #define QUOT_ONLY 0 @@ -511,11 +502,15 @@ public: static bool is_neg(mpz const & a) { return sign(a) < 0; } - static bool is_zero(mpz const & a) { return sign(a) == 0; } + static bool is_zero(mpz const & a) { + if (a.is_small()) + return a.value() == 0; + return size(a) == 1 && digits(a)[0] == 0; + } static int sign(mpz const & a) { if (is_small(a)) { - int v = a.value(); + int64_t v = a.value(); return (v > 0) - (v < 0); // Returns -1, 0, or 1 } else @@ -597,10 +592,10 @@ public: void set(mpz & target, mpz const & source) { if (is_small(source)) { - target.set(source.value()); + set(target, source.value()); } else { - big_set(target, source); + set(*source.ptr(), target, source.sign(), size(source)); } } @@ -685,21 +680,21 @@ public: static unsigned hash(mpz const & a); static bool is_one(mpz const & a) { -#ifndef _MP_GMP - return is_small(a) && a.value() == 1; -#else if (is_small(a)) return a.value() == 1; +#ifndef _MP_GMP + return size(a) == 1 && digits(a)[0] == 1 && a.sign() > 0; +#else return mpz_cmp_si(*a.ptr(), 1) == 0; #endif } static bool is_minus_one(mpz const & a) { -#ifndef _MP_GMP - return is_small(a) && a.value() == -1; -#else if (is_small(a)) return a.value() == -1; +#ifndef _MP_GMP + return size(a) == 1 && digits(a)[0] == 1 && a.sign() < 0; +#else return mpz_cmp_si(*a.ptr(), -1) == 0; #endif } From 04f07fe1d5f54291f05a9b683294639c5b81faf6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Feb 2026 22:48:21 +0000 Subject: [PATCH 34/41] Fix invalid zero representation in set_big_i64 and set_big_ui64 Co-authored-by: nunoplopes <2998477+nunoplopes@users.noreply.github.com> --- src/util/mpz.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 8445ec134..9b0949114 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -259,6 +259,10 @@ void mpz_manager::sub(mpz const & a, mpz const & b, mpz & c) { template void mpz_manager::set_big_i64(mpz & c, int64_t v) { + if (v == 0) { + set(c, 0); + return; + } uint64_t _v; bool sign = v < 0; if (v == std::numeric_limits::min()) { @@ -309,6 +313,10 @@ void mpz_manager::set_big_i64(mpz & c, int64_t v) { template void mpz_manager::set_big_ui64(mpz & c, uint64_t v) { + if (v == 0) { + set(c, 0); + return; + } #ifndef _MP_GMP if (c.is_small()) { c.set_ptr(allocate(m_init_cell_capacity), false, false); // positive, owned From 957ba91fa6c0b654d90ca3db04405bf9b11f8ae4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Feb 2026 08:21:41 +0000 Subject: [PATCH 35/41] Revert "Fix invalid zero representation in set_big_i64 and set_big_ui64" Co-authored-by: nunoplopes <2998477+nunoplopes@users.noreply.github.com> --- src/util/mpz.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 9b0949114..8445ec134 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -259,10 +259,6 @@ void mpz_manager::sub(mpz const & a, mpz const & b, mpz & c) { template void mpz_manager::set_big_i64(mpz & c, int64_t v) { - if (v == 0) { - set(c, 0); - return; - } uint64_t _v; bool sign = v < 0; if (v == std::numeric_limits::min()) { @@ -313,10 +309,6 @@ void mpz_manager::set_big_i64(mpz & c, int64_t v) { template void mpz_manager::set_big_ui64(mpz & c, uint64_t v) { - if (v == 0) { - set(c, 0); - return; - } #ifndef _MP_GMP if (c.is_small()) { c.set_ptr(allocate(m_init_cell_capacity), false, false); // positive, owned From cc7902ac509b870b268e6664a3e4d0ba7412247e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Feb 2026 09:09:18 +0000 Subject: [PATCH 36/41] Fix set_big_i64/set_big_ui64 zero handling: zero is always represented as small Co-authored-by: nunoplopes <2998477+nunoplopes@users.noreply.github.com> --- src/util/mpz.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/util/mpz.h b/src/util/mpz.h index e74da812a..f6ed99689 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -605,7 +605,11 @@ public: void set(mpz & a, unsigned val) { set(a, (uint64_t)val); } void set(mpz & a, int64_t val) { - if (mpz::fits_in_small(val) && is_small(a)) { + if (val == 0) { + // Zero is always represented as small + reset(a); + } + else if (mpz::fits_in_small(val) && is_small(a)) { a.set(val); } else { @@ -614,7 +618,11 @@ public: } void set(mpz & a, uint64_t val) { - if (mpz::fits_in_small(val) && is_small(a)) { + if (val == 0) { + // Zero is always represented as small + reset(a); + } + else if (mpz::fits_in_small(val) && is_small(a)) { a.set(static_cast(val)); } else { From c01062e93b6a396673c150faff3c10541b4d6dd0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Feb 2026 11:16:34 +0000 Subject: [PATCH 37/41] Revert "Fix set_big_i64/set_big_ui64 zero handling: zero is always represented as small" Co-authored-by: nunoplopes <2998477+nunoplopes@users.noreply.github.com> --- src/util/mpz.h | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/util/mpz.h b/src/util/mpz.h index f6ed99689..e74da812a 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -605,11 +605,7 @@ public: void set(mpz & a, unsigned val) { set(a, (uint64_t)val); } void set(mpz & a, int64_t val) { - if (val == 0) { - // Zero is always represented as small - reset(a); - } - else if (mpz::fits_in_small(val) && is_small(a)) { + if (mpz::fits_in_small(val) && is_small(a)) { a.set(val); } else { @@ -618,11 +614,7 @@ public: } void set(mpz & a, uint64_t val) { - if (val == 0) { - // Zero is always represented as small - reset(a); - } - else if (mpz::fits_in_small(val) && is_small(a)) { + if (mpz::fits_in_small(val) && is_small(a)) { a.set(static_cast(val)); } else { From 4a8aeb0050febae508ab8ce020f39dc1cb0f055f Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 17 Feb 2026 09:49:09 +0000 Subject: [PATCH 38/41] fixes --- src/util/mpz.cpp | 57 +++++++++++++++++++++---------------------- src/util/mpz.h | 63 ++++++++++++++++++++++++++---------------------- 2 files changed, 62 insertions(+), 58 deletions(-) diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 8445ec134..eca7436b7 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -225,7 +225,7 @@ mpz_manager::sign_cell::sign_cell(mpz_manager& m, mpz const& a): template void mpz_manager::del(mpz_manager* m, mpz & a) { - if (!a.is_small()) { + if (a.has_ptr()) { SASSERT(m); mpz::mpz_type* p = a.ptr(); m->deallocate(!a.is_external(), p); @@ -272,7 +272,7 @@ void mpz_manager::set_big_i64(mpz & c, int64_t v) { _v = v; } #ifndef _MP_GMP - if (c.is_small()) { + if (!c.has_ptr()) { c.set_ptr(allocate(m_init_cell_capacity), sign, false); } else { c.set_sign(sign ? -1 : 1); @@ -290,7 +290,7 @@ void mpz_manager::set_big_i64(mpz & c, int64_t v) { c.ptr()->m_size = digits(c)[1] == 0 ? 1 : 2; } #else - if (c.is_small()) { + if (!c.has_ptr()) { c.set_ptr(allocate(), false, false); } mpz_set_ui(*c.ptr(), static_cast(_v)); @@ -310,7 +310,7 @@ void mpz_manager::set_big_i64(mpz & c, int64_t v) { template void mpz_manager::set_big_ui64(mpz & c, uint64_t v) { #ifndef _MP_GMP - if (c.is_small()) { + if (!c.has_ptr()) { c.set_ptr(allocate(m_init_cell_capacity), false, false); // positive, owned } else { c.set_sign(1); // positive @@ -328,7 +328,7 @@ void mpz_manager::set_big_ui64(mpz & c, uint64_t v) { c.ptr()->m_size = digits(c)[1] == 0 ? 1 : 2; } #else - if (c.is_small()) { + if (!c.has_ptr()) { c.set_ptr(allocate(), false, false); // positive, owned } mpz_set_ui(*c.ptr(), static_cast(v)); @@ -344,7 +344,7 @@ void mpz_manager::set_big_ui64(mpz & c, uint64_t v) { template mpz_manager::ensure_mpz_t::ensure_mpz_t(mpz const& a) { - if (is_small(a)) { + if (!a.has_ptr()) { m_result = &m_local; mpz_init(m_local); mpz_set_si(m_local, a.value()); @@ -377,15 +377,15 @@ void mpz_manager::set(mpz_cell& src, mpz & a, int sign, unsigned sz) { unsigned d = src.m_digits[0]; int64_t val = sign < 0 ? -static_cast(d) : static_cast(d); - if (i == 1 && mpz::fits_in_small(val) && a.is_small()) { - a.set(val); + if (i == 1 && mpz::fits_in_small(val) && !a.has_ptr()) { + set(a, val); return; } set_digits(a, i, src.m_digits); a.set_sign(sign); - SASSERT(!a.is_small()); + SASSERT(a.has_ptr()); } #endif @@ -668,7 +668,7 @@ template void mpz_manager::neg(mpz & a) { STRACE(mpz, tout << "[mpz] 0 - " << to_string(a) << " == ";); if (is_small(a)) { - a.set(-a.value()); + set(a, -a.value()); } #ifndef _MP_GMP else { @@ -687,7 +687,7 @@ void mpz_manager::abs(mpz & a) { if (is_small(a)) { int64_t v = a.value(); if (v < 0) { - a.set(-v); + set(a, -v); } } else { @@ -947,7 +947,7 @@ void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { // reset least significant bit if (is_small(v)) - v.set(v.value() & ~1); + set(v, v.value() & ~1); else v.ptr()->m_digits[0] &= ~static_cast(1); k_v = power_of_two_multiple(v); @@ -1079,7 +1079,7 @@ void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { } } sign_cell ca(*this, a1); - SASSERT(!is_small(b1)); + SASSERT(b1.has_ptr()); a_sz = ca.cell()->m_size; b_sz = b1.ptr()->m_size; SASSERT(b_sz <= a_sz); @@ -1145,7 +1145,7 @@ void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { template unsigned mpz_manager::size_info(mpz const & a) { - if (is_small(a)) + if (!a.has_ptr()) return 1; #ifndef _MP_GMP return a.ptr()->m_size + 1; @@ -1343,7 +1343,7 @@ void mpz_manager::bitwise_or(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_nonneg(b)); TRACE(mpz, tout << "is_small(a): " << is_small(a) << ", is_small(b): " << is_small(b) << "\n";); if (is_small(a) && is_small(b)) { - c.set(a.value() | b.value()); + set(c, a.value() | b.value()); } else { #ifndef _MP_GMP @@ -1388,7 +1388,7 @@ void mpz_manager::bitwise_and(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_nonneg(a)); SASSERT(is_nonneg(b)); if (is_small(a) && is_small(b)) { - c.set(a.value() & b.value()); + set(c, a.value() & b.value()); } else { #ifndef _MP_GMP @@ -1535,16 +1535,13 @@ int mpz_manager::big_compare(mpz const & a, mpz const & b) { template bool mpz_manager::is_uint64(mpz const & a) const { -#ifndef _MP_GMP if (is_small(a)) return a.value() >= 0; +#ifndef _MP_GMP if (a.sign() < 0) return false; return size(a) <= (sizeof(digit_t) == sizeof(uint64_t) ? 1 : 2); #else - // GMP version - if (is_small(a)) - return a.value() >= 0; return is_nonneg(a) && mpz_cmp(*a.ptr(), m_uint64_max) <= 0; #endif } @@ -1831,7 +1828,7 @@ void mpz_manager::power(mpz const & a, unsigned p, mpz & b) { if (is_small(a)) { if (a.value() == 2) { if (p < 8 * sizeof(int) - 1) { - b.set(1 << p); + set(b, 1 << p); } else { unsigned sz = p/(8 * sizeof(digit_t)) + 1; @@ -1927,9 +1924,9 @@ void mpz_manager::ensure_capacity(mpz & a, unsigned capacity) { if (capacity < m_init_cell_capacity) capacity = m_init_cell_capacity; - if (is_small(a)) { + if (!a.has_ptr()) { int64_t val = a.value(); - uint64_t abs_val = static_cast(-val); + uint64_t abs_val = static_cast(val < 0 ? -val : val); allocate_if_needed(a, capacity); if (sizeof(digit_t) == sizeof(uint64_t)) { a.ptr()->m_digits[0] = static_cast(abs_val); @@ -1974,10 +1971,10 @@ void mpz_manager::machine_div2k(mpz & a, unsigned k) { if (k < 64) { int64_t twok = 1ull << ((int64_t)k); int64_t val = a.value(); - a.set(val/twok); + set(a, val/twok); } else { - a.set(0); + set(a, 0); } return; } @@ -2040,20 +2037,22 @@ template void mpz_manager::mul2k(mpz & a, unsigned k) { if (k == 0 || is_zero(a)) return; - if (is_small(a) && k < 32) { - set(a, a.value() * (static_cast(1) << k)); + + int64_t result; + if (is_small(a) && k < 64 && !mul_overflows(a.value(), static_cast(1) << k, result)) { + set(a, result); return; } #ifndef _MP_GMP TRACE(mpz_mul2k, tout << "mul2k\na: " << to_string(a) << "\nk: " << k << "\n";); unsigned word_shift = k / (8 * sizeof(digit_t)); unsigned bit_shift = k % (8 * sizeof(digit_t)); - unsigned old_sz = is_small(a) ? 1 : a.ptr()->m_size; + unsigned old_sz = a.has_ptr() ? size(a) : (sizeof(int64_t) / sizeof(digit_t)); unsigned new_sz = old_sz + word_shift + 1; ensure_capacity(a, new_sz); TRACE(mpz_mul2k, tout << "word_shift: " << word_shift << "\nbit_shift: " << bit_shift << "\nold_sz: " << old_sz << "\nnew_sz: " << new_sz << "\na after ensure capacity:\n" << to_string(a) << "\n";); - SASSERT(!is_small(a)); + SASSERT(a.has_ptr()); mpz_cell * cell_a = a.ptr(); old_sz = cell_a->m_size; digit_t * ds = cell_a->m_digits; diff --git a/src/util/mpz.h b/src/util/mpz.h index e74da812a..d1746bf35 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -57,6 +57,7 @@ class mpz_cell { unsigned m_size; unsigned m_capacity; digit_t m_digits[0]; + friend class mpz; friend class mpz_manager; friend class mpz_manager; friend class mpz_stack; @@ -110,12 +111,12 @@ private: } mpz_type * ptr() const { - SASSERT(!is_small()); + SASSERT(has_ptr()); return reinterpret_cast(m_value & MPZ_PTR_MASK); } void set_ptr(mpz_type* p, bool is_negative, bool is_external) { - SASSERT(is_small()); + SASSERT(!has_ptr()); SASSERT((reinterpret_cast(p) & 0x7) == 0); // Check alignment m_value = reinterpret_cast(p) | LARGE_BIT; if (is_negative) @@ -124,13 +125,8 @@ private: m_value |= EXTERNAL_BIT; } - int get_sign() const { - SASSERT(!is_small()); - return (m_value & SIGN_BIT) ? -1 : 1; - } - void set_sign(int s) { - SASSERT(!is_small()); + SASSERT(has_ptr()); if (s < 0) m_value |= SIGN_BIT; else @@ -138,7 +134,7 @@ private: } bool is_external() const { - SASSERT(!is_small()); + SASSERT(has_ptr()); return (m_value & EXTERNAL_BIT) != 0; } @@ -180,24 +176,33 @@ public: } void set(int64_t v) { - SASSERT(is_small()); + SASSERT(!has_ptr()); SASSERT(fits_in_small(v)); m_value = static_cast(v) << 1; } + inline bool has_ptr() const { + return (m_value & LARGE_BIT) != 0; + } + inline bool is_small() const { - return (m_value & LARGE_BIT) == 0; + return !has_ptr() || ptr()->m_size == 1; } inline int64_t value() const { SASSERT(is_small()); + if (has_ptr()) { + // Small value stored in a single digit + int64_t v = static_cast(ptr()->m_digits[0]); + return sign() < 0 ? -v : v; + } // Decode small integer: shift right by 1 (arithmetic shift to preserve sign) return static_cast(static_cast(m_value) >> 1); } inline int sign() const { - SASSERT(!is_small()); - return get_sign(); + SASSERT(has_ptr()); + return (m_value & SIGN_BIT) ? -1 : 1; } }; @@ -243,7 +248,7 @@ class mpz_manager { // make sure that n is a big number and has capacity equal to at least c. void allocate_if_needed(mpz & n, unsigned c) { if (m_init_cell_capacity > c) c = m_init_cell_capacity; - if (n.is_small() || capacity(n) < c) { + if (!n.has_ptr() || capacity(n) < c) { deallocate(n); n.set_ptr(allocate(c), false, false); // positive, owned } @@ -298,11 +303,11 @@ class mpz_manager { } } - void clear(mpz& n) { if (!n.is_small()) { mpz_clear(*n.ptr()); }} + void clear(mpz& n) { if (n.has_ptr()) { mpz_clear(*n.ptr()); }} #endif void deallocate(mpz& n) { - if (!n.is_small()) { + if (n.has_ptr()) { deallocate(!n.is_external(), n.ptr()); n.m_value = 0; // reset to small } @@ -317,17 +322,17 @@ class mpz_manager { #ifndef _MP_GMP static unsigned capacity(mpz const & c) { - SASSERT(!c.is_small()); + SASSERT(c.has_ptr()); return c.ptr()->m_capacity; } static unsigned size(mpz const & c) { - SASSERT(!c.is_small()); + SASSERT(c.has_ptr()); return c.ptr()->m_size; } static digit_t * digits(mpz const & c) { - SASSERT(!c.is_small()); + SASSERT(c.has_ptr()); return c.ptr()->m_digits; } @@ -369,7 +374,7 @@ class mpz_manager { }; void get_sign_cell(mpz const & a, int & sign, mpz_cell * & cell, mpz_cell* reserve) { - if (is_small(a)) { + if (!a.has_ptr()) { int64_t val = a.value(); bool neg = val < 0; uint64_t abs_val = static_cast(neg ? -val : val); @@ -405,7 +410,7 @@ class mpz_manager { }; void mk_big(mpz & a) { - if (a.is_small()) { + if (!a.has_ptr()) { a.set_ptr(allocate(), false, false); // positive, owned } } @@ -503,13 +508,11 @@ public: static bool is_neg(mpz const & a) { return sign(a) < 0; } static bool is_zero(mpz const & a) { - if (a.is_small()) - return a.value() == 0; - return size(a) == 1 && digits(a)[0] == 0; + return a.is_small() && a.value() == 0; } static int sign(mpz const & a) { - if (is_small(a)) { + if (a.is_small()) { int64_t v = a.value(); return (v > 0) - (v < 0); // Returns -1, 0, or 1 } @@ -605,7 +608,7 @@ public: void set(mpz & a, unsigned val) { set(a, (uint64_t)val); } void set(mpz & a, int64_t val) { - if (mpz::fits_in_small(val) && is_small(a)) { + if (mpz::fits_in_small(val) && !a.has_ptr()) { a.set(val); } else { @@ -614,7 +617,7 @@ public: } void set(mpz & a, uint64_t val) { - if (mpz::fits_in_small(val) && is_small(a)) { + if (mpz::fits_in_small(val) && !a.has_ptr()) { a.set(static_cast(val)); } else { @@ -683,17 +686,19 @@ public: if (is_small(a)) return a.value() == 1; #ifndef _MP_GMP - return size(a) == 1 && digits(a)[0] == 1 && a.sign() > 0; + return false; #else return mpz_cmp_si(*a.ptr(), 1) == 0; #endif } + // best effort static bool is_minus_one(mpz const & a) { if (is_small(a)) return a.value() == -1; #ifndef _MP_GMP - return size(a) == 1 && digits(a)[0] == 1 && a.sign() < 0; + //return eq(a, mpz(-1)); + return false; #else return mpz_cmp_si(*a.ptr(), -1) == 0; #endif From 6db067ca67de6c1ea48e1d923d5363b2617ac6e6 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 17 Feb 2026 14:11:41 +0000 Subject: [PATCH 39/41] fixes --- src/math/polynomial/polynomial.cpp | 2 +- src/sat/smt/pb_solver.cpp | 2 +- src/smt/theory_pb.cpp | 2 +- src/util/mpz.cpp | 189 +++++++++++------------------ src/util/mpz.h | 9 +- 5 files changed, 77 insertions(+), 127 deletions(-) diff --git a/src/math/polynomial/polynomial.cpp b/src/math/polynomial/polynomial.cpp index f4cfa1fbd..b53c382f2 100644 --- a/src/math/polynomial/polynomial.cpp +++ b/src/math/polynomial/polynomial.cpp @@ -2687,7 +2687,7 @@ namespace polynomial { } if (j == 1 || j == -1) return; - g = u_gcd(abs(j), g); + g = std::gcd(abs(j), g); if (g == 1) return; } diff --git a/src/sat/smt/pb_solver.cpp b/src/sat/smt/pb_solver.cpp index 41628372f..f7a641067 100644 --- a/src/sat/smt/pb_solver.cpp +++ b/src/sat/smt/pb_solver.cpp @@ -1250,7 +1250,7 @@ namespace pb { g = coeff; } else { - g = u_gcd(g, coeff); + g = std::gcd(g, coeff); } } diff --git a/src/smt/theory_pb.cpp b/src/smt/theory_pb.cpp index 862440e43..915b2845c 100644 --- a/src/smt/theory_pb.cpp +++ b/src/smt/theory_pb.cpp @@ -1788,7 +1788,7 @@ namespace smt { g = static_cast(coeff); } else { - g = u_gcd(g, static_cast(coeff)); + g = std::gcd(g, static_cast(coeff)); } } if (g >= 2) { diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index eca7436b7..30ff13535 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -36,7 +36,7 @@ static bool mul_overflows(int64_t a, int64_t b, int64_t & result) { __int64 high; result = _mul128(a, b, &high); // Overflow if high bits are not the sign extension of result - return high != 0 && high != -1; + return high != (result >> 63); #else static_assert(false); #endif @@ -63,71 +63,6 @@ static bool mul_overflows(int64_t a, int64_t b, int64_t & result) { #define LEHMER_GCD #endif -#ifdef __has_builtin - #define HAS_BUILTIN(X) __has_builtin(X) -#else - #define HAS_BUILTIN(X) 0 -#endif -#if HAS_BUILTIN(__builtin_ctz) -#define _trailing_zeros32(X) __builtin_ctz(X) -#elif defined(_WINDOWS) && (defined(_M_X86) || (defined(_M_X64) && !defined(_M_ARM64EC))) && !defined(__clang__) -// This is needed for _tzcnt_u32 and friends. -#include -#define _trailing_zeros32(X) _tzcnt_u32(X) -#else -static uint32_t _trailing_zeros32(uint32_t x) { - uint32_t r = 0; - for (; 0 == (x & 1) && r < 32; ++r, x >>= 1); - return r; -} -#endif - -#if (defined(__LP64__) || defined(_WIN64)) && defined(_M_X64) && !defined(_M_ARM64EC) -#if HAS_BUILTIN(__builtin_ctzll) -#define _trailing_zeros64(X) __builtin_ctzll(X) -#elif !defined(__clang__) -#define _trailing_zeros64(X) _tzcnt_u64(X) -#endif -#else -static uint64_t _trailing_zeros64(uint64_t x) { - uint64_t r = 0; - for (; 0 == (x & 1) && r < 64; ++r, x >>= 1); - return r; -} -#endif - -#undef HAS_BUILTIN - -unsigned trailing_zeros(uint32_t x) { - return static_cast(_trailing_zeros32(x)); -} - -unsigned trailing_zeros(uint64_t x) { - return static_cast(_trailing_zeros64(x)); -} - -#define _bit_min(x, y) (y + ((x - y) & ((int)(x - y) >> 31))) -#define _bit_max(x, y) (x - ((x - y) & ((int)(x - y) >> 31))) - - -unsigned u_gcd(unsigned u, unsigned v) { - if (u == 0) return v; - if (v == 0) return u; - unsigned shift = _trailing_zeros32(u | v); - u >>= _trailing_zeros32(u); - if (u == 1 || v == 1) return 1 << shift; - if (u == v) return u << shift; - do { - v >>= _trailing_zeros32(v); - unsigned diff = u - v; - unsigned mdiff = diff & (unsigned)((int)diff >> 31); - u = v + mdiff; // min - v = diff - 2 * mdiff; // if v <= u: u - v, if v > u: v - u = u - v - 2 * (u - v) - } - while (v != 0); - return u << shift; -} - template mpz_manager::mpz_manager(): @@ -242,7 +177,7 @@ void mpz_manager::add(mpz const & a, mpz const & b, mpz & c) { else { big_add(a, b, c); } - STRACE(mpz, tout << to_string(c) << "\n";); + STRACE(mpz, tout << to_string(c) << '\n';); } template @@ -254,7 +189,7 @@ void mpz_manager::sub(mpz const & a, mpz const & b, mpz & c) { else { big_sub(a, b, c); } - STRACE(mpz, tout << to_string(c) << "\n";); + STRACE(mpz, tout << to_string(c) << '\n';); } template @@ -384,7 +319,6 @@ void mpz_manager::set(mpz_cell& src, mpz & a, int sign, unsigned sz) { set_digits(a, i, src.m_digits); a.set_sign(sign); - SASSERT(a.has_ptr()); } #endif @@ -456,12 +390,13 @@ void mpz_manager::mul(mpz const & a, mpz const & b, mpz & c) { else { big_mul(a, b, c); } - STRACE(mpz, tout << to_string(c) << "\n";); + STRACE(mpz, tout << to_string(c) << '\n';); } // d <- a + b*c template void mpz_manager::addmul(mpz const & a, mpz const & b, mpz const & c, mpz & d) { + STRACE(mpz, tout << "[mpz] " << to_string(a) << " + " << to_string(b) << " * " << to_string(c) << " == ";); if (is_one(b)) { add(a, c, d); } @@ -474,12 +409,14 @@ void mpz_manager::addmul(mpz const & a, mpz const & b, mpz const & c, mpz add(a,tmp,d); del(tmp); } + STRACE(mpz, tout << to_string(d) << '\n';); } // d <- a - b*c template void mpz_manager::submul(mpz const & a, mpz const & b, mpz const & c, mpz & d) { + STRACE(mpz, tout << "[mpz] " << to_string(a) << " - " << to_string(b) << " * " << to_string(c) << " == ";); if (is_one(b)) { sub(a, c, d); } @@ -492,6 +429,7 @@ void mpz_manager::submul(mpz const & a, mpz const & b, mpz const & c, mpz sub(a,tmp,d); del(tmp); } + STRACE(mpz, tout << to_string(d) << '\n';); } @@ -520,7 +458,7 @@ void mpz_manager::machine_div(mpz const & a, mpz const & b, mpz & c) { set(c, a.value() / b.value()); else big_div(a, b, c); - STRACE(mpz, tout << to_string(c) << "\n";); + STRACE(mpz, tout << to_string(c) << '\n';); } template @@ -538,7 +476,7 @@ void mpz_manager::rem(mpz const & a, mpz const & b, mpz & c) { else { big_rem(a, b, c); } - STRACE(mpz, tout << to_string(c) << "\n";); + STRACE(mpz, tout << to_string(c) << '\n';); } @@ -551,7 +489,7 @@ void mpz_manager::div_gcd(mpz const& a, mpz const& b, mpz & c) { else { machine_div(a, b, c); } - STRACE(mpz, tout << to_string(c) << "\n";); + STRACE(mpz, tout << to_string(c) << '\n';); } template @@ -575,7 +513,7 @@ void mpz_manager::div(mpz const & a, mpz const & b, mpz & c) { else { machine_div(a, b, c); } - STRACE(mpz, tout << to_string(c) << "\n";); + STRACE(mpz, tout << to_string(c) << '\n';); } template @@ -588,13 +526,16 @@ void mpz_manager::mod(mpz const & a, mpz const & b, mpz & c) { else sub(c, b, c); } - STRACE(mpz, tout << to_string(c) << "\n";); + STRACE(mpz, tout << to_string(c) << '\n';); } template mpz mpz_manager::mod2k(mpz const & a, unsigned k) { - if (is_zero(a)) + STRACE(mpz, tout << "[mpz] " << to_string(a) << " mod 2^" << k << " == ";); + if (is_zero(a)) { + STRACE(mpz, tout << "0\n";); return 0; + } mpz result; @@ -602,10 +543,12 @@ mpz mpz_manager::mod2k(mpz const & a, unsigned k) { uint64_t mask = ((1ULL << k) - 1); uint64_t uval = static_cast(a.value()); set(result, static_cast(uval & mask)); + STRACE(mpz, tout << to_string(result) << '\n';); return result; } if (is_nonneg(a) && bitsize(a) <= k) { + STRACE(mpz, tout << to_string(a) << '\n';); return dup(a); } @@ -661,6 +604,7 @@ mpz mpz_manager::mod2k(mpz const & a, unsigned k) { mpz_tdiv_r_2exp(*result.ptr(), a1(), k); MPZ_END_CRITICAL(); #endif + STRACE(mpz, tout << to_string(result) << '\n';); return result; } @@ -679,11 +623,12 @@ void mpz_manager::neg(mpz & a) { mpz_neg(*a.ptr(), *a.ptr()); } #endif - STRACE(mpz, tout << to_string(a) << "\n";); + STRACE(mpz, tout << to_string(a) << '\n';); } template void mpz_manager::abs(mpz & a) { + STRACE(mpz, tout << "[mpz] abs(" << to_string(a) << ") == ";); if (is_small(a)) { int64_t v = a.value(); if (v < 0) { @@ -697,6 +642,7 @@ void mpz_manager::abs(mpz & a) { mpz_abs(*a.ptr(), *a.ptr()); #endif } + STRACE(mpz, tout << to_string(a) << '\n';); } @@ -891,9 +837,11 @@ void mpz_manager::big_rem(mpz const & a, mpz const & b, mpz & c) { template void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { + STRACE(mpz, tout << "[mpz] gcd(" << to_string(a) << ", " << to_string(b) << ") == ";); static_assert(sizeof(mpz) <= 16, "mpz size overflow"); if (is_small(a) && is_small(b)) { set(c, std::gcd(a.value(), b.value())); + STRACE(mpz, tout << to_string(c) << '\n';); return; } else { @@ -906,11 +854,13 @@ void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { if (is_zero(a)) { set(c, b); abs(c); + STRACE(mpz, tout << to_string(c) << '\n';); return; } if (is_zero(b)) { set(c, a); abs(c); + STRACE(mpz, tout << to_string(c) << '\n';); return; } #ifdef BINARY_GCD @@ -1141,6 +1091,7 @@ void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { del(a1); del(b1); del(r); del(t); del(tmp); #endif // LEHMER_GCD } + STRACE(mpz, tout << to_string(c) << '\n';); } template @@ -1195,7 +1146,7 @@ void mpz_manager::gcd(unsigned sz, mpz const * as, mpz & g) { for (i = 0; i < sz; ++i) p.push_back(i); std::sort(p.begin(), p.end(), sz_lt{as}); - TRACE(mpz_gcd, for (unsigned i = 0; i < sz; ++i) tout << p[i] << ":" << size_info(as[p[i]]) << " "; tout << "\n";); + TRACE(mpz_gcd, for (unsigned i = 0; i < sz; ++i) tout << p[i] << ":" << size_info(as[p[i]]) << " "; tout << '\n';); gcd(as[p[0]], as[p[1]], g); for (i = 2; i < sz; ++i) { if (is_one(g)) @@ -1236,6 +1187,7 @@ void mpz_manager::gcd(unsigned sz, mpz const * as, mpz & g) { template void mpz_manager::gcd(mpz const & r1, mpz const & r2, mpz & a, mpz & b, mpz & r) { + STRACE(mpz, tout << "[mpz-ext] extended gcd of " << to_string(r1) << " and " << to_string(r2) << '\n';); mpz tmp1, tmp2; mpz aux, quot; set(tmp1, r1); @@ -1308,30 +1260,30 @@ template void mpz_manager::lcm(mpz const & a, mpz const & b, mpz & c) { if (is_one(b)) { set(c, a); - TRACE(lcm_bug, tout << "1. lcm(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(c) << "\n";); + TRACE(lcm_bug, tout << "1. lcm(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(c) << '\n';); } else if (is_one(a) || eq(a, b)) { set(c, b); - TRACE(lcm_bug, tout << "2. lcm(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(c) << "\n";); + TRACE(lcm_bug, tout << "2. lcm(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(c) << '\n';); } else { mpz r; gcd(a, b, r); - TRACE(lcm_bug, tout << "gcd(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(r) << "\n";); + TRACE(lcm_bug, tout << "gcd(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(r) << '\n';); if (eq(r, a)) { set(c, b); - TRACE(lcm_bug, tout << "3. lcm(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(c) << "\n";); + TRACE(lcm_bug, tout << "3. lcm(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(c) << '\n';); } else if (eq(r, b)) { set(c, a); - TRACE(lcm_bug, tout << "4. lcm(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(c) << "\n";); + TRACE(lcm_bug, tout << "4. lcm(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(c) << '\n';); } else { // c contains gcd(a, b) // so c divides a, and machine_div(a, c) is equal to div(a, c) machine_div(a, r, r); mul(r, b, c); - TRACE(lcm_bug, tout << "5. lcm(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(c) << "\n";); + TRACE(lcm_bug, tout << "5. lcm(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(c) << '\n';); } del(r); } @@ -1339,9 +1291,9 @@ void mpz_manager::lcm(mpz const & a, mpz const & b, mpz & c) { template void mpz_manager::bitwise_or(mpz const & a, mpz const & b, mpz & c) { + STRACE(mpz, tout << "[mpz] bitwise_or(" << to_string(a) << ", " << to_string(b) << ") == ";); SASSERT(is_nonneg(a)); SASSERT(is_nonneg(b)); - TRACE(mpz, tout << "is_small(a): " << is_small(a) << ", is_small(b): " << is_small(b) << "\n";); if (is_small(a) && is_small(b)) { set(c, a.value() | b.value()); } @@ -1353,12 +1305,11 @@ void mpz_manager::bitwise_or(mpz const & a, mpz const & b, mpz & c) { set(m, 1); set(c, 0); while (!is_zero(a1) && !is_zero(b1)) { - TRACE(mpz, tout << "a1: " << to_string(a1) << ", b1: " << to_string(b1) << "\n";); mod(a1, m_two64, a2); mod(b1, m_two64, b2); - TRACE(mpz, tout << "a2: " << to_string(a2) << ", b2: " << to_string(b2) << "\n";); + TRACE(mpz, tout << "a2: " << to_string(a2) << ", b2: " << to_string(b2) << '\n';); uint64_t v = get_uint64(a2) | get_uint64(b2); - TRACE(mpz, tout << "uint(a2): " << get_uint64(a2) << ", uint(b2): " << get_uint64(b2) << "\n";); + TRACE(mpz, tout << "uint(a2): " << get_uint64(a2) << ", uint(b2): " << get_uint64(b2) << '\n';); set(tmp, v); mul(tmp, m, tmp); add(c, tmp, c); // c += m * v @@ -1381,10 +1332,12 @@ void mpz_manager::bitwise_or(mpz const & a, mpz const & b, mpz & c) { mpz_ior(*c.ptr(), a1(), b1()); #endif } + STRACE(mpz, tout << to_string(c) << '\n';); } template void mpz_manager::bitwise_and(mpz const & a, mpz const & b, mpz & c) { + STRACE(mpz, tout << "[mpz] bitwise_and(" << to_string(a) << ", " << to_string(b) << ") == ";); SASSERT(is_nonneg(a)); SASSERT(is_nonneg(b)); if (is_small(a) && is_small(b)) { @@ -1415,10 +1368,12 @@ void mpz_manager::bitwise_and(mpz const & a, mpz const & b, mpz & c) { mpz_and(*c.ptr(), a1(), b1()); #endif } + STRACE(mpz, tout << to_string(c) << '\n';); } template void mpz_manager::bitwise_xor(mpz const & a, mpz const & b, mpz & c) { + STRACE(mpz, tout << "[mpz] bitwise_xor(" << to_string(a) << ", " << to_string(b) << ") == ";); SASSERT(is_nonneg(a)); SASSERT(is_nonneg(b)); if (is_small(a) && is_small(b)) { @@ -1457,10 +1412,12 @@ void mpz_manager::bitwise_xor(mpz const & a, mpz const & b, mpz & c) { mpz_xor(*c.ptr(), a1(), b1()); #endif } + STRACE(mpz, tout << to_string(c) << '\n';); } template void mpz_manager::bitwise_not(unsigned sz, mpz const & a, mpz & c) { + STRACE(mpz, tout << "[mpz] bitwise_not(" << to_string(a) << ", sz=" << sz << ") == ";); SASSERT(is_nonneg(a)); if (is_small(a) && sz <= 64) { uint64_t v = ~get_uint64(a); @@ -1482,7 +1439,6 @@ void mpz_manager::bitwise_not(unsigned sz, mpz const & a, mpz & c) { uint64_t mask = (1ull << static_cast(sz)) - 1ull; v = mask & v; } - TRACE(bitwise_not, tout << "sz: " << sz << ", v: " << v << ", n: " << n << "\n";); set(tmp, v); SASSERT(get_uint64(tmp) == v); mul(tmp, m, tmp); @@ -1492,14 +1448,13 @@ void mpz_manager::bitwise_not(unsigned sz, mpz const & a, mpz & c) { sz -= (sz<64) ? sz : 64; } del(a1); del(a2); del(m); del(tmp); - TRACE(bitwise_not, tout << "sz: " << sz << " a: " << to_string(a) << " c: " << to_string(c) << "\n";); } + STRACE(mpz, tout << to_string(c) << '\n';); } template int mpz_manager::big_compare(mpz const & a, mpz const & b) { #ifndef _MP_GMP - if (sign(a) > 0) { // a is positive if (sign(b) > 0) { @@ -1743,13 +1698,13 @@ void mpz_manager::display_hex(std::ostream & out, mpz const & a, unsigned } static void display_binary_data(std::ostream &out, uint64_t val, uint64_t numBits) { - for (uint64_t shift = numBits; shift-- > 64ull; ) out << "0"; + for (uint64_t shift = numBits; shift-- > 64ull; ) out << '0'; if (numBits > 64) numBits = 64; for (uint64_t shift = numBits; shift-- > 0; ) { if (val & (1ull << shift)) { - out << "1"; + out << '1'; } else { - out << "0"; + out << '0'; } } } @@ -1818,6 +1773,7 @@ unsigned mpz_manager::hash(mpz const & a) { template void mpz_manager::power(mpz const & a, unsigned p, mpz & b) { + STRACE(mpz, tout << "power(" << to_string(a) << ", " << p << ") == ";); #ifdef _MP_GMP if (!is_small(a)) { mk_big(b); @@ -1827,8 +1783,8 @@ void mpz_manager::power(mpz const & a, unsigned p, mpz & b) { #else if (is_small(a)) { if (a.value() == 2) { - if (p < 8 * sizeof(int) - 1) { - set(b, 1 << p); + if (p < 63) { + set(b, int64_t(1ull << p)); } else { unsigned sz = p/(8 * sizeof(digit_t)) + 1; @@ -1838,20 +1794,19 @@ void mpz_manager::power(mpz const & a, unsigned p, mpz & b) { b.ptr()->m_size = sz; for (unsigned i = 0; i < sz - 1; ++i) b.ptr()->m_digits[i] = 0; - b.ptr()->m_digits[sz-1] = 1 << shift; + b.ptr()->m_digits[sz-1] = digit_t(1ULL << shift); b.set_sign(1); } - return; } - if (a.value() == 0) { + else if (a.value() == 0) { SASSERT(p != 0); set(b, 0); - return; } - if (a.value() == 1) { + else if (a.value() == 1) { set(b, 1); - return; } + STRACE(mpz, tout << to_string(b) << '\n';); + return; } #endif // general purpose @@ -1866,6 +1821,7 @@ void mpz_manager::power(mpz const & a, unsigned p, mpz & b) { mask = mask << 1; } del(power); + STRACE(mpz, tout << to_string(b) << '\n';); } template @@ -1968,14 +1924,7 @@ void mpz_manager::machine_div2k(mpz & a, unsigned k) { if (k == 0 || is_zero(a)) return; if (is_small(a)) { - if (k < 64) { - int64_t twok = 1ull << ((int64_t)k); - int64_t val = a.value(); - set(a, val/twok); - } - else { - set(a, 0); - } + set(a, k < 64 ? (a.value() >> k) : 0); return; } #ifndef _MP_GMP @@ -1991,7 +1940,7 @@ void mpz_manager::machine_div2k(mpz & a, unsigned k) { unsigned new_sz = sz - digit_shift; SASSERT(new_sz >= 1); digit_t * ds = c->m_digits; - TRACE(mpz_2k, tout << "bit_shift: " << bit_shift << ", comp_shift: " << comp_shift << ", new_sz: " << new_sz << ", sz: " << sz << "\n";); + TRACE(mpz_2k, tout << "bit_shift: " << bit_shift << ", comp_shift: " << comp_shift << ", new_sz: " << new_sz << ", sz: " << sz << '\n';); if (new_sz < sz) { unsigned i = 0; unsigned j = digit_shift; @@ -2035,23 +1984,25 @@ void mpz_manager::machine_div2k(mpz & a, unsigned k) { template void mpz_manager::mul2k(mpz & a, unsigned k) { + STRACE(mpz, tout << "[mpz-mul2k] a=" << to_string(a) << " k=" << k << '\n';); if (k == 0 || is_zero(a)) return; int64_t result; - if (is_small(a) && k < 64 && !mul_overflows(a.value(), static_cast(1) << k, result)) { + if (is_small(a) && k < 63 && !mul_overflows(a.value(), 1ULL << k, result)) { set(a, result); + TRACE(mpz_mul2k, tout << "mul2k result:\n" << to_string(a) << '\n';); return; } #ifndef _MP_GMP - TRACE(mpz_mul2k, tout << "mul2k\na: " << to_string(a) << "\nk: " << k << "\n";); + TRACE(mpz_mul2k, tout << "mul2k\na: " << to_string(a) << "\nk: " << k << '\n';); unsigned word_shift = k / (8 * sizeof(digit_t)); unsigned bit_shift = k % (8 * sizeof(digit_t)); unsigned old_sz = a.has_ptr() ? size(a) : (sizeof(int64_t) / sizeof(digit_t)); unsigned new_sz = old_sz + word_shift + 1; ensure_capacity(a, new_sz); TRACE(mpz_mul2k, tout << "word_shift: " << word_shift << "\nbit_shift: " << bit_shift << "\nold_sz: " << old_sz << "\nnew_sz: " << new_sz - << "\na after ensure capacity:\n" << to_string(a) << "\n";); + << "\na after ensure capacity:\n" << to_string(a) << '\n';); SASSERT(a.has_ptr()); mpz_cell * cell_a = a.ptr(); old_sz = cell_a->m_size; @@ -2088,7 +2039,7 @@ void mpz_manager::mul2k(mpz & a, unsigned k) { } } normalize(a); - TRACE(mpz_mul2k, tout << "mul2k result:\n" << to_string(a) << "\n";); + TRACE(mpz_mul2k, tout << "mul2k result:\n" << to_string(a) << '\n';); #else ensure_mpz_t a1(a); mk_big(a); @@ -2340,7 +2291,7 @@ bool mpz_manager::root(mpz & a, unsigned n) { while (true) { add(upper, lower, mid); machine_div2k(mid, 1); - TRACE(mpz, tout << "upper: "; display(tout, upper); tout << "\nlower: "; display(tout, lower); tout << "\nmid: "; display(tout, mid); tout << "\n";); + TRACE(mpz, tout << "upper: "; display(tout, upper); tout << "\nlower: "; display(tout, lower); tout << "\nmid: "; display(tout, mid); tout << '\n';); power(mid, n, mid_n); if (eq(mid_n, a)) { swap(a, mid); @@ -2389,7 +2340,7 @@ digit_t mpz_manager::get_least_significant(mpz const& a) { template bool mpz_manager::decompose(mpz const & a, svector & digits) { digits.reset(); - if (is_small(a)) { + if (!a.has_ptr()) { int64_t v = a.value(); bool is_neg = v < 0; uint64_t abs_v = static_cast(is_neg ? -v : v); @@ -2473,8 +2424,8 @@ bool mpz_manager::divides(mpz const & a, mpz const & b) { rem(b, a, tmp); r = is_zero(tmp); } - STRACE(divides, tout << "[mpz] Divisible["; display(tout, b); tout << ", "; display(tout, a); tout << "] == " << (r?"True":"False") << "\n";); - TRACE(divides_bug, tout << "tmp: "; display(tout, tmp); tout << "\n";); + STRACE(divides, tout << "[mpz] Divisible["; display(tout, b); tout << ", "; display(tout, a); tout << "] == " << (r?"True":"False") << '\n';); + TRACE(divides_bug, tout << "tmp: "; display(tout, tmp); tout << '\n';); return r; } diff --git a/src/util/mpz.h b/src/util/mpz.h index d1746bf35..f572406a4 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -18,18 +18,17 @@ Revision History: --*/ #pragma once -#include +#include +#include #include "util/mutex.h" #include "util/util.h" #include "util/small_object_allocator.h" -#include "util/trace.h" #include "util/scoped_numeral.h" #include "util/scoped_numeral_vector.h" #include "util/mpn.h" -unsigned u_gcd(unsigned u, unsigned v); -unsigned trailing_zeros(uint64_t); -unsigned trailing_zeros(uint32_t); +static inline unsigned trailing_zeros(uint64_t x) { return std::countr_zero(x); } +static inline unsigned trailing_zeros(uint32_t x) { return std::countr_zero(x); } #ifdef _MP_GMP From fdc57935090312f148dfaed575363b27b4f3306c Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 17 Feb 2026 14:20:12 +0000 Subject: [PATCH 40/41] fixes --- src/math/polynomial/polynomial.cpp | 1 + src/sat/smt/pb_solver.cpp | 2 +- src/smt/theory_pb.cpp | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/math/polynomial/polynomial.cpp b/src/math/polynomial/polynomial.cpp index b53c382f2..a2209901c 100644 --- a/src/math/polynomial/polynomial.cpp +++ b/src/math/polynomial/polynomial.cpp @@ -35,6 +35,7 @@ Notes: #include "util/ref_buffer.h" #include "util/common_msgs.h" #include +#include namespace polynomial { diff --git a/src/sat/smt/pb_solver.cpp b/src/sat/smt/pb_solver.cpp index f7a641067..3dd7f6eee 100644 --- a/src/sat/smt/pb_solver.cpp +++ b/src/sat/smt/pb_solver.cpp @@ -16,7 +16,7 @@ Author: --*/ #include -#include "util/mpz.h" +#include #include "sat/sat_types.h" #include "sat/smt/pb_solver.h" #include "sat/smt/euf_solver.h" diff --git a/src/smt/theory_pb.cpp b/src/smt/theory_pb.cpp index 915b2845c..3cd81955c 100644 --- a/src/smt/theory_pb.cpp +++ b/src/smt/theory_pb.cpp @@ -29,7 +29,7 @@ Notes: #include "ast/rewriter/pb_rewriter_def.h" #include "math/simplex/sparse_matrix_def.h" #include "math/simplex/simplex_def.h" - +#include namespace smt { From e6607df8317ef8b4a4e547be5a1ca6c45ffb121d Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 17 Feb 2026 15:41:00 +0000 Subject: [PATCH 41/41] fixes --- src/util/mpz.cpp | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 30ff13535..03f729e0b 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -1775,14 +1775,15 @@ template void mpz_manager::power(mpz const & a, unsigned p, mpz & b) { STRACE(mpz, tout << "power(" << to_string(a) << ", " << p << ") == ";); #ifdef _MP_GMP - if (!is_small(a)) { + if (a.has_ptr()) { mk_big(b); mpz_pow_ui(*b.ptr(), *a.ptr(), p); return; } #else if (is_small(a)) { - if (a.value() == 2) { + int64_t v = a.value(); + if (v == 2) { if (p < 63) { set(b, int64_t(1ull << p)); } @@ -1797,16 +1798,20 @@ void mpz_manager::power(mpz const & a, unsigned p, mpz & b) { b.ptr()->m_digits[sz-1] = digit_t(1ULL << shift); b.set_sign(1); } + STRACE(mpz, tout << to_string(b) << '\n';); + return; } - else if (a.value() == 0) { + else if (v == 0) { SASSERT(p != 0); set(b, 0); + STRACE(mpz, tout << to_string(b) << '\n';); + return; } - else if (a.value() == 1) { + else if (v == 1) { set(b, 1); + STRACE(mpz, tout << to_string(b) << '\n';); + return; } - STRACE(mpz, tout << to_string(b) << '\n';); - return; } #endif // general purpose @@ -1921,10 +1926,20 @@ void mpz_manager::normalize(mpz & a) { template void mpz_manager::machine_div2k(mpz & a, unsigned k) { + STRACE(mpz, tout << "[mpz-machine-div2k] a=" << to_string(a) << " k=" << k << '\n';); if (k == 0 || is_zero(a)) return; if (is_small(a)) { - set(a, k < 64 ? (a.value() >> k) : 0); + if (k >= 64) { + set(a, 0); + } + else if (k == 63) { + // Only possible non-zero result is for INT64_MIN + set(a, a.value() == std::numeric_limits::min() ? -1 : 0); + } + else { + set(a, a.value() / int64_t(1ULL << k)); + } return; } #ifndef _MP_GMP