summaryrefslogtreecommitdiffstats
path: root/arith.c
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2022-07-27 23:02:02 -0700
committerKaz Kylheku <kaz@kylheku.com>2022-07-27 23:02:02 -0700
commitd0b49b2d85ba30207964ec8bfc99d4b7fb6e1328 (patch)
tree3e9686d8043b56e4a4aa318dc860c101d7966e90 /arith.c
parentd5e8aeab7a6cb91e3144c14fed6a5d19dfb4a6db (diff)
downloadtxr-d0b49b2d85ba30207964ec8bfc99d4b7fb6e1328.tar.gz
txr-d0b49b2d85ba30207964ec8bfc99d4b7fb6e1328.tar.bz2
txr-d0b49b2d85ba30207964ec8bfc99d4b7fb6e1328.zip
gcd: rewrite for better efficiency.
* arith.c (gcd): New implementation which uses arithmetic in the unsigned type ucnum if both operands are in that type's range. This uses Stein's algorithm a.k.a. binary GCD. The mpi_gcd function is used only if at least one argument is a bignum whose value doesn't fit into a ucnum. * tests/016/arith.tl: gcd test cases added.
Diffstat (limited to 'arith.c')
-rw-r--r--arith.c99
1 files changed, 75 insertions, 24 deletions
diff --git a/arith.c b/arith.c
index 1159de96..05edf13f 100644
--- a/arith.c
+++ b/arith.c
@@ -2603,33 +2603,84 @@ val square(val anum)
val gcd(val anum, val bnum)
{
- val n;
-
- if (!integerp(anum) || !integerp(bnum))
- goto inval;
-
- if (anum == zero)
- return bnum;
-
- if (bnum == zero)
- return anum;
+ val self = lit("gcd");
+ ucnum ua, ub;
- if (fixnump(anum))
- anum = bignum(c_n(anum));
-
- if (fixnump(bnum))
- bnum = bignum(c_n(bnum));
-
- n = make_bignum();
+ switch (TYPE_PAIR(type(anum), type(bnum))) {
+ case TYPE_PAIR(BGNUM, BGNUM):
+ if (mp_in_uintptr_range(mp(anum)) && mp_in_uintptr_range(mp(bnum))) {
+ ua = c_unum(anum, self);
+ ub = c_unum(bnum, self);
+ goto both_ucnum;
+ } else {
+ val n = make_bignum();
+ if (mp_gcd(mp(anum), mp(bnum), mp(n)) != MP_OKAY)
+ break;
+ return normalize(n);
+ }
+ case TYPE_PAIR(NUM, NUM):
+ if (anum == zero)
+ return bnum;
+ if (bnum == zero)
+ return anum;
+ ua = c_u(anum);
+ ub = c_u(bnum);
+ both_ucnum:
+ {
+ int k = 0;
+ while ((ua & 1) == 0 && (ub & 1) == 0) {
+ ua >>= 1;
+ ub >>= 1;
+ k++;
+ }
+ while ((ub & 1) == 0)
+ ub >>= 1;
+ while ((ua & 1) == 0)
+ ua >>= 1;
+ for (;;) {
+ if (ua > ub) {
+ ucnum tmp = ua;
+ ua = ub;
+ ub = tmp;
+ }
+ ub -= ua;
+ if (ub == 0)
+ break;
+ while ((ub & 1) == 0)
+ ub >>= 1;
+ }
+ return unum(ua << k);
+ }
+ case TYPE_PAIR(NUM, BGNUM):
+ {
+ val tmp = anum;
+ anum = bnum;
+ bnum = tmp;
+ }
+ /* fallthrough */
+ case TYPE_PAIR(BGNUM, NUM):
+ if (mp_in_uintptr_range(mp(anum))) {
+ ua = c_unum(anum, self);
+ ub = c_u(bnum);
+ goto both_ucnum;
+ } else {
+ mp_int bn;
+ val n = make_bignum();
- if (mp_gcd(mp(anum), mp(bnum), mp(n)) != MP_OKAY)
- goto bad;
+ mp_init(&bn);
+ mp_set_intptr(&bn, c_u(bnum));
+ if (mp_gcd(mp(anum), &bn, mp(n)) != MP_OKAY) {
+ mp_clear(&bn);
+ break;
+ }
+ mp_clear(&bn);
+ return normalize(n);
+ }
+ default:
+ uw_throwf(error_s, lit("gcd: non-integral operands ~s ~s"),
+ anum, bnum, nao);
+ }
- return normalize(n);
-inval:
- uw_throwf(error_s, lit("gcd: non-integral operands ~s ~s"),
- anum, bnum, nao);
-bad:
uw_throwf(error_s, lit("gcd: operation failed on ~s ~s"),
anum, bnum, nao);
}