libzahl

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

commit c0bc7b6e2d090554c9d940bc3614e089a688503a
parent d133f10c9a98c10f60df827e134f45f56e6ad0a0
Author: Mattias Andrée <maandree@kth.se>
Date:   Thu,  3 Mar 2016 10:33:29 +0100

Add zabs, zadd, zdiv, zmod, zmodmul, zmodpow, zneg, zpow, zsub, and the newly introduced zmodsqr

Signed-off-by: Mattias Andrée <maandree@kth.se>

Diffstat:
Mman/zmodmul.3 | 3++-
Mman/zmodpow.3 | 1+
Aman/zmodsqr.3 | 45+++++++++++++++++++++++++++++++++++++++++++++
Msrc/internals.h | 10+++++++++-
Asrc/zabs.c | 11+++++++++++
Asrc/zadd.c | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/zdiv.c | 9+++++++++
Asrc/zmod.c | 9+++++++++
Asrc/zmodmul.c | 17+++++++++++++++++
Asrc/zmodpow.c | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/zmodsqr.c | 17+++++++++++++++++
Asrc/zneg.c | 11+++++++++++
Asrc/zpow.c | 45+++++++++++++++++++++++++++++++++++++++++++++
Asrc/zsub.c | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mzahl.h | 4++--
15 files changed, 392 insertions(+), 4 deletions(-)

diff --git a/man/zmodmul.3 b/man/zmodmul.3 @@ -1,6 +1,6 @@ .TH ZMODMUL 3 libzahl .SH NAME -zmodmul - Calculate the modular product of two big integer +zmodmul - Calculate a modular product of two big integer .SH SYNOPSIS .nf #include <zahl.h> @@ -34,6 +34,7 @@ It is possible to calculate the modular product with a faster algorithm than calculating the product and than the modulus of that product. .SH SEE ALSO +.BR zmodsqr (3), .BR zmodpow (3), .BR zstr (3), .BR zadd (3), diff --git a/man/zmodpow.3 b/man/zmodpow.3 @@ -34,6 +34,7 @@ It is possible to calculate the modular power with a faster algorithm than calculating the power and than the modulus of that power. .SH SEE ALSO +.BR zmodsqr (3), .BR zmodmul (3), .BR zsqr (3), .BR zstr (3), diff --git a/man/zmodsqr.3 b/man/zmodsqr.3 @@ -0,0 +1,45 @@ +.TH ZMODSQR 3 libzahl +.SH NAME +zsqr - Calculate a modular square of a big integer +.SH SYNOPSIS +.nf +#include <zahl.h> + +void zmodsqr(z_t \fIsquare\fP, z_t \fIinteger\fP, z_t \fImodulator\fP); +.fi +.SH DESCRIPTION +.B zmodsqr +calculates the square of an +.IR integer , +modulus a +.IR modulator , +and stores the result in +.IR square . +That is, +.I square +gets +.IR integer ². +Mod +.IR modulator . +.P +It is safe to call +.B zmodsqr +with non-unique parameters. +.SH RATIONALE +See rationle for +.BR zmodmul (3), +and +.BR zsqr (3). +.SH SEE ALSO +.BR zmodmul (3), +.BR zmodpow (3), +.BR zsqr (3), +.BR zstr (3), +.BR zadd (3), +.BR zsub (3), +.BR zmul (3), +.BR zdiv (3), +.BR zmod (3), +.BR zneg (3), +.BR zabs (3), +.BR zpow (3) diff --git a/src/internals.h b/src/internals.h @@ -3,6 +3,7 @@ #define BITS_PER_CHAR 32 #define LB_BITS_PER_CHAR 5 +#define ZAHL_CHAR_MAX UINT32_MAX #define FLOOR_BITS_TO_CHARS(bits) ((bits) >> LB_BITS_PER_CHAR) #define CEILING_BITS_TO_CHARS(bits) (((bits) + (BITS_PER_CHAR - 1)) >> LB_BITS_PER_CHAR) @@ -15,7 +16,14 @@ X(libzahl_tmp_str_div)\ X(libzahl_tmp_str_rem)\ X(libzahl_tmp_gcd_u)\ - X(libzahl_tmp_gcd_v) + X(libzahl_tmp_gcd_v)\ + X(libzahl_tmp_modmul)\ + X(libzahl_tmp_div)\ + X(libzahl_tmp_mod)\ + X(libzahl_tmp_pow_b)\ + X(libzahl_tmp_pow_c)\ + X(libzahl_tmp_pow_d)\ + X(libzahl_tmp_modsqr) #define LIST_CONSTS\ X(libzahl_const_1e19, zsetu, 10000000000000000000ULL) /* The largest power of 10 < 2⁶⁴. */\ diff --git a/src/zabs.c b/src/zabs.c @@ -0,0 +1,11 @@ +/* See LICENSE file for copyright and license details. */ +#include "internals" + + +void +zabs(z_t a, z_t b) +{ + if (a != b) + zset(a, b); + SET_SIGNUM(a, !zzero(a)); +} diff --git a/src/zadd.c b/src/zadd.c @@ -0,0 +1,94 @@ +/* See LICENSE file for copyright and license details. */ +#include "internals" + +#include <stdlib.h> +#include <string.h> + + +void +zadd_unsigned(z_t a, z_t b, z_t c) +{ + size_t i, size, n; + uint32_t carry[] = {0, 0}; + zahl_char_t *addend; + + if (a == c) { + zadd_unsigned(a, c, b); + return; + } + + size = b->used > c->used ? b->used : c->used; + if (a->alloced < size + 1) { + a->alloced = size + 1; + a->chars = realloc(a->chars, a->alloced * sizeof(*(a->chars))); + } + a->chars[size] = 0; + + n = b->used + c->used - size; + if (a == b) { + if (a->used < c->used) { + n = c->used; + memset(a->chars + a->used, 0, (n - a->used) * sizeof(*(a->chars))); + } + addend = c->chars; + } else if (a == c) { + if (a->used < b->used) { + n = b->used; + memset(a->chars + a->used, 0, (n - a->used) * sizeof(*(a->chars))); + } + addend = b->chars; + } else if (b->used > c->used) { + memcpy(a->chars, b->chars, b->used * sizeof(*(a->chars))); + a->used = b->used; + addend = c->chars; + } else { + memcpy(a->chars, c->chars, c->used * sizeof(*(a->chars))); + a->used = c->used; + addend = b->chars; + } + + for (i = 0; i < n; i++) { + if (carry[i & 1]) + carry[~i & 1] = (ZAHL_CHAR_MAX - a->chars[i] <= addend[i]); + else + carry[~i & 1] = (ZAHL_CHAR_MAX - a->chars[i] < addend[i]); + a->chars[i] += addend[i] + carry[i & 1]; + } + + while (carry[~i & 1]) { + carry[i & 1] = a->chars[i] == ZAHL_CHAR_MAX; + a->chars[i++] += 1; + } + + if (a->used < i) + a->used = i; + SET_SIGNUM(a, !zzero(b) | !zzero(c)); +} + + +void +zadd(z_t a, z_t b, z_t c) +{ + if (zzero(b)) { + if (a != b) + zset(a, c); + } else if (zzero(c)) { + if (a != b) + zset(a, b); + } else if (b == c) { + zlsh(a, b, 1); + } else if ((zsignum(b) | zsignum(c)) < 0) { + if (zsignum(b) < 0) { + if (zsignum(c) < 0) { + zadd_unsigned(a, b, c); + SET_SIGNUM(a, -zsignum(a)); + } else { + zsub_unsigned(a, c, b); + } + } else { + zsub_unsigned(a, b, c); + } + } else { + zadd_unsigned(a, b, c); + } +} diff --git a/src/zdiv.c b/src/zdiv.c @@ -0,0 +1,9 @@ +/* See LICENSE file for copyright and license details. */ +#include "internals" + + +void +zdiv(z_t a, z_t b, z_t c) +{ + zdivmod(a, libzahl_tmp_div, b, c); +} diff --git a/src/zmod.c b/src/zmod.c @@ -0,0 +1,9 @@ +/* See LICENSE file for copyright and license details. */ +#include "internals" + + +void +zmod(z_t a, z_t b, z_t c) +{ + zdivmod(libzahl_tmp_mod, a, b, c); +} diff --git a/src/zmodmul.c b/src/zmodmul.c @@ -0,0 +1,17 @@ +/* See LICENSE file for copyright and license details. */ +#include "internals" + + +void +zmodmul(z_t a, z_t b, z_t c) +{ + /* TODO Montgomery modular multiplication */ + if (a == d) { + zset(libzahl_tmp_modmul, d); + zmul(a, b, c); + zmod(a, a, libzahl_tmp_modmul); + } else { + zmul(a, b, c); + zmod(a, a, d); + } +} diff --git a/src/zmodpow.c b/src/zmodpow.c @@ -0,0 +1,50 @@ +/* See LICENSE file for copyright and license details. */ +#include "internals" + +#include <errno.h> + +#define tb libzahl_tmp_pow_b +#define tc libzahl_tmp_pow_c +#define td libzahl_tmp_pow_d + + +void +zmodpow(z_t a, z_t b, z_t c, z_t d) +{ + size_t i, n; + + if (zsignum(c) <= 0) { + if (zzero(c)) { + if (zzero(b)) { + errno = EDOM; /* Indeterminate form: 0:th power of 0 */ + FAILURE_JUMP(); + } + zsetu(a, 1); + } else if (zzero(b) || zzero(d)) { + errno = EDOM; /* Undefined form: Division by 0 */ + FAILURE_JUMP(); + } else { + SET_SIGNUM(a, 0); + } + return; + } else if (zzero(d)) { + errno = EDOM; /* Undefined form: Division by 0 */ + FAILURE_JUMP(); + } else if (zzero(b)) { + SET_SIGNUM(a, 0); + return; + } + + n = zbits(c); + + zmod(tb, b, d); + zset(tc, c); + zset(td, d); + zsetu(a, 1); + + for (i = 0; i < n; i++) { + if (zbtest(tc, i)) + zmodmul(a, a, tb, td); + zmodsqr(tb, tb, td); + } +} diff --git a/src/zmodsqr.c b/src/zmodsqr.c @@ -0,0 +1,17 @@ +/* See LICENSE file for copyright and license details. */ +#include "internals" + + +void +zmodsqr(z_t a, z_t b, z_t c) +{ + /* TODO What is the fastest way to do zmodsqr? */ + if (a == c) { + zset(libzahl_tmp_modsqr, c); + zsqr(a, b); + zmod(a, a, libzahl_tmp_modsqr); + } else { + zsqr(a, b); + zmod(a, a, c); + } +} diff --git a/src/zneg.c b/src/zneg.c @@ -0,0 +1,11 @@ +/* See LICENSE file for copyright and license details. */ +#include "internals" + + +void +zneg(z_t a, z_t b) +{ + if (a != b) + zset(a, b); + SET_SIGNUM(a, -zsignum(a)); +} diff --git a/src/zpow.c b/src/zpow.c @@ -0,0 +1,45 @@ +/* See LICENSE file for copyright and license details. */ +#include "internals" + +#include <errno.h> + +#define tb libzahl_tmp_pow_b +#define tc libzahl_tmp_pow_c + + +void +zpow(z_t a, z_t b, z_t c) +{ + size_t i, n; + + if (zsignum(c) <= 0) { + if (zzero(c)) { + if (zzero(b)) { + errno = EDOM; /* Indeterminate form: 0:th power of 0 */ + FAILURE_JUMP(); + } + zsetu(a, 1); + } else if (zzero(b)) { + errno = EDOM; /* Undefined form: Division by 0 */ + FAILURE_JUMP(); + } else { + SET_SIGNUM(a, 0); + } + return; + } else if (zzero(b)) { + SET_SIGNUM(a, 0); + return; + } + + n = zbits(c); + + zset(tb, b); + zset(tc, c); + zsetu(a, 1); + + for (i = 0; i < n; i++) { + if (zbtest(tc, i)) + zmul(a, a, tb); + zsqr(tb, tb); + } +} diff --git a/src/zsub.c b/src/zsub.c @@ -0,0 +1,70 @@ +/* See LICENSE file for copyright and license details. */ +#include "internals" + +#include <stdlib.h> +#include <string.h> + + +void +zsub_unsigned(z_t a, z_t b, z_t c) +{ + int magcmp = zcmpmag(b, c); + z_t s; + size_t i, n; + zahl_char_t carry[] = {0, 0}; + + if (magcmp <= 0) { + if (magcmp == 0) { + SET_SIGNUM(a, 0); + return; + } + if (a != c) + zset(a, c); + *subtrahend = *b; + } else { + if (a != b) + zset(a, b); + *subtrahend = *c; + } + + n = a->used < s->used ? a->used : s->used; + + for (i = 0; i < n; i++) { + carry[~i & 1] = carry[i & 1] ? (a->chars[i] <= s->chars[i]) : (a->chars[i] < s->chars[i]); + a->chars[i] -= s->chars[i]; + a->chars[i] -= carry[i & 1]; + } + + if (carry[i] & 1) { + while (!a->chars[i]) + a->chars[i++] = ZAHL_CHAR_MAX; + a->chars[i] -= 1; + } + SET_SIGNUM(a, magcmp); +} + +void +zsub(z_t a, z_t b, z_t c) +{ + if (b == c) { + SET_SIGNUM(a, 0); + } else if (zzero(b)) { + zneg(a, c); + } else if (zzero(c)) { + if (a != b) + zset(a, b); + } else if ((zsignum(b) | zsignum(c)) < 0) { + if (zsignum(b) < 0) { + if (zsignum(c) < 0) { + zsub_unsigned(a, c, b); + } else { + zadd_unsigned(a, b, c); + SET_SIGNUM(a, -zsignum(a)); + } + } else { + zadd_unsigned(a, b, c); + } + } else { + zsub_unsigned(a, b, c); + } +} diff --git a/zahl.h b/zahl.h @@ -73,6 +73,7 @@ void zdiv(z_t, z_t, z_t); /* a := b / c */ void zdivmod(z_t, z_t, z_t, z_t); /* a := c / d, b = c % d */ void zmod(z_t, z_t, z_t); /* a := b % c */ void zsqr(z_t, z_t); /* a := b² */ +void zmodsqr(z_t, z_t, z_t); /* a := b² % c */ void zneg(z_t, z_t); /* a := -b */ void zabs(z_t, z_t); /* a := |b| */ void zpow(z_t, z_t, z_t); /* a := b ↑ c */ @@ -80,8 +81,7 @@ void zmodpow(z_t, z_t, z_t, z_t); /* a := (b ↑ c) % d */ /* These are used internally and may be removed in a future version. */ void zadd_unsigned(z_t, z_t, z_t); /* a := |b| + |c|, b and c must not be the same reference. */ -void zsub_unsigned(z_t, z_t, z_t); /* a := |b| - |c|, b and c must not be the same reference. */ -void zsub_positive(z_t, z_t, z_t); /* a := |b| - |c|, assumes b ≥ c and that, and b is not c. */ +void zsub_unsigned(z_t, z_t, z_t); /* a := |b| - |c| */ /* Bitwise operations. */