diff options
Diffstat (limited to 'src/stdlib')
| -rw-r--r-- | src/stdlib/bigint.c | 548 | ||||
| -rw-r--r-- | src/stdlib/bigint.h | 212 | ||||
| -rw-r--r-- | src/stdlib/bytes.h | 1 | ||||
| -rw-r--r-- | src/stdlib/int16.c | 3 | ||||
| -rw-r--r-- | src/stdlib/int16.h | 4 | ||||
| -rw-r--r-- | src/stdlib/int32.c | 3 | ||||
| -rw-r--r-- | src/stdlib/int32.h | 4 | ||||
| -rw-r--r-- | src/stdlib/int64.c | 3 | ||||
| -rw-r--r-- | src/stdlib/int64.h | 4 | ||||
| -rw-r--r-- | src/stdlib/int8.c | 3 | ||||
| -rw-r--r-- | src/stdlib/int8.h | 4 | ||||
| -rw-r--r-- | src/stdlib/intX.c.h | 275 | ||||
| -rw-r--r-- | src/stdlib/intX.h | 167 | ||||
| -rw-r--r-- | src/stdlib/integers.c | 747 | ||||
| -rw-r--r-- | src/stdlib/integers.h | 395 |
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 |
