libzahl

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

commit 8d82bea597a2969db0780e3142437ea91929613f
parent 8e65feb3bf14f50760507c3a269c21cfce49d2c1
Author: Mattias Andrée <maandree@kth.se>
Date:   Thu,  7 Apr 2016 16:04:07 +0200

Split out zahl-inlines.h zahl-internals.h from zahl.h to hide uninteresting stuff

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

Diffstat:
MMakefile | 17+++++++++++------
Msrc/internals.h | 32+++++++++++++-------------------
Azahl-inlines.h | 198+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Azahl-internals.h | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mzahl.h | 251+------------------------------------------------------------------------------
5 files changed, 287 insertions(+), 274 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,7 +1,11 @@ include config.mk -HDR =\ +HDR_PUBLIC =\ zahl.h\ + zahl-internals.h\ + zahl-inlines.h + +HDR_PRIVATE =\ src/internals.h FUN =\ @@ -63,6 +67,7 @@ INLINE_FUN =\ zcmpu\ zbtest +HDR = $(HDR_PUBLIC) $(HDR_PRIVATE) OBJ = $(FUN:=.o) allocator.o MAN3 = $(FUN:=.3) $(INLINE_FUN:=.3) MAN7 = libzahl.7 @@ -106,15 +111,15 @@ install: libzahl.a mkdir -p -- "$(DESTDIR)$(MANPREFIX)/man3" mkdir -p -- "$(DESTDIR)$(MANPREFIX)/man7" cp -- libzahl.a "$(DESTDIR)$(EXECPREFIX)/lib" - cp -- zahl.h "$(DESTDIR)$(PREFIX)/include" + cp -- $(HDR_PUBLIC) "$(DESTDIR)$(PREFIX)/include" cp -- $(foreach M,$(MAN3),man/$(M)) "$(DESTDIR)$(MANPREFIX)/man3" cp -- $(foreach M,$(MAN7),man/$(M)) "$(DESTDIR)$(MANPREFIX)/man7" uninstall: - rm -- "$(DESTDIR)$(EXECPREFIX)/lib/libzahl.a" - rm -- "$(DESTDIR)$(PREFIX)/include/zahl.h" - cd "$(DESTDIR)$(MANPREFIX)/man3" && rm $(MAN3) - cd "$(DESTDIR)$(MANPREFIX)/man7" && rm $(MAN7) + -rm -- "$(DESTDIR)$(EXECPREFIX)/lib/libzahl.a" + -cd -- "$(DESTDIR)$(PREFIX)/include" && rm $(HDR_PUBLIC) + -cd -- "$(DESTDIR)$(MANPREFIX)/man3" && rm $(MAN3) + -cd -- "$(DESTDIR)$(MANPREFIX)/man7" && rm $(MAN7) clean: -rm -- *.o *.su *.a *.so test test-random.c 2>/dev/null diff --git a/src/internals.h b/src/internals.h @@ -10,13 +10,12 @@ # undef __GNUC__ #endif -#define BITS_PER_CHAR 64 -#define LB_BITS_PER_CHAR 6 -#define ZAHL_CHAR_MAX UINT64_MAX +#define BITS_PER_CHAR ZAHL_BITS_PER_CHAR +#define LB_BITS_PER_CHAR ZAHL_LB_BITS_PER_CHAR -#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) -#define BITS_IN_LAST_CHAR(bits) ((bits) & (BITS_PER_CHAR - 1)) +#define FLOOR_BITS_TO_CHARS(bits) ZAHL_FLOOR_BITS_TO_CHARS(bits) +#define CEILING_BITS_TO_CHARS(bits) ZAHL_CEILING_BITS_TO_CHARS(bits) +#define BITS_IN_LAST_CHAR(bits) ZAHL_BITS_IN_LAST_CHAR(bits) #if defined(__GNUC__) # define O0 __attribute__((optimize("O0"))) @@ -90,24 +89,19 @@ extern zahl_char_t **libzahl_pool[sizeof(size_t) * 8]; extern size_t libzahl_pool_n[sizeof(size_t) * 8]; extern size_t libzahl_pool_alloc[sizeof(size_t) * 8]; -#if defined(__GNUC__) || defined(__clang__) -# define likely(value) __builtin_expect(!!(value), 1) -# define unlikely(value) __builtin_expect(!!(value), 0) -#else -# define likely(value) (value) -# define unlikely(value) (value) -#endif +#define likely(expr) ZAHL_LIKELY(expr) +#define unlikely(expr) ZAHL_UNLIKELY(expr) #define libzahl_failure(error) (libzahl_error = (error), longjmp(libzahl_jmp_buf, 1)) -#define SET_SIGNUM(a, signum) ((a)->sign = (signum)) -#define SET(a, b) do { if ((a) != (b)) zset(a, b); } while (0) +#define SET_SIGNUM(a, signum) ZAHL_SET_SIGNUM(a, signum) +#define SET(a, b) ZAHL_SET(a, b) #define ENSURE_SIZE(a, n) do { if ((a)->alloced < (n)) libzahl_realloc(a, (n)); } while (0) +#define TRIM(a) ZAHL_TRIM(a) +#define TRIM_NONZERO(a) ZAHL_TRIM_NONZERO(a) +#define TRIM_AND_ZERO(a) ZAHL_TRIM_AND_ZERO(a) +#define TRIM_AND_SIGN(a, s) ZAHL_TRIM_AND_SIGN(a, s) #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MAX(a, b) ((a) > (b) ? (a) : (b)) -#define TRIM(a) for (; (a)->used && !(a)->chars[(a)->used - 1]; (a)->used--) -#define TRIM_NONZERO(a) for (; !(a)->chars[(a)->used - 1]; (a)->used--) -#define TRIM_AND_ZERO(a) do { TRIM(a); if (!(a)->used) SET_SIGNUM(a, 0); } while (0) -#define TRIM_AND_SIGN(a, s) do { TRIM(a); SET_SIGNUM(a, (a)->used ? (s) : 0); } while (0) #define znegative(a) (zsignum(a) < 0) #define znegative1(a, b) ((zsignum(a) | zsignum(b)) < 0) #define znegative2(a, b) ((zsignum(a) & zsignum(b)) < 0) diff --git a/zahl-inlines.h b/zahl-inlines.h @@ -0,0 +1,198 @@ +/* See LICENSE file for copyright and license details. */ + +ZAHL_INLINE void zinit(z_t a) { a->alloced = 0; a->chars = 0; } +ZAHL_INLINE int zeven(z_t a) { return !a->sign || !(a->chars[0] & 1); } +ZAHL_INLINE int zodd(z_t a) { return a->sign && (a->chars[0] & 1); } +ZAHL_INLINE int zeven_nonzero(z_t a) { return !(a->chars[0] & 1); } +ZAHL_INLINE int zodd_nonzero(z_t a) { return (a->chars[0] & 1); } +ZAHL_INLINE int zzero(z_t a) { return !a->sign; } +ZAHL_INLINE int zsignum(z_t a) { return a->sign; } +ZAHL_INLINE void zabs(z_t a, z_t b) { ZAHL_SET(a, b); a->sign = !!a->sign; } +ZAHL_INLINE void zneg(z_t a, z_t b) { ZAHL_SET(a, b); a->sign = -a->sign; } + + +ZAHL_INLINE void +zswap(z_t a, z_t b) +{ + z_t t; + t->sign = a->sign; + a->sign = b->sign; + b->sign = t->sign; + t->used = b->used; + b->used = a->used; + a->used = t->used; + t->alloced = a->alloced; + a->alloced = b->alloced; + b->alloced = t->alloced; + t->chars = b->chars; + b->chars = a->chars; + a->chars = t->chars; +} + + +ZAHL_INLINE void +zseti(z_t a, int64_t b) +{ + if (ZAHL_UNLIKELY(b >= 0)) { + zsetu(a, (uint64_t)b); + } else { + zsetu(a, (uint64_t)-b); + ZAHL_SET_SIGNUM(a, -1); + } +} + + +ZAHL_INLINE size_t +zlsb(z_t a) +{ + size_t i = 0; + if (ZAHL_UNLIKELY(zzero(a))) + return SIZE_MAX; + for (; !a->chars[i]; i++); + i *= 8 * sizeof(zahl_char_t); + ZAHL_ADD_CTZ(i, a->chars[i]); + return i; +} + + +ZAHL_INLINE size_t +zbits(z_t a) +{ + size_t rc; + if (ZAHL_UNLIKELY(zzero(a))) + return 1; + while (!a->chars[a->used - 1]) a->used--; /* TODO should not be necessary */ + rc = a->used * 8 * sizeof(zahl_char_t); + ZAHL_SUB_CLZ(rc, a->chars[a->used - 1]); + return rc; +} + + +ZAHL_INLINE int +zcmpmag(z_t a, z_t b) +{ + size_t i, j; + if (ZAHL_UNLIKELY(zzero(a))) return -!zzero(b); + if (ZAHL_UNLIKELY(zzero(b))) return 1; + i = a->used - 1; + j = b->used - 1; +#if 0 /* TODO this should be sufficient. */ + if (i != j) + return (i > j) * 2 - 1; +#else + for (; i > j; i--) { + if (a->chars[i]) + return +1; + a->used--; + } + for (; j > i; j--) { + if (b->chars[j]) + return -1; + b->used--; + } +#endif + for (; i && a->chars[i] == b->chars[i]; i--); + return a->chars[i] < b->chars[i] ? -1 : a->chars[i] > b->chars[i]; +} + + +ZAHL_INLINE int +zcmp(z_t a, z_t b) +{ + if (zsignum(a) != zsignum(b)) + return zsignum(a) < zsignum(b) ? -1 : zsignum(a) > zsignum(b); + return zsignum(a) * zcmpmag(a, b); +} + + +ZAHL_INLINE int +zcmpu(z_t a, uint64_t b) +{ + extern z_t libzahl_tmp_cmp; + if (ZAHL_UNLIKELY(!b)) + return zsignum(a); + if (ZAHL_UNLIKELY(zsignum(a) <= 0)) + return -1; + libzahl_tmp_cmp->chars[0] = b; + return zcmpmag(a, libzahl_tmp_cmp); +} + + +ZAHL_INLINE int +zcmpi(z_t a, int64_t b) +{ + extern z_t libzahl_tmp_cmp; + if (ZAHL_UNLIKELY(!b)) + return zsignum(a); + if (ZAHL_UNLIKELY(zzero(a))) + return ZAHL_LIKELY(b < 0) ? 1 : -1; + if (ZAHL_LIKELY(b < 0)) { + if (zsignum(a) > 0) + return +1; + libzahl_tmp_cmp->chars[0] = (zahl_char_t)-b; + return -zcmpmag(a, libzahl_tmp_cmp); + } else { + if (zsignum(a) < 0) + return -1; + libzahl_tmp_cmp->chars[0] = b; + return +zcmpmag(a, libzahl_tmp_cmp); + } +} + + +void zbset_impl_set(z_t a, z_t b, size_t bit); +void zbset_impl_clear(z_t a, z_t b, size_t bit); +void zbset_impl_flip(z_t a, z_t b, size_t bit); + +ZAHL_INLINE void +zbset(z_t a, z_t b, size_t bit, int action) +{ + if (ZAHL_UNLIKELY(a != b)) + zset(a, b); + +#ifdef ZAHL_CONST_P + if (ZAHL_CONST_P(action) && ZAHL_CONST_P(bit)) { + zahl_char_t mask = 1; + if (zzero(a) || ZAHL_FLOOR_BITS_TO_CHARS(bit) >= a->used) { + if (!action) + return; + goto fallback; + } + mask <<= ZAHL_BITS_IN_LAST_CHAR(bit); + if (action > 0) { + a->chars[ZAHL_FLOOR_BITS_TO_CHARS(bit)] |= mask; + return; + } else if (action < 0) { + a->chars[ZAHL_FLOOR_BITS_TO_CHARS(bit)] ^= mask; + } else { + a->chars[ZAHL_FLOOR_BITS_TO_CHARS(bit)] &= ~mask; + } + ZAHL_TRIM_AND_ZERO(a); + return; + } +fallback: +#endif + + if (action > 0) + zbset_impl_set(a, b, bit); + else if (action < 0) + zbset_impl_flip(a, b, bit); + else + zbset_impl_clear(a, b, bit); +} + + +ZAHL_INLINE int +zbtest(z_t a, size_t bit) +{ + size_t chars; + if (ZAHL_UNLIKELY(zzero(a))) + return 0; + + chars = ZAHL_FLOOR_BITS_TO_CHARS(bit); + if (ZAHL_UNLIKELY(chars >= a->used)) + return 0; + + bit &= ZAHL_BITS_IN_LAST_CHAR(bit); + return (a->chars[chars] >> bit) & 1; +} diff --git a/zahl-internals.h b/zahl-internals.h @@ -0,0 +1,63 @@ +/* See LICENSE file for copyright and license details. */ + +#ifndef ZAHL_INLINE +# if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +# define ZAHL_INLINE static inline +# else +# define ZAHL_INLINE static +# endif +#endif + + +#if defined(__GNUC__) || defined(__clang__) +# define ZAHL_LIKELY(expr) __builtin_expect(!!(expr), 1) +# define ZAHL_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +# define ZAHL_CONST_P(value) __builtin_constant_p(value) +#else +# define ZAHL_LIKELY(expr) (expr) +# define ZAHL_UNLIKELY(expr) (expr) +#endif + + +#define ZAHL_BITS_PER_CHAR 64 +#define ZAHL_LB_BITS_PER_CHAR 6 +#define ZAHL_CHAR_MAX UINT64_MAX + + +#define ZAHL_FLOOR_BITS_TO_CHARS(bits) ((bits) >> ZAHL_LB_BITS_PER_CHAR) +#define ZAHL_CEILING_BITS_TO_CHARS(bits) (((bits) + (ZAHL_BITS_PER_CHAR - 1)) >> ZAHL_LB_BITS_PER_CHAR) +#define ZAHL_BITS_IN_LAST_CHAR(bits) ((bits) & (ZAHL_BITS_PER_CHAR - 1)) + + +#define ZAHL_SET_SIGNUM(a, signum) ((a)->sign = (signum)) +#define ZAHL_SET(a, b) do { if ((a) != (b)) zset(a, b); } while (0) +#define ZAHL_TRIM(a) for (; (a)->used && !(a)->chars[(a)->used - 1]; (a)->used--) +#define ZAHL_TRIM_NONZERO(a) for (; !(a)->chars[(a)->used - 1]; (a)->used--) +#define ZAHL_TRIM_AND_ZERO(a) do { ZAHL_TRIM(a); if (!(a)->used) ZAHL_SET_SIGNUM(a, 0); } while (0) +#define ZAHL_TRIM_AND_SIGN(a, s) do { ZAHL_TRIM(a); ZAHL_SET_SIGNUM(a, (a)->used ? (s) : 0); } while (0) + + +#if defined(__GNUC__) || defined(__clang__) +# if ZAHL_CHAR_MAX == LONG_MAX +# define ZAHL_ADD_CTZ(r, x) ((r) += (size_t)__builtin_ctzl(x)) +# define ZAHL_SUB_CLZ(r, x) ((r) -= (size_t)__builtin_clzl(x)) +# else +# define ZAHL_ADD_CTZ(r, x) ((r) += (size_t)__builtin_ctzll(x)) +# define ZAHL_SUB_CLZ(r, x) ((r) -= (size_t)__builtin_clzll(x)) +# endif +#else +# define ZAHL_ADD_CTZ(r, x) \ + do { \ + zahl_char_t zahl_x__ = (x); \ + for (; zahl_x__ & 1; zahl_x__ >>= 1, (r)++); \ + } while (0) +# define ZAHL_SUB_CLZ(r, x) \ + do { \ + zahl_char_t zahl_x__ = (x); \ + (r) -= 8 * sizeof(zahl_char_t); \ + for (; zahl_x__; zahl_x__ >>= 1, (r)++); \ + } while (0) +#endif + + +typedef uint64_t zahl_char_t; diff --git a/zahl.h b/zahl.h @@ -12,21 +12,10 @@ #include <stdint.h> #include <limits.h> - - -#ifndef ZAHL_INLINE -# if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L -# define ZAHL_INLINE static inline -# else -# define ZAHL_INLINE static -# endif -#endif +#include "zahl-internals.h" -/* You should pretend like this typedef does not exist. */ -typedef uint64_t zahl_char_t; - /* This structure should be considered opaque. */ typedef struct { int sign; @@ -185,242 +174,6 @@ void zperror(const char *); /* Identical to perror(3p) except it sup -/* ------------------------------- Implementations of inline functions. ------------------------------- */ - - -#if defined(__GNUC__) || defined(__clang__) -# define ZAHL_UNLIKELY(expr) __builtin_expect(!!(expr), 0) -# define ZAHL_LIKELY(expr) __builtin_expect(!!(expr), 1) -#else -# define ZAHL_UNLIKELY(expr) (expr) -# define ZAHL_LIKELY(expr) (expr) -#endif - - -ZAHL_INLINE void zinit(z_t a) { a->alloced = 0; a->chars = 0; } -ZAHL_INLINE int zeven(z_t a) { return !a->sign || !(a->chars[0] & 1); } -ZAHL_INLINE int zodd(z_t a) { return a->sign && (a->chars[0] & 1); } -ZAHL_INLINE int zeven_nonzero(z_t a) { return !(a->chars[0] & 1); } -ZAHL_INLINE int zodd_nonzero(z_t a) { return (a->chars[0] & 1); } -ZAHL_INLINE int zzero(z_t a) { return !a->sign; } -ZAHL_INLINE int zsignum(z_t a) { return a->sign; } -ZAHL_INLINE void zabs(z_t a, z_t b) { if (a != b) zset(a, b); a->sign = !!a->sign; } -ZAHL_INLINE void zneg(z_t a, z_t b) { if (a != b) zset(a, b); a->sign = -a->sign; } - - -ZAHL_INLINE void -zswap(z_t a, z_t b) -{ - z_t t; - t->sign = a->sign; - a->sign = b->sign; - b->sign = t->sign; - t->used = b->used; - b->used = a->used; - a->used = t->used; - t->alloced = a->alloced; - a->alloced = b->alloced; - b->alloced = t->alloced; - t->chars = b->chars; - b->chars = a->chars; - a->chars = t->chars; -} - - -ZAHL_INLINE void -zseti(z_t a, int64_t b) -{ - if (ZAHL_UNLIKELY(b >= 0)) { - zsetu(a, (uint64_t)b); - } else { - zsetu(a, (uint64_t)-b); - a->sign = -1; - } -} - - -ZAHL_INLINE size_t -zlsb(z_t a) -{ -#if defined(__GNUC__) || defined(__clang__) - size_t i = 0; - if (ZAHL_UNLIKELY(zzero(a))) - return SIZE_MAX; - for (; !a->chars[i]; i++); - i *= 8 * sizeof(zahl_char_t); - i += (size_t)__builtin_ctzll(a->chars[i]); - return i; -#else - size_t i = 0; - zahl_char_t x; - if (ZAHL_UNLIKELY(zzero(a))) - return SIZE_MAX; - for (; !a->chars[i]; i++); - i *= 8 * sizeof(zahl_char_t); - x = ~(a->chars[i]); - for (; x & 1; x >>= 1, i++); - return i; -#endif -} - - -ZAHL_INLINE size_t -zbits(z_t a) -{ -#if defined(__GNUC__) || defined(__clang__) - size_t rc; - if (ZAHL_UNLIKELY(zzero(a))) - return 1; - while (!a->chars[a->used - 1]) a->used--; /* TODO should not be necessary */ - rc = a->used * 8 * sizeof(zahl_char_t); - rc -= (size_t)__builtin_clzll(a->chars[a->used - 1]); - return rc; -#else - size_t rc; - zahl_char_t x; - if (ZAHL_UNLIKELY(zzero(a))) - return 1; - while (!a->chars[a->used - 1]) a->used--; /* TODO should not be necessary */ - x = a->chars[a->used - 1]; - rc = (a->used - 1) * 8 * sizeof(zahl_char_t); - for (; x; x >>= 1, rc++); - return rc; -#endif -} - - -ZAHL_INLINE int -zcmpmag(z_t a, z_t b) -{ - size_t i, j; - if (ZAHL_UNLIKELY(zzero(a))) - return -!zzero(b); - if (ZAHL_UNLIKELY(zzero(b))) - return 1; - i = a->used - 1; - j = b->used - 1; -#if 0 /* TODO this should be sufficient. */ - if (i != j) - return (i > j) * 2 - 1; -#else - for (; i > j; i--) { - if (a->chars[i]) - return +1; - a->used--; - } - for (; j > i; j--) { - if (b->chars[j]) - return -1; - b->used--; - } -#endif - for (; i && a->chars[i] == b->chars[i]; i--); - return a->chars[i] < b->chars[i] ? -1 : a->chars[i] > b->chars[i]; -} - - -ZAHL_INLINE int -zcmp(z_t a, z_t b) -{ - if (zsignum(a) != zsignum(b)) - return zsignum(a) < zsignum(b) ? -1 : zsignum(a) > zsignum(b); - return zsignum(a) * zcmpmag(a, b); -} - - -ZAHL_INLINE int -zcmpu(z_t a, uint64_t b) -{ - extern z_t libzahl_tmp_cmp; - if (ZAHL_UNLIKELY(!b)) - return zsignum(a); - if (ZAHL_UNLIKELY(zsignum(a) <= 0)) - return -1; - libzahl_tmp_cmp->chars[0] = b; - return zcmpmag(a, libzahl_tmp_cmp); -} - - -ZAHL_INLINE int -zcmpi(z_t a, int64_t b) -{ - extern z_t libzahl_tmp_cmp; - if (ZAHL_UNLIKELY(!b)) - return zsignum(a); - if (ZAHL_UNLIKELY(zzero(a))) - return ZAHL_LIKELY(b < 0) ? 1 : -1; - if (ZAHL_LIKELY(b < 0)) { - if (zsignum(a) > 0) - return +1; - libzahl_tmp_cmp->chars[0] = (uint64_t)-b; - return -zcmpmag(a, libzahl_tmp_cmp); - } else { - if (zsignum(a) < 0) - return -1; - libzahl_tmp_cmp->chars[0] = b; - return +zcmpmag(a, libzahl_tmp_cmp); - } -} - - -void zbset_impl_set(z_t a, z_t b, size_t bit); -void zbset_impl_clear(z_t a, z_t b, size_t bit); -void zbset_impl_flip(z_t a, z_t b, size_t bit); -ZAHL_INLINE void -zbset(z_t a, z_t b, size_t bit, int action) -{ - if (ZAHL_UNLIKELY(a != b)) - zset(a, b); - -#if defined(__GNUC__) || defined(__clang__) - if (__builtin_constant_p(action) && __builtin_constant_p(bit)) { - zahl_char_t mask = 1; - if (zzero(a) || bit >> 6 >= a->used) { - if (!action) - return; - goto fallback; - } - mask <<= bit & 63; - if (action > 0) { - a->chars[bit >> 6] |= mask; - return; - } else if (action < 0) { - a->chars[bit >> 6] ^= mask; - } else { - a->chars[bit >> 6] &= ~mask; - } - for (; a->used && !a->chars[a->used - 1]; a->used--); - if (!a->used) - a->sign = 0; - return; - } -fallback: -#endif - - if (action > 0) { - zbset_impl_set(a, b, bit); - } else if (action < 0) { - zbset_impl_flip(a, b, bit); - } else { - zbset_impl_clear(a, b, bit); - } -} - - -ZAHL_INLINE int -zbtest(z_t a, size_t bit) -{ - size_t chars; - if (ZAHL_UNLIKELY(zzero(a))) - return 0; - - chars = bit >> 6; - if (ZAHL_UNLIKELY(chars >= a->used)) - return 0; - - bit &= 63; - return (a->chars[chars] >> bit) & 1; -} - +#include "zahl-inlines.h" #endif