commit 76d0af5599554d11f104d582cdac8fbaa8569fcc
Author:     Mattias Andrée <[email protected]>
AuthorDate: Fri Mar 4 23:50:00 2016 +0100
Commit:     Mattias Andrée <[email protected]>
CommitDate: Fri Mar 4 23:50:00 2016 +0100

    Clean up, add zerror and zperror, fix bugs and add more tests
    
    Signed-off-by: Mattias Andrée <[email protected]>

diff --git a/Makefile b/Makefile
index f3a6ad7..ee672f0 100644
--- a/Makefile
+++ b/Makefile
@@ -17,6 +17,7 @@ FUN =\
        zcmpu\
        zdiv\
        zdivmod\
+       zerror\
        zfree\
        zgcd\
        zinit\
@@ -32,6 +33,7 @@ FUN =\
        zneg\
        znot\
        zor\
+       zperror\
        zpow\
        zpowu\
        zptest\
diff --git a/man/zerror.3 b/man/zerror.3
new file mode 100644
index 0000000..837b3df
--- /dev/null
+++ b/man/zerror.3
@@ -0,0 +1,41 @@
+.TH ZERROR 3 libzahl
+.SH NAME
+zerror - Get the error that caused a jump to the jmp_buf passed to zsetup
+.SH SYNOPSIS
+.nf
+#include <zahl.h>
+
+enum zerror zerror(const char **\fIdesc\fP);
+.fi
+.SH DESCRIPTION
+.B zerror
+shall return the error that caused a libzahl
+function to perform a long jump to the point
+specified to
+.BR zsetup (3).
+If
+.I desc
+is not
+.BR 0 ,
+a textual description is set stored in
+.IR *desc .
+This string may be changed by a subsequent
+call to
+.BR strerror (3),
+.BR perror (3),
+.BR zperror (3),
+and similar functions.
+.P
+Currently available
+.B "enum zerror"
+values are:
+.P
+.TP
+.B ZERROR_ERRNO_SET
+The error is stored in
+.IR errno .
+(The error may not be stored in
+.I errno
+until this function is called.)
+.SH SEE ALSO
+.BR zperror (3)
diff --git a/man/zperror.3 b/man/zperror.3
new file mode 100644
index 0000000..fa01d90
--- /dev/null
+++ b/man/zperror.3
@@ -0,0 +1,21 @@
+.TH ZPERROR 3 libzahl
+.SH NAME
+zperror - Print a libzahl error message
+.SH SYNOPSIS
+.nf
+#include <zahl.h>
+
+void zperror(const char *\fIprefix\fP);
+.fi
+.SH DESCRIPTION
+.B zperror
+prints a libzahl error message to standard error.
+Unless
+.I prefix
+is
+.B 0
+or an empty string, the message is prefixed by
+.IR prefix ,
+a colon and a blank space.
+.SH SEE ALSO
+.BR zerror (3)
diff --git a/man/zsetup.3 b/man/zsetup.3
index df81556..14c04c5 100644
--- a/man/zsetup.3
+++ b/man/zsetup.3
@@ -56,4 +56,6 @@ code. Instead libzahl goes directly to the
 part of the program that handles the error.
 .SH SEE ALSO
 .BR zunsetup (3),
-.BR zinit (3)
+.BR zinit (3),
+.BR zerror (3),
+.BR zperror (3)
diff --git a/src/internals.h b/src/internals.h
index 5b1f9c3..9f276db 100644
--- a/src/internals.h
+++ b/src/internals.h
@@ -54,8 +54,9 @@ LIST_CONSTS
 extern z_t libzahl_tmp_divmod_ds[BITS_PER_CHAR];
 extern jmp_buf libzahl_jmp_buf;
 extern int libzahl_set_up;
+extern int libzahl_error;
 
-#define FAILURE_JUMP()               longjmp(libzahl_jmp_buf, 1)
+#define FAILURE(error)               (libzahl_error = (error), 
longjmp(libzahl_jmp_buf, 1))
 #define zmemcpy(d, s, n)             memcpy(d, s, (n) * sizeof(zahl_char_t))
 #define zmemmove(d, s, n)            memmove(d, s, (n) * sizeof(zahl_char_t))
 #define zmemset(a, v, n)             memset(a, v, (n) * sizeof(zahl_char_t))
@@ -72,7 +73,10 @@ static inline void
 zahl_realloc(z_t p, size_t n)
 {
        p->chars = realloc(p->chars, n * sizeof(zahl_char_t));
-       if (!p->chars)
-               FAILURE_JUMP();
+       if (!p->chars) {
+               if (!errno) /* sigh... */
+                       errno = ENOMEM;
+               FAILURE(errno);
+       }
        p->alloced = n;
 }
diff --git a/src/zadd.c b/src/zadd.c
index 6ab3ae8..73e5b42 100644
--- a/src/zadd.c
+++ b/src/zadd.c
@@ -49,8 +49,8 @@ zadd_unsigned(z_t a, z_t b, z_t c)
                a->chars[i] += addend[i] + carry[i & 1];
        }
 
-       while (carry[~i & 1]) {
-               carry[i & 1] = a->chars[i] == ZAHL_CHAR_MAX;
+       while (carry[i & 1]) {
+               carry[~i & 1] = a->chars[i] == ZAHL_CHAR_MAX;
                a->chars[i++] += 1;
        }
 
diff --git a/src/zdivmod.c b/src/zdivmod.c
index 9340810..e145c27 100644
--- a/src/zdivmod.c
+++ b/src/zdivmod.c
@@ -18,15 +18,13 @@ zdivmod(z_t a, z_t b, z_t c, z_t d)
        if (!sign) {
                if (zzero(c)) {
                        if (zzero(d)) {
-                               errno = EDOM; /* Indeterminate form: 0 divided 
by 0 */
-                               FAILURE_JUMP();
+                               FAILURE(EDOM); /* Indeterminate form: 0 divided 
by 0 */
                        } else {
                                SET_SIGNUM(a, 0);
                                SET_SIGNUM(b, 0);
                        }
                } else {
-                       errno = EDOM; /* Undefined form: Division by 0 */
-                       FAILURE_JUMP();
+                       FAILURE(EDOM); /* Undefined form: Division by 0 */
                }
                return;
        } else if ((cmpmag = zcmpmag(c, d)) <= 0) {
@@ -64,7 +62,7 @@ zdivmod(z_t a, z_t b, z_t c, z_t d)
                                zsub(tb, tb, td);
                                zbset(ta, ta, bit, 1);
                        }
-                       if (!bit--)
+                       if (!bit-- || zzero(tb))
                                goto done;
                        zrsh(td, td, 1);
                }
@@ -78,11 +76,10 @@ zdivmod(z_t a, z_t b, z_t c, z_t d)
                                        zsub(tb, tb, tds[i]);
                                        zbset(ta, ta, bit, 1);
                                }
-                               if (!bit--)
+                               if (!bit-- || zzero(tb))
                                        goto done;
-                               zrsh(tds[i], tds[i], 1);
                        }
-                       for (i = MIN(bit, BITS_PER_CHAR); i--;)
+                       for (i = MIN(bit, BITS_PER_CHAR - 1) + 1; i--;)
                                zrsh(tds[i], tds[i], BITS_PER_CHAR);
                }
        }
diff --git a/src/zerror.c b/src/zerror.c
new file mode 100644
index 0000000..c59b092
--- /dev/null
+++ b/src/zerror.c
@@ -0,0 +1,19 @@
+/* See LICENSE file for copyright and license details. */
+#include "internals.h"
+
+
+enum zerror
+zerror(const char **desc)
+{
+       if (libzahl_error >= 0) {
+               if (desc)
+                       *desc = strerror(libzahl_error);
+               errno = libzahl_error;
+               return ZERROR_ERRNO_SET;
+       } else {
+               /* Current, we should not be able to get here. */
+               if (desc)
+                       abort();
+               return -libzahl_error;
+       }
+}
diff --git a/src/zload.c b/src/zload.c
index ff54afb..a77ae57 100644
--- a/src/zload.c
+++ b/src/zload.c
@@ -6,15 +6,14 @@ size_t
 zload(z_t a, const void *buffer)
 {
        const char *buf = buffer;
-       size_t alloced;
        a->sign = *((const int *)buf),    buf += sizeof(int);
        a->used = *((const size_t *)buf), buf += sizeof(size_t);
-       alloced = *((const size_t *)buf), buf += sizeof(size_t);
-       if (alloced)
-               ENSURE_SIZE(a, alloced);
+       a->alloced = 0;
+       if (a->sign)
+               ENSURE_SIZE(a, a->used);
        else
                a->chars = 0;
        if (!zzero(a))
                zmemcpy(a->chars, buf, a->used);
-       return sizeof(z_t) - sizeof(a->chars) + (zzero(a) ? 0 : a->used * 
sizeof(*(a->chars)));
+       return sizeof(int) + sizeof(size_t) + (zzero(a) ? 0 : a->used * 
sizeof(zahl_char_t));
 }
diff --git a/src/zlsh.c b/src/zlsh.c
index b631c1a..2a76fbc 100644
--- a/src/zlsh.c
+++ b/src/zlsh.c
@@ -19,25 +19,27 @@ zlsh(z_t a, z_t b, size_t bits)
 
        chars = FLOOR_BITS_TO_CHARS(bits);
        bits = BITS_IN_LAST_CHAR(bits);
-       cbits = BITS_PER_CHAR - 1 - bits;
+       cbits = BITS_PER_CHAR - bits;
 
-       a->used = b->used + chars;
-       ENSURE_SIZE(a, a->used);
+       ENSURE_SIZE(a, b->used + chars);
        if (a == b)
-               zmemmove(a->chars + chars, b->chars, a->used);
+               zmemmove(a->chars + chars, b->chars, b->used);
        else
-               zmemcpy(a->chars + chars, b->chars, a->used);
+               zmemcpy(a->chars + chars, b->chars, b->used);
        zmemset(a->chars, 0, chars);
+       a->used = b->used + chars;
 
-       for (i = chars; i < a->used; i++) {
-               carry[~i & 1] = a->chars[i] >> cbits;
-               a->chars[i] <<= bits;
-               a->chars[i] |= carry[i & 1];
-       }
-       if (carry[i & 1]) {
-               ENSURE_SIZE(a, a->alloced << 1);
-               a->chars[i] = carry[i & 1];
-               a->used++;
+       if (bits) { /* This if statement is very important in C. */
+               for (i = chars; i < a->used; i++) {
+                       carry[~i & 1] = a->chars[i] >> cbits;
+                       a->chars[i] <<= bits;
+                       a->chars[i] |= carry[i & 1];
+               }
+               if (carry[i & 1]) {
+                       ENSURE_SIZE(a, a->used + 1);
+                       a->chars[i] = carry[i & 1];
+                       a->used++;
+               }
        }
 
        SET_SIGNUM(a, zsignum(b));
diff --git a/src/zmodpow.c b/src/zmodpow.c
index 884e9ff..6cb50d2 100644
--- a/src/zmodpow.c
+++ b/src/zmodpow.c
@@ -14,21 +14,19 @@ zmodpow(z_t a, z_t b, z_t c, z_t d)
 
        if (zsignum(c) <= 0) {
                if (zzero(c)) {
-                       if (zzero(b)) {
-                               errno = EDOM; /* Indeterminate form: 0:th power 
of 0 */
-                               FAILURE_JUMP();
-                       }
+                       if (zzero(b))
+                               FAILURE(EDOM); /* Indeterminate form: 0:th 
power of 0 */
+                       else if (zzero(d))
+                               FAILURE(EDOM); /* Undefined form: Division by 0 
*/
                        zsetu(a, 1);
                } else if (zzero(b) || zzero(d)) {
-                       errno = EDOM; /* Undefined form: Division by 0 */
-                       FAILURE_JUMP();
+                       FAILURE(EDOM); /* Undefined form: Division by 0 */
                } else {
                        SET_SIGNUM(a, 0);
                }
                return;
        } else if (zzero(d)) {
-               errno = EDOM; /* Undefined form: Division by 0 */
-               FAILURE_JUMP();
+               FAILURE(EDOM); /* Undefined form: Division by 0 */
        } else if (zzero(b)) {
                SET_SIGNUM(a, 0);
                return;
diff --git a/src/zmodpowu.c b/src/zmodpowu.c
index 8a94379..7fb2c3d 100644
--- a/src/zmodpowu.c
+++ b/src/zmodpowu.c
@@ -9,19 +9,15 @@ void
 zmodpowu(z_t a, z_t b, unsigned long long int c, z_t d)
 {
        if (!c) {
-               if (zzero(b)) {
-                       errno = EDOM; /* Indeterminate form: 0:th power of 0 */
-                       FAILURE_JUMP();
-               } else if (zzero(d)) {
-                       errno = EDOM; /* Undefined form: Division by 0 */
-                       FAILURE_JUMP();
-               } else {
+               if (zzero(b))
+                       FAILURE(EDOM); /* Indeterminate form: 0:th power of 0 */
+               else if (zzero(d))
+                       FAILURE(EDOM); /* Undefined form: Division by 0 */
+               else
                        zsetu(a, 1);
-               }
                return;
        } else if (zzero(d)) {
-               errno = EDOM; /* Undefined form: Division by 0 */
-               FAILURE_JUMP();
+               FAILURE(EDOM); /* Undefined form: Division by 0 */
        } else if (zzero(b)) {
                SET_SIGNUM(a, 0);
                return;
diff --git a/src/zmul.c b/src/zmul.c
index f237bc9..53e19f5 100644
--- a/src/zmul.c
+++ b/src/zmul.c
@@ -13,7 +13,10 @@ zmul(z_t a, z_t b, z_t c)
        z_t z0, z1, z2, b_high, b_low, c_high, c_low;
        int b_sign, c_sign;
 
-       if (zzero(b) || zzero(c)) {
+       b_sign = zsignum(b);
+       c_sign = zsignum(c);
+
+       if (!b_sign || !c_sign) {
                SET_SIGNUM(a, 0);
                return;
        }
@@ -21,11 +24,12 @@ zmul(z_t a, z_t b, z_t c)
        m = zbits(b);
        m2 = b == c ? m : zbits(c);
 
-       b_sign = zsignum(b);
-       c_sign = zsignum(c);
-
-       if (m <= BITS_PER_CHAR / 2 && m2 <= BITS_PER_CHAR / 2) {
-               zsetu(a, b->chars[0] * c->chars[0]);
+       if (m + m2 <= BITS_PER_CHAR) {
+               /* zsetu(a, b->chars[0] * c->chars[0]); { */
+               ENSURE_SIZE(a, 1);
+               a->used = 1;
+               a->chars[0] = b->chars[0] * c->chars[0];
+               /* } */
                SET_SIGNUM(a, b_sign * c_sign);
                return;
        }
@@ -47,7 +51,7 @@ zmul(z_t a, z_t b, z_t c)
        zsplit(b_high, b_low, b, m2);
        zsplit(c_high, c_low, c, m2);
 
-#if 0
+#if 1
        zmul(z0, b_low, c_low);
        zmul(z2, b_high, c_high);
        zadd(b_low, b_low, b_high);
@@ -57,9 +61,9 @@ zmul(z_t a, z_t b, z_t c)
        zsub(z1, z1, z0);
        zsub(z1, z1, z2);
 
-       zlsh(z2, z2, m2);
-       m2 <<= 1;
        zlsh(z1, z1, m2);
+       m2 <<= 1;
+       zlsh(z2, z2, m2);
 
        zadd(a, z2, z1);
        zadd(a, a, z0);
diff --git a/src/zor.c b/src/zor.c
index f594828..240024e 100644
--- a/src/zor.c
+++ b/src/zor.c
@@ -20,6 +20,7 @@ zor(z_t a, z_t b, z_t c)
 
        m = MAX(b->used, c->used);
        n = b->used + c->used - m;
+       a->used = m;
 
        ENSURE_SIZE(a, m);
 
diff --git a/src/zperror.c b/src/zperror.c
new file mode 100644
index 0000000..37bd5c7
--- /dev/null
+++ b/src/zperror.c
@@ -0,0 +1,17 @@
+/* See LICENSE file for copyright and license details. */
+#include "internals.h"
+
+#include <stdio.h>
+
+
+void
+zperror(const char *prefix)
+{
+       if (libzahl_error >= 0) {
+               errno = libzahl_error;
+               perror(prefix);
+       } else {
+               /* Current, we should not be able to get here. */
+               abort();
+       }
+}
diff --git a/src/zpow.c b/src/zpow.c
index c064f60..7388bde 100644
--- a/src/zpow.c
+++ b/src/zpow.c
@@ -13,14 +13,11 @@ zpow(z_t a, z_t b, z_t c)
 
        if (zsignum(c) <= 0) {
                if (zzero(c)) {
-                       if (zzero(b)) {
-                               errno = EDOM; /* Indeterminate form: 0:th power 
of 0 */
-                               FAILURE_JUMP();
-                       }
+                       if (zzero(b))
+                               FAILURE(EDOM); /* Indeterminate form: 0:th 
power of 0 */
                        zsetu(a, 1);
                } else if (zzero(b)) {
-                       errno = EDOM; /* Undefined form: Division by 0 */
-                       FAILURE_JUMP();
+                       FAILURE(EDOM); /* Undefined form: Division by 0 */
                } else {
                        SET_SIGNUM(a, 0);
                }
diff --git a/src/zpowu.c b/src/zpowu.c
index d83a7ac..f8e5897 100644
--- a/src/zpowu.c
+++ b/src/zpowu.c
@@ -8,10 +8,8 @@ void
 zpowu(z_t a, z_t b, unsigned long long int c)
 {
        if (!c) {
-               if (zzero(b)) {
-                       errno = EDOM; /* Indeterminate form: 0:th power of 0 */
-                       FAILURE_JUMP();
-               }
+               if (zzero(b))
+                       FAILURE(EDOM); /* Indeterminate form: 0:th power of 0 */
                zsetu(a, 1);
                return;
        } else if (zzero(b)) {
diff --git a/src/zrand.c b/src/zrand.c
index cb0b375..30dc61d 100644
--- a/src/zrand.c
+++ b/src/zrand.c
@@ -27,7 +27,7 @@ zrand_get_random_bits(z_t r, size_t bits, int fd)
        for (n = chars * sizeof(zahl_char_t); n;) {
                read_just = read(fd, buf + read_total, n);
                if (read_just < 0)
-                       FAILURE_JUMP();
+                       FAILURE(errno);
                read_total += (size_t)read_just;
                n -= (size_t)read_just;
        }
@@ -72,14 +72,12 @@ zrand(z_t r, enum zranddev dev, enum zranddist dist, z_t n)
 
        fd = open(pathname, O_RDONLY);
        if (fd < 0)
-               FAILURE_JUMP();
+               FAILURE(errno);
 
        switch (dist) {
        case QUASIUNIFORM:
-               if (zsignum(n) < 0) {
-                       errno = EDOM; /* n must be non-negative. */
-                       FAILURE_JUMP();
-               }
+               if (zsignum(n) < 0)
+                       FAILURE(EDOM); /* n must be non-negative. */
                bits = zbits(n);
                zrand_get_random_bits(r, bits, fd);
                zadd(r, r, libzahl_const_1);
@@ -88,10 +86,8 @@ zrand(z_t r, enum zranddev dev, enum zranddist dist, z_t n)
                break;
 
        case UNIFORM:
-               if (zsignum(n) < 0) {
-                       errno = EDOM; /* n must be non-negative. */
-                       FAILURE_JUMP();
-               }
+               if (zsignum(n) < 0)
+                       FAILURE(EDOM); /* n must be non-negative. */
                bits = zbits(n);
                do
                        zrand_get_random_bits(r, bits, fd);
diff --git a/src/zrsh.c b/src/zrsh.c
index c5a1a05..a7b886b 100644
--- a/src/zrsh.c
+++ b/src/zrsh.c
@@ -20,7 +20,7 @@ zrsh(z_t a, z_t b, size_t bits)
        }
 
        bits = BITS_IN_LAST_CHAR(bits);
-       cbits = BITS_PER_CHAR - 1 - bits;
+       cbits = BITS_PER_CHAR - bits;
 
        if (chars && a == b) {
                a->used -= chars;
@@ -31,10 +31,14 @@ zrsh(z_t a, z_t b, size_t bits)
                zmemcpy(a->chars, b->chars + chars, a->used);
        }
 
-       a->chars[0] >>= bits;
-       for (i = 1; i < a->used; i++) {
-               a->chars[i - 1] |= a->chars[i] >> cbits;
-               a->chars[i] >>= bits;
+       if (bits) { /* This if statement is very important in C. */
+               a->chars[0] >>= bits;
+               for (i = 1; i < a->used; i++) {
+                       a->chars[i - 1] |= a->chars[i] << cbits;
+                       a->chars[i] >>= bits;
+               }
+               while (!a->chars[a->used - 1])
+                       a->used--;
        }
 
        SET_SIGNUM(a, zsignum(b));
diff --git a/src/zsave.c b/src/zsave.c
index bb788d3..9a171d6 100644
--- a/src/zsave.c
+++ b/src/zsave.c
@@ -9,9 +9,8 @@ zsave(z_t a, void *buffer)
                char *buf = buffer;
                *((int *)buf)    = a->sign,    buf += sizeof(int);
                *((size_t *)buf) = a->used,    buf += sizeof(size_t);
-               *((size_t *)buf) = a->alloced, buf += sizeof(size_t);
                if (!zzero(a))
                        zmemcpy(buf, a->chars, a->used);
        }
-       return sizeof(z_t) - sizeof(a->chars) + (zzero(a) ? 0 : a->used * 
sizeof(*(a->chars)));
+       return sizeof(int) + sizeof(size_t) + (zzero(a) ? 0 : a->used * 
sizeof(zahl_char_t));
 }
diff --git a/src/zset.c b/src/zset.c
index b67fe4a..f7fe52e 100644
--- a/src/zset.c
+++ b/src/zset.c
@@ -8,7 +8,7 @@ zset(z_t a, z_t b)
        if (zzero(b)) {
                SET_SIGNUM(a, 0);
        } else {
-               ENSURE_SIZE(a, b->alloced);
+               ENSURE_SIZE(a, b->used);
                a->sign = b->sign;
                a->used = b->used;
                zmemcpy(a->chars, b->chars, b->used);
diff --git a/src/zsets.c b/src/zsets.c
index 24cdd48..2b91864 100644
--- a/src/zsets.c
+++ b/src/zsets.c
@@ -26,21 +26,45 @@ zsets(z_t a, const char *str)
 
        SET_SIGNUM(a, 0);
 
+#if 1
+       zset(libzahl_tmp_str_num, libzahl_const_1e19);
        switch ((str_end - str) % 19) {
                while (*str) {
+                       zmul(a, a, libzahl_const_1e19);
+                       temp = 0;
 #define X(n)\
                case n:\
                        temp *= 10, temp += *str++ & 15;
-               X(0) X(18) X(17) X(16) X(15) X(14) X(13) X(12) X(11)
-               X(10) X(9) X(8) X(7) X(6) X(5) X(4) X(3) X(2) X(1)
+                       X(0) X(18) X(17) X(16) X(15) X(14) X(13) X(12) X(11)
+                       X(10) X(9) X(8) X(7) X(6) X(5) X(4) X(3) X(2) X(1)
 #undef X
-
-                       zmul(a, a, libzahl_const_1e19);
-                       zsetu(libzahl_tmp_str_num, temp);
+                       if (!temp)
+                               continue;
+                       libzahl_tmp_str_num->chars[0] = (zahl_char_t)temp;
+                       temp >>= BITS_PER_CHAR;
+                       libzahl_tmp_str_num->chars[1] = (zahl_char_t)temp;
+                       libzahl_tmp_str_num->used = 1 + !!temp;
                        zadd(a, a, libzahl_tmp_str_num);
+               }
+       }
+#else
+       zset(libzahl_tmp_str_num, libzahl_const_1);
+       switch ((str_end - str) % 9) {
+               while (*str) {
+                       zmul(a, a, libzahl_const_1e9);
                        temp = 0;
+#define X(n)\
+               case n:\
+                       temp *= 10, temp += *str++ & 15;
+                       X(0) X(8) X(7) X(6) X(5) X(4) X(3) X(2) X(1)
+#undef X
+                       if (!temp)
+                               continue;
+                       libzahl_tmp_str_num->chars[0] = temp;
+                       zadd(a, a, libzahl_tmp_str_num);
                }
        }
+#endif
 
        if (neg)
                SET_SIGNUM(a, -zsignum(a));
diff --git a/src/zsetup.c b/src/zsetup.c
index 8d1cc19..8b5221d 100644
--- a/src/zsetup.c
+++ b/src/zsetup.c
@@ -11,6 +11,7 @@ LIST_CONSTS
 z_t libzahl_tmp_divmod_ds[BITS_PER_CHAR];
 jmp_buf libzahl_jmp_buf;
 int libzahl_set_up = 0;
+int libzahl_error;
 
 
 void
diff --git a/src/zsqr.c b/src/zsqr.c
index a9cebd0..39f5591 100644
--- a/src/zsqr.c
+++ b/src/zsqr.c
@@ -21,7 +21,11 @@ zsqr(z_t a, z_t b)
        m2 = zbits(b);
 
        if (m2 <= BITS_PER_CHAR / 2) {
-               zsetu(a, b->chars[0] * b->chars[0]);
+               /* zsetu(a, b->chars[0] * b->chars[0]); { */
+               ENSURE_SIZE(a, 1);
+               a->used = 1;
+               a->chars[0] = b->chars[0] * b->chars[0];
+               /* } */
                SET_SIGNUM(a, 1);
                return;
        }
@@ -38,14 +42,14 @@ zsqr(z_t a, z_t b)
 
        zsplit(high, low, b, m2);
 
-#if 0
+#if 1
        zsqr(z0, low);
        zsqr(z2, high);
        zmul(z1, low, high);
 
+       zlsh(z1, z1, m2 + 1);
+       m2 <<= 1;
        zlsh(z2, z2, m2);
-       m2 = (m2 << 1) | 1;
-       zlsh(z1, z1, m2);
 
        zadd(a, z2, z1);
        zadd(a, a, z0);
diff --git a/src/zstr.c b/src/zstr.c
index 1b89118..f7273ce 100644
--- a/src/zstr.c
+++ b/src/zstr.c
@@ -15,15 +15,16 @@
 char *
 zstr(z_t a, char *b)
 {
-       size_t n;
-       char overridden;
+       char buf[9 + 1];
+       size_t n, len;
+       char overridden = 0;
        int neg;
 
        if (zzero(a)) {
                if (!b) {
                        b = malloc(2);
                        if (!b)
-                               FAILURE_JUMP();
+                               FAILURE(errno);
                }
                b[0] = '0';
                b[1] = 0;
@@ -31,30 +32,32 @@ zstr(z_t a, char *b)
        }
 
        n = zstr_length(a, 10);
+
        if (!b) {
                b = malloc(n + 1);
                if (!b)
-                       FAILURE_JUMP();
+                       FAILURE(errno);
        }
 
        neg = zsignum(a) < 0;
        zabs(num, a);
-       n -= (size_t)neg;
-       n = n > 9 ? (n - 9) : 0;
        b[0] = '-';
        b += neg;
-       overridden = 0;
+       n -= neg;
+       n = n > 9 ? (n - 9) : 0;
 
        for (;;) {
                zdivmod(num, rem, num, libzahl_const_1e9);
                if (!zzero(num)) {
-                       sprintf(b + n, "%09lu", (unsigned long)(rem->chars[0]));
+                       sprintf(b + n, "%09lu", zzero(rem) ? 0UL : (unsigned 
long)(rem->chars[0]));
                        b[n + 9] = overridden;
-                       overridden = b[n + (9 - 1)];
+                       overridden = b[n];
                        n = n > 9 ? (n - 9) : 0;
                } else {
-                       n += (size_t)sprintf(b + n, "%lu", (unsigned 
long)(rem->chars[0]));
-                       b[n] = overridden;
+                       len = (size_t)sprintf(buf, "%lu", (unsigned 
long)(rem->chars[0]));
+                       if (overridden)
+                               buf[len] = b[n + len];
+                       memcpy(b + n, buf, len + 1);
                        break;
                }
        }
diff --git a/src/zsub.c b/src/zsub.c
index 1e2fe79..8616542 100644
--- a/src/zsub.c
+++ b/src/zsub.c
@@ -30,7 +30,7 @@ zsub_unsigned(z_t a, z_t b, z_t c)
                a->chars[i] -= carry[i & 1];
        }
 
-       if (carry[i] & 1) {
+       if (carry[i & 1]) {
                while (!a->chars[i])
                        a->chars[i++] = ZAHL_CHAR_MAX;
                a->chars[i] -= 1;
diff --git a/src/ztrunc.c b/src/ztrunc.c
index f86fb8e..c634707 100644
--- a/src/ztrunc.c
+++ b/src/ztrunc.c
@@ -19,7 +19,7 @@ ztrunc(z_t a, z_t b, size_t bits)
        if (a->used < chars)
                bits = 0;
        if (a != b) {
-               ENSURE_SIZE(a, b->alloced);
+               ENSURE_SIZE(a, a->used);
                zmemcpy(a->chars, b->chars, a->used);
        }
        bits = BITS_IN_LAST_CHAR(bits);
diff --git a/test.c b/test.c
index d82c7bc..69d5170 100644
--- a/test.c
+++ b/test.c
@@ -11,7 +11,8 @@
                        fprintf(stderr,\
                                "Failure at line %i: %s, expected %s, but got 
%i.\n",\
                                __LINE__, #expr, #expected, got);\
-                       return 1;\
+                       ret = 1;\
+                       goto done;\
                }\
        } while (0)
 
@@ -22,7 +23,8 @@
                        fprintf(stderr,\
                                "Failure at line %i: %s, expected %zu, but got 
%zu.\n",\
                                __LINE__, #expr, (size_t)(expected), got);\
-                       return 1;\
+                       ret = 1;\
+                       goto done;\
                }\
        } while (0)
 
@@ -33,7 +35,24 @@
                        fprintf(stderr,\
                                "Failure at line %i: %s, expected %s, but got 
%s.\n",\
                                __LINE__, #expr, expected, got);\
-                       return 1;\
+                       ret = 1;\
+                       goto done;\
+               }\
+       } while (0)
+
+#define assert_nr(expr)\
+       do {\
+               if (setjmp(env2)) {\
+                       ret = 0;\
+                       zsetup(env);\
+               } else {\
+                       zsetup(env2);\
+                       expr;\
+                       fprintf(stderr,\
+                               "Failure at line %i: %s, should not have 
returned.\n",\
+                               __LINE__, #expr);\
+                       ret = 1;\
+                       goto done;\
                }\
        } while (0)
 
@@ -43,14 +62,14 @@ main(void)
        /* static because otherwise it would have to be volatile yeilding a lot 
of stupid
         * warnings. auto variables are not guaranteed to be readable after a 
long jump. */
        static z_t a, b, c, d, _0, _1, _2, _3;
-       
-       jmp_buf env;
-       char buf[1000];
-       int ret = 0;
-       size_t n;
+       static char buf[1000];
+       static int ret = 0;
+       static jmp_buf env, env2;
+       static size_t n;
 
        if (setjmp(env)) {
-               ret = 1;
+               zperror(0);
+               ret = 2;
                goto done;
        }
 
@@ -183,6 +202,9 @@ main(void)
        zneg(b, _3);
        assert(zcmp(a, b), == 0);
 
+       zunsetup();
+       zsetup(env);
+
        zsub(a, _2, _1);
        assert(zcmpmag(_2, _1), > 0);
        assert(zcmpmag(_2, _0), > 0);
@@ -471,6 +493,11 @@ main(void)
        znot(a, a);
        assert(zcmp(a, _0), == 0);
 
+       zsetu(a, 0x1234);
+       zsetu(c, 0x234);
+       ztrunc(a, a, 12);
+       assert(zcmp(a, c), == 0);
+
        zsetu(a, 0xEEFF);
        zsetu(c, 0xEE);
        zsetu(d, 0xFF);
@@ -717,6 +744,254 @@ main(void)
        assert((zseti(a, 11), zptest(0, a, 100)), != NONPRIME);
        assert((zseti(a, 101), zptest(0, a, 100)), != NONPRIME);
 
+       assert_nr(zdivmod(a, b, _0, _0));
+       assert_nr(zdivmod(a, b, _1, _0));
+       zdivmod(a, b, _0, _1);
+       zdivmod(a, b, _1, _1);
+       assert_nr(zdiv(a, _0, _0));
+       assert_nr(zdiv(a, _1, _0));
+       zdiv(a, _0, _1);
+       zdiv(a, _1, _1);
+       assert_nr(zmod(a, _0, _0));
+       assert_nr(zmod(a, _1, _0));
+       zmod(a, _0, _1);
+       zmod(a, _1, _1);
+       assert_nr(zpow(a, _0, _0));
+       assert_nr((zneg(_1, _1), zpow(a, _0, _1))); zneg(_1, _1);
+       zpow(a, _0, _1);
+       zpow(a, _1, _0);
+       zneg(_1, _1), zpow(a, _1, _0), zneg(_1, _1);
+       assert_nr(zmodmul(a, _1, _1, _0));
+       assert_nr(zmodpow(a, _0, _0, _1));
+       assert_nr((zneg(_1, _1), zmodpow(a, _0, _1, _1))); zneg(_1, _1);
+       zmodpow(a, _0, _1, _1);
+       zmodpow(a, _1, _0, _1);
+       zneg(_1, _1), zmodpow(a, _1, _0, _1), zneg(_1, _1);
+       assert_nr(zmodpow(a, _0, _0, _0));
+       assert_nr((zneg(_1, _1), zmodpow(a, _0, _1, _0))); zneg(_1, _1);
+       assert_nr(zmodpow(a, _0, _1, _0));
+       assert_nr(zmodpow(a, _1, _0, _0));
+       assert_nr((zneg(_1, _1), zmodpow(a, _1, _0, _0))); zneg(_1, _1);
+       assert_nr(zpowu(a, _0, 0));
+       zpowu(a, _0, 1);
+       zpowu(a, _1, 0);
+       zneg(_1, _1), zpowu(a, _1, 0), zneg(_1, _1);
+       assert_nr(zmodpowu(a, _0, 0, _1));
+       zmodpowu(a, _0, 1, _1);
+       zmodpowu(a, _1, 0, _1);
+       zneg(_1, _1), zmodpowu(a, _1, 0, _1), zneg(_1, _1);
+       assert_nr(zmodpowu(a, _0, 0, _0));
+       assert_nr((zneg(_1, _1), zmodpowu(a, _0, 1, _0))); zneg(_1, _1);
+       assert_nr(zmodpowu(a, _0, 1, _0));
+       assert_nr(zmodpowu(a, _1, 0, _0));
+       assert_nr((zneg(_1, _1), zmodpowu(a, _1, 0, _0))); zneg(_1, _1);
+
+       zsetu(a, 1LL);
+       assert_s(zstr(a, buf), "1");
+       zsetu(a, 10LL);
+       assert_s(zstr(a, buf), "10");
+       zsetu(a, 100LL);
+       assert_s(zstr(a, buf), "100");
+       zsetu(a, 1000LL);
+       assert_s(zstr(a, buf), "1000");
+       zsetu(a, 10000LL);
+       assert_s(zstr(a, buf), "10000");
+       zsetu(a, 100000LL);
+       assert_s(zstr(a, buf), "100000");
+       zsetu(a, 1000000LL);
+       assert_s(zstr(a, buf), "1000000");
+       zsetu(a, 10000000LL);
+       assert_s(zstr(a, buf), "10000000");
+       zsetu(a, 100000000LL);
+       assert_s(zstr(a, buf), "100000000");
+       zsetu(a, 999999999LL);
+       assert_s(zstr(a, buf), "999999999");
+       zsetu(a, 1000000000LL);
+       assert_s(zstr(a, buf), "1000000000");
+       zsetu(a, 1000000001LL);
+       assert_s(zstr(a, buf), "1000000001");
+       zsetu(a, 2000000000LL);
+       assert_s(zstr(a, buf), "2000000000");
+       zsetu(a, 2050000000LL);
+       assert_s(zstr(a, buf), "2050000000");
+       zsetu(a, 2100000000LL);
+       assert_s(zstr(a, buf), "2100000000");
+       zsetu(a, 2140000000LL);
+       assert_s(zstr(a, buf), "2140000000");
+       zsetu(a, 2147000000LL);
+       assert_s(zstr(a, buf), "2147000000");
+       zsetu(a, 2147483000LL);
+       assert_s(zstr(a, buf), "2147483000");
+       zsetu(a, 2147483640LL);
+       assert_s(zstr(a, buf), "2147483640");
+       zsetu(a, 2147483646LL);
+       assert_s(zstr(a, buf), "2147483646");
+
+       zseti(a, 2147483647LL);
+       assert_s(zstr(a, buf), "2147483647");
+       zseti(a, -2147483647LL);
+       assert_s(zstr(a, buf), "-2147483647");
+       zseti(a, -2147483647LL - 1LL);
+       assert_s(zstr(a, buf), "-2147483648");
+
+       zsetu(a, 2147483647ULL);
+       assert_s(zstr(a, buf), "2147483647");
+       zsetu(a, 2147483648ULL);
+       assert_s(zstr(a, buf), "2147483648");
+       zsetu(a, 2147483649ULL);
+       assert_s(zstr(a, buf), "2147483649");
+
+       zsetu(a, 3000000000ULL);
+       assert_s(zstr(a, buf), "3000000000");
+       zsetu(a, 3100000000ULL);
+       assert_s(zstr(a, buf), "3100000000");
+       zsetu(a, 3200000000ULL);
+       assert_s(zstr(a, buf), "3200000000");
+       zsetu(a, 3300000000ULL);
+       assert_s(zstr(a, buf), "3300000000");
+       zsetu(a, 3400000000ULL);
+       assert_s(zstr(a, buf), "3400000000");
+       zsetu(a, 3500000000ULL);
+       assert_s(zstr(a, buf), "3500000000");
+       zsetu(a, 3600000000ULL);
+       assert_s(zstr(a, buf), "3600000000");
+       zsetu(a, 3700000000ULL);
+       assert_s(zstr(a, buf), "3700000000");
+       zsetu(a, 3800000000ULL);
+       assert_s(zstr(a, buf), "3800000000");
+       zsetu(a, 3900000000ULL);
+       assert_s(zstr(a, buf), "3900000000");
+       zsetu(a, 3999999999ULL);
+       assert_s(zstr(a, buf), "3999999999");
+       zsetu(a, 4000000000ULL);
+       assert_s(zstr(a, buf), "4000000000");
+       zsetu(a, 4000000001ULL);
+       assert_zu(zstr_length(a, 10), 10);
+       assert_s(zstr(a, buf), "4000000001");
+
+       zsetu(a, 4000000000ULL);
+       zsetu(b, 4000000000ULL);
+       zadd(c, a, a);
+       zsets(d, "8000000000");
+       assert(zcmp(c, d), == 0);
+       zadd(c, a, b);
+       assert(zcmp(c, d), == 0);
+       zadd(c, c, a);
+       zsets(d, "12000000000");
+       assert(zcmp(c, d), == 0);
+       zsub(c, c, a);
+       zsets(d, "8000000000");
+       assert(zcmp(c, d), == 0);
+       zsub(c, c, a);
+       zsets(d, "4000000000");
+       assert(zcmp(c, d), == 0);
+       zsets(d, "8000000000");
+       zrsh(d, d, 1);
+       assert(zcmp(c, d), == 0);
+       zsets(a, "6234216714");
+       zsets(b, "9424614147");
+       zsets(d, "830476546");
+       zand(c, a, b);
+       assert(zcmp(c, d), == 0);
+       zsets(a, "234216714");
+       zsets(b, "9424614147");
+       zsets(d, "9629466379");
+       zor(c, a, b);
+       assert(zcmp(c, d), == 0);
+       zsets(a, "6234216714");
+       zsets(b, "9424614147");
+       zsets(d, "13997877769");
+       zxor(c, a, b);
+       assert(zcmp(c, d), == 0);
+       zsets(a, "34216714");
+       zsets(b, "9424614147");
+       zsets(d, "9458821129");
+       zxor(c, a, b);
+       assert(zcmp(c, d), == 0);
+       zsetu(a, 1000000000ULL);
+       zsets(d, "1000000000000000000");
+       zmul(c, a, a);
+       assert(zcmp(c, d), == 0);
+       zdiv(c, c, a);
+       assert(zcmp(c, a), == 0);
+       zsetu(a, 1000000000ULL);
+       zsets(d, "1000000000000000000");
+       zsqr(c, a);
+       assert(zcmp(c, d), == 0);
+       zsetu(a, 1000000000ULL);
+       zmodpowu(c, a, 5, _3);
+       assert(zcmpu(c, 1), == 0);
+       zsetu(a, 1000000000ULL);
+       zsets(d, "1");
+       zpowu(c, a, 0);
+       assert(zcmp(c, d), == 0);
+       zsetu(a, 1000000000ULL);
+       zsets(d, "1000000000");
+       zpowu(c, a, 1);
+       assert(zcmp(c, d), == 0);
+       zsetu(a, 1000000000ULL);
+       zsets(d, "1000000000000000000");
+       zpowu(c, a, 2);
+       assert(zcmp(c, d), == 0);
+       zsetu(a, 1000000000ULL);
+       zsets(b, "1000000000000000000");
+       zsets(d, "1000000000000000000000000000");
+       zmul(c, a, b);
+       assert(zcmp(c, d), == 0);
+       zsetu(a, 1000000000ULL);
+       zsets(d, "1000000000000000000000000000");
+       zmul(b, a, a);
+       zmul(b, b, a);
+       assert(zcmp(c, d), == 0);
+       zsetu(a, 1000000000ULL);
+       zsets(d, "1000000000000000000000000000");
+       zpowu(c, a, 3);
+       assert(zcmp(c, d), == 0);
+       zsetu(a, 1000000000ULL);
+       zsets(d, "1000000000000000000000000000000000000");
+       zpowu(c, a, 4);
+       assert(zcmp(c, d), == 0);
+       zsetu(a, 1000000000ULL);
+       zsets(d, "1000000000000000000000000000000000000000000000");
+       zpowu(c, a, 5);
+       assert(zcmp(c, d), == 0);
+
+       zsetu(a, 4294967294ULL);
+       assert_s(zstr(a, buf), "4294967294");
+       zsetu(a, 4294967295ULL);
+       assert_s(zstr(a, buf), "4294967295");
+       zsetu(a, 4294967296ULL);
+       assert_s(zstr(a, buf), "4294967296");
+       zsetu(a, 4294967297ULL);
+       assert_s(zstr(a, buf), "4294967297");
+
+       zseti(a, 9223372036854775807LL);
+       assert_s(zstr(a, buf), "9223372036854775807");
+       zseti(a, -9223372036854775807LL);
+       assert_s(zstr(a, buf), "-9223372036854775807");
+       zseti(a, -9223372036854775807LL - 1LL);
+       assert_s(zstr(a, buf), "-9223372036854775808");
+
+       zsetu(a, 18446744073709551614ULL);
+       assert_s(zstr(a, buf), "18446744073709551614");
+       zsetu(a, 18446744073709551615ULL);
+       assert_s(zstr(a, buf), "18446744073709551615");
+       zadd(a, a, _1);
+       assert_s(zstr(a, buf), "18446744073709551616");
+       zadd(a, a, _1);
+       assert_s(zstr(a, buf), "18446744073709551617");
+
+       zsets(a, "1000000000000000000000000000000");
+       assert_s(zstr(a, buf), "1000000000000000000000000000000");
+       zsets(a, "+1000000000000000000000000000000");
+       assert_s(zstr(a, buf), "1000000000000000000000000000000");
+       zsets(a, "-1000000000000000000000000000000");
+       assert_s(zstr(a, buf), "-1000000000000000000000000000000");
+
+       zsetu(a, 1000000000000000ULL);
+       zsqr(a, a);
+       assert_s(zstr(a, buf), "1000000000000000000000000000000");
+
 done:
        zfree(a), zfree(b), zfree(c), zfree(d), zfree(_0), zfree(_1), 
zfree(_2), zfree(_3);
        zunsetup();
diff --git a/zahl.h b/zahl.h
index 18c0d7a..993d7b1 100644
--- a/zahl.h
+++ b/zahl.h
@@ -25,6 +25,10 @@ enum zprimality { NONPRIME = 0, PROBABLY_PRIME, PRIME };
 enum zranddev { FAST_RANDOM = 0, SECURE_RANDOM };
 enum zranddist { QUASIUNIFORM = 0, UNIFORM };
 
+enum zerror {
+       ZERROR_ERRNO_SET = 0
+};
+
 
 
 /* The parameters in the functions below are numbers a, b, c, ... */
@@ -128,6 +132,12 @@ int zsets(z_t, const char *);          /* a := b */
 size_t zstr_length(z_t, unsigned long long int);
 
 
+/* Error handling functions. */
+
+enum zerror zerror(const char **);     /* Return the current error code, and 
unless a is 0, a description in *a. */
+void zperror(const char *);            /* Identical to perror(3p) except it 
supports libzahl errors. */
+
+
 /* Inline functions. */
 
 static inline int zeven(z_t a)         { return !a->sign || !(a->chars[0] & 
1); } /* Is a even? */

Reply via email to