aboutsummaryrefslogtreecommitdiff
path: root/src/stdlib/bigint.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/stdlib/bigint.c')
-rw-r--r--src/stdlib/bigint.c84
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()");