commit 56680b5fa737dd7aa1cd7446cad62f5b1da2235c
parent 0e905d00aceaa79849c25d359d7b7a6ee79175d7
Author: Mattias Andrée <maandree@kth.se>
Date: Thu, 3 Mar 2016 12:23:39 +0100
Add zdivmod
Signed-off-by: Mattias Andrée <maandree@kth.se>
Diffstat:
2 files changed, 80 insertions(+), 1 deletion(-)
diff --git a/src/internals.h b/src/internals.h
@@ -23,7 +23,11 @@
X(libzahl_tmp_pow_b)\
X(libzahl_tmp_pow_c)\
X(libzahl_tmp_pow_d)\
- X(libzahl_tmp_modsqr)
+ X(libzahl_tmp_modsqr)\
+ X(libzahl_tmp_divmod_a)\
+ X(libzahl_tmp_divmod_b)\
+ X(libzahl_tmp_divmod_d)\
+ X(libzahl_tmp_divmod_e)
#define LIST_CONSTS\
X(libzahl_const_1e19, zsetu, 10000000000000000000ULL) /* The largest power of 10 < 2⁶⁴. */\
diff --git a/src/zdivmod.c b/src/zdivmod.c
@@ -0,0 +1,75 @@
+/* See LICENSE file for copyright and license details. */
+#include "internals"
+
+#define ta libzahl_tmp_divmod_a
+#define tb libzahl_tmp_divmod_b
+#define td libzahl_tmp_divmod_d
+#define te libzahl_tmp_divmod_e
+
+
+void
+zdivmod(z_t a, z_t b, z_t c, z_t d)
+{
+ size_t c_bits, d_bits, shift;
+ int sign, cmpmag;
+
+ sign = zsignum(c) * zsignum(d);
+
+ if (!sign) {
+ if (zzero(c)) {
+ if (zzero(d)) {
+ errno = EDOM; /* Indeterminate form: 0 divided by 0 */
+ FAILURE_JUMP();
+ } else {
+ SET_SIGNUM(a, 0);
+ SET_SIGNUM(b, 0);
+ }
+ } else {
+ errno = EDOM; /* Undefined form: Division by 0 */
+ FAILURE_JUMP();
+ }
+ return;
+ } else if ((cmpmag = zcmpmag(c, d)) <= 0) {
+ if (cmpmag == 0) {
+ zseti(a, sign);
+ SET_SIGNUM(b, 0);
+ return;
+ } else if (sign < 0) {
+ zsub_unsigned(b, d, c);
+ } else if (b != c) {
+ zset(b, c);
+ }
+ SET_SIGNUM(b, 1);
+ SET_SIGNUM(a, 0);
+ return;
+ }
+
+ c_bits = zbits(c);
+ d_bits = zbits(d);
+
+ shift = c_bits - d_bits;
+ zlsh(td, d, shift);
+ SET_SIGNUM(td, 1);
+ if (zcmpmag(td, c) > 0) {
+ zrsh(td, td, 1);
+ shift -= 1;
+ }
+
+ zsetu(te, 1);
+ zlsh(te, te, shift);
+ SET_SIGNUM(ta, 0);
+ zabs(tb, c);
+
+ while (!zzero(te)) {
+ if (zcmpmag(td, tb) <= 0) {
+ zsub(tb, tb, td);
+ zor(ta, ta, te);
+ }
+ zrsh(te, te, 1);
+ zrsh(td, td, 1);
+ }
+
+ zset(a, ta);
+ zset(b, tb);
+ SET_SIGNUM(a, sign);
+}