commit 4997ab80851b257d486790dcd75f79a7175555c9
parent 7130cc2fa7dbe1821e5cfd0eb612c3e82228e3c4
Author: Louis Burda <quent.burda@gmail.com>
Date: Fri, 15 Mar 2024 22:56:14 +0100
Merge commit '3f40d51dc0273948d88b3ea07fafaf847e305138' as 'src/lib/libmaxint'
Diffstat:
7 files changed, 882 insertions(+), 0 deletions(-)
diff --git a/src/lib/libmaxint/.gitignore b/src/lib/libmaxint/.gitignore
@@ -0,0 +1,3 @@
+compile_commands.json
+build
+.cache
diff --git a/src/lib/libmaxint/Makefile b/src/lib/libmaxint/Makefile
@@ -0,0 +1,39 @@
+PREFIX ?= /usr/local
+LIBDIR ?= /lib
+
+CFLAGS = -Wunused-variable -Wunused-function -I include
+
+ifeq "$(DEBUG)" "1"
+CFLAGS += -Og -g
+else
+CFLAGS += -O2
+endif
+
+all: build/libmaxint.a build/libmaxint.so build/test
+
+clean:
+ rm -rf build
+
+build:
+ mkdir build
+
+build/libmaxint.a: src/maxint.c include/maxint.h libmaxint.api | build
+ $(CC) -o build/tmp.o $< $(CFLAGS) -r
+ objcopy --keep-global-symbols=libmaxint.api build/tmp.o build/fixed.o
+ ar rcs $@ build/fixed.o
+
+build/libmaxint.so: src/maxint.c include/maxint.h libmaxint.lds | build
+ $(CC) -o $@ $< -fPIC $(CFLAGS) -shared -Wl,-version-script libmaxint.lds
+
+build/test: src/test.c build/libmaxint.a | build
+ $(CC) -o $@ $^ -g -I include
+
+install:
+ install -m755 build/libmaxint.a -t "$(DESTDIR)$(PREFIX)$(LIBDIR)"
+ install -m755 build/libmaxint.so -t "$(DESTDIR)$(PREFIX)$(LIBDIR)"
+
+uninstall:
+ rm -f "$(DESTDIR)$(PREFIX)$(LIBDIR)/libmaxint.a"
+ rm -f "$(DESTDIR)$(PREFIX)$(LIBDIR)/libmaxint.so"
+
+.PHONY: all clean install uninstall
diff --git a/src/lib/libmaxint/include/maxint.h b/src/lib/libmaxint/include/maxint.h
@@ -0,0 +1,151 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#define MI_NEG 1
+#define MI_POS 0
+
+#define MI_BIT(a,n) ((mi_ul) ((a) >> (n)) & 1)
+#define MI_BITA(a,m,n) ((mi_ul) ((a)[(n) / (m)] >> ((n) % (m))) & 1)
+
+#define MI_UL_BYTES (sizeof(mi_ul))
+#define MI_UL_BITS (8 * sizeof(mi_ul))
+
+#define MI_ULS_BYTES (sizeof(mi_uls))
+#define MI_ULS_BITS (8 * sizeof(mi_uls))
+
+#define MI_ABS(x) ((mi_ul) ((x) > 0 ? (x) : -(x)))
+#define MI_SIGN(x) ((x) >= 0 ? MI_POS : MI_NEG)
+#define MI_TO_SIGN(x) ((x)->sign == MI_POS ? 1 : -1)
+
+#define MI_INIT ((struct maxint) { \
+ .data = NULL, \
+ .size = 0, \
+ .cap = 0, \
+ .sign = MI_POS, \
+})
+
+/* unsigned underlying type */
+typedef uint64_t mi_ul;
+typedef int64_t mi_uls;
+
+enum {
+ MI_OK,
+ MI_ERR_OOM,
+ MI_ERR_INVAL,
+};
+
+struct maxint {
+ mi_ul *data;
+ size_t size, cap;
+ int sign;
+};
+
+/* void mi_init(struct maxint *m); */
+void mi_deinit(struct maxint *m);
+
+/* struct maxint *mi_imm(struct maxint *storage, mi_ul tmp, int sign); */
+
+int mi_set(struct maxint *m1, const struct maxint *m2);
+int mi_setv(struct maxint *m, mi_ul v, int sign);
+int mi_setstr(struct maxint *m, const char *str,
+ const char **end, const char *alph);
+
+int mi_resize(struct maxint *m, size_t size);
+
+static inline mi_ul mi_cast_ul(const struct maxint *m);
+static inline mi_uls mi_cast_uls(const struct maxint *m);
+
+bool mi_zero(const struct maxint *m);
+
+size_t mi_lastset(const struct maxint *m);
+
+ssize_t mi_str(const struct maxint *m, char *buf, size_t buflen,
+ mi_ul base, const char *alph, size_t zfill);
+ssize_t mi_hex(const struct maxint *m, char *buf, size_t buflen,
+ bool capital, size_t zfill);
+ssize_t mi_dec(const struct maxint *m, char *buf, size_t buflen, size_t zfill);
+ssize_t mi_bin(const struct maxint *m, char *buf, size_t buflen, size_t zfill);
+
+/* mi_add: o1 = i1 + i2
+ * allowed: o1 == i1 */
+int mi_add(struct maxint *o1, const struct maxint *i1, const struct maxint *i2);
+
+/* mi_sub: o1 = i1 - i2
+ * allowed: o1 == i1 */
+int mi_sub(struct maxint *o1, const struct maxint *i1, const struct maxint *i2);
+
+/* mi_shl: o1 = i1 << shift
+ * allowed: o1 == i1 */
+int mi_shl(struct maxint *o1, const struct maxint *i1, size_t shift);
+
+/* mi_shr: o1 = i1 >> shift
+ * allowed: o1 == i1 */
+int mi_shr(struct maxint *o1, const struct maxint *i1, size_t shift);
+
+/* mi_mul: o1 = i1 * i2
+ * allowed: o1 == i1 */
+int mi_mul(struct maxint *o1, const struct maxint *i1, const struct maxint *i2);
+
+/* mi_div: o1 = i1 / i2, o2 = i1 % i2
+ * allowed: o1 == i1 */
+int mi_div(struct maxint *o1, struct maxint *o2,
+ const struct maxint *i1, const struct maxint *i2);
+
+/* mi_swap: o1 <=> o2 */
+void mi_swap(struct maxint *o1, struct maxint *o2);
+
+/* mi_gcm: o1 = gcm(i1, i2)
+ * allowed: o1 == i1 */
+int mi_gcm(struct maxint *o1, const struct maxint *i1, const struct maxint *i2);
+
+/* mi_lcm: o1 = lcm(i1, i2)
+ * allowed: o1 == i1 */
+int mi_lcm(struct maxint *o1, const struct maxint *i1, const struct maxint *i2);
+
+/* mi_cmp: (i1 < i2) => -1 | (i1 == i2) => 0 | (i1 < i2) => 1 */
+int mi_cmp(const struct maxint *i1, const struct maxint *i2);
+
+static inline void
+mi_init(struct maxint *m)
+{
+ m->data = NULL;
+ m->size = 0;
+ m->cap = 0;
+ m->sign = MI_POS;
+}
+
+static inline struct maxint *
+mi_imm(struct maxint *mi_tmp, mi_ul *ul_tmp, mi_ul imm, int sign)
+{
+ mi_tmp->sign = sign;
+ if (mi_tmp->data) {
+ mi_tmp->data[0] = imm;
+ } else {
+ *ul_tmp = imm;
+ mi_tmp->data = ul_tmp;
+ }
+ mi_tmp->size = 1;
+ return mi_tmp;
+}
+
+static inline mi_ul
+mi_cast_ul(const struct maxint *m)
+{
+ return m->data[0];
+}
+
+static inline mi_uls
+mi_cast_uls(const struct maxint *m)
+{
+ mi_uls val;
+ mi_ul mask;
+
+ mask = (((mi_ul) 1) << (MI_ULS_BITS - 1)) - 1;
+ val = (mi_uls) (m->data[0] & mask);
+
+ return val * MI_TO_SIGN(m);;
+}
+
diff --git a/src/lib/libmaxint/libmaxint.api b/src/lib/libmaxint/libmaxint.api
@@ -0,0 +1,26 @@
+mi_init
+mi_deinit
+mi_set
+mi_setv
+mi_setstr
+
+mi_resize
+mi_cast_ul
+mi_zero
+mi_lastset
+
+mi_str
+mi_hex
+mi_dec
+mi_bin
+
+mi_add
+mi_sub
+mi_shl
+mi_shr
+mi_mul
+mi_div
+mi_swap
+mi_gcm
+mi_lcm
+mi_cmp
diff --git a/src/lib/libmaxint/libmaxint.lds b/src/lib/libmaxint/libmaxint.lds
@@ -0,0 +1,30 @@
+LIBMAXINT_1.0 {
+ global:
+ mi_init;
+ mi_deinit;
+ mi_set;
+ mi_setv;
+ mi_setstr;
+
+ mi_resize;
+ mi_cast_ul;
+ mi_zero;
+ mi_lastset;
+
+ mi_str;
+ mi_hex;
+ mi_dec;
+ mi_bin;
+
+ mi_add;
+ mi_sub;
+ mi_shl;
+ mi_shr;
+ mi_mul;
+ mi_div;
+ mi_swap;
+ mi_gcm;
+ mi_lcm;
+ mi_cmp;
+ local: *;
+};
diff --git a/src/lib/libmaxint/src/maxint.c b/src/lib/libmaxint/src/maxint.c
@@ -0,0 +1,544 @@
+#include "maxint.h"
+
+#include <sys/types.h>
+#include <err.h>
+#include <string.h>
+#include <stdbool.h>
+
+#define MAXINT_MAX_OVERCOMMIT 256
+
+#define MIN(a, b) ((b) > (a) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define DIVCEIL(a, b) ((a) / (b) + ((a) % (b) != 0))
+
+static mi_ul mi_one_v = 1;
+static const struct maxint mi_one = {
+ .data = &mi_one_v,
+ .size = 1,
+ .cap = 1,
+ .sign = MI_POS
+};
+
+void
+mi_deinit(struct maxint *m)
+{
+ free(m->data);
+}
+
+int
+mi_set(struct maxint *m1, const struct maxint *m2)
+{
+ int rc;
+
+ rc = mi_resize(m1, m2->size);
+ if (rc) return rc;
+
+ memcpy(m1->data, m2->data, m2->size * MI_UL_BYTES);
+ m1->sign = m2->sign;
+
+ return 0;
+}
+
+int
+mi_setv(struct maxint *m, mi_ul v, int sign)
+{
+ int rc;
+
+ rc = mi_resize(m, 1);
+ if (rc) return rc;
+
+ memcpy(m->data, &v, MI_UL_BYTES);
+ m->sign = sign;
+
+ return 0;
+}
+
+int
+mi_setstr(struct maxint *m, const char *str, const char **end, const char *alph)
+{
+ const char *p, *cp;
+ struct maxint tmp, base;
+ int sign;
+ int rc;
+
+ sign = MI_POS;
+ if (*str == '-') {
+ sign = MI_NEG;
+ str++;
+ } else if (*str == '+') {
+ str++;
+ }
+
+ mi_init(&base);
+ rc = mi_setv(&base, strlen(alph), sign);
+ if (rc) return rc;
+
+ mi_init(&tmp);
+ rc = mi_setv(m, 0, MI_POS);
+ if (rc) return rc;
+
+ for (p = str; *p && (cp = strchr(alph, *p)); p++) {
+ rc = mi_mul(m, m, &base);
+ if (rc) return rc;
+
+ rc = mi_setv(&tmp, (mi_ul) (cp - alph), sign);
+ if (rc) return rc;
+
+ rc = mi_add(m, m, &tmp);
+ if (rc) return rc;
+ }
+
+ *end = p;
+
+ mi_deinit(&tmp);
+ mi_deinit(&base);
+
+ return 0;
+}
+
+int
+mi_resize(struct maxint *m, size_t size)
+{
+ void *alloc;
+
+ if (m->cap < size || m->cap > size + MAXINT_MAX_OVERCOMMIT) {
+ if (size > m->cap) {
+ m->cap = MAX(size, m->cap * 2);
+ m->cap = MIN(m->cap, size + MAXINT_MAX_OVERCOMMIT - 1);
+ } else {
+ m->cap = size + 1;
+ }
+ if (m->cap) {
+ /* intentionally separate case m->cap == 0,
+ * since data pointer might point to immediate */
+ alloc = realloc(m->data, m->cap * MI_UL_BYTES);
+ if (!alloc) return -MI_ERR_OOM;
+ } else {
+ alloc = malloc(m->cap * MI_UL_BYTES);
+ if (!alloc) return -MI_ERR_OOM;
+ }
+ m->data = alloc;
+ }
+
+ if (size > m->size)
+ memset(&m->data[m->size], 0, (size - m->size) * MI_UL_BYTES);
+
+ m->size = size;
+
+ return 0;
+}
+
+static int
+mi_add_internal(struct maxint *o1, const struct maxint *i1,
+ const struct maxint *i2, int i2_sign)
+{
+ mi_ul v;
+ size_t carry;
+ size_t i, msbs;
+ int rc;
+
+ if (o1 != i1) {
+ rc = mi_set(o1, i1);
+ if (rc) return rc;
+ }
+
+ msbs = i2->data[i2->size - 1] >> (MI_UL_BITS - 1);
+ msbs &= i1->data[i1->size - 1] >> (MI_UL_BITS - 1);
+ rc = mi_resize(o1, MAX(i1->size, i2->size) + (msbs != 0));
+ if (rc) return rc;
+
+ if (o1->sign != i2_sign) {
+ for (carry = i = 0; i < o1->size; i++) {
+ v = (i < i2->size ? i2->data[i] : 0);
+ o1->data[i] -= v + carry;
+ carry = (o1->data[i] + v + carry) < o1->data[i];
+ }
+
+ if (carry == 1) {
+ if (o1->data[0] > 0) {
+ o1->data[0]--;
+ for (i = 0; i < o1->size; i++)
+ o1->data[i] = ~o1->data[i];
+ } else {
+ for (i = 0; i < o1->size; i++)
+ o1->data[i] = ~o1->data[i];
+ o1->data[0]++;
+ }
+ o1->sign ^= 1;
+ }
+ } else {
+ for (carry = i = 0; i < o1->size; i++) {
+ v = (i < i2->size ? i2->data[i] : 0);
+ o1->data[i] += v + carry;
+ carry = (o1->data[i] - v - carry) > o1->data[i];
+ }
+ }
+
+ return 0;
+}
+
+size_t
+mi_lastset(const struct maxint *m1)
+{
+ size_t i;
+
+ for (i = m1->size - 1; (ssize_t) i >= 0; i--)
+ if (m1->data[i] != 0) return i;
+
+ return 0;
+}
+
+int
+mi_add(struct maxint *o1, const struct maxint *i1, const struct maxint *i2)
+{
+ return mi_add_internal(o1, i1, i2, i2->sign);
+}
+
+int
+mi_sub(struct maxint *o1, const struct maxint *i1, const struct maxint *i2)
+{
+ return mi_add_internal(o1, i1, i2, i2->sign ^ 1);
+}
+
+int
+mi_shl(struct maxint *o1, const struct maxint *i1, size_t shift)
+{
+ size_t i;
+ mi_ul bit;
+ int rc;
+
+ if (!shift) return 0;
+
+ rc = mi_resize(o1, mi_lastset(i1) + 1 + DIVCEIL(shift, MI_UL_BITS));
+ if (rc) return rc;
+
+ o1->sign = i1->sign;
+ for (i = o1->size * MI_UL_BITS - 1; (ssize_t) i >= 0; i--) {
+ o1->data[i / MI_UL_BITS] &= ~(((mi_ul) 1) << (i % MI_UL_BITS));
+ if (i >= shift) {
+ bit = MI_BITA(i1->data, MI_UL_BITS, i - shift);
+ o1->data[i / MI_UL_BITS] |= bit << (i % MI_UL_BITS);
+ }
+ }
+
+ return 0;
+}
+
+int
+mi_shr(struct maxint *o1, const struct maxint *i1, size_t shift)
+{
+ size_t i, bit;
+ int rc;
+
+ if (!shift) return 0;
+
+ if (o1 != i1) {
+ rc = mi_resize(o1, mi_lastset(i1) + 1 - shift / MI_UL_BITS);
+ if (rc) return rc;
+ }
+
+ o1->sign = i1->sign;
+ for (i = 0; i < i1->size * MI_UL_BITS; i++) {
+ i1->data[i / MI_UL_BITS] &= ~((mi_ul) 1 << (i % MI_UL_BITS));
+ if (i + shift < i1->size * MI_UL_BITS) {
+ bit = MI_BITA(i1->data, MI_UL_BITS, i + shift);
+ i1->data[i / MI_UL_BITS] |= (mi_ul) bit << (i % MI_UL_BITS);
+ }
+ }
+
+ return 0;
+}
+
+int
+mi_mul(struct maxint *osum, const struct maxint *i1, const struct maxint *i2)
+{
+ struct maxint isum = MI_INIT;
+ struct maxint *sum;
+ size_t i;
+ int rc = 0;
+
+ sum = (osum == i1) ? &isum : osum;
+
+ rc = mi_setv(sum, 0, MI_POS);
+ if (rc) goto exit;
+
+ for (i = i1->size * MI_UL_BITS - 1; (ssize_t) i >= 0; i--) {
+ rc = mi_shl(sum, sum, 1);
+ if (rc) goto exit;
+
+ if (MI_BITA(i1->data, MI_UL_BITS, i)) {
+ if (i1->sign == MI_POS) {
+ rc = mi_add(sum, sum, i2);
+ if (rc) goto exit;
+ } else {
+ rc = mi_sub(sum, sum, i2);
+ if (rc) goto exit;
+ }
+ }
+ }
+
+ if (osum == i1) {
+ mi_swap(sum, osum);
+ mi_deinit(sum);
+ }
+
+exit:
+
+ return rc;
+}
+
+int
+mi_div(struct maxint *oquot, struct maxint *orem,
+ const struct maxint *num, const struct maxint *div)
+{
+ struct maxint iquot = MI_INIT;
+ struct maxint irem = MI_INIT;
+ struct maxint *quot, *rem;
+ size_t i;
+ int rc;
+
+ quot = (oquot == NULL || oquot == num || oquot == div) ? &iquot : oquot;
+ rem = (orem == NULL || orem == num || orem == div) ? &irem : orem;
+
+ rc = mi_setv(quot, 0, MI_POS);
+ if (rc) goto exit;
+
+ rc = mi_setv(rem, 0, MI_POS);
+ if (rc) goto exit;
+
+ for (i = num->size * MI_UL_BITS - 1; (ssize_t) i >= 0; i--) {
+ /* rem = rem * 2 + [bit set] */
+ rc = mi_shl(rem, rem, 1);
+ if (rc) goto exit;
+ if (MI_BITA(num->data, MI_UL_BITS, i)) {
+ rc = mi_add(rem, rem, &mi_one);
+ if (rc) goto exit;
+ }
+
+ /* quot = 2 * quot + [rem >= div] */
+ rc = mi_shl(quot, quot, 1);
+ if (rc) goto exit;
+ if (mi_cmp(rem, div) >= 0) {
+ rc = mi_add(quot, quot, &mi_one);
+ if (rc) goto exit;
+ rc = mi_sub(rem, rem, div);
+ if (rc) goto exit;
+ }
+ }
+
+ if (quot->sign != div->sign) {
+ rc = mi_sub(rem, rem, div);
+ if (rc) goto exit;
+ }
+
+ rem->sign = div->sign;
+ quot->sign = num->sign ^ div->sign;
+
+ if (quot == &iquot && oquot)
+ mi_swap(&iquot, oquot);
+
+ if (rem == &irem && orem)
+ mi_swap(&irem, orem);
+
+exit:
+ mi_deinit(&iquot);
+ mi_deinit(&irem);
+
+ return rc;
+}
+
+void
+mi_swap(struct maxint *m1, struct maxint *m2)
+{
+ struct maxint tmp;
+
+ memcpy(&tmp, m1, sizeof(struct maxint));
+ memcpy(m1, m2, sizeof(struct maxint));
+ memcpy(m2, &tmp, sizeof(struct maxint));
+}
+
+int
+mi_gcd(struct maxint *o1, const struct maxint *i1, const struct maxint *i2)
+{
+ struct maxint rem = MI_INIT;
+ struct maxint tmp = MI_INIT;
+ int rc = 0;
+
+ rc = mi_setv(&rem, 0, MI_POS);
+ if (rc) goto exit;
+
+ rc = mi_set(&tmp, i2);
+ if (rc) goto exit;
+
+ if (o1 != i1) {
+ rc = mi_set(o1, i1);
+ if (rc) goto exit;
+ }
+
+ do {
+ rc = mi_div(o1, &rem, o1, &tmp);
+ if (rc) goto exit;
+ mi_swap(o1, &tmp);
+ mi_swap(&tmp, &rem);
+ } while (!mi_zero(&tmp));
+
+exit:
+ mi_deinit(&rem);
+ mi_deinit(&tmp);
+
+ return rc;
+}
+
+int
+mi_lcm(struct maxint *o1, const struct maxint *i1, const struct maxint *i2)
+{
+ struct maxint lcm = MI_INIT;
+ int rc = 0;
+
+ rc = mi_set(&lcm, i1);
+ if (rc) goto exit;
+
+ rc = mi_gcd(&lcm, i1, i2);
+ if (rc) goto exit;
+
+ rc = mi_mul(o1, i1, i2);
+ if (rc) goto exit;
+
+ rc = mi_div(o1, NULL, o1, &lcm);
+ if (rc) goto exit;
+
+exit:
+ mi_deinit(&lcm);
+
+ return rc;
+}
+
+int
+mi_cmp(const struct maxint *m1, const struct maxint *m2)
+{
+ mi_ul v1, v2;
+ size_t i;
+ int sign;
+
+ if (m1->sign != m2->sign) {
+ if (m1->size == 1 && m2->size == 1
+ && mi_cast_ul(m1) == 0 && mi_cast_ul(m2) == 0)
+ return 0;
+
+ if (m1->sign == MI_NEG)
+ return -1;
+ else
+ return 1;
+ }
+
+ sign = m1->sign;
+ for (i = 0; i < MAX(m1->size, m2->size); i++) {
+ v1 = (i >= m1->size ? 0 : m1->data[i]);
+ v2 = (i >= m2->size ? 0 : m2->data[i]);
+ if (v1 < v2) {
+ return sign == MI_POS ? -1 : 1;
+ } else if (v1 > v2) {
+ return sign == MI_POS ? 1 : -1;
+ }
+ }
+
+ return 0;
+}
+
+bool
+mi_zero(const struct maxint *m1)
+{
+ size_t i;
+
+ for (i = 0; i < m1->size; i++) {
+ if (m1->data[i] != 0)
+ return false;
+ }
+
+ return true;
+}
+
+ssize_t
+mi_str(const struct maxint *m, char *buf, size_t buflen,
+ mi_ul base_ul, const char *alph, size_t zfill)
+{
+ struct maxint num = MI_INIT;
+ struct maxint rem = MI_INIT;
+ struct maxint base = MI_INIT;
+ size_t i, len;
+ ssize_t ret = -1;
+ mi_ul rem_ul;
+ char tmp;
+ int rc;
+
+ if (!base_ul) return -1;
+ if (!buflen) return -1;
+
+ rc = mi_set(&num, m);
+ if (rc) goto exit;
+ num.sign = MI_POS;
+
+ rc = mi_setv(&rem, 0, MI_POS);
+ if (rc) goto exit;
+
+ rc = mi_setv(&base, base_ul, MI_POS);
+ if (rc) goto exit;
+
+ len = 0;
+ do {
+ if (mi_div(&num, &rem, &num, &base))
+ goto exit;
+ rem_ul = mi_cast_ul(&rem);
+
+ buf[len++] = alph[rem_ul];
+ if (len == buflen)
+ goto exit;
+ } while (!mi_zero(&num));
+
+ if (zfill >= buflen) goto exit;
+ for (; len < zfill; len++) {
+ buf[len] = alph[0];
+ }
+
+ if (m->sign == MI_NEG) {
+ buf[len++] = '-';
+ if (len == buflen)
+ goto exit;
+ }
+
+ for (i = 0; i < len / 2; i++) {
+ tmp = buf[i];
+ buf[i] = buf[len - 1 - i];
+ buf[len - 1 - i] = tmp;
+ }
+
+ buf[len++] = '\0';
+ ret = (ssize_t) len;
+
+exit:
+ mi_deinit(&rem);
+ mi_deinit(&num);
+ mi_deinit(&base);
+
+ return ret;
+}
+
+ssize_t
+mi_hex(const struct maxint *m, char *buf, size_t buflen,
+ bool capital, size_t zfill)
+{
+ return mi_str(m, buf, buflen, 16,
+ capital ? "0123456789ABCDEF" : "0123456789abcdef", zfill);
+}
+
+ssize_t
+mi_dec(const struct maxint *m, char *buf, size_t buflen, size_t zfill)
+{
+ return mi_str(m, buf, buflen, 10, "0123456789", zfill);
+}
+
+ssize_t
+mi_bin(const struct maxint *m, char *buf, size_t buflen, size_t zfill)
+{
+ return mi_str(m, buf, buflen, 2, "01", zfill);
+}
diff --git a/src/lib/libmaxint/src/test.c b/src/lib/libmaxint/src/test.c
@@ -0,0 +1,89 @@
+#include "maxint.h"
+
+#include <stdio.h>
+
+int
+main(int argc, const char **argv)
+{
+ char sa[256], sb[256], sc[256];
+ struct maxint a = MI_INIT;
+ struct maxint b = MI_INIT;
+ struct maxint c = MI_INIT;
+ int rc = 0, val;
+
+ rc |= mi_setv(&a, 9, MI_POS);
+ rc |= mi_setv(&b, 1014, MI_POS);
+ rc |= mi_sub(&c, &a, &b);
+ rc |= mi_dec(&a, sa, sizeof(sa), 0) < 0;
+ rc |= mi_dec(&b, sb, sizeof(sb), 0) < 0;
+ rc |= mi_dec(&c, sc, sizeof(sc), 0) < 0;
+ if (rc) goto exit;
+ printf("SUB: %s - %s = %s\n", sa, sb, sc);
+
+ rc |= mi_setv(&a, 10000000000000000000ULL, MI_POS);
+ rc |= mi_setv(&b, 10000000000000000000ULL, MI_POS);
+ rc |= mi_add(&c, &a, &b);
+ rc |= mi_dec(&a, sa, sizeof(sa), 0) < 0;
+ rc |= mi_dec(&b, sb, sizeof(sb), 0) < 0;
+ rc |= mi_dec(&c, sc, sizeof(sc), 0) < 0;
+ if (rc) goto exit;
+ printf("ADD: %s + %s = %s\n", sa, sb, sc);
+
+ rc |= mi_setv(&a, 1, MI_NEG);
+ rc |= mi_setv(&b, 1, MI_NEG);
+ rc |= mi_mul(&c, &a, &b);
+ rc |= mi_bin(&a, sa, sizeof(sa), 0) < 0;
+ rc |= mi_bin(&b, sb, sizeof(sb), 0) < 0;
+ rc |= mi_bin(&c, sc, sizeof(sc), 0) < 0;
+ if (rc) goto exit;
+ printf("MUL: %s * %s = %s\n", sa, sb, sc);
+
+ rc |= mi_setv(&a, 3, MI_NEG);
+ rc |= mi_setv(&b, 3, MI_NEG);
+ val = mi_cmp(&a, &b);
+ rc |= mi_bin(&a, sa, sizeof(sa), 0) < 0;
+ rc |= mi_bin(&b, sb, sizeof(sb), 0) < 0;
+ if (rc) goto exit;
+ printf("CMP: %s vs %s = %i\n", sa, sb, val);
+
+ rc |= mi_setv(&a, 10000000000000000000ULL, MI_POS);
+ rc |= mi_bin(&a, sa, sizeof(sa), 0) < 0;
+ rc |= mi_shl(&b, &a, 2);
+ rc |= mi_bin(&b, sb, sizeof(sb), 0) < 0;
+ if (rc) goto exit;
+ printf("SHL: %s << 2 = %s\n", sa, sb);
+
+ rc |= mi_setv(&a, 10000000000000000000ULL, MI_POS);
+ rc |= mi_setv(&b, 10000000000000000000ULL, MI_POS);
+ rc |= mi_mul(&c, &a, &b);
+ if (rc) goto exit;
+ printf("%lu * %lu != %lu\n", a.data[0], b.data[0], a.data[0] * b.data[0]);
+ rc |= mi_dec(&a, sa, sizeof(sa), 0) < 0;
+ rc |= mi_dec(&b, sb, sizeof(sb), 0) < 0;
+ rc |= mi_dec(&c, sc, sizeof(sc), 0) < 0;
+ if (rc) goto exit;
+ printf("%s * %s = %s\n", sa, sb, sc);
+
+ rc |= mi_setv(&b, 33, MI_POS);
+ if (rc) goto exit;
+ rc |= mi_dec(&a, sa, sizeof(sa), 0) < 0;
+ rc |= mi_dec(&b, sb, sizeof(sb), 0) < 0;
+ printf("%s / %s = ", sa, sb);
+ rc |= mi_div(&a, &c, &a, &b);
+ rc |= mi_dec(&a, sa, sizeof(sa), 0) < 0;
+ rc |= mi_dec(&c, sc, sizeof(sc), 0) < 0;
+ if (rc) goto exit;
+ printf("%s R %s\n", sa, sc);
+
+ mi_dec(&a, sa, sizeof(sa), 0);
+ printf("DEC: %s\n", sa);
+ mi_hex(&a, sa, sizeof(sa), true, 0);
+ printf("HEX: %s\n", sa);
+
+exit:
+ mi_deinit(&a);
+ mi_deinit(&b);
+ mi_deinit(&c);
+
+ return rc;
+}