diff options
Diffstat (limited to 'src/stdlib/bigint.c')
| -rw-r--r-- | src/stdlib/bigint.c | 84 |
1 files changed, 55 insertions, 29 deletions
diff --git a/src/stdlib/bigint.c b/src/stdlib/bigint.c index 8bffbaf1..7af1fa32 100644 --- a/src/stdlib/bigint.c +++ b/src/stdlib/bigint.c @@ -10,6 +10,7 @@ #include <stdio.h> #include <stdlib.h> +#include "../util.h" #include "datatypes.h" #include "integers.h" #include "optionals.h" @@ -18,12 +19,49 @@ #include "text.h" #include "types.h" +#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 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(GC_MALLOC(sizeof(__mpz_struct)), mpz, sizeof(__mpz_struct))})) + +#define Int_mpz(i) (__mpz_struct *)((i).big) + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif +public +PUREFUNC Int_t Int$from_num64(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); +} + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +public +PUREFUNC 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); +} + public int Int$print(FILE *f, Int_t i) { if (likely(i.small & 1L)) { - return _print_int(f, (int64_t)((i.small) >> 2L)); + return Int64$print(f, (int64_t)((i.small) >> 2L)); } else { - return gmp_fprintf(f, "%Zd", i.big); + return gmp_fprintf(f, "%Zd", Int_mpz(i)); } } @@ -50,7 +88,7 @@ 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); + char *str = mpz_get_str(NULL, 10, Int_mpz(i)); return Text$from_str(str); } } @@ -72,9 +110,9 @@ static bool Int$is_none(const void *i, const TypeInfo_t *info) { 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 >> 2)); - else if (y.small & 1) return mpz_cmp_si(x.big, (y.small >> 2)); - else return x.big == y.big ? 0 : mpz_cmp(x.big, y.big); + else if (x.small & 1) return -mpz_cmp_si(Int_mpz(y), (x.small >> 2)); + else if (y.small & 1) return mpz_cmp_si(Int_mpz(x), (y.small >> 2)); + else return x.big == y.big ? 0 : mpz_cmp(Int_mpz(x), Int_mpz(y)); } public @@ -86,7 +124,7 @@ PUREFUNC int32_t Int$compare(const void *x, const void *y, const TypeInfo_t *inf 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); + else return x.big == y.big ? 0 : (mpz_cmp(Int_mpz(x), Int_mpz(y)) == 0); } public @@ -114,7 +152,7 @@ PUREFUNC uint64_t Int$hash(const void *vx, const TypeInfo_t *info) { if (likely(x->small & 1L)) { return siphash24((void *)x, sizeof(Int_t)); } else { - char *str = mpz_get_str(NULL, 16, x->big); + char *str = mpz_get_str(NULL, 16, Int_mpz(*x)); return siphash24((void *)str, strlen(str)); } } @@ -128,7 +166,7 @@ Text_t Int$hex(Int_t i, Int_t digits_int, bool uppercase, bool prefix) { 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); + char *str = mpz_get_str(NULL, 16, Int_mpz(i)); if (uppercase) { for (char *c = str; *c; c++) *c = (char)toupper(*c); @@ -153,7 +191,7 @@ Text_t Int$octal(Int_t i, Int_t digits_int, bool prefix) { 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); + char *str = mpz_get_str(NULL, 8, Int_mpz(i)); 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); @@ -172,7 +210,7 @@ Int_t Int$slow_plus(Int_t x, Int_t y) { 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); + mpz_add(result, result, Int_mpz(y)); } return Int$from_mpz(result); } @@ -185,7 +223,7 @@ Int_t Int$slow_minus(Int_t x, Int_t y) { 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); + mpz_sub(result, result, Int_mpz(y)); } return Int$from_mpz(result); } @@ -195,7 +233,7 @@ 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); + else mpz_mul(result, result, Int_mpz(y)); return Int$from_mpz(result); } @@ -208,7 +246,7 @@ Int_t Int$slow_divided_by(Int_t dividend, Int_t divisor) { 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; + bool d_positive = likely(divisor.small & 1L) ? divisor.small > 0x1L : mpz_sgn(Int_mpz(divisor)) > 0; if (d_positive) mpz_sub_ui(quotient, quotient, 1); else mpz_add_ui(quotient, quotient, 1); } @@ -330,9 +368,9 @@ Int_t Int$gcd(Int_t x, Int_t y) { 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); + if (x.small & 0x1L) mpz_gcd_ui(result, Int_mpz(y), (uint64_t)labs(x.small >> 2L)); + else if (y.small & 0x1L) mpz_gcd_ui(result, Int_mpz(x), (uint64_t)labs(y.small >> 2L)); + else mpz_gcd(result, Int_mpz(x), Int_mpz(y)); return Int$from_mpz(result); } @@ -510,18 +548,6 @@ Int_t Int$next_prime(Int_t x) { 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()"); |
