@@ -99,7 +99,7 @@ pool_alloc(mpz_pool_t *pool, size_t limbs)
9999100100/* Zero n limbs at p */
101101static inline void
102-limb_zero(mp_limb *p, size_t n)
102+mpn_zero(mp_limb *p, size_t n)
103103{
104104memset(p, 0, n * sizeof(mp_limb));
105105}
@@ -124,7 +124,7 @@ mpz_init_heap(mpz_ctx_t *ctx, mpz_t *s, size_t hint)
124124mrb_raise(mrb, E_RUNTIME_ERROR, "bigint size too large");
125125 }
126126s->p = (mp_limb*)mrb_malloc(MPZ_MRB(ctx), hint * sizeof(mp_limb));
127-limb_zero(s->p, hint);
127+mpn_zero(s->p, hint);
128128s->sz = hint;
129129 }
130130else {
@@ -200,7 +200,7 @@ mpz_realloc(mpz_ctx_t *ctx, mpz_t *x, size_t size)
200200#endif
201201202202/* Zero-initialize new limbs */
203-limb_zero(x->p + old_sz, size - old_sz);
203+mpn_zero(x->p + old_sz, size - old_sz);
204204x->sz = size;
205205 }
206206}
@@ -638,7 +638,7 @@ mpz_sub_int(mpz_ctx_t *ctx, mpz_t *x, mrb_int n)
638638639639/* Multiply-and-add: rp[0..n-1] += s1p[0..n-1] * limb; return carry (high limb) */
640640static inline mp_limb
641-limb_addmul_1(mp_limb *rp, const mp_limb *s1p, size_t n, mp_limb limb)
641+mpn_addmul_1(mp_limb *rp, const mp_limb *s1p, size_t n, mp_limb limb)
642642{
643643#if defined(__SIZEOF_INT128__) && (__SIZEOF_INT128__ == 16)
644644/* Use 128-bit arithmetic with 8x unrolling for maximum efficiency */
@@ -743,6 +743,33 @@ limb_addmul_1(mp_limb *rp, const mp_limb *s1p, size_t n, mp_limb limb)
743743#endif
744744}
745745746+/* Multiply-and-subtract: rp[0..n-1] -= s1p[0..n-1] * limb; return borrow */
747+static inline mp_limb
748+mpn_submul_1(mp_limb *rp, const mp_limb *s1p, size_t n, mp_limb limb)
749+{
750+mp_dbl_limb borrow = 0;
751+for (size_t i = 0; i < n; i++) {
752+mp_dbl_limb prod = (mp_dbl_limb)s1p[i] * (mp_dbl_limb)limb;
753+mp_dbl_limb sub = (mp_dbl_limb)rp[i] - LOW(prod) - borrow;
754+rp[i] = LOW(sub);
755+/* Borrow is 1 if sub underflowed, plus HIGH(prod) */
756+borrow = HIGH(prod) + (sub >> (sizeof(mp_dbl_limb) * 8 - 1));
757+ }
758+return (mp_limb)borrow;
759+}
760+761+/* Compare two same-length limb arrays: returns <0, 0, or >0 */
762+static inline int
763+mpn_cmp(const mp_limb *ap, const mp_limb *bp, size_t n)
764+{
765+while (n-- > 0) {
766+if (ap[n] != bp[n]) {
767+return (ap[n] > bp[n]) ? 1 : -1;
768+ }
769+ }
770+return 0;
771+}
772+746773#define KARATSUBA_THRESHOLD 32
747774748775static inline mrb_bool
@@ -751,7 +778,7 @@ should_use_karatsuba(size_t x_len, size_t y_len)
751778return x_len >= KARATSUBA_THRESHOLD && y_len >= KARATSUBA_THRESHOLD;
752779}
753780754-/* w = u * v (optimized schoolbook using limb_addmul_1) */
781+/* w = u * v (optimized schoolbook using mpn_addmul_1) */
755782static void
756783mpz_mul_basic(mpz_ctx_t *ctx, mpz_t *ww, mpz_t *u, mpz_t *v)
757784{
@@ -774,9 +801,9 @@ mpz_mul_basic(mpz_ctx_t *ctx, mpz_t *ww, mpz_t *u, mpz_t *v)
774801mp_limb scalar = b->p[0];
775802mpz_t w;
776803mpz_init_heap(ctx, &w, a->sz + 1);
777-limb_zero(w.p, a->sz + 1);
804+mpn_zero(w.p, a->sz + 1);
778805779-mp_limb carry = limb_addmul_1(w.p, a->p, a->sz, scalar);
806+mp_limb carry = mpn_addmul_1(w.p, a->p, a->sz, scalar);
780807w.p[a->sz] = carry;
781808782809w.sn = a->sn * b->sn;
@@ -787,13 +814,13 @@ mpz_mul_basic(mpz_ctx_t *ctx, mpz_t *ww, mpz_t *u, mpz_t *v)
787814788815mpz_t w;
789816mpz_init_heap(ctx, &w, a->sz + b->sz);
790-limb_zero(w.p, a->sz + b->sz);
817+mpn_zero(w.p, a->sz + b->sz);
791818792819for (size_t j = 0; j < a->sz; j++) {
793820mp_limb a_limb = a->p[j];
794821if (a_limb == 0) continue;
795822796-mp_limb carry = limb_addmul_1(w.p + j, b->p, b->sz, a_limb);
823+mp_limb carry = mpn_addmul_1(w.p + j, b->p, b->sz, a_limb);
797824798825/* Properly handle carry propagation to avoid overflow */
799826size_t k = j + b->sz;
@@ -812,9 +839,9 @@ mpz_mul_basic(mpz_ctx_t *ctx, mpz_t *ww, mpz_t *u, mpz_t *v)
812839813840/* Allocation-free Karatsuba helper functions */
814841815-/* Copy limbs: dest[0..n-1] = src[0..n-1] */
842+/* Copy limbs forward: dest[0..n-1] = src[0..n-1] */
816843static void
817-limb_copy(mp_limb *dest, const mp_limb *src, size_t n)
844+mpn_copyi(mp_limb *dest, const mp_limb *src, size_t n)
818845{
819846if (n > 0) {
820847memcpy(dest, src, n * sizeof(mp_limb));
@@ -857,11 +884,11 @@ static void
857884mpz_mul_basic_limbs(mp_limb *result, const mp_limb *x, size_t x_len,
858885const mp_limb *y, size_t y_len)
859886{
860-limb_zero(result, x_len + y_len);
887+mpn_zero(result, x_len + y_len);
861888862889for (size_t i = 0; i < x_len; i++) {
863890if (x[i] == 0) continue;
864-mp_limb carry = limb_addmul_1(result + i, y, y_len, x[i]);
891+mp_limb carry = mpn_addmul_1(result + i, y, y_len, x[i]);
865892if (i + y_len < x_len + y_len) {
866893result[i + y_len] += carry;
867894 }
@@ -886,7 +913,7 @@ static void
886913mpz_sqr_basic_limbs(mp_limb *result, const mp_limb *x, size_t n)
887914{
888915size_t result_len = 2 * n;
889-limb_zero(result, result_len);
916+mpn_zero(result, result_len);
890917891918/* Step 1: Compute off-diagonal terms xi * xj for i < j */
892919for (size_t i = 0; i < n; i++) {
@@ -895,7 +922,7 @@ mpz_sqr_basic_limbs(mp_limb *result, const mp_limb *x, size_t n)
895922896923if (i + 1 < n) {
897924/* Compute xi * x[i+1..n-1] and add at position 2*i+1 */
898-mp_limb carry = limb_addmul_1(result + 2*i + 1, x + i + 1, n - i - 1, xi);
925+mp_limb carry = mpn_addmul_1(result + 2*i + 1, x + i + 1, n - i - 1, xi);
899926900927/* Propagate carry */
901928size_t k = 2*i + 1 + (n - i - 1);
@@ -1064,8 +1091,8 @@ mpz_mul_karatsuba(mpz_ctx_t *ctx, mp_limb *result,
1064109110651092/* Step 4: Final assembly: result = z0 + z1*B + z2*B^2 */
10661093size_t result_len = x_len + y_len;
1067-limb_zero(result, result_len);
1068-limb_copy(result, z0, z0_len);
1094+mpn_zero(result, result_len);
1095+mpn_copyi(result, z0, z0_len);
10691096limb_add_at(result, result_len, z1, z1_len, half);
10701097limb_add_at(result, result_len, z2, z2_len, 2 * half);
10711098}
@@ -1171,8 +1198,8 @@ mpz_sqr_karatsuba(mpz_ctx_t *ctx, mp_limb *result, const mp_limb *x, size_t n,
1171119811721199/* Step 4: Final assembly: result = z0 + z1*B + z2*B^2 */
11731200size_t result_len = 2 * n;
1174-limb_zero(result, result_len);
1175-limb_copy(result, z0, z0_len);
1201+mpn_zero(result, result_len);
1202+mpn_copyi(result, z0, z0_len);
11761203limb_add_at(result, result_len, z1, z1_len, half);
11771204limb_add_at(result, result_len, z2, z2_len, 2 * half);
11781205}
@@ -1938,7 +1965,6 @@ div_limb(mpz_ctx_t *ctx, mpz_t *q, mpz_t *r, mpz_t *x, mp_limb d)
19381965pool_restore(ctx, pool_state);
19391966}
194019671941-19421968static void
19431969udiv(mpz_ctx_t *ctx, mpz_t *qq, mpz_t *rr, mpz_t *xx, mpz_t *yy)
19441970{