libzahl

big integer library
git clone git://git.suckless.org/libzahl
Log | Files | Refs | README | LICENSE

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:
MMakefile | 2++
Aman/zerror.3 | 41+++++++++++++++++++++++++++++++++++++++++
Aman/zperror.3 | 21+++++++++++++++++++++
Mman/zsetup.3 | 4+++-
Msrc/internals.h | 10+++++++---
Msrc/zadd.c | 4++--
Msrc/zdivmod.c | 13+++++--------
Asrc/zerror.c | 19+++++++++++++++++++
Msrc/zload.c | 9++++-----
Msrc/zlsh.c | 30++++++++++++++++--------------
Msrc/zmodpow.c | 14++++++--------
Msrc/zmodpowu.c | 16++++++----------
Msrc/zmul.c | 22+++++++++++++---------
Msrc/zor.c | 1+
Asrc/zperror.c | 17+++++++++++++++++
Msrc/zpow.c | 9+++------
Msrc/zpowu.c | 6++----
Msrc/zrand.c | 16++++++----------
Msrc/zrsh.c | 14+++++++++-----
Msrc/zsave.c | 3+--
Msrc/zset.c | 2+-
Msrc/zsets.c | 34+++++++++++++++++++++++++++++-----
Msrc/zsetup.c | 1+
Msrc/zsqr.c | 12++++++++----
Msrc/zstr.c | 25++++++++++++++-----------
Msrc/zsub.c | 2+-
Msrc/ztrunc.c | 2+-
Mtest.c | 293++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Mzahl.h | 10++++++++++
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? */