aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/stdlib/bigint.c548
-rw-r--r--src/stdlib/bigint.h212
-rw-r--r--src/stdlib/bytes.h1
-rw-r--r--src/stdlib/int16.c3
-rw-r--r--src/stdlib/int16.h4
-rw-r--r--src/stdlib/int32.c3
-rw-r--r--src/stdlib/int32.h4
-rw-r--r--src/stdlib/int64.c3
-rw-r--r--src/stdlib/int64.h4
-rw-r--r--src/stdlib/int8.c3
-rw-r--r--src/stdlib/int8.h4
-rw-r--r--src/stdlib/intX.c.h275
-rw-r--r--src/stdlib/intX.h167
-rw-r--r--src/stdlib/integers.c747
-rw-r--r--src/stdlib/integers.h395
15 files changed, 1236 insertions, 1137 deletions
diff --git a/src/stdlib/bigint.c b/src/stdlib/bigint.c
new file mode 100644
index 00000000..3ed3f3cf
--- /dev/null
+++ b/src/stdlib/bigint.c
@@ -0,0 +1,548 @@
+// Integer type infos and methods
+
+#include <stdio.h> // Must be before gmp.h
+
+#include <ctype.h>
+#include <gc.h>
+#include <gmp.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "datatypes.h"
+#include "integers.h"
+#include "optionals.h"
+#include "print.h"
+#include "siphash.h"
+#include "text.h"
+#include "types.h"
+
+public
+int Int$print(FILE *f, Int_t i) {
+ if (likely(i.small & 1L)) {
+ return _print_int(f, (int64_t)((i.small) >> 2L));
+ } else {
+ return gmp_fprintf(f, "%Zd", *i.big);
+ }
+}
+
+static Text_t _int64_to_text(int64_t n) {
+ if (n == INT64_MIN) return Text("-9223372036854775808");
+
+ char buf[21] = {[20] = 0}; // Big enough for INT64_MIN + '\0'
+ char *p = &buf[19];
+ bool negative = n < 0;
+ if (negative) n = -n; // Safe to do because we checked for INT64_MIN earlier
+
+ do {
+ *(p--) = '0' + (n % 10);
+ n /= 10;
+ } while (n > 0);
+
+ if (negative) *(p--) = '-';
+
+ return Text$from_strn(p + 1, (size_t)(&buf[19] - p));
+}
+
+public
+Text_t Int$value_as_text(Int_t i) {
+ if (likely(i.small & 1L)) {
+ return _int64_to_text(i.small >> 2L);
+ } else {
+ char *str = mpz_get_str(NULL, 10, *i.big);
+ return Text$from_str(str);
+ }
+}
+
+public
+Text_t Int$as_text(const void *i, bool colorize, const TypeInfo_t *info) {
+ (void)info;
+ if (!i) return Text("Int");
+ Text_t text = Int$value_as_text(*(Int_t *)i);
+ if (colorize) text = Text$concat(Text("\x1b[35m"), text, Text("\x1b[m"));
+ return text;
+}
+
+static bool Int$is_none(const void *i, const TypeInfo_t *info) {
+ (void)info;
+ return ((Int_t *)i)->small == 0L;
+}
+
+public
+PUREFUNC int32_t Int$compare_value(const Int_t x, const Int_t y) {
+ if (likely(x.small & y.small & 1L)) return (x.small > y.small) - (x.small < y.small);
+ else if (x.small & 1) return -mpz_cmp_si(*y.big, x.small);
+ else if (y.small & 1) return mpz_cmp_si(*x.big, y.small);
+ else return x.big == y.big ? 0 : mpz_cmp(*x.big, *y.big);
+}
+
+public
+PUREFUNC int32_t Int$compare(const void *x, const void *y, const TypeInfo_t *info) {
+ (void)info;
+ return Int$compare_value(*(Int_t *)x, *(Int_t *)y);
+}
+
+public
+PUREFUNC bool Int$equal_value(const Int_t x, const Int_t y) {
+ if (likely((x.small | y.small) & 1L)) return x.small == y.small;
+ else return x.big == y.big ? 0 : (mpz_cmp(*x.big, *y.big) == 0);
+}
+
+public
+PUREFUNC bool Int$equal(const void *x, const void *y, const TypeInfo_t *info) {
+ (void)info;
+ return Int$equal_value(*(Int_t *)x, *(Int_t *)y);
+}
+
+public
+CONSTFUNC Int_t Int$clamped(Int_t x, Int_t low, Int_t high) {
+ return (Int$compare(&x, &low, &Int$info) <= 0) ? low : (Int$compare(&x, &high, &Int$info) >= 0 ? high : x);
+}
+
+public
+CONSTFUNC bool Int$is_between(const Int_t x, const Int_t low, const Int_t high) {
+ return Int$compare_value(low, x) <= 0 && Int$compare_value(x, high) <= 0;
+}
+
+public
+PUREFUNC uint64_t Int$hash(const void *vx, const TypeInfo_t *info) {
+ (void)info;
+ Int_t *x = (Int_t *)vx;
+ if (likely(x->small & 1L)) {
+ return siphash24((void *)x, sizeof(Int_t));
+ } else {
+ char *str = mpz_get_str(NULL, 16, *x->big);
+ return siphash24((void *)str, strlen(str));
+ }
+}
+
+public
+Text_t Int$hex(Int_t i, Int_t digits_int, bool uppercase, bool prefix) {
+ if (Int$is_negative(i)) return Text$concat(Text("-"), Int$hex(Int$negative(i), digits_int, uppercase, prefix));
+
+ if (likely(i.small & 1L)) {
+ uint64_t u64 = (uint64_t)(i.small >> 2);
+ return Text$from_str(String(
+ hex(u64, .no_prefix = !prefix, .digits = Int32$from_int(digits_int, false), .uppercase = uppercase)));
+ } else {
+ char *str = mpz_get_str(NULL, 16, *i.big);
+ if (uppercase) {
+ for (char *c = str; *c; c++)
+ *c = (char)toupper(*c);
+ }
+ int64_t digits = Int64$from_int(digits_int, false);
+ int64_t needed_zeroes = digits - (int64_t)strlen(str);
+ if (needed_zeroes <= 0) return prefix ? Text$concat(Text("0x"), Text$from_str(str)) : Text$from_str(str);
+
+ char *zeroes = GC_MALLOC_ATOMIC((size_t)(needed_zeroes));
+ memset(zeroes, '0', (size_t)(needed_zeroes));
+ if (prefix) return Text$concat(Text("0x"), Text$from_str(zeroes), Text$from_str(str));
+ else return Text$concat(Text$from_str(zeroes), Text$from_str(str));
+ }
+}
+
+public
+Text_t Int$octal(Int_t i, Int_t digits_int, bool prefix) {
+ if (Int$is_negative(i)) return Text$concat(Text("-"), Int$octal(Int$negative(i), digits_int, prefix));
+
+ if (likely(i.small & 1L)) {
+ uint64_t u64 = (uint64_t)(i.small >> 2);
+ return Text$from_str(String(oct(u64, .no_prefix = !prefix, .digits = Int32$from_int(digits_int, false))));
+ } else {
+ int64_t digits = Int64$from_int(digits_int, false);
+ char *str = mpz_get_str(NULL, 8, *i.big);
+ int64_t needed_zeroes = digits - (int64_t)strlen(str);
+ if (needed_zeroes <= 0) return prefix ? Text$concat(Text("0o"), Text$from_str(str)) : Text$from_str(str);
+
+ char *zeroes = GC_MALLOC_ATOMIC((size_t)(needed_zeroes));
+ memset(zeroes, '0', (size_t)(needed_zeroes));
+ if (prefix) return Text$concat(Text("0o"), Text$from_str(zeroes), Text$from_str(str));
+ else return Text$concat(Text$from_str(zeroes), Text$from_str(str));
+ }
+}
+
+public
+Int_t Int$slow_plus(Int_t x, Int_t y) {
+ mpz_t result;
+ mpz_init_set_int(result, x);
+ if (y.small & 1L) {
+ if (y.small < 0L) mpz_sub_ui(result, result, (uint64_t)(-(y.small >> 2L)));
+ else mpz_add_ui(result, result, (uint64_t)(y.small >> 2L));
+ } else {
+ mpz_add(result, result, *y.big);
+ }
+ return Int$from_mpz(result);
+}
+
+public
+Int_t Int$slow_minus(Int_t x, Int_t y) {
+ mpz_t result;
+ mpz_init_set_int(result, x);
+ if (y.small & 1L) {
+ if (y.small < 0L) mpz_add_ui(result, result, (uint64_t)(-(y.small >> 2L)));
+ else mpz_sub_ui(result, result, (uint64_t)(y.small >> 2L));
+ } else {
+ mpz_sub(result, result, *y.big);
+ }
+ return Int$from_mpz(result);
+}
+
+public
+Int_t Int$slow_times(Int_t x, Int_t y) {
+ mpz_t result;
+ mpz_init_set_int(result, x);
+ if (y.small & 1L) mpz_mul_si(result, result, y.small >> 2L);
+ else mpz_mul(result, result, *y.big);
+ return Int$from_mpz(result);
+}
+
+public
+Int_t Int$slow_divided_by(Int_t dividend, Int_t divisor) {
+ // Euclidean division, see:
+ // https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf
+ mpz_t quotient, remainder;
+ mpz_init_set_int(quotient, dividend);
+ mpz_init_set_int(remainder, divisor);
+ mpz_tdiv_qr(quotient, remainder, quotient, remainder);
+ if (mpz_sgn(remainder) < 0) {
+ bool d_positive = likely(divisor.small & 1L) ? divisor.small > 0x1L : mpz_sgn(*divisor.big) > 0;
+ if (d_positive) mpz_sub_ui(quotient, quotient, 1);
+ else mpz_add_ui(quotient, quotient, 1);
+ }
+ return Int$from_mpz(quotient);
+}
+
+public
+Int_t Int$slow_modulo(Int_t x, Int_t modulus) {
+ mpz_t result;
+ mpz_init_set_int(result, x);
+ mpz_t divisor;
+ mpz_init_set_int(divisor, modulus);
+ mpz_mod(result, result, divisor);
+ return Int$from_mpz(result);
+}
+
+public
+Int_t Int$slow_modulo1(Int_t x, Int_t modulus) {
+ mpz_t result;
+ mpz_init_set_int(result, x);
+ mpz_sub_ui(result, result, 1);
+ mpz_t divisor;
+ mpz_init_set_int(divisor, modulus);
+ mpz_mod(result, result, divisor);
+ mpz_add_ui(result, result, 1);
+ return Int$from_mpz(result);
+}
+
+public
+Int_t Int$slow_left_shifted(Int_t x, Int_t y) {
+ mp_bitcnt_t bits = (mp_bitcnt_t)Int64$from_int(y, false);
+ mpz_t result;
+ mpz_init_set_int(result, x);
+ mpz_mul_2exp(result, result, bits);
+ return Int$from_mpz(result);
+}
+
+public
+Int_t Int$slow_right_shifted(Int_t x, Int_t y) {
+ mp_bitcnt_t bits = (mp_bitcnt_t)Int64$from_int(y, false);
+ mpz_t result;
+ mpz_init_set_int(result, x);
+ mpz_tdiv_q_2exp(result, result, bits);
+ return Int$from_mpz(result);
+}
+
+public
+Int_t Int$slow_bit_and(Int_t x, Int_t y) {
+ mpz_t result;
+ mpz_init_set_int(result, x);
+ mpz_t y_mpz;
+ mpz_init_set_int(y_mpz, y);
+ mpz_and(result, result, y_mpz);
+ return Int$from_mpz(result);
+}
+
+public
+Int_t Int$slow_bit_or(Int_t x, Int_t y) {
+ mpz_t result;
+ mpz_init_set_int(result, x);
+ mpz_t y_mpz;
+ mpz_init_set_int(y_mpz, y);
+ mpz_ior(result, result, y_mpz);
+ return Int$from_mpz(result);
+}
+
+public
+Int_t Int$slow_bit_xor(Int_t x, Int_t y) {
+ mpz_t result;
+ mpz_init_set_int(result, x);
+ mpz_t y_mpz;
+ mpz_init_set_int(y_mpz, y);
+ mpz_xor(result, result, y_mpz);
+ return Int$from_mpz(result);
+}
+
+public
+Int_t Int$slow_negated(Int_t x) {
+ mpz_t result;
+ mpz_init_set_int(result, x);
+ mpz_neg(result, result);
+ mpz_sub_ui(result, result, 1);
+ return Int$from_mpz(result);
+}
+
+public
+Int_t Int$slow_negative(Int_t x) {
+ if (likely(x.small & 1L)) return (Int_t){.small = 4L * -((x.small) >> 2L) + 1L};
+
+ mpz_t result;
+ mpz_init_set_int(result, x);
+ mpz_neg(result, result);
+ return Int$from_mpz(result);
+}
+
+public
+Int_t Int$abs(Int_t x) {
+ if (likely(x.small & 1L)) return (Int_t){.small = 4L * labs((x.small) >> 2L) + 1L};
+
+ mpz_t result;
+ mpz_init_set_int(result, x);
+ mpz_abs(result, result);
+ return Int$from_mpz(result);
+}
+
+public
+Int_t Int$power(Int_t base, Int_t exponent) {
+ int64_t exp = Int64$from_int(exponent, false);
+ if (unlikely(exp < 0)) fail("Cannot take a negative power of an integer!");
+ mpz_t result;
+ mpz_init_set_int(result, base);
+ mpz_pow_ui(result, result, (uint64_t)exp);
+ return Int$from_mpz(result);
+}
+
+public
+Int_t Int$gcd(Int_t x, Int_t y) {
+ if (likely(x.small & y.small & 0x1L)) return I_small(Int32$gcd(x.small >> 2L, y.small >> 2L));
+
+ mpz_t result;
+ mpz_init(result);
+ if (x.small & 0x1L) mpz_gcd_ui(result, *y.big, (uint64_t)labs(x.small >> 2L));
+ else if (y.small & 0x1L) mpz_gcd_ui(result, *x.big, (uint64_t)labs(y.small >> 2L));
+ else mpz_gcd(result, *x.big, *y.big);
+ return Int$from_mpz(result);
+}
+
+public
+OptionalInt_t Int$sqrt(Int_t i) {
+ if (Int$compare_value(i, I(0)) < 0) return NONE_INT;
+ mpz_t result;
+ mpz_init_set_int(result, i);
+ mpz_sqrt(result, result);
+ return Int$from_mpz(result);
+}
+
+public
+bool Int$get_bit(Int_t x, Int_t bit_index) {
+ mpz_t i;
+ mpz_init_set_int(i, x);
+ if (Int$compare_value(bit_index, I(1)) < 0) fail("Invalid bit index (expected 1 or higher): ", bit_index);
+ if (Int$compare_value(bit_index, Int$from_int64(INT64_MAX)) > 0) fail("Bit index is too large! ", bit_index);
+
+ int is_bit_set = mpz_tstbit(i, (mp_bitcnt_t)(Int64$from_int(bit_index, true) - 1));
+ return (bool)is_bit_set;
+}
+
+typedef struct {
+ OptionalInt_t current, last;
+ Int_t step;
+} IntRange_t;
+
+static OptionalInt_t _next_int(IntRange_t *info) {
+ OptionalInt_t i = info->current;
+ if (!Int$is_none(&i, &Int$info)) {
+ Int_t next = Int$plus(i, info->step);
+ if (!Int$is_none(&info->last, &Int$info)
+ && Int$compare_value(next, info->last) == Int$compare_value(info->step, I(0)))
+ next = NONE_INT;
+ info->current = next;
+ }
+ return i;
+}
+
+public
+PUREFUNC Closure_t Int$to(Int_t first, Int_t last, OptionalInt_t step) {
+ IntRange_t *range = GC_MALLOC(sizeof(IntRange_t));
+ range->current = first;
+ range->last = last;
+ range->step = Int$is_none(&step, &Int$info) ? Int$compare_value(last, first) >= 0
+ ? (Int_t){.small = (1L << 2L) | 1L}
+ : (Int_t){.small = (-1L >> 2L) | 1L}
+ : step;
+ return (Closure_t){.fn = _next_int, .userdata = range};
+}
+
+public
+PUREFUNC Closure_t Int$onward(Int_t first, Int_t step) {
+ IntRange_t *range = GC_MALLOC(sizeof(IntRange_t));
+ range->current = first;
+ range->last = NONE_INT;
+ range->step = step;
+ return (Closure_t){.fn = _next_int, .userdata = range};
+}
+
+public
+Int_t Int$from_str(const char *str) {
+ mpz_t i;
+ int result;
+ if (strncmp(str, "0x", 2) == 0) {
+ result = mpz_init_set_str(i, str + 2, 16);
+ } else if (strncmp(str, "0o", 2) == 0) {
+ result = mpz_init_set_str(i, str + 2, 8);
+ } else if (strncmp(str, "0b", 2) == 0) {
+ result = mpz_init_set_str(i, str + 2, 2);
+ } else {
+ result = mpz_init_set_str(i, str, 10);
+ }
+ if (result != 0) return NONE_INT;
+ return Int$from_mpz(i);
+}
+
+public
+OptionalInt_t Int$parse(Text_t text, Text_t *remainder) {
+ const char *str = Text$as_c_string(text);
+ mpz_t i;
+ int result;
+ if (strncmp(str, "0x", 2) == 0) {
+ const char *end = str + 2 + strspn(str + 2, "0123456789abcdefABCDEF");
+ if (remainder) *remainder = Text$from_str(end);
+ else if (*end != '\0') return NONE_INT;
+ result = mpz_init_set_str(i, str + 2, 16);
+ } else if (strncmp(str, "0o", 2) == 0) {
+ const char *end = str + 2 + strspn(str + 2, "01234567");
+ if (remainder) *remainder = Text$from_str(end);
+ else if (*end != '\0') return NONE_INT;
+ result = mpz_init_set_str(i, str + 2, 8);
+ } else if (strncmp(str, "0b", 2) == 0) {
+ const char *end = str + 2 + strspn(str + 2, "01");
+ if (remainder) *remainder = Text$from_str(end);
+ else if (*end != '\0') return NONE_INT;
+ result = mpz_init_set_str(i, str + 2, 2);
+ } else {
+ const char *end = str + strspn(str, "0123456789");
+ if (remainder) *remainder = Text$from_str(end);
+ else if (*end != '\0') return NONE_INT;
+ result = mpz_init_set_str(i, str, 10);
+ }
+ if (result != 0) {
+ if (remainder) *remainder = text;
+ return NONE_INT;
+ }
+ return Int$from_mpz(i);
+}
+
+public
+bool Int$is_prime(Int_t x, Int_t reps) {
+ mpz_t p;
+ mpz_init_set_int(p, x);
+ if (unlikely(Int$compare_value(reps, I(9999)) > 0))
+ fail("Number of prime-test repetitions should not be above 9999");
+ int reps_int = Int32$from_int(reps, false);
+ return (mpz_probab_prime_p(p, reps_int) != 0);
+}
+
+public
+Int_t Int$next_prime(Int_t x) {
+ mpz_t p;
+ mpz_init_set_int(p, x);
+ mpz_nextprime(p, p);
+ return Int$from_mpz(p);
+}
+
+#if __GNU_MP_VERSION >= 6
+#if __GNU_MP_VERSION_MINOR >= 3
+public
+OptionalInt_t Int$prev_prime(Int_t x) {
+ mpz_t p;
+ mpz_init_set_int(p, x);
+ if (unlikely(mpz_prevprime(p, p) == 0)) return NONE_INT;
+ return Int$from_mpz(p);
+}
+#endif
+#endif
+
+public
+Int_t Int$choose(Int_t n, Int_t k) {
+ if unlikely (Int$compare_value(n, I_small(0)) < 0) fail("Negative inputs are not supported for choose()");
+
+ mpz_t ret;
+ mpz_init(ret);
+
+ int64_t k_i64 = Int64$from_int(k, false);
+ if unlikely (k_i64 < 0) fail("Negative inputs are not supported for choose()");
+
+ if likely (n.small & 1L) {
+ mpz_bin_uiui(ret, (unsigned long)(n.small >> 2L), (unsigned long)k_i64);
+ } else {
+ mpz_t n_mpz;
+ mpz_init_set_int(n_mpz, n);
+ mpz_bin_ui(ret, n_mpz, (unsigned long)k_i64);
+ }
+ return Int$from_mpz(ret);
+}
+
+public
+Int_t Int$factorial(Int_t n) {
+ mpz_t ret;
+ mpz_init(ret);
+ int64_t n_i64 = Int64$from_int(n, false);
+ if unlikely (n_i64 < 0) fail("Factorials are not defined for negative numbers");
+ mpz_fac_ui(ret, (unsigned long)n_i64);
+ return Int$from_mpz(ret);
+}
+
+static void Int$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *info) {
+ (void)info;
+ Int_t i = *(Int_t *)obj;
+ if (likely(i.small & 1L)) {
+ fputc(0, out);
+ int64_t i64 = i.small >> 2L;
+ Int64$serialize(&i64, out, pointers, &Int64$info);
+ } else {
+ fputc(1, out);
+ mpz_t n;
+ mpz_init_set_int(n, *(Int_t *)obj);
+ mpz_out_raw(out, n);
+ }
+}
+
+static void Int$deserialize(FILE *in, void *obj, List_t *pointers, const TypeInfo_t *info) {
+ (void)info;
+ if (fgetc(in) == 0) {
+ int64_t i = 0;
+ Int64$deserialize(in, &i, pointers, &Int64$info);
+ *(Int_t *)obj = (Int_t){.small = (i << 2L) | 1L};
+ } else {
+ mpz_t n;
+ mpz_init(n);
+ mpz_inp_raw(n, in);
+ *(Int_t *)obj = Int$from_mpz(n);
+ }
+}
+
+public
+const TypeInfo_t Int$info = {
+ .size = sizeof(Int_t),
+ .align = __alignof__(Int_t),
+ .metamethods =
+ {
+ .compare = Int$compare,
+ .equal = Int$equal,
+ .hash = Int$hash,
+ .as_text = Int$as_text,
+ .is_none = Int$is_none,
+ .serialize = Int$serialize,
+ .deserialize = Int$deserialize,
+ },
+};
diff --git a/src/stdlib/bigint.h b/src/stdlib/bigint.h
new file mode 100644
index 00000000..558bc099
--- /dev/null
+++ b/src/stdlib/bigint.h
@@ -0,0 +1,212 @@
+// Big integer type (`Int` in Tomo)
+
+#include <gmp.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "datatypes.h"
+#include "stdlib.h"
+#include "types.h"
+#include "util.h"
+
+Text_t Int$as_text(const void *i, bool colorize, const TypeInfo_t *type);
+Text_t Int$value_as_text(Int_t i);
+PUREFUNC uint64_t Int$hash(const void *x, const TypeInfo_t *type);
+PUREFUNC int32_t Int$compare(const void *x, const void *y, const TypeInfo_t *type);
+PUREFUNC int32_t Int$compare_value(const Int_t x, const Int_t y);
+CONSTFUNC bool Int$is_between(const Int_t x, const Int_t low, const Int_t high);
+CONSTFUNC Int_t Int$clamped(Int_t x, Int_t low, Int_t high);
+PUREFUNC bool Int$equal(const void *x, const void *y, const TypeInfo_t *type);
+PUREFUNC bool Int$equal_value(const Int_t x, const Int_t y);
+Text_t Int$hex(Int_t i, Int_t digits, bool uppercase, bool prefix);
+Text_t Int$octal(Int_t i, Int_t digits, bool prefix);
+PUREFUNC Closure_t Int$to(Int_t first, Int_t last, OptionalInt_t step);
+PUREFUNC Closure_t Int$onward(Int_t first, Int_t step);
+OptionalInt_t Int$from_str(const char *str);
+OptionalInt_t Int$parse(Text_t text, Text_t *remainder);
+Int_t Int$abs(Int_t x);
+Int_t Int$power(Int_t base, Int_t exponent);
+Int_t Int$gcd(Int_t x, Int_t y);
+OptionalInt_t Int$sqrt(Int_t i);
+bool Int$get_bit(Int_t x, Int_t bit_index);
+
+#define BIGGEST_SMALL_INT 0x3fffffff
+#define SMALLEST_SMALL_INT -0x40000000
+
+#define Int$from_mpz(mpz) \
+ (mpz_cmpabs_ui(mpz, BIGGEST_SMALL_INT) <= 0 ? ((Int_t){.small = (mpz_get_si(mpz) << 2L) | 1L}) \
+ : ((Int_t){.big = memcpy(new (mpz_t), &mpz, sizeof(mpz_t))}))
+
+#define mpz_init_set_int(mpz, i) \
+ do { \
+ if likely ((i).small & 1L) mpz_init_set_si(mpz, (i).small >> 2L); \
+ else mpz_init_set(mpz, *(i).big); \
+ } while (0)
+
+#define I_small(i) ((Int_t){.small = (int64_t)((uint64_t)(i) << 2L) | 1L})
+#define I(i) _Generic(i, int8_t: I_small(i), int16_t: I_small(i), default: Int$from_int64(i))
+#define I_is_zero(i) ((i).small == 1L)
+
+Int_t Int$slow_plus(Int_t x, Int_t y);
+Int_t Int$slow_minus(Int_t x, Int_t y);
+Int_t Int$slow_times(Int_t x, Int_t y);
+Int_t Int$slow_divided_by(Int_t x, Int_t y);
+Int_t Int$slow_modulo(Int_t x, Int_t y);
+Int_t Int$slow_modulo1(Int_t x, Int_t y);
+Int_t Int$slow_left_shifted(Int_t x, Int_t y);
+Int_t Int$slow_right_shifted(Int_t x, Int_t y);
+Int_t Int$slow_bit_and(Int_t x, Int_t y);
+Int_t Int$slow_bit_or(Int_t x, Int_t y);
+Int_t Int$slow_bit_xor(Int_t x, Int_t y);
+Int_t Int$slow_negative(Int_t x);
+Int_t Int$slow_negated(Int_t x);
+bool Int$is_prime(Int_t x, Int_t reps);
+Int_t Int$next_prime(Int_t x);
+#if __GNU_MP_VERSION >= 6
+#if __GNU_MP_VERSION_MINOR >= 3
+OptionalInt_t Int$prev_prime(Int_t x);
+#endif
+#endif
+Int_t Int$choose(Int_t n, Int_t k);
+Int_t Int$factorial(Int_t n);
+
+extern const TypeInfo_t Int$info;
+
+// Fast-path inline versions for the common case where integer arithmetic is
+// between two small ints.
+
+MACROLIKE Int_t Int$plus(Int_t x, Int_t y) {
+ const int64_t z = (int64_t)((uint64_t)x.small + (uint64_t)y.small);
+ if likely ((z | 2L) == (int32_t)z) return (Int_t){.small = (z - 1L)};
+ return Int$slow_plus(x, y);
+}
+
+MACROLIKE Int_t Int$minus(Int_t x, Int_t y) {
+ const int64_t z = (int64_t)(((uint64_t)x.small ^ 3L) - (uint64_t)y.small);
+ if likely ((z & ~2L) == (int32_t)z) return (Int_t){.small = z};
+ return Int$slow_minus(x, y);
+}
+
+MACROLIKE Int_t Int$times(Int_t x, Int_t y) {
+ if likely ((x.small & y.small) & 1L) {
+ const int64_t z = (x.small >> 1L) * (y.small >> 1L);
+ if likely (z == (int32_t)z) return (Int_t){.small = z + 1L};
+ }
+ return Int$slow_times(x, y);
+}
+
+MACROLIKE Int_t Int$divided_by(Int_t x, Int_t y) {
+ if likely (x.small & y.small & 1L) {
+ // Euclidean division, see:
+ // https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf
+ const int64_t D = (x.small >> 2L);
+ const int64_t d = (y.small >> 2L);
+ int64_t q = D / d, r = D % d;
+ q -= (r < 0L) * (2L * (d > 0L) - 1L);
+ if likely (q == (int32_t)q) return (Int_t){.small = (q << 2L) | 1L};
+ }
+ return Int$slow_divided_by(x, y);
+}
+
+MACROLIKE Int_t Int$modulo(Int_t x, Int_t y) {
+ if likely (x.small & y.small & 1L) {
+ // Euclidean modulus, see:
+ // https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf
+ const int64_t D = (x.small >> 2L);
+ const int64_t d = (y.small >> 2L);
+ int64_t r = D % d;
+ r -= (r < 0L) * (2L * (d < 0L) - 1L) * d;
+ return (Int_t){.small = (r << 2L) | 1L};
+ }
+ return Int$slow_modulo(x, y);
+}
+
+MACROLIKE Int_t Int$modulo1(Int_t x, Int_t y) {
+ if likely (x.small & y.small & 1L) {
+ // Euclidean modulus, see:
+ // https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf
+ const int64_t D = (x.small >> 2L) - 1L;
+ const int64_t d = (y.small >> 2L);
+ int64_t r = D % d;
+ r -= (r < 0L) * (2L * (d < 0L) - 1L) * d;
+ return (Int_t){.small = ((r + 1L) << 2L) | 1L};
+ }
+ return Int$slow_modulo1(x, y);
+}
+
+MACROLIKE Int_t Int$left_shifted(Int_t x, Int_t y) {
+ if likely (x.small & y.small & 1L) {
+ const int64_t z = ((x.small >> 2L) << (y.small >> 2L)) << 2L;
+ if likely (z == (int32_t)z) return (Int_t){.small = z + 1L};
+ }
+ return Int$slow_left_shifted(x, y);
+}
+
+MACROLIKE Int_t Int$right_shifted(Int_t x, Int_t y) {
+ if likely (x.small & y.small & 1L) {
+ const int64_t z = ((x.small >> 2L) >> (y.small >> 2L)) << 2L;
+ if likely (z == (int32_t)z) return (Int_t){.small = z + 1L};
+ }
+ return Int$slow_right_shifted(x, y);
+}
+
+MACROLIKE Int_t Int$bit_and(Int_t x, Int_t y) {
+ const int64_t z = x.small & y.small;
+ if likely (z & 1L) return (Int_t){.small = z};
+ return Int$slow_bit_and(x, y);
+}
+
+MACROLIKE Int_t Int$bit_or(Int_t x, Int_t y) {
+ if likely (x.small & y.small & 1L) return (Int_t){.small = (x.small | y.small)};
+ return Int$slow_bit_or(x, y);
+}
+
+MACROLIKE Int_t Int$bit_xor(Int_t x, Int_t y) {
+ if likely (x.small & y.small & 1L) return (Int_t){.small = (x.small ^ y.small) | 1L};
+ return Int$slow_bit_xor(x, y);
+}
+
+MACROLIKE Int_t Int$negated(Int_t x) {
+ if likely (x.small & 1L) return (Int_t){.small = (~x.small) ^ 3L};
+ return Int$slow_negated(x);
+}
+
+MACROLIKE Int_t Int$negative(Int_t x) {
+ if likely (x.small & 1L) return (Int_t){.small = ((-((x.small) >> 2L)) << 2L) | 1L};
+ return Int$slow_negative(x);
+}
+
+MACROLIKE PUREFUNC bool Int$is_negative(Int_t x) {
+ if likely (x.small & 1L) return x.small < 0L;
+ return Int$compare_value(x, I_small(0)) < 0L;
+}
+
+// Constructors/conversion functions:
+
+// Int constructors:
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+MACROLIKE PUREFUNC Int_t Int$from_num(double n, bool truncate) {
+ mpz_t result;
+ mpz_init_set_d(result, n);
+ if (!truncate && unlikely(mpz_get_d(result) != n)) fail("Could not convert to an integer without truncation: ", n);
+ return Int$from_mpz(result);
+}
+MACROLIKE PUREFUNC Int_t Int$from_num32(float n, bool truncate) { return Int$from_num((double)n, truncate); }
+MACROLIKE Int_t Int$from_int64(int64_t i) {
+ if likely (i >= SMALLEST_SMALL_INT && i <= BIGGEST_SMALL_INT) return (Int_t){.small = (i << 2L) | 1L};
+ mpz_t result;
+ mpz_init_set_si(result, i);
+ return Int$from_mpz(result);
+}
+MACROLIKE CONSTFUNC Int_t Int$from_int32(Int32_t i) { return Int$from_int64((Int32_t)i); }
+MACROLIKE CONSTFUNC Int_t Int$from_int16(Int16_t i) { return I_small(i); }
+MACROLIKE CONSTFUNC Int_t Int$from_int8(Int8_t i) { return I_small(i); }
+MACROLIKE CONSTFUNC Int_t Int$from_byte(Byte_t b) { return I_small(b); }
+MACROLIKE CONSTFUNC Int_t Int$from_bool(Bool_t b) { return I_small(b); }
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
diff --git a/src/stdlib/bytes.h b/src/stdlib/bytes.h
index f99e39ee..2f948177 100644
--- a/src/stdlib/bytes.h
+++ b/src/stdlib/bytes.h
@@ -20,6 +20,7 @@ Byte_t Byte$from_int32(int32_t i, bool truncate);
Byte_t Byte$from_int16(int16_t i, bool truncate);
OptionalByte_t Byte$parse(Text_t text, Text_t *remainder);
Closure_t Byte$to(Byte_t first, Byte_t last, OptionalInt8_t step);
+
MACROLIKE Byte_t Byte$from_int8(int8_t i) { return (Byte_t)i; }
MACROLIKE Byte_t Byte$from_bool(bool b) { return (Byte_t)b; }
CONSTFUNC bool Byte$is_between(const Byte_t x, const Byte_t low, const Byte_t high);
diff --git a/src/stdlib/int16.c b/src/stdlib/int16.c
new file mode 100644
index 00000000..47cc4611
--- /dev/null
+++ b/src/stdlib/int16.c
@@ -0,0 +1,3 @@
+#define INTX_C_H__INT_BITS 16
+#include "intX.c.h"
+#undef INTX_C_H__INT_BITS
diff --git a/src/stdlib/int16.h b/src/stdlib/int16.h
new file mode 100644
index 00000000..d7490292
--- /dev/null
+++ b/src/stdlib/int16.h
@@ -0,0 +1,4 @@
+#define INTX_H__INT_BITS 16
+#define I16(i) (int16_t)(i)
+#include "intX.h" // IWYU pragma: export
+#define NONE_INT16 ((OptionalInt16_t){.has_value = false})
diff --git a/src/stdlib/int32.c b/src/stdlib/int32.c
new file mode 100644
index 00000000..910cf8de
--- /dev/null
+++ b/src/stdlib/int32.c
@@ -0,0 +1,3 @@
+#define INTX_C_H__INT_BITS 32
+#include "intX.c.h"
+#undef INTX_C_H__INT_BITS
diff --git a/src/stdlib/int32.h b/src/stdlib/int32.h
new file mode 100644
index 00000000..9400494f
--- /dev/null
+++ b/src/stdlib/int32.h
@@ -0,0 +1,4 @@
+#define INTX_H__INT_BITS 32
+#define I32(i) (int32_t)(i)
+#include "intX.h" // IWYU pragma: export
+#define NONE_INT32 ((OptionalInt32_t){.has_value = false})
diff --git a/src/stdlib/int64.c b/src/stdlib/int64.c
new file mode 100644
index 00000000..754ac619
--- /dev/null
+++ b/src/stdlib/int64.c
@@ -0,0 +1,3 @@
+#define INTX_C_H__INT_BITS 64
+#include "intX.c.h"
+#undef INTX_C_H__INT_BITS
diff --git a/src/stdlib/int64.h b/src/stdlib/int64.h
new file mode 100644
index 00000000..dc47fa95
--- /dev/null
+++ b/src/stdlib/int64.h
@@ -0,0 +1,4 @@
+#define INTX_H__INT_BITS 64
+#define I64(i) (int64_t)(i)
+#include "intX.h" // IWYU pragma: export
+#define NONE_INT64 ((OptionalInt64_t){.has_value = false})
diff --git a/src/stdlib/int8.c b/src/stdlib/int8.c
new file mode 100644
index 00000000..aede5ec6
--- /dev/null
+++ b/src/stdlib/int8.c
@@ -0,0 +1,3 @@
+#define INTX_C_H__INT_BITS 8
+#include "intX.c.h"
+#undef INTX_C_H__INT_BITS
diff --git a/src/stdlib/int8.h b/src/stdlib/int8.h
new file mode 100644
index 00000000..1d0e7c68
--- /dev/null
+++ b/src/stdlib/int8.h
@@ -0,0 +1,4 @@
+#define INTX_H__INT_BITS 8
+#define I8(i) (int8_t)(i)
+#include "intX.h" // IWYU pragma: export
+#define NONE_INT8 ((OptionalInt8_t){.has_value = false})
diff --git a/src/stdlib/intX.c.h b/src/stdlib/intX.c.h
new file mode 100644
index 00000000..9c096a19
--- /dev/null
+++ b/src/stdlib/intX.c.h
@@ -0,0 +1,275 @@
+// Fixed-width integer type infos and methods
+// This file is intended to be used by defining `INTX_C_H__INT_BITS` before including:
+//
+// #define INTX_C_H__INT_BITS 32
+// #include "intX.c.h"
+//
+
+#include <gc.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "datatypes.h"
+#include "integers.h"
+#include "text.h"
+#include "types.h"
+
+#ifndef INTX_C_H__INT_BITS
+#define INTX_C_H__INT_BITS 32
+#endif
+
+#define PASTE3_(a, b, c) a##b##c
+#define PASTE3(a, b, c) PASTE3_(a, b, c)
+#define INT_T PASTE3(int, INTX_C_H__INT_BITS, _t)
+
+#define STRINGIFY_(s) #s
+#define STRINGIFY(s) STRINGIFY_(s)
+#define NAME_STR "Int" STRINGIFY(INTX_C_H__INT_BITS)
+
+#define UNSIGNED_(t) u##t
+#define UNSIGNED(t) UNSIGNED_(t)
+#define UINT_T UNSIGNED(INT_T)
+
+#define OPT_T PASTE3(OptionalInt, INTX_C_H__INT_BITS, _t)
+
+#define PASTE4_(a, b, c, d) a##b##c##d
+#define PASTE4(a, b, c, d) PASTE4_(a, b, c, d)
+#define NAMESPACED(method_name) PASTE4(Int, INTX_C_H__INT_BITS, $, method_name)
+
+static Text_t _int64_to_text(int64_t n) {
+ if (n == INT64_MIN) return Text("-9223372036854775808");
+
+ char buf[21] = {[20] = 0}; // Big enough for INT64_MIN + '\0'
+ char *p = &buf[19];
+ bool negative = n < 0;
+ if (negative) n = -n; // Safe to do because we checked for INT64_MIN earlier
+
+ do {
+ *(p--) = '0' + (n % 10);
+ n /= 10;
+ } while (n > 0);
+
+ if (negative) *(p--) = '-';
+
+ return Text$from_strn(p + 1, (size_t)(&buf[19] - p));
+}
+
+public
+void NAMESPACED(serialize)(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *info) {
+ (void)info, (void)pointers;
+#if INTX_C_H__INT_BITS < 32
+ fwrite(obj, sizeof(INT_T), 1, out);
+#else
+ INT_T i = *(INT_T *)obj;
+ UINT_T z = (UINT_T)((i << 1L) ^ (i >> (INTX_C_H__INT_BITS - 1L))); // Zigzag encode
+ while (z >= 0x80L) {
+ fputc((uint8_t)(z | 0x80L), out);
+ z >>= 7L;
+ }
+ fputc((uint8_t)z, out);
+#endif
+}
+
+public
+void NAMESPACED(deserialize)(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *info) {
+ (void)info, (void)pointers;
+#if INTX_C_H__INT_BITS < 32
+ fread(outval, sizeof(INT_T), 1, in);
+#else
+ UINT_T z = 0;
+ for (size_t shift = 0;; shift += 7) {
+ uint8_t byte = (uint8_t)fgetc(in);
+ z |= ((UINT_T)(byte & 0x7F)) << shift;
+ if ((byte & 0x80) == 0) break;
+ }
+ *(INT_T *)outval = (INT_T)((z >> 1L) ^ -(z & 1L)); // Zigzag decode
+#endif
+}
+
+#ifdef __TINYC__
+#define __builtin_add_overflow(x, y, result) \
+ ({ \
+ *(result) = (x) + (y); \
+ false; \
+ })
+#endif
+
+public
+Text_t NAMESPACED(as_text)(const void *i, bool colorize, const TypeInfo_t *info) {
+ (void)info;
+ if (!i) return Text(NAME_STR);
+ Text_t text = _int64_to_text((int64_t)(*(INT_T *)i));
+ return colorize ? Texts(Text("\033[35m"), text, Text("\033[m")) : text;
+}
+public
+Text_t NAMESPACED(value_as_text)(INT_T i) { return _int64_to_text((int64_t)i); }
+public
+PUREFUNC int32_t NAMESPACED(compare)(const void *x, const void *y, const TypeInfo_t *info) {
+ (void)info;
+ return (*(INT_T *)x > *(INT_T *)y) - (*(INT_T *)x < *(INT_T *)y);
+}
+public
+PUREFUNC bool NAMESPACED(equal)(const void *x, const void *y, const TypeInfo_t *info) {
+ (void)info;
+ return *(INT_T *)x == *(INT_T *)y;
+}
+public
+CONSTFUNC bool NAMESPACED(is_between)(const INT_T x, const INT_T low, const INT_T high) {
+ return low <= x && x <= high;
+}
+public
+CONSTFUNC INT_T NAMESPACED(clamped)(INT_T x, INT_T min, INT_T max) { return x < min ? min : (x > max ? max : x); }
+public
+Text_t NAMESPACED(hex)(INT_T i, Int_t digits_int, bool uppercase, bool prefix) {
+ Int_t as_int = Int$from_int64((int64_t)i);
+ return Int$hex(as_int, digits_int, uppercase, prefix);
+}
+public
+Text_t NAMESPACED(octal)(INT_T i, Int_t digits_int, bool prefix) {
+ Int_t as_int = Int$from_int64((int64_t)i);
+ return Int$octal(as_int, digits_int, prefix);
+}
+public
+List_t NAMESPACED(bits)(INT_T x) {
+ List_t bit_list = (List_t){.data = GC_MALLOC_ATOMIC(sizeof(bool[8 * sizeof(INT_T)])),
+ .atomic = 1,
+ .stride = sizeof(bool),
+ .length = 8 * sizeof(INT_T)};
+ bool *bits = bit_list.data + sizeof(INT_T) * 8;
+ for (size_t i = 0; i < 8 * sizeof(INT_T); i++) {
+ *(bits--) = x & 1;
+ x >>= 1;
+ }
+ return bit_list;
+}
+public
+bool NAMESPACED(get_bit)(INT_T x, Int_t bit_index) {
+ if (Int$compare_value(bit_index, I(1)) < 0) fail("Invalid bit index (expected 1 or higher): ", bit_index);
+ if (Int$compare_value(bit_index, Int$from_int64(sizeof(INT_T) * 8)) > 0)
+ fail("Bit index is too large! There are only ", (uint64_t)sizeof(INT_T) * 8,
+ " bits, but index is: ", bit_index);
+ return ((x & (INT_T)(1L << (Int64$from_int(bit_index, true) - 1L))) != 0);
+}
+typedef struct {
+ OPT_T current, last;
+ INT_T step;
+} NAMESPACED(Range_t);
+
+static OPT_T _next_int(NAMESPACED(Range_t) * info) {
+ OPT_T i = info->current;
+ if (i.has_value) {
+ INT_T next;
+ bool overflow = __builtin_add_overflow(i.value, info->step, &next);
+ if (overflow || (info->last.has_value && (info->step >= 0 ? next > info->last.value : next < info->last.value)))
+ info->current = (OPT_T){.has_value = false};
+ else info->current = (OPT_T){.has_value = true, .value = next};
+ }
+ return i;
+}
+
+public
+#if INTX_C_H__INT_BITS < 64
+CONSTFUNC
+#endif
+Closure_t NAMESPACED(to)(INT_T first, INT_T last, OPT_T step) {
+ NAMESPACED(Range_t) *range = GC_MALLOC(sizeof(NAMESPACED(Range_t)));
+ range->current = (OPT_T){.has_value = true, .value = first};
+ range->last = (OPT_T){.has_value = true, .value = last};
+ range->step = step.has_value ? step.value : (last >= first ? 1 : -1);
+ return (Closure_t){.fn = _next_int, .userdata = range};
+}
+
+public
+#if INTX_C_H__INT_BITS < 64
+CONSTFUNC
+#endif
+Closure_t NAMESPACED(onward)(INT_T first, INT_T step) {
+ NAMESPACED(Range_t) *range = GC_MALLOC(sizeof(NAMESPACED(Range_t)));
+ range->current = (OPT_T){.has_value = true, .value = first};
+ range->last = (OPT_T){.has_value = false};
+ range->step = step;
+ return (Closure_t){.fn = _next_int, .userdata = range};
+}
+public
+PUREFUNC OPT_T NAMESPACED(parse)(Text_t text, Text_t *remainder) {
+ OptionalInt_t full_int = Int$parse(text, remainder);
+ if (full_int.small == 0L) return (OPT_T){.has_value = false};
+ if (Int$compare_value(full_int, I(NAMESPACED(min))) < 0) {
+ return (OPT_T){.has_value = false};
+ }
+ if (Int$compare_value(full_int, I(NAMESPACED(max))) > 0) {
+ return (OPT_T){.has_value = false};
+ }
+ return (OPT_T){.has_value = true, .value = NAMESPACED(from_int)(full_int, true)};
+}
+
+public
+CONSTFUNC INT_T NAMESPACED(gcd)(INT_T x, INT_T y) {
+ if (x == 0 || y == 0) return 0;
+ x = NAMESPACED(abs)(x);
+ y = NAMESPACED(abs)(y);
+ while (x != y) {
+ if (x > y) x -= y;
+ else y -= x;
+ }
+ return x;
+}
+
+public
+const INT_T NAMESPACED(min) =
+#if INTX_C_H__INT_BITS == 64
+ INT64_MIN
+#elif INTX_C_H__INT_BITS == 32
+ INT32_MIN
+#elif INTX_C_H__INT_BITS == 16
+ INT16_MIN
+#elif INTX_C_H__INT_BITS == 8
+ INT8_MIN
+#else
+#error "Unsupported integer bit width"
+#endif
+ ;
+
+public
+const INT_T NAMESPACED(max) =
+#if INTX_C_H__INT_BITS == 64
+ INT64_MAX
+#elif INTX_C_H__INT_BITS == 32
+ INT32_MAX
+#elif INTX_C_H__INT_BITS == 16
+ INT16_MAX
+#elif INTX_C_H__INT_BITS == 8
+ INT8_MAX
+#else
+#error "Unsupported integer bit width"
+#endif
+ ;
+
+public
+const TypeInfo_t NAMESPACED(info) = {
+ .size = sizeof(INT_T),
+ .align = __alignof__(INT_T),
+ .metamethods =
+ {
+ .compare = NAMESPACED(compare),
+ .as_text = NAMESPACED(as_text),
+ .serialize = NAMESPACED(serialize),
+ .deserialize = NAMESPACED(deserialize),
+ },
+};
+
+#undef PASTE3_
+#undef PASTE3
+#undef INT_T
+#undef STRINGIFY_
+#undef STRINGIFY
+#undef NAME_STR
+#undef UNSIGNED_
+#undef UNSIGNED
+#undef UINT_T
+#undef OPT_T
+#undef PASTE4_
+#undef PASTE4
+#undef NAMESPACED
diff --git a/src/stdlib/intX.h b/src/stdlib/intX.h
new file mode 100644
index 00000000..7b1f90d5
--- /dev/null
+++ b/src/stdlib/intX.h
@@ -0,0 +1,167 @@
+// Integer type infos and methods
+// This file expects `c_type` and `type_name` to be defined before including:
+//
+// #define INTX_H__INT_BITS 64
+// #include "intX.h"
+//
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "datatypes.h"
+#include "stdlib.h"
+#include "types.h"
+#include "util.h"
+
+#ifndef INTX_H__INT_BITS
+#define INTX_H__INT_BITS 32
+#endif
+
+#define PASTE3_(a, b, c) a##b##c
+#define PASTE3(a, b, c) PASTE3_(a, b, c)
+#define INT_T PASTE3(int, INTX_H__INT_BITS, _t)
+
+#define STRINGIFY_(s) #s
+#define STRINGIFY(s) STRINGIFY_(s)
+#define NAME_STR "Int" STRINGIFY(INTX_H__INT_BITS)
+
+#define UNSIGNED_(t) u##t
+#define UNSIGNED(t) UNSIGNED_(t)
+#define UINT_T UNSIGNED(INT_T)
+
+#define OPT_T PASTE3(OptionalInt, INTX_H__INT_BITS, _t)
+
+#define PASTE4_(a, b, c, d) a##b##c##d
+#define PASTE4(a, b, c, d) PASTE4_(a, b, c, d)
+#define NAMESPACED(method_name) PASTE4(Int, INTX_H__INT_BITS, $, method_name)
+
+typedef struct {
+ INT_T value;
+ bool has_value : 1;
+} OPT_T;
+
+Text_t NAMESPACED(as_text)(const void *i, bool colorize, const TypeInfo_t *type);
+Text_t NAMESPACED(value_as_text)(INT_T i);
+PUREFUNC int32_t NAMESPACED(compare)(const void *x, const void *y, const TypeInfo_t *type);
+PUREFUNC bool NAMESPACED(equal)(const void *x, const void *y, const TypeInfo_t *type);
+Text_t NAMESPACED(hex)(INT_T i, Int_t digits, bool uppercase, bool prefix);
+Text_t NAMESPACED(octal)(INT_T i, Int_t digits, bool prefix);
+List_t NAMESPACED(bits)(INT_T x);
+bool NAMESPACED(get_bit)(INT_T x, Int_t bit_index);
+Closure_t NAMESPACED(to)(INT_T first, INT_T last, OPT_T step);
+Closure_t NAMESPACED(onward)(INT_T first, INT_T step);
+PUREFUNC OPT_T NAMESPACED(parse)(Text_t text, Text_t *remainder);
+CONSTFUNC bool NAMESPACED(is_between)(const INT_T x, const INT_T low, const INT_T high);
+CONSTFUNC INT_T NAMESPACED(clamped)(INT_T x, INT_T min, INT_T max);
+MACROLIKE CONSTFUNC INT_T NAMESPACED(from_byte)(Byte_t b) { return (INT_T)b; }
+MACROLIKE CONSTFUNC INT_T NAMESPACED(from_bool)(Bool_t b) { return (INT_T)b; }
+CONSTFUNC INT_T NAMESPACED(gcd)(INT_T x, INT_T y);
+extern const INT_T NAMESPACED(min), NAMESPACED(max);
+extern const TypeInfo_t NAMESPACED(info);
+
+MACROLIKE INT_T NAMESPACED(abs)(INT_T x) {
+#if INTX_H__INT_BITS >= 64
+ return (INT_T)labs(x);
+#else
+ return (INT_T)abs(x);
+#endif
+}
+
+MACROLIKE INT_T NAMESPACED(divided_by)(INT_T D, INT_T d) {
+ INT_T q = D / d, r = D % d;
+ q -= (r < 0) * (2 * (d > 0) - 1);
+ return q;
+}
+
+MACROLIKE INT_T NAMESPACED(modulo)(INT_T D, INT_T d) {
+ INT_T r = D % d;
+ r -= (r < 0) * (2 * (d < 0) - 1) * d;
+ return r;
+}
+
+MACROLIKE INT_T NAMESPACED(modulo1)(INT_T D, INT_T d) { return NAMESPACED(modulo)(D - 1, d) + 1; }
+
+MACROLIKE PUREFUNC INT_T NAMESPACED(wrapping_plus)(INT_T x, INT_T y) { return (INT_T)((UINT_T)x + (UINT_T)y); }
+
+MACROLIKE PUREFUNC INT_T NAMESPACED(wrapping_minus)(INT_T x, INT_T y) { return (INT_T)((UINT_T)x + (UINT_T)y); }
+
+MACROLIKE PUREFUNC INT_T NAMESPACED(unsigned_left_shifted)(INT_T x, INT_T y) { return (INT_T)((UINT_T)x << y); }
+
+MACROLIKE PUREFUNC INT_T NAMESPACED(unsigned_right_shifted)(INT_T x, INT_T y) { return (INT_T)((UINT_T)x >> y); }
+
+void NAMESPACED(serialize)(const void *obj, FILE *out, Table_t *, const TypeInfo_t *);
+void NAMESPACED(deserialize)(FILE *in, void *outval, List_t *, const TypeInfo_t *);
+
+MACROLIKE PUREFUNC INT_T NAMESPACED(from_num)(Num_t n, bool truncate) {
+ INT_T i = (INT_T)n;
+ if (!truncate && unlikely((Num_t)i != n)) fail("Could not convert Num to an " NAME_STR " without truncation: ", n);
+ return i;
+}
+
+MACROLIKE PUREFUNC INT_T NAMESPACED(from_num32)(Num32_t n, bool truncate) {
+ INT_T i = (INT_T)n;
+ if (!truncate && unlikely((Num32_t)i != n))
+ fail("Could not convert Num32 to an " NAME_STR " without truncation: ", n);
+ return i;
+}
+
+MACROLIKE PUREFUNC INT_T NAMESPACED(from_int)(Int_t i, bool truncate) {
+ if likely (i.small & 1L) {
+ INT_T ret = i.small >> 2L;
+#if INTX_H__INT_BITS < 32
+ if (!truncate && unlikely((int64_t)ret != (i.small >> 2L)))
+ fail("Integer is too big to fit in an " NAME_STR ": ", i);
+#endif
+ return ret;
+ }
+ if (!truncate && unlikely(!mpz_fits_slong_p(*i.big))) fail("Integer is too big to fit in an " NAME_STR ": ", i);
+ return mpz_get_si(*i.big);
+}
+
+#if INTX_H__INT_BITS < 64
+MACROLIKE PUREFUNC INT_T NAMESPACED(from_int64)(Int64_t i64, bool truncate) {
+ INT_T i = (INT_T)i64;
+ if (!truncate && unlikely((int64_t)i != i64)) fail("Integer is too big to fit in an " NAME_STR ": ", i64);
+ return i;
+}
+#elif INTX_H__INT_BITS > 64
+MACROLIKE CONSTFUNC INT_T NAMESPACED(from_int64)(Int64_t i) { return (INT_T)i; }
+#endif
+
+#if INTX_H__INT_BITS < 32
+MACROLIKE PUREFUNC INT_T NAMESPACED(from_int32)(Int32_t i32, bool truncate) {
+ INT_T i = (INT_T)i32;
+ if (!truncate && unlikely((int32_t)i != i32)) fail("Integer is too big to fit in an " NAME_STR ": ", i32);
+ return i;
+}
+#elif INTX_H__INT_BITS > 32
+MACROLIKE CONSTFUNC INT_T NAMESPACED(from_int32)(Int32_t i) { return (INT_T)i; }
+#endif
+
+#if INTX_H__INT_BITS < 16
+MACROLIKE PUREFUNC INT_T NAMESPACED(from_int16)(Int16_t i16, bool truncate) {
+ INT_T i = (INT_T)i16;
+ if (!truncate && unlikely((int16_t)i != i16)) fail("Integer is too big to fit in an " NAME_STR ": ", i16);
+ return i;
+}
+#elif INTX_H__INT_BITS > 16
+MACROLIKE CONSTFUNC INT_T NAMESPACED(from_int16)(Int16_t i) { return (INT_T)i; }
+#endif
+
+#if INTX_H__INT_BITS > 8
+MACROLIKE CONSTFUNC INT_T NAMESPACED(from_int8)(Int8_t i) { return (INT_T)i; }
+#endif
+
+#undef PASTE3_
+#undef PASTE3
+#undef INT_T
+#undef STRINGIFY_
+#undef STRINGIFY
+#undef NAME_STR
+#undef UNSIGNED_
+#undef UNSIGNED
+#undef UINT_T
+#undef OPT_T
+#undef PASTE4_
+#undef PASTE4
+#undef NAMESPACED
+#undef INTX_H__INT_BITS
diff --git a/src/stdlib/integers.c b/src/stdlib/integers.c
deleted file mode 100644
index 962c97cf..00000000
--- a/src/stdlib/integers.c
+++ /dev/null
@@ -1,747 +0,0 @@
-// Integer type infos and methods
-
-#include <stdio.h> // Must be before gmp.h
-
-#include <ctype.h>
-#include <gc.h>
-#include <gmp.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "datatypes.h"
-#include "integers.h"
-#include "optionals.h"
-#include "print.h"
-#include "siphash.h"
-#include "text.h"
-#include "types.h"
-
-public
-int Int$print(FILE *f, Int_t i) {
- if (likely(i.small & 1L)) {
- return _print_int(f, (int64_t)((i.small) >> 2L));
- } else {
- return gmp_fprintf(f, "%Zd", *i.big);
- }
-}
-
-static Text_t _int64_to_text(int64_t n) {
- if (n == INT64_MIN) return Text("-9223372036854775808");
-
- char buf[21] = {[20] = 0}; // Big enough for INT64_MIN + '\0'
- char *p = &buf[19];
- bool negative = n < 0;
- if (negative) n = -n; // Safe to do because we checked for INT64_MIN earlier
-
- do {
- *(p--) = '0' + (n % 10);
- n /= 10;
- } while (n > 0);
-
- if (negative) *(p--) = '-';
-
- return Text$from_strn(p + 1, (size_t)(&buf[19] - p));
-}
-
-public
-Text_t Int$value_as_text(Int_t i) {
- if (likely(i.small & 1L)) {
- return _int64_to_text(i.small >> 2L);
- } else {
- char *str = mpz_get_str(NULL, 10, *i.big);
- return Text$from_str(str);
- }
-}
-
-public
-Text_t Int$as_text(const void *i, bool colorize, const TypeInfo_t *info) {
- (void)info;
- if (!i) return Text("Int");
- Text_t text = Int$value_as_text(*(Int_t *)i);
- if (colorize) text = Text$concat(Text("\x1b[35m"), text, Text("\x1b[m"));
- return text;
-}
-
-static bool Int$is_none(const void *i, const TypeInfo_t *info) {
- (void)info;
- return ((Int_t *)i)->small == 0L;
-}
-
-public
-PUREFUNC int32_t Int$compare_value(const Int_t x, const Int_t y) {
- if (likely(x.small & y.small & 1L)) return (x.small > y.small) - (x.small < y.small);
- else if (x.small & 1) return -mpz_cmp_si(*y.big, x.small);
- else if (y.small & 1) return mpz_cmp_si(*x.big, y.small);
- else return x.big == y.big ? 0 : mpz_cmp(*x.big, *y.big);
-}
-
-public
-PUREFUNC int32_t Int$compare(const void *x, const void *y, const TypeInfo_t *info) {
- (void)info;
- return Int$compare_value(*(Int_t *)x, *(Int_t *)y);
-}
-
-public
-PUREFUNC bool Int$equal_value(const Int_t x, const Int_t y) {
- if (likely((x.small | y.small) & 1L)) return x.small == y.small;
- else return x.big == y.big ? 0 : (mpz_cmp(*x.big, *y.big) == 0);
-}
-
-public
-PUREFUNC bool Int$equal(const void *x, const void *y, const TypeInfo_t *info) {
- (void)info;
- return Int$equal_value(*(Int_t *)x, *(Int_t *)y);
-}
-
-public
-CONSTFUNC Int_t Int$clamped(Int_t x, Int_t low, Int_t high) {
- return (Int$compare(&x, &low, &Int$info) <= 0) ? low : (Int$compare(&x, &high, &Int$info) >= 0 ? high : x);
-}
-
-public
-CONSTFUNC bool Int$is_between(const Int_t x, const Int_t low, const Int_t high) {
- return Int$compare_value(low, x) <= 0 && Int$compare_value(x, high) <= 0;
-}
-
-public
-PUREFUNC uint64_t Int$hash(const void *vx, const TypeInfo_t *info) {
- (void)info;
- Int_t *x = (Int_t *)vx;
- if (likely(x->small & 1L)) {
- return siphash24((void *)x, sizeof(Int_t));
- } else {
- char *str = mpz_get_str(NULL, 16, *x->big);
- return siphash24((void *)str, strlen(str));
- }
-}
-
-public
-Text_t Int$hex(Int_t i, Int_t digits_int, bool uppercase, bool prefix) {
- if (Int$is_negative(i)) return Text$concat(Text("-"), Int$hex(Int$negative(i), digits_int, uppercase, prefix));
-
- if (likely(i.small & 1L)) {
- uint64_t u64 = (uint64_t)(i.small >> 2);
- return Text$from_str(String(
- hex(u64, .no_prefix = !prefix, .digits = Int32$from_int(digits_int, false), .uppercase = uppercase)));
- } else {
- char *str = mpz_get_str(NULL, 16, *i.big);
- if (uppercase) {
- for (char *c = str; *c; c++)
- *c = (char)toupper(*c);
- }
- int64_t digits = Int64$from_int(digits_int, false);
- int64_t needed_zeroes = digits - (int64_t)strlen(str);
- if (needed_zeroes <= 0) return prefix ? Text$concat(Text("0x"), Text$from_str(str)) : Text$from_str(str);
-
- char *zeroes = GC_MALLOC_ATOMIC((size_t)(needed_zeroes));
- memset(zeroes, '0', (size_t)(needed_zeroes));
- if (prefix) return Text$concat(Text("0x"), Text$from_str(zeroes), Text$from_str(str));
- else return Text$concat(Text$from_str(zeroes), Text$from_str(str));
- }
-}
-
-public
-Text_t Int$octal(Int_t i, Int_t digits_int, bool prefix) {
- if (Int$is_negative(i)) return Text$concat(Text("-"), Int$octal(Int$negative(i), digits_int, prefix));
-
- if (likely(i.small & 1L)) {
- uint64_t u64 = (uint64_t)(i.small >> 2);
- return Text$from_str(String(oct(u64, .no_prefix = !prefix, .digits = Int32$from_int(digits_int, false))));
- } else {
- int64_t digits = Int64$from_int(digits_int, false);
- char *str = mpz_get_str(NULL, 8, *i.big);
- int64_t needed_zeroes = digits - (int64_t)strlen(str);
- if (needed_zeroes <= 0) return prefix ? Text$concat(Text("0o"), Text$from_str(str)) : Text$from_str(str);
-
- char *zeroes = GC_MALLOC_ATOMIC((size_t)(needed_zeroes));
- memset(zeroes, '0', (size_t)(needed_zeroes));
- if (prefix) return Text$concat(Text("0o"), Text$from_str(zeroes), Text$from_str(str));
- else return Text$concat(Text$from_str(zeroes), Text$from_str(str));
- }
-}
-
-public
-Int_t Int$slow_plus(Int_t x, Int_t y) {
- mpz_t result;
- mpz_init_set_int(result, x);
- if (y.small & 1L) {
- if (y.small < 0L) mpz_sub_ui(result, result, (uint64_t)(-(y.small >> 2L)));
- else mpz_add_ui(result, result, (uint64_t)(y.small >> 2L));
- } else {
- mpz_add(result, result, *y.big);
- }
- return Int$from_mpz(result);
-}
-
-public
-Int_t Int$slow_minus(Int_t x, Int_t y) {
- mpz_t result;
- mpz_init_set_int(result, x);
- if (y.small & 1L) {
- if (y.small < 0L) mpz_add_ui(result, result, (uint64_t)(-(y.small >> 2L)));
- else mpz_sub_ui(result, result, (uint64_t)(y.small >> 2L));
- } else {
- mpz_sub(result, result, *y.big);
- }
- return Int$from_mpz(result);
-}
-
-public
-Int_t Int$slow_times(Int_t x, Int_t y) {
- mpz_t result;
- mpz_init_set_int(result, x);
- if (y.small & 1L) mpz_mul_si(result, result, y.small >> 2L);
- else mpz_mul(result, result, *y.big);
- return Int$from_mpz(result);
-}
-
-public
-Int_t Int$slow_divided_by(Int_t dividend, Int_t divisor) {
- // Euclidean division, see:
- // https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf
- mpz_t quotient, remainder;
- mpz_init_set_int(quotient, dividend);
- mpz_init_set_int(remainder, divisor);
- mpz_tdiv_qr(quotient, remainder, quotient, remainder);
- if (mpz_sgn(remainder) < 0) {
- bool d_positive = likely(divisor.small & 1L) ? divisor.small > 0x1L : mpz_sgn(*divisor.big) > 0;
- if (d_positive) mpz_sub_ui(quotient, quotient, 1);
- else mpz_add_ui(quotient, quotient, 1);
- }
- return Int$from_mpz(quotient);
-}
-
-public
-Int_t Int$slow_modulo(Int_t x, Int_t modulus) {
- mpz_t result;
- mpz_init_set_int(result, x);
- mpz_t divisor;
- mpz_init_set_int(divisor, modulus);
- mpz_mod(result, result, divisor);
- return Int$from_mpz(result);
-}
-
-public
-Int_t Int$slow_modulo1(Int_t x, Int_t modulus) {
- mpz_t result;
- mpz_init_set_int(result, x);
- mpz_sub_ui(result, result, 1);
- mpz_t divisor;
- mpz_init_set_int(divisor, modulus);
- mpz_mod(result, result, divisor);
- mpz_add_ui(result, result, 1);
- return Int$from_mpz(result);
-}
-
-public
-Int_t Int$slow_left_shifted(Int_t x, Int_t y) {
- mp_bitcnt_t bits = (mp_bitcnt_t)Int64$from_int(y, false);
- mpz_t result;
- mpz_init_set_int(result, x);
- mpz_mul_2exp(result, result, bits);
- return Int$from_mpz(result);
-}
-
-public
-Int_t Int$slow_right_shifted(Int_t x, Int_t y) {
- mp_bitcnt_t bits = (mp_bitcnt_t)Int64$from_int(y, false);
- mpz_t result;
- mpz_init_set_int(result, x);
- mpz_tdiv_q_2exp(result, result, bits);
- return Int$from_mpz(result);
-}
-
-public
-Int_t Int$slow_bit_and(Int_t x, Int_t y) {
- mpz_t result;
- mpz_init_set_int(result, x);
- mpz_t y_mpz;
- mpz_init_set_int(y_mpz, y);
- mpz_and(result, result, y_mpz);
- return Int$from_mpz(result);
-}
-
-public
-Int_t Int$slow_bit_or(Int_t x, Int_t y) {
- mpz_t result;
- mpz_init_set_int(result, x);
- mpz_t y_mpz;
- mpz_init_set_int(y_mpz, y);
- mpz_ior(result, result, y_mpz);
- return Int$from_mpz(result);
-}
-
-public
-Int_t Int$slow_bit_xor(Int_t x, Int_t y) {
- mpz_t result;
- mpz_init_set_int(result, x);
- mpz_t y_mpz;
- mpz_init_set_int(y_mpz, y);
- mpz_xor(result, result, y_mpz);
- return Int$from_mpz(result);
-}
-
-public
-Int_t Int$slow_negated(Int_t x) {
- mpz_t result;
- mpz_init_set_int(result, x);
- mpz_neg(result, result);
- mpz_sub_ui(result, result, 1);
- return Int$from_mpz(result);
-}
-
-public
-Int_t Int$slow_negative(Int_t x) {
- if (likely(x.small & 1L)) return (Int_t){.small = 4L * -((x.small) >> 2L) + 1L};
-
- mpz_t result;
- mpz_init_set_int(result, x);
- mpz_neg(result, result);
- return Int$from_mpz(result);
-}
-
-public
-Int_t Int$abs(Int_t x) {
- if (likely(x.small & 1L)) return (Int_t){.small = 4L * labs((x.small) >> 2L) + 1L};
-
- mpz_t result;
- mpz_init_set_int(result, x);
- mpz_abs(result, result);
- return Int$from_mpz(result);
-}
-
-public
-Int_t Int$power(Int_t base, Int_t exponent) {
- int64_t exp = Int64$from_int(exponent, false);
- if (unlikely(exp < 0)) fail("Cannot take a negative power of an integer!");
- mpz_t result;
- mpz_init_set_int(result, base);
- mpz_pow_ui(result, result, (uint64_t)exp);
- return Int$from_mpz(result);
-}
-
-public
-Int_t Int$gcd(Int_t x, Int_t y) {
- if (likely(x.small & y.small & 0x1L)) return I_small(Int32$gcd(x.small >> 2L, y.small >> 2L));
-
- mpz_t result;
- mpz_init(result);
- if (x.small & 0x1L) mpz_gcd_ui(result, *y.big, (uint64_t)labs(x.small >> 2L));
- else if (y.small & 0x1L) mpz_gcd_ui(result, *x.big, (uint64_t)labs(y.small >> 2L));
- else mpz_gcd(result, *x.big, *y.big);
- return Int$from_mpz(result);
-}
-
-public
-OptionalInt_t Int$sqrt(Int_t i) {
- if (Int$compare_value(i, I(0)) < 0) return NONE_INT;
- mpz_t result;
- mpz_init_set_int(result, i);
- mpz_sqrt(result, result);
- return Int$from_mpz(result);
-}
-
-public
-bool Int$get_bit(Int_t x, Int_t bit_index) {
- mpz_t i;
- mpz_init_set_int(i, x);
- if (Int$compare_value(bit_index, I(1)) < 0) fail("Invalid bit index (expected 1 or higher): ", bit_index);
- if (Int$compare_value(bit_index, Int$from_int64(INT64_MAX)) > 0) fail("Bit index is too large! ", bit_index);
-
- int is_bit_set = mpz_tstbit(i, (mp_bitcnt_t)(Int64$from_int(bit_index, true) - 1));
- return (bool)is_bit_set;
-}
-
-typedef struct {
- OptionalInt_t current, last;
- Int_t step;
-} IntRange_t;
-
-static OptionalInt_t _next_int(IntRange_t *info) {
- OptionalInt_t i = info->current;
- if (!Int$is_none(&i, &Int$info)) {
- Int_t next = Int$plus(i, info->step);
- if (!Int$is_none(&info->last, &Int$info)
- && Int$compare_value(next, info->last) == Int$compare_value(info->step, I(0)))
- next = NONE_INT;
- info->current = next;
- }
- return i;
-}
-
-public
-PUREFUNC Closure_t Int$to(Int_t first, Int_t last, OptionalInt_t step) {
- IntRange_t *range = GC_MALLOC(sizeof(IntRange_t));
- range->current = first;
- range->last = last;
- range->step = Int$is_none(&step, &Int$info) ? Int$compare_value(last, first) >= 0
- ? (Int_t){.small = (1L << 2L) | 1L}
- : (Int_t){.small = (-1L >> 2L) | 1L}
- : step;
- return (Closure_t){.fn = _next_int, .userdata = range};
-}
-
-public
-PUREFUNC Closure_t Int$onward(Int_t first, Int_t step) {
- IntRange_t *range = GC_MALLOC(sizeof(IntRange_t));
- range->current = first;
- range->last = NONE_INT;
- range->step = step;
- return (Closure_t){.fn = _next_int, .userdata = range};
-}
-
-public
-Int_t Int$from_str(const char *str) {
- mpz_t i;
- int result;
- if (strncmp(str, "0x", 2) == 0) {
- result = mpz_init_set_str(i, str + 2, 16);
- } else if (strncmp(str, "0o", 2) == 0) {
- result = mpz_init_set_str(i, str + 2, 8);
- } else if (strncmp(str, "0b", 2) == 0) {
- result = mpz_init_set_str(i, str + 2, 2);
- } else {
- result = mpz_init_set_str(i, str, 10);
- }
- if (result != 0) return NONE_INT;
- return Int$from_mpz(i);
-}
-
-public
-OptionalInt_t Int$parse(Text_t text, Text_t *remainder) {
- const char *str = Text$as_c_string(text);
- mpz_t i;
- int result;
- if (strncmp(str, "0x", 2) == 0) {
- const char *end = str + 2 + strspn(str + 2, "0123456789abcdefABCDEF");
- if (remainder) *remainder = Text$from_str(end);
- else if (*end != '\0') return NONE_INT;
- result = mpz_init_set_str(i, str + 2, 16);
- } else if (strncmp(str, "0o", 2) == 0) {
- const char *end = str + 2 + strspn(str + 2, "01234567");
- if (remainder) *remainder = Text$from_str(end);
- else if (*end != '\0') return NONE_INT;
- result = mpz_init_set_str(i, str + 2, 8);
- } else if (strncmp(str, "0b", 2) == 0) {
- const char *end = str + 2 + strspn(str + 2, "01");
- if (remainder) *remainder = Text$from_str(end);
- else if (*end != '\0') return NONE_INT;
- result = mpz_init_set_str(i, str + 2, 2);
- } else {
- const char *end = str + strspn(str, "0123456789");
- if (remainder) *remainder = Text$from_str(end);
- else if (*end != '\0') return NONE_INT;
- result = mpz_init_set_str(i, str, 10);
- }
- if (result != 0) {
- if (remainder) *remainder = text;
- return NONE_INT;
- }
- return Int$from_mpz(i);
-}
-
-public
-bool Int$is_prime(Int_t x, Int_t reps) {
- mpz_t p;
- mpz_init_set_int(p, x);
- if (unlikely(Int$compare_value(reps, I(9999)) > 0))
- fail("Number of prime-test repetitions should not be above 9999");
- int reps_int = Int32$from_int(reps, false);
- return (mpz_probab_prime_p(p, reps_int) != 0);
-}
-
-public
-Int_t Int$next_prime(Int_t x) {
- mpz_t p;
- mpz_init_set_int(p, x);
- mpz_nextprime(p, p);
- return Int$from_mpz(p);
-}
-
-#if __GNU_MP_VERSION >= 6
-#if __GNU_MP_VERSION_MINOR >= 3
-public
-OptionalInt_t Int$prev_prime(Int_t x) {
- mpz_t p;
- mpz_init_set_int(p, x);
- if (unlikely(mpz_prevprime(p, p) == 0)) return NONE_INT;
- return Int$from_mpz(p);
-}
-#endif
-#endif
-
-public
-Int_t Int$choose(Int_t n, Int_t k) {
- if unlikely (Int$compare_value(n, I_small(0)) < 0) fail("Negative inputs are not supported for choose()");
-
- mpz_t ret;
- mpz_init(ret);
-
- int64_t k_i64 = Int64$from_int(k, false);
- if unlikely (k_i64 < 0) fail("Negative inputs are not supported for choose()");
-
- if likely (n.small & 1L) {
- mpz_bin_uiui(ret, (unsigned long)(n.small >> 2L), (unsigned long)k_i64);
- } else {
- mpz_t n_mpz;
- mpz_init_set_int(n_mpz, n);
- mpz_bin_ui(ret, n_mpz, (unsigned long)k_i64);
- }
- return Int$from_mpz(ret);
-}
-
-public
-Int_t Int$factorial(Int_t n) {
- mpz_t ret;
- mpz_init(ret);
- int64_t n_i64 = Int64$from_int(n, false);
- if unlikely (n_i64 < 0) fail("Factorials are not defined for negative numbers");
- mpz_fac_ui(ret, (unsigned long)n_i64);
- return Int$from_mpz(ret);
-}
-
-static void Int$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *info) {
- (void)info;
- Int_t i = *(Int_t *)obj;
- if (likely(i.small & 1L)) {
- fputc(0, out);
- int64_t i64 = i.small >> 2L;
- Int64$serialize(&i64, out, pointers, &Int64$info);
- } else {
- fputc(1, out);
- mpz_t n;
- mpz_init_set_int(n, *(Int_t *)obj);
- mpz_out_raw(out, n);
- }
-}
-
-static void Int$deserialize(FILE *in, void *obj, List_t *pointers, const TypeInfo_t *info) {
- (void)info;
- if (fgetc(in) == 0) {
- int64_t i = 0;
- Int64$deserialize(in, &i, pointers, &Int64$info);
- *(Int_t *)obj = (Int_t){.small = (i << 2L) | 1L};
- } else {
- mpz_t n;
- mpz_init(n);
- mpz_inp_raw(n, in);
- *(Int_t *)obj = Int$from_mpz(n);
- }
-}
-
-public
-const TypeInfo_t Int$info = {
- .size = sizeof(Int_t),
- .align = __alignof__(Int_t),
- .metamethods =
- {
- .compare = Int$compare,
- .equal = Int$equal,
- .hash = Int$hash,
- .as_text = Int$as_text,
- .is_none = Int$is_none,
- .serialize = Int$serialize,
- .deserialize = Int$deserialize,
- },
-};
-
-public
-void Int64$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *info) {
- (void)info, (void)pointers;
- int64_t i = *(int64_t *)obj;
- uint64_t z = (uint64_t)((i << 1L) ^ (i >> 63L)); // Zigzag encode
- while (z >= 0x80L) {
- fputc((uint8_t)(z | 0x80L), out);
- z >>= 7L;
- }
- fputc((uint8_t)z, out);
-}
-
-public
-void Int64$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *info) {
- (void)info, (void)pointers;
- uint64_t z = 0;
- for (size_t shift = 0;; shift += 7) {
- uint8_t byte = (uint8_t)fgetc(in);
- z |= ((uint64_t)(byte & 0x7F)) << shift;
- if ((byte & 0x80) == 0) break;
- }
- *(int64_t *)outval = (int64_t)((z >> 1L) ^ -(z & 1L)); // Zigzag decode
-}
-
-public
-void Int32$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *info) {
- (void)info, (void)pointers;
- int32_t i = *(int32_t *)obj;
- uint32_t z = (uint32_t)((i << 1) ^ (i >> 31)); // Zigzag encode
- while (z >= 0x80) {
- fputc((uint8_t)(z | 0x80), out);
- z >>= 7;
- }
- fputc((uint8_t)z, out);
-}
-
-public
-void Int32$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *info) {
- (void)info, (void)pointers;
- uint32_t z = 0;
- for (size_t shift = 0;; shift += 7) {
- uint8_t byte = (uint8_t)fgetc(in);
- z |= ((uint32_t)(byte & 0x7F)) << shift;
- if ((byte & 0x80) == 0) break;
- }
- *(int32_t *)outval = (int32_t)((z >> 1L) ^ -(z & 1L)); // Zigzag decode
-}
-
-// The space savings for smaller ints are not worth having:
-#define Int16$serialize NULL
-#define Int16$deserialize NULL
-#define Int8$serialize NULL
-#define Int8$deserialize NULL
-
-#ifdef __TINYC__
-#define __builtin_add_overflow(x, y, result) \
- ({ \
- *(result) = (x) + (y); \
- false; \
- })
-#endif
-
-#define DEFINE_INT_TYPE(c_type, KindOfInt, min_val, max_val, to_attr) \
- public \
- Text_t KindOfInt##$as_text(const void *i, bool colorize, const TypeInfo_t *info) { \
- (void)info; \
- if (!i) return Text(#KindOfInt); \
- Text_t text = _int64_to_text((int64_t)(*(c_type *)i)); \
- return colorize ? Texts(Text("\033[35m"), text, Text("\033[m")) : text; \
- } \
- public \
- Text_t KindOfInt##$value_as_text(c_type i) { return _int64_to_text((int64_t)i); } \
- public \
- PUREFUNC int32_t KindOfInt##$compare(const void *x, const void *y, const TypeInfo_t *info) { \
- (void)info; \
- return (*(c_type *)x > *(c_type *)y) - (*(c_type *)x < *(c_type *)y); \
- } \
- public \
- PUREFUNC bool KindOfInt##$equal(const void *x, const void *y, const TypeInfo_t *info) { \
- (void)info; \
- return *(c_type *)x == *(c_type *)y; \
- } \
- public \
- CONSTFUNC bool KindOfInt##$is_between(const c_type x, const c_type low, const c_type high) { \
- return low <= x && x <= high; \
- } \
- public \
- CONSTFUNC c_type KindOfInt##$clamped(c_type x, c_type min, c_type max) { \
- return x < min ? min : (x > max ? max : x); \
- } \
- public \
- Text_t KindOfInt##$hex(c_type i, Int_t digits_int, bool uppercase, bool prefix) { \
- Int_t as_int = Int$from_int64((int64_t)i); \
- return Int$hex(as_int, digits_int, uppercase, prefix); \
- } \
- public \
- Text_t KindOfInt##$octal(c_type i, Int_t digits_int, bool prefix) { \
- Int_t as_int = Int$from_int64((int64_t)i); \
- return Int$octal(as_int, digits_int, prefix); \
- } \
- public \
- List_t KindOfInt##$bits(c_type x) { \
- List_t bit_list = (List_t){.data = GC_MALLOC_ATOMIC(sizeof(bool[8 * sizeof(c_type)])), \
- .atomic = 1, \
- .stride = sizeof(bool), \
- .length = 8 * sizeof(c_type)}; \
- bool *bits = bit_list.data + sizeof(c_type) * 8; \
- for (size_t i = 0; i < 8 * sizeof(c_type); i++) { \
- *(bits--) = x & 1; \
- x >>= 1; \
- } \
- return bit_list; \
- } \
- public \
- bool KindOfInt##$get_bit(c_type x, Int_t bit_index) { \
- if (Int$compare_value(bit_index, I(1)) < 0) fail("Invalid bit index (expected 1 or higher): ", bit_index); \
- if (Int$compare_value(bit_index, Int$from_int64(sizeof(c_type) * 8)) > 0) \
- fail("Bit index is too large! There are only ", (uint64_t)sizeof(c_type) * 8, \
- " bits, but index is: ", bit_index); \
- return ((x & (c_type)(1L << (Int64$from_int(bit_index, true) - 1L))) != 0); \
- } \
- typedef struct { \
- Optional##KindOfInt##_t current, last; \
- KindOfInt##_t step; \
- } KindOfInt##Range_t; \
- static Optional##KindOfInt##_t _next_##KindOfInt(KindOfInt##Range_t *info) { \
- Optional##KindOfInt##_t i = info->current; \
- if (i.has_value) { \
- KindOfInt##_t next; \
- bool overflow = __builtin_add_overflow(i.value, info->step, &next); \
- if (overflow \
- || (info->last.has_value && (info->step >= 0 ? next > info->last.value : next < info->last.value))) \
- info->current = (Optional##KindOfInt##_t){.has_value = false}; \
- else info->current = (Optional##KindOfInt##_t){.has_value = true, .value = next}; \
- } \
- return i; \
- } \
- public \
- to_attr Closure_t KindOfInt##$to(c_type first, c_type last, Optional##KindOfInt##_t step) { \
- KindOfInt##Range_t *range = GC_MALLOC(sizeof(KindOfInt##Range_t)); \
- range->current = (Optional##KindOfInt##_t){.has_value = true, .value = first}; \
- range->last = (Optional##KindOfInt##_t){.has_value = true, .value = last}; \
- range->step = step.has_value ? step.value : (last >= first ? 1 : -1); \
- return (Closure_t){.fn = _next_##KindOfInt, .userdata = range}; \
- } \
- public \
- to_attr Closure_t KindOfInt##$onward(c_type first, c_type step) { \
- KindOfInt##Range_t *range = GC_MALLOC(sizeof(KindOfInt##Range_t)); \
- range->current = (Optional##KindOfInt##_t){.has_value = true, .value = first}; \
- range->last = (Optional##KindOfInt##_t){.has_value = false}; \
- range->step = step; \
- return (Closure_t){.fn = _next_##KindOfInt, .userdata = range}; \
- } \
- public \
- PUREFUNC Optional##KindOfInt##_t KindOfInt##$parse(Text_t text, Text_t *remainder) { \
- OptionalInt_t full_int = Int$parse(text, remainder); \
- if (full_int.small == 0L) return (Optional##KindOfInt##_t){.has_value = false}; \
- if (Int$compare_value(full_int, I(min_val)) < 0) { \
- return (Optional##KindOfInt##_t){.has_value = false}; \
- } \
- if (Int$compare_value(full_int, I(max_val)) > 0) { \
- return (Optional##KindOfInt##_t){.has_value = false}; \
- } \
- return (Optional##KindOfInt##_t){.has_value = true, .value = KindOfInt##$from_int(full_int, true)}; \
- } \
- public \
- CONSTFUNC c_type KindOfInt##$gcd(c_type x, c_type y) { \
- if (x == 0 || y == 0) return 0; \
- x = KindOfInt##$abs(x); \
- y = KindOfInt##$abs(y); \
- while (x != y) { \
- if (x > y) x -= y; \
- else y -= x; \
- } \
- return x; \
- } \
- public \
- const c_type KindOfInt##$min = min_val; \
- public \
- const c_type KindOfInt##$max = max_val; \
- public \
- const TypeInfo_t KindOfInt##$info = { \
- .size = sizeof(c_type), \
- .align = __alignof__(c_type), \
- .metamethods = \
- { \
- .compare = KindOfInt##$compare, \
- .as_text = KindOfInt##$as_text, \
- .serialize = KindOfInt##$serialize, \
- .deserialize = KindOfInt##$deserialize, \
- }, \
- };
-
-DEFINE_INT_TYPE(int64_t, Int64, INT64_MIN, INT64_MAX, __attribute__(()))
-DEFINE_INT_TYPE(int32_t, Int32, INT32_MIN, INT32_MAX, CONSTFUNC)
-DEFINE_INT_TYPE(int16_t, Int16, INT16_MIN, INT16_MAX, CONSTFUNC)
-DEFINE_INT_TYPE(int8_t, Int8, INT8_MIN, INT8_MAX, CONSTFUNC)
-#undef DEFINE_INT_TYPE
diff --git a/src/stdlib/integers.h b/src/stdlib/integers.h
index 7eafd134..246956fa 100644
--- a/src/stdlib/integers.h
+++ b/src/stdlib/integers.h
@@ -2,393 +2,8 @@
#pragma once
-#include <gmp.h>
-#include <stdbool.h>
-#include <stdint.h>
-
-#include "datatypes.h"
-#include "stdlib.h"
-#include "types.h"
-#include "util.h"
-
-#define I64(x) ((int64_t)x)
-#define I32(x) ((int32_t)x)
-#define I16(x) ((int16_t)x)
-#define I8(x) ((int8_t)x)
-
-#define DEFINE_INT_TYPE(c_type, type_name) \
- typedef struct { \
- c_type value; \
- bool has_value : 1; \
- } Optional##type_name##_t; \
- Text_t type_name##$as_text(const void *i, bool colorize, const TypeInfo_t *type); \
- Text_t type_name##$value_as_text(c_type i); \
- PUREFUNC int32_t type_name##$compare(const void *x, const void *y, const TypeInfo_t *type); \
- PUREFUNC bool type_name##$equal(const void *x, const void *y, const TypeInfo_t *type); \
- Text_t type_name##$hex(c_type i, Int_t digits, bool uppercase, bool prefix); \
- Text_t type_name##$octal(c_type i, Int_t digits, bool prefix); \
- List_t type_name##$bits(c_type x); \
- bool type_name##$get_bit(c_type x, Int_t bit_index); \
- Closure_t type_name##$to(c_type first, c_type last, Optional##type_name##_t step); \
- Closure_t type_name##$onward(c_type first, c_type step); \
- PUREFUNC Optional##type_name##_t type_name##$parse(Text_t text, Text_t *remainder); \
- CONSTFUNC bool type_name##$is_between(const c_type x, const c_type low, const c_type high); \
- CONSTFUNC c_type type_name##$clamped(c_type x, c_type min, c_type max); \
- MACROLIKE CONSTFUNC c_type type_name##$from_byte(Byte_t b) { return (c_type)b; } \
- MACROLIKE CONSTFUNC c_type type_name##$from_bool(Bool_t b) { return (c_type)b; } \
- CONSTFUNC c_type type_name##$gcd(c_type x, c_type y); \
- extern const c_type type_name##$min, type_name##$max; \
- extern const TypeInfo_t type_name##$info; \
- MACROLIKE c_type type_name##$divided_by(c_type D, c_type d) { \
- c_type q = D / d, r = D % d; \
- q -= (r < 0) * (2 * (d > 0) - 1); \
- return q; \
- } \
- MACROLIKE c_type type_name##$modulo(c_type D, c_type d) { \
- c_type r = D % d; \
- r -= (r < 0) * (2 * (d < 0) - 1) * d; \
- return r; \
- } \
- MACROLIKE c_type type_name##$modulo1(c_type D, c_type d) { return type_name##$modulo(D - 1, d) + 1; } \
- MACROLIKE PUREFUNC c_type type_name##$wrapping_plus(c_type x, c_type y) { \
- return (c_type)((u##c_type)x + (u##c_type)y); \
- } \
- MACROLIKE PUREFUNC c_type type_name##$wrapping_minus(c_type x, c_type y) { \
- return (c_type)((u##c_type)x + (u##c_type)y); \
- } \
- MACROLIKE PUREFUNC c_type type_name##$unsigned_left_shifted(c_type x, c_type y) { \
- return (c_type)((u##c_type)x << y); \
- } \
- MACROLIKE PUREFUNC c_type type_name##$unsigned_right_shifted(c_type x, c_type y) { \
- return (c_type)((u##c_type)x >> y); \
- }
-
-DEFINE_INT_TYPE(int64_t, Int64)
-DEFINE_INT_TYPE(int32_t, Int32)
-DEFINE_INT_TYPE(int16_t, Int16)
-DEFINE_INT_TYPE(int8_t, Int8)
-#undef DEFINE_INT_TYPE
-
-#define NONE_INT64 ((OptionalInt64_t){.has_value = false})
-#define NONE_INT32 ((OptionalInt32_t){.has_value = false})
-#define NONE_INT16 ((OptionalInt16_t){.has_value = false})
-#define NONE_INT8 ((OptionalInt8_t){.has_value = false})
-
-#define Int64$abs(...) I64(labs(__VA_ARGS__))
-#define Int32$abs(...) I32(abs(__VA_ARGS__))
-#define Int16$abs(...) I16(abs(__VA_ARGS__))
-#define Int8$abs(...) I8(abs(__VA_ARGS__))
-
-void Int64$serialize(const void *obj, FILE *out, Table_t *, const TypeInfo_t *);
-void Int64$deserialize(FILE *in, void *outval, List_t *, const TypeInfo_t *);
-void Int32$serialize(const void *obj, FILE *out, Table_t *, const TypeInfo_t *);
-void Int32$deserialize(FILE *in, void *outval, List_t *, const TypeInfo_t *);
-
-Text_t Int$as_text(const void *i, bool colorize, const TypeInfo_t *type);
-Text_t Int$value_as_text(Int_t i);
-PUREFUNC uint64_t Int$hash(const void *x, const TypeInfo_t *type);
-PUREFUNC int32_t Int$compare(const void *x, const void *y, const TypeInfo_t *type);
-PUREFUNC int32_t Int$compare_value(const Int_t x, const Int_t y);
-CONSTFUNC bool Int$is_between(const Int_t x, const Int_t low, const Int_t high);
-CONSTFUNC Int_t Int$clamped(Int_t x, Int_t low, Int_t high);
-PUREFUNC bool Int$equal(const void *x, const void *y, const TypeInfo_t *type);
-PUREFUNC bool Int$equal_value(const Int_t x, const Int_t y);
-Text_t Int$hex(Int_t i, Int_t digits, bool uppercase, bool prefix);
-Text_t Int$octal(Int_t i, Int_t digits, bool prefix);
-PUREFUNC Closure_t Int$to(Int_t first, Int_t last, OptionalInt_t step);
-PUREFUNC Closure_t Int$onward(Int_t first, Int_t step);
-OptionalInt_t Int$from_str(const char *str);
-OptionalInt_t Int$parse(Text_t text, Text_t *remainder);
-Int_t Int$abs(Int_t x);
-Int_t Int$power(Int_t base, Int_t exponent);
-Int_t Int$gcd(Int_t x, Int_t y);
-OptionalInt_t Int$sqrt(Int_t i);
-bool Int$get_bit(Int_t x, Int_t bit_index);
-
-#define BIGGEST_SMALL_INT 0x3fffffff
-#define SMALLEST_SMALL_INT -0x40000000
-
-#define Int$from_mpz(mpz) \
- (mpz_cmpabs_ui(mpz, BIGGEST_SMALL_INT) <= 0 ? ((Int_t){.small = (mpz_get_si(mpz) << 2L) | 1L}) \
- : ((Int_t){.big = memcpy(new (mpz_t), &mpz, sizeof(mpz_t))}))
-
-#define mpz_init_set_int(mpz, i) \
- do { \
- if likely ((i).small & 1L) mpz_init_set_si(mpz, (i).small >> 2L); \
- else mpz_init_set(mpz, *(i).big); \
- } while (0)
-
-#define I_small(i) ((Int_t){.small = (int64_t)((uint64_t)(i) << 2L) | 1L})
-#define I(i) _Generic(i, int8_t: I_small(i), int16_t: I_small(i), default: Int$from_int64(i))
-#define I_is_zero(i) ((i).small == 1L)
-
-Int_t Int$slow_plus(Int_t x, Int_t y);
-Int_t Int$slow_minus(Int_t x, Int_t y);
-Int_t Int$slow_times(Int_t x, Int_t y);
-Int_t Int$slow_divided_by(Int_t x, Int_t y);
-Int_t Int$slow_modulo(Int_t x, Int_t y);
-Int_t Int$slow_modulo1(Int_t x, Int_t y);
-Int_t Int$slow_left_shifted(Int_t x, Int_t y);
-Int_t Int$slow_right_shifted(Int_t x, Int_t y);
-Int_t Int$slow_bit_and(Int_t x, Int_t y);
-Int_t Int$slow_bit_or(Int_t x, Int_t y);
-Int_t Int$slow_bit_xor(Int_t x, Int_t y);
-Int_t Int$slow_negative(Int_t x);
-Int_t Int$slow_negated(Int_t x);
-bool Int$is_prime(Int_t x, Int_t reps);
-Int_t Int$next_prime(Int_t x);
-#if __GNU_MP_VERSION >= 6
-#if __GNU_MP_VERSION_MINOR >= 3
-OptionalInt_t Int$prev_prime(Int_t x);
-#endif
-#endif
-Int_t Int$choose(Int_t n, Int_t k);
-Int_t Int$factorial(Int_t n);
-
-extern const TypeInfo_t Int$info;
-
-// Fast-path inline versions for the common case where integer arithmetic is
-// between two small ints.
-
-MACROLIKE Int_t Int$plus(Int_t x, Int_t y) {
- const int64_t z = (int64_t)((uint64_t)x.small + (uint64_t)y.small);
- if likely ((z | 2L) == (int32_t)z) return (Int_t){.small = (z - 1L)};
- return Int$slow_plus(x, y);
-}
-
-MACROLIKE Int_t Int$minus(Int_t x, Int_t y) {
- const int64_t z = (int64_t)(((uint64_t)x.small ^ 3L) - (uint64_t)y.small);
- if likely ((z & ~2L) == (int32_t)z) return (Int_t){.small = z};
- return Int$slow_minus(x, y);
-}
-
-MACROLIKE Int_t Int$times(Int_t x, Int_t y) {
- if likely ((x.small & y.small) & 1L) {
- const int64_t z = (x.small >> 1L) * (y.small >> 1L);
- if likely (z == (int32_t)z) return (Int_t){.small = z + 1L};
- }
- return Int$slow_times(x, y);
-}
-
-MACROLIKE Int_t Int$divided_by(Int_t x, Int_t y) {
- if likely (x.small & y.small & 1L) {
- // Euclidean division, see:
- // https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf
- const int64_t D = (x.small >> 2L);
- const int64_t d = (y.small >> 2L);
- int64_t q = D / d, r = D % d;
- q -= (r < 0L) * (2L * (d > 0L) - 1L);
- if likely (q == (int32_t)q) return (Int_t){.small = (q << 2L) | 1L};
- }
- return Int$slow_divided_by(x, y);
-}
-
-MACROLIKE Int_t Int$modulo(Int_t x, Int_t y) {
- if likely (x.small & y.small & 1L) {
- // Euclidean modulus, see:
- // https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf
- const int64_t D = (x.small >> 2L);
- const int64_t d = (y.small >> 2L);
- int64_t r = D % d;
- r -= (r < 0L) * (2L * (d < 0L) - 1L) * d;
- return (Int_t){.small = (r << 2L) | 1L};
- }
- return Int$slow_modulo(x, y);
-}
-
-MACROLIKE Int_t Int$modulo1(Int_t x, Int_t y) {
- if likely (x.small & y.small & 1L) {
- // Euclidean modulus, see:
- // https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf
- const int64_t D = (x.small >> 2L) - 1L;
- const int64_t d = (y.small >> 2L);
- int64_t r = D % d;
- r -= (r < 0L) * (2L * (d < 0L) - 1L) * d;
- return (Int_t){.small = ((r + 1L) << 2L) | 1L};
- }
- return Int$slow_modulo1(x, y);
-}
-
-MACROLIKE Int_t Int$left_shifted(Int_t x, Int_t y) {
- if likely (x.small & y.small & 1L) {
- const int64_t z = ((x.small >> 2L) << (y.small >> 2L)) << 2L;
- if likely (z == (int32_t)z) return (Int_t){.small = z + 1L};
- }
- return Int$slow_left_shifted(x, y);
-}
-
-MACROLIKE Int_t Int$right_shifted(Int_t x, Int_t y) {
- if likely (x.small & y.small & 1L) {
- const int64_t z = ((x.small >> 2L) >> (y.small >> 2L)) << 2L;
- if likely (z == (int32_t)z) return (Int_t){.small = z + 1L};
- }
- return Int$slow_right_shifted(x, y);
-}
-
-MACROLIKE Int_t Int$bit_and(Int_t x, Int_t y) {
- const int64_t z = x.small & y.small;
- if likely (z & 1L) return (Int_t){.small = z};
- return Int$slow_bit_and(x, y);
-}
-
-MACROLIKE Int_t Int$bit_or(Int_t x, Int_t y) {
- if likely (x.small & y.small & 1L) return (Int_t){.small = (x.small | y.small)};
- return Int$slow_bit_or(x, y);
-}
-
-MACROLIKE Int_t Int$bit_xor(Int_t x, Int_t y) {
- if likely (x.small & y.small & 1L) return (Int_t){.small = (x.small ^ y.small) | 1L};
- return Int$slow_bit_xor(x, y);
-}
-
-MACROLIKE Int_t Int$negated(Int_t x) {
- if likely (x.small & 1L) return (Int_t){.small = (~x.small) ^ 3L};
- return Int$slow_negated(x);
-}
-
-MACROLIKE Int_t Int$negative(Int_t x) {
- if likely (x.small & 1L) return (Int_t){.small = ((-((x.small) >> 2L)) << 2L) | 1L};
- return Int$slow_negative(x);
-}
-
-MACROLIKE PUREFUNC bool Int$is_negative(Int_t x) {
- if likely (x.small & 1L) return x.small < 0L;
- return Int$compare_value(x, I_small(0)) < 0L;
-}
-
-// Constructors/conversion functions:
-
-// Int constructors:
-#ifdef __GNUC__
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-MACROLIKE PUREFUNC Int_t Int$from_num(double n, bool truncate) {
- mpz_t result;
- mpz_init_set_d(result, n);
- if (!truncate && unlikely(mpz_get_d(result) != n)) fail("Could not convert to an integer without truncation: ", n);
- return Int$from_mpz(result);
-}
-MACROLIKE PUREFUNC Int_t Int$from_num32(float n, bool truncate) { return Int$from_num((double)n, truncate); }
-MACROLIKE Int_t Int$from_int64(int64_t i) {
- if likely (i >= SMALLEST_SMALL_INT && i <= BIGGEST_SMALL_INT) return (Int_t){.small = (i << 2L) | 1L};
- mpz_t result;
- mpz_init_set_si(result, i);
- return Int$from_mpz(result);
-}
-MACROLIKE CONSTFUNC Int_t Int$from_int32(Int32_t i) { return Int$from_int64((Int32_t)i); }
-MACROLIKE CONSTFUNC Int_t Int$from_int16(Int16_t i) { return I_small(i); }
-MACROLIKE CONSTFUNC Int_t Int$from_int8(Int8_t i) { return I_small(i); }
-MACROLIKE CONSTFUNC Int_t Int$from_byte(Byte_t b) { return I_small(b); }
-MACROLIKE CONSTFUNC Int_t Int$from_bool(Bool_t b) { return I_small(b); }
-
-// Int64 constructors
-MACROLIKE PUREFUNC Int64_t Int64$from_num(Num_t n, bool truncate) {
- int64_t i64 = (int64_t)n;
- if (!truncate && unlikely((Num_t)i64 != n)) fail("Could not convert Num to Int64 without truncation: ", n);
- return i64;
-}
-MACROLIKE PUREFUNC Int64_t Int64$from_num32(Num32_t n, bool truncate) {
- int64_t i64 = (int64_t)n;
- if (!truncate && unlikely((Num32_t)i64 != n)) fail("Could not convert Num32 to Int64 without truncation: ", n);
- return i64;
-}
-MACROLIKE PUREFUNC Int64_t Int64$from_int(Int_t i, bool truncate) {
- if likely (i.small & 1L) return (int64_t)(i.small >> 2L);
- if (!truncate && unlikely(!mpz_fits_slong_p(*i.big))) fail("Integer is too big to fit in a 64-bit integer: ", i);
- return mpz_get_si(*i.big);
-}
-MACROLIKE CONSTFUNC Int64_t Int64$from_int32(Int32_t i) { return (Int64_t)i; }
-MACROLIKE CONSTFUNC Int64_t Int64$from_int16(Int16_t i) { return (Int64_t)i; }
-MACROLIKE CONSTFUNC Int64_t Int64$from_int8(Int8_t i) { return (Int64_t)i; }
-
-// Int32 constructors
-MACROLIKE PUREFUNC Int32_t Int32$from_num(Num_t n, bool truncate) {
- int32_t i32 = (int32_t)n;
- if (!truncate && unlikely((Num_t)i32 != n)) fail("Could not convert Num to Int32 without truncation: ", n);
- return i32;
-}
-MACROLIKE PUREFUNC Int32_t Int32$from_num32(Num32_t n, bool truncate) {
- int32_t i32 = (int32_t)n;
- if (!truncate && unlikely((Num32_t)i32 != n)) fail("Could not convert Num32 to Int32 without truncation: ", n);
- return i32;
-}
-MACROLIKE PUREFUNC Int32_t Int32$from_int(Int_t i, bool truncate) {
- int64_t i64 = Int64$from_int(i, truncate);
- int32_t i32 = (int32_t)i64;
- if (!truncate && unlikely((int64_t)i32 != i64)) fail("Integer is too big to fit in a 32-bit integer: ", i);
- return i32;
-}
-MACROLIKE PUREFUNC Int32_t Int32$from_int64(Int64_t i64, bool truncate) {
- int32_t i32 = (int32_t)i64;
- if (!truncate && unlikely((int64_t)i32 != i64)) fail("Integer is too big to fit in a 32-bit integer: ", i64);
- return i32;
-}
-MACROLIKE CONSTFUNC Int32_t Int32$from_int16(Int16_t i) { return (Int32_t)i; }
-MACROLIKE CONSTFUNC Int32_t Int32$from_int8(Int8_t i) { return (Int32_t)i; }
-
-// Int16 constructors
-MACROLIKE PUREFUNC Int16_t Int16$from_num(Num_t n, bool truncate) {
- int16_t i16 = (int16_t)n;
- if (!truncate && unlikely((Num_t)i16 != n)) fail("Could not convert Num to Int16 without truncation: ", n);
- return i16;
-}
-MACROLIKE PUREFUNC Int16_t Int16$from_num32(Num32_t n, bool truncate) {
- int16_t i16 = (int16_t)n;
- if (!truncate && unlikely((Num32_t)i16 != n))
- fail("Could not convert Num32 to Int16 without truncation: ", (double)n);
- return i16;
-}
-MACROLIKE PUREFUNC Int16_t Int16$from_int(Int_t i, bool truncate) {
- int64_t i64 = Int64$from_int(i, truncate);
- int16_t i16 = (int16_t)i64;
- if (!truncate && unlikely((int64_t)i16 != i64)) fail("Integer is too big to fit in a 16-bit integer!");
- return i16;
-}
-MACROLIKE PUREFUNC Int16_t Int16$from_int64(Int64_t i64, bool truncate) {
- int16_t i16 = (int16_t)i64;
- if (!truncate && unlikely((int64_t)i16 != i64)) fail("Integer is too big to fit in a 16-bit integer: ", i64);
- return i16;
-}
-MACROLIKE PUREFUNC Int16_t Int16$from_int32(Int32_t i32, bool truncate) {
- int16_t i16 = (int16_t)i32;
- if (!truncate && unlikely((int32_t)i16 != i32)) fail("Integer is too big to fit in a 16-bit integer: ", i32);
- return i16;
-}
-MACROLIKE CONSTFUNC Int16_t Int16$from_int8(Int8_t i) { return (Int16_t)i; }
-
-// Int8 constructors
-MACROLIKE PUREFUNC Int8_t Int8$from_num(Num_t n, bool truncate) {
- int8_t i8 = (int8_t)n;
- if (!truncate && unlikely((Num_t)i8 != n)) fail("Could not convert Num to Int8 without truncation: ", n);
- return i8;
-}
-MACROLIKE PUREFUNC Int8_t Int8$from_num32(Num32_t n, bool truncate) {
- int8_t i8 = (int8_t)n;
- if (!truncate && unlikely((Num32_t)i8 != n)) fail("Could not convert Num32 to Int8 without truncation: ", n);
- return i8;
-}
-MACROLIKE PUREFUNC Int8_t Int8$from_int(Int_t i, bool truncate) {
- int64_t i64 = Int64$from_int(i, truncate);
- int8_t i8 = (int8_t)i64;
- if (!truncate && unlikely((int64_t)i8 != i64)) fail("Integer is too big to fit in an 8-bit integer!");
- return i8;
-}
-MACROLIKE PUREFUNC Int8_t Int8$from_int64(Int64_t i64, bool truncate) {
- int8_t i8 = (int8_t)i64;
- if (!truncate && unlikely((int64_t)i8 != i64)) fail("Integer is too big to fit in a 8-bit integer: ", i64);
- return i8;
-}
-MACROLIKE PUREFUNC Int8_t Int8$from_int32(Int32_t i32, bool truncate) {
- int8_t i8 = (int8_t)i32;
- if (!truncate && unlikely((int32_t)i8 != i32)) fail("Integer is too big to fit in a 8-bit integer: ", i32);
- return i8;
-}
-MACROLIKE PUREFUNC Int8_t Int8$from_int16(Int16_t i16, bool truncate) {
- int8_t i8 = (int8_t)i16;
- if (!truncate && unlikely((int16_t)i8 != i16)) fail("Integer is too big to fit in a 8-bit integer: ", i16);
- return i8;
-}
-#ifdef __GNUC__
-#pragma GCC diagnostic pop
-#endif
+#include "bigint.h" // IWYU pragma: export
+#include "int16.h" // IWYU pragma: export
+#include "int32.h" // IWYU pragma: export
+#include "int64.h" // IWYU pragma: export
+#include "int8.h" // IWYU pragma: export