aoc-2019-c

git clone https://git.sinitax.com/sinitax/aoc-2019-c
Log | Files | Refs | README | sfeed.txt

commit 3f40d51dc0273948d88b3ea07fafaf847e305138
Author: Louis Burda <quent.burda@gmail.com>
Date:   Fri, 15 Mar 2024 22:56:14 +0100

Squashed 'src/lib/libmaxint/' content from commit a236ac4

git-subtree-dir: src/lib/libmaxint
git-subtree-split: a236ac40c73efcab3995c08b3d8f2eff33a9cbfc

Diffstat:
A.gitignore | 3+++
AMakefile | 39+++++++++++++++++++++++++++++++++++++++
Ainclude/maxint.h | 151++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alibmaxint.api | 26++++++++++++++++++++++++++
Alibmaxint.lds | 30++++++++++++++++++++++++++++++
Asrc/maxint.c | 544+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test.c | 89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 882 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,3 @@ +compile_commands.json +build +.cache diff --git a/Makefile b/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/include/maxint.h b/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/libmaxint.api b/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/libmaxint.lds b/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/maxint.c b/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/test.c b/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; +}