aboutsummaryrefslogtreecommitdiffstats
path: root/src/zgcd.c
diff options
context:
space:
mode:
authorMattias Andrée <maandree@kth.se>2016-03-02 10:23:14 +0100
committerMattias Andrée <maandree@kth.se>2016-03-02 10:23:14 +0100
commit5810e189b55eaf51912dace3f338e1c4f68c199c (patch)
tree80e4f03fb9a766329ec9abeb929225f3bf723404 /src/zgcd.c
parentAdd zsets and zstr (diff)
downloadlibzahl-5810e189b55eaf51912dace3f338e1c4f68c199c.tar.gz
libzahl-5810e189b55eaf51912dace3f338e1c4f68c199c.tar.bz2
libzahl-5810e189b55eaf51912dace3f338e1c4f68c199c.tar.xz
Add zgcd
Signed-off-by: Mattias Andrée <maandree@kth.se>
Diffstat (limited to 'src/zgcd.c')
-rw-r--r--src/zgcd.c60
1 files changed, 60 insertions, 0 deletions
diff --git a/src/zgcd.c b/src/zgcd.c
new file mode 100644
index 0000000..91216ac
--- /dev/null
+++ b/src/zgcd.c
@@ -0,0 +1,60 @@
+/* See LICENSE file for copyright and license details. */
+#include "internals"
+
+#define u libzahl_tmp_gcd_u
+#define v libzahl_tmp_gcd_v
+
+
+void
+zgcd(z_t a, z_t b, z_t c)
+{
+ /*
+ * Binary GCD algorithm.
+ */
+
+ size_t shifts = 0, i = 0;
+ zahl_char_t uv, bit;
+ int neg;
+
+ if (!zcmp(b, c)) {
+ if (a != b)
+ zset(a, b);
+ return;
+ }
+ if (zzero(b)) {
+ if (a != c)
+ zset(a, c);
+ return;
+ }
+ if (zzero(c)) {
+ if (a != b)
+ zset(a, b);
+ return;
+ }
+
+ zabs(u, b);
+ zabs(v, c);
+ neg = zsignum(b) < 0 && zsignum(c) < 0;
+
+ for (;; i++) {
+ uv = (i < u->used ? u->chars[i] : 0)
+ | (i < v->used ? v->chars[i] : 0);
+ for (bit = 1; bit; bit <<= 1, shifts++)
+ if (uv & bit)
+ goto loop_done;
+ }
+loop_done:
+ zrsh(u, u, shifts);
+ zrsh(v, v, shifts);
+
+ zrsh(u, u, zlsb(u));
+ do {
+ zrsh(v, v, zlsb(v));
+ if (zcmpmag(u, v) > 0) /* Both are non-negative. */
+ zswap(u, v);
+ zsub_unsigned(v, v, u);
+ } while (!zzero(v));
+
+ zlsh(a, u, shifts);
+ SET_SIGNUM(a, neg ? -1 : 1);
+}