commit 76d0af5599554d11f104d582cdac8fbaa8569fcc
parent aff09967d194d062ae8d83c0fbe1edf158804ef9
Author: Mattias Andrée <maandree@kth.se>
Date:   Fri,  4 Mar 2016 23:50:00 +0100
Clean up, add zerror and zperror, fix bugs and add more tests
Signed-off-by: Mattias Andrée <maandree@kth.se>
Diffstat:
29 files changed, 533 insertions(+), 119 deletions(-)
diff --git 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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? */