From 69a80c94b979fec0ab3032cfc636b801a204b2ed Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Thu, 25 Jul 2024 14:49:45 +0900 Subject: [PATCH] mruby-bigint (mpz_and): support bit-wise `and` with negative numbers Negative integers are virtually considered as 2's compliment of the absolute value of the corresponding number. It means `-1` is considered as infinite sequence of `1` toward msb side. ref #6314 --- mrbgems/mruby-bigint/core/bigint.c | 84 +++++++++++++++++++++++++----- 1 file changed, 72 insertions(+), 12 deletions(-) diff --git a/mrbgems/mruby-bigint/core/bigint.c b/mrbgems/mruby-bigint/core/bigint.c index 81dffcbc5..e22601bd7 100644 --- a/mrbgems/mruby-bigint/core/bigint.c +++ b/mrbgems/mruby-bigint/core/bigint.c @@ -867,19 +867,79 @@ mpz_neg(mrb_state *mrb, mpz_t *x, mpz_t *y) } static void -mpz_and(mrb_state *mrb, mpz_t *z, mpz_t *x, mpz_t *y) /* not the most efficient way to do this */ +mpz_2comp(mrb_state *mrb, mpz_t *x) { - size_t sz = imin(x->sz, y->sz); + size_t i = x->sz; + uint32_t *ds = x->p; + uint64_t num; - mpz_realloc(mrb, z, sz); - for (size_t i=0; i < sz; i++) - z->p[i] = x->p[i] & y->p[i]; - if (x->sn < 0 && y->sn < 0) - z->sn = (-1); - else - z->sn = 1; - if (uzero(z)) - z->sn = 0; + if (!i) return; + while (i--) ds[i] = ~ds[i]; + i = 0; num = 1; + do { + num += ds[i]; + ds[i++] = LOW(num); + num >>= DIG_SIZE; + } while (i < x->sz); + if (num != 0) { + mpz_realloc(mrb, x, x->sz + 1); + ds = x->p; + ds[x->sz-1] = x->sn > 0 ? ~0 : 1; + } +} + +void +mpz_and(mrb_state *mrb, mpz_t *z, mpz_t *x, mpz_t *y) +{ + if (x->sn == 0 || y->sn == 0) { + zero(z); + return; + } + mrb_assert(x->sz > 0 || y->sz > 0); + + z->sn = x->sn < 0 && y->sn < 0 ? -1 : 1; + + mpz_t xx, yy; + if (x->sn < 0) { + mpz_init_set(mrb, &xx, x); + x = &xx; + mpz_2comp(mrb, x); + } + if (y->sn < 0) { + mpz_init_set(mrb, &yy, y); + y = &yy; + mpz_2comp(mrb, y); + } + + uint32_t *ds1, *ds2, *zds; + size_t l1, l2; + short sign; + + if (x->sz > y->sz) { + l1 = y->sz; + l2 = x->sz; + ds1 = y->p; + ds2 = x->p; + sign = y->sn; + } + else { + l1 = x->sz; + l2 = y->sz; + ds1 = x->p; + ds2 = y->p; + sign =x->sn; + } + mpz_realloc(mrb, z, l2); + zds = z->p; + + size_t i; + for (i=0; i0?0:ds2[i]; + } + if (z->sn < 0) mpz_2comp(mrb, z); } static void @@ -1509,7 +1569,7 @@ mrb_bint_and(mrb_state *mrb, mrb_value x, mrb_value y) if (mrb_integer_p(y)) { mrb_int z = mrb_integer(y); - if (z < DIG_BASE) { + if (z >= 0 && z < DIG_BASE) { z &= b1->mp.p[0]; return mrb_int_value(mrb, z); }