mruby-bigint: fixed a bug with bigint +/- mrb_int; close #6445

This commit is contained in:
Yukihiro "Matz" Matsumoto
2024-12-07 23:44:55 +09:00
parent 33841519a9
commit 441d4be0ad
+53 -32
View File
@@ -312,18 +312,16 @@ mpz_add(mrb_state *mrb, mpz_t *zz, mpz_t *x, mpz_t *y)
mpz_move(mrb, zz, &z);
}
/* x = x + n (only called from mpz_init_set_str) */
/* assumes x and n are positive or zero */
/* assumes n is small (fits in mp_limb) */
/* x += n */
/* ignores sign of x */
/* assumes n is positive and small (fits in mp_limb) */
static void
mpz_add_int(mrb_state *mrb, mpz_t *x, mrb_int n)
{
if (n == 0) {
// If n is zero, no operation is needed
return;
}
// If n is zero, no operation is needed
if (n == 0) return;
// Assume x is positive and n is a small positive integer (n < 36)
// Assume x is positive and n is a small positive integer
mp_dbl_limb carry = n; // Initialize carry with n
for (size_t i = 0; i < x->sz && carry; i++) {
carry += (mp_dbl_limb)x->p[i]; // Add current limb and carry
@@ -352,20 +350,26 @@ mpz_sub(mrb_state *mrb, mpz_t *z, mpz_t *x, mpz_t *y)
mpz_add(mrb, z, x, &u);
}
/* x -= n */
/* assumes n is small (fits in mp_limb) */
/* x -= n */
/* ignores sign of x */
/* assumes n is positive and small (fits in mp_limb) */
static void
mpz_sub_int(mrb_state *mrb, mpz_t *x, mrb_int n)
{
// If x is zero, set x to -1
// If n is zero, no operation is needed
if (n == 0) return;
// If x is zero, set x to n
if (zero_p(x) || x->sz == 0) {
mpz_set_int(mrb, x, n);
return;
}
#if 0
if (x->sn < 0) {
mpz_add_int(mrb, x, 1);
mpz_add_int(mrb, x, n);
return;
}
#endif
// Initialize borrow and start decrement
mp_dbl_limb_signed borrow = (mp_limb)n;
@@ -1480,22 +1484,17 @@ mrb_bint_as_uint64(mrb_state *mrb, mrb_value x)
static mrb_bool
int_fit_limb_p(mrb_int i)
{
#ifdef MRB_INT64
#if DIG_SIZE == 32
# ifdef MRB_INT64
// if mp_limb is int32_t
return (i >= INT32_MIN && i <= INT32_MAX);
#else /* if DIG_SIZE == 16 */
// if mp_limb is int16_t
return (i >= INT16_MIN && i <= INT16_MAX);
#endif
#else /* MRB_INT32 */
#if DIG_SIZE == 32
return (i > INT32_MIN && i <= INT32_MAX);
# else
// if mp_limb is also int32_t, it always fits
return TRUE;
# endif
#else /* if DIG_SIZE == 16 */
// if mp_limb is int16_t
return (i >= INT16_MIN && i <= INT16_MAX);
#endif
return (i > INT16_MIN && i <= INT16_MAX);
#endif
}
@@ -1507,14 +1506,25 @@ mrb_bint_add_n(mrb_state *mrb, mrb_value x, mrb_value y)
bint_as_mpz(RBIGINT(x), &a);
if (mrb_integer_p(y)) {
mrb_int i = mrb_integer(y);
if (int_fit_limb_p(i)) {
mrb_int n = mrb_integer(y);
if (int_fit_limb_p(n)) {
mpz_init_set(mrb, &z, &a);
if ((i > 0) ^ (z.sn > 0)) {
mpz_sub_int(mrb, &z, i);
if (n > 0) {
if (z.sn > 0) {
mpz_add_int(mrb, &z, n);
}
else {
mpz_sub_int(mrb, &z, n);
}
}
else {
mpz_add_int(mrb, &z, i);
n = -n;
if (z.sn > 0) {
mpz_sub_int(mrb, &z, n);
}
else {
mpz_add_int(mrb, &z, n);
}
}
struct RBigint *v = bint_new(mrb, &z);
return mrb_obj_value(v);
@@ -1550,14 +1560,25 @@ mrb_bint_sub_n(mrb_state *mrb, mrb_value x, mrb_value y)
bint_as_mpz(RBIGINT(x), &a);
if (mrb_integer_p(y)) {
mrb_int i = mrb_integer(y);
if (int_fit_limb_p(i)) {
mrb_int n = mrb_integer(y);
if (int_fit_limb_p(n)) {
mpz_init_set(mrb, &z, &a);
if ((i > 0) ^ (z.sn > 0)) {
mpz_add_int(mrb, &z, i);
if (n > 0) {
if (z.sn > 0) {
mpz_sub_int(mrb, &z, n);
}
else {
mpz_add_int(mrb, &z, n);
}
}
else {
mpz_sub_int(mrb, &z, i);
n = -n;
if (z.sn > 0) {
mpz_add_int(mrb, &z, n);
}
else {
mpz_sub_int(mrb, &z, n);
}
}
struct RBigint *v = bint_new(mrb, &z);
return mrb_obj_value(v);