1 // Integer type infos and methods
3 #include <stdio.h> // Must be before gmp.h
13 #include "datatypes.h"
15 #include "optionals.h"
22 int Int$print(FILE *f, Int_t i) {
23 if (likely(i.small & 1L)) {
24 return _print_int(f, (int64_t)((i.small) >> 2L));
26 return gmp_fprintf(f, "%Zd", i.big);
30 static Text_t _int64_to_text(int64_t n) {
31 if (n == INT64_MIN) return Text("-9223372036854775808");
33 char buf[21] = {[20] = 0}; // Big enough for INT64_MIN + '\0'
35 bool negative = n < 0;
36 if (negative) n = -n; // Safe to do because we checked for INT64_MIN earlier
39 *(p--) = '0' + (n % 10);
43 if (negative) *(p--) = '-';
45 return Text$from_strn(p + 1, (size_t)(&buf[19] - p));
49 Text_t Int$value_as_text(Int_t i) {
50 if (likely(i.small & 1L)) {
51 return _int64_to_text(i.small >> 2L);
53 char *str = mpz_get_str(NULL, 10, i.big);
54 return Text$from_str(str);
59 Text_t Int$as_text(const void *i, bool colorize, const TypeInfo_t *info) {
61 if (!i) return Text("Int");
62 Text_t text = Int$value_as_text(*(Int_t *)i);
63 if (colorize) text = Text$concat(Text("\x1b[35m"), text, Text("\x1b[m"));
67 static bool Int$is_none(const void *i, const TypeInfo_t *info) {
69 return ((Int_t *)i)->small == 0L;
73 PUREFUNC int32_t Int$compare_value(const Int_t x, const Int_t y) {
74 if (likely(x.small & y.small & 1L)) return (x.small > y.small) - (x.small < y.small);
75 else if (x.small & 1) return -mpz_cmp_si(y.big, (x.small >> 2));
76 else if (y.small & 1) return mpz_cmp_si(x.big, (y.small >> 2));
77 else return x.big == y.big ? 0 : mpz_cmp(x.big, y.big);
81 PUREFUNC int32_t Int$compare(const void *x, const void *y, const TypeInfo_t *info) {
83 return Int$compare_value(*(Int_t *)x, *(Int_t *)y);
87 PUREFUNC bool Int$equal_value(const Int_t x, const Int_t y) {
88 if (likely((x.small | y.small) & 1L)) return x.small == y.small;
89 else return x.big == y.big ? 0 : (mpz_cmp(x.big, y.big) == 0);
93 PUREFUNC bool Int$equal(const void *x, const void *y, const TypeInfo_t *info) {
95 return Int$equal_value(*(Int_t *)x, *(Int_t *)y);
99 CONSTFUNC Int_t Int$clamped(Int_t x, Int_t low, Int_t high) {
100 return (Int$compare(&x, &low, &Int$info) <= 0) ? low : (Int$compare(&x, &high, &Int$info) >= 0 ? high : x);
104 CONSTFUNC bool Int$is_between(const Int_t x, const Int_t low, const Int_t high) {
105 int32_t low_cmp = Int$compare_value(x, low);
106 int32_t high_cmp = Int$compare_value(x, high);
107 return (low_cmp >= 0 && high_cmp <= 0) || (low_cmp <= 0 && high_cmp >= 0);
111 PUREFUNC uint64_t Int$hash(const void *vx, const TypeInfo_t *info) {
113 Int_t *x = (Int_t *)vx;
114 if (likely(x->small & 1L)) {
115 return siphash24((void *)x, sizeof(Int_t));
117 char *str = mpz_get_str(NULL, 16, x->big);
118 return siphash24((void *)str, strlen(str));
123 Text_t Int$hex(Int_t i, Int_t digits_int, bool uppercase, bool prefix) {
124 if (Int$is_negative(i)) return Text$concat(Text("-"), Int$hex(Int$negative(i), digits_int, uppercase, prefix));
126 if (likely(i.small & 1L)) {
127 uint64_t u64 = (uint64_t)(i.small >> 2);
128 return Text$from_str(String(
129 hex(u64, .no_prefix = !prefix, .digits = Int32$from_int(digits_int, false), .uppercase = uppercase)));
131 char *str = mpz_get_str(NULL, 16, i.big);
133 for (char *c = str; *c; c++)
134 *c = (char)toupper(*c);
136 int64_t digits = Int64$from_int(digits_int, false);
137 int64_t needed_zeroes = digits - (int64_t)strlen(str);
138 if (needed_zeroes <= 0) return prefix ? Text$concat(Text("0x"), Text$from_str(str)) : Text$from_str(str);
140 char *zeroes = GC_MALLOC_ATOMIC((size_t)(needed_zeroes));
141 memset(zeroes, '0', (size_t)(needed_zeroes));
142 if (prefix) return Text$concat(Text("0x"), Text$from_str(zeroes), Text$from_str(str));
143 else return Text$concat(Text$from_str(zeroes), Text$from_str(str));
148 Text_t Int$octal(Int_t i, Int_t digits_int, bool prefix) {
149 if (Int$is_negative(i)) return Text$concat(Text("-"), Int$octal(Int$negative(i), digits_int, prefix));
151 if (likely(i.small & 1L)) {
152 uint64_t u64 = (uint64_t)(i.small >> 2);
153 return Text$from_str(String(oct(u64, .no_prefix = !prefix, .digits = Int32$from_int(digits_int, false))));
155 int64_t digits = Int64$from_int(digits_int, false);
156 char *str = mpz_get_str(NULL, 8, i.big);
157 int64_t needed_zeroes = digits - (int64_t)strlen(str);
158 if (needed_zeroes <= 0) return prefix ? Text$concat(Text("0o"), Text$from_str(str)) : Text$from_str(str);
160 char *zeroes = GC_MALLOC_ATOMIC((size_t)(needed_zeroes));
161 memset(zeroes, '0', (size_t)(needed_zeroes));
162 if (prefix) return Text$concat(Text("0o"), Text$from_str(zeroes), Text$from_str(str));
163 else return Text$concat(Text$from_str(zeroes), Text$from_str(str));
168 Int_t Int$slow_plus(Int_t x, Int_t y) {
170 mpz_init_set_int(result, x);
172 if (y.small < 0L) mpz_sub_ui(result, result, (uint64_t)(-(y.small >> 2L)));
173 else mpz_add_ui(result, result, (uint64_t)(y.small >> 2L));
175 mpz_add(result, result, y.big);
177 return Int$from_mpz(result);
181 Int_t Int$slow_minus(Int_t x, Int_t y) {
183 mpz_init_set_int(result, x);
185 if (y.small < 0L) mpz_add_ui(result, result, (uint64_t)(-(y.small >> 2L)));
186 else mpz_sub_ui(result, result, (uint64_t)(y.small >> 2L));
188 mpz_sub(result, result, y.big);
190 return Int$from_mpz(result);
194 Int_t Int$slow_times(Int_t x, Int_t y) {
196 mpz_init_set_int(result, x);
197 if (y.small & 1L) mpz_mul_si(result, result, y.small >> 2L);
198 else mpz_mul(result, result, y.big);
199 return Int$from_mpz(result);
203 Int_t Int$slow_divided_by(Int_t dividend, Int_t divisor) {
204 // Euclidean division, see:
205 // https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf
206 mpz_t quotient, remainder;
207 mpz_init_set_int(quotient, dividend);
208 mpz_init_set_int(remainder, divisor);
209 mpz_tdiv_qr(quotient, remainder, quotient, remainder);
210 if (mpz_sgn(remainder) < 0) {
211 bool d_positive = likely(divisor.small & 1L) ? divisor.small > 0x1L : mpz_sgn(divisor.big) > 0;
212 if (d_positive) mpz_sub_ui(quotient, quotient, 1);
213 else mpz_add_ui(quotient, quotient, 1);
215 return Int$from_mpz(quotient);
219 Int_t Int$slow_modulo(Int_t x, Int_t modulus) {
221 mpz_init_set_int(result, x);
223 mpz_init_set_int(divisor, modulus);
224 mpz_mod(result, result, divisor);
225 return Int$from_mpz(result);
229 Int_t Int$slow_modulo1(Int_t x, Int_t modulus) {
231 mpz_init_set_int(result, x);
232 mpz_sub_ui(result, result, 1);
234 mpz_init_set_int(divisor, modulus);
235 mpz_mod(result, result, divisor);
236 mpz_add_ui(result, result, 1);
237 return Int$from_mpz(result);
241 Int_t Int$slow_left_shifted(Int_t x, Int_t y) {
242 mp_bitcnt_t bits = (mp_bitcnt_t)Int64$from_int(y, false);
244 mpz_init_set_int(result, x);
245 mpz_mul_2exp(result, result, bits);
246 return Int$from_mpz(result);
250 Int_t Int$slow_right_shifted(Int_t x, Int_t y) {
251 mp_bitcnt_t bits = (mp_bitcnt_t)Int64$from_int(y, false);
253 mpz_init_set_int(result, x);
254 mpz_tdiv_q_2exp(result, result, bits);
255 return Int$from_mpz(result);
259 Int_t Int$slow_bit_and(Int_t x, Int_t y) {
261 mpz_init_set_int(result, x);
263 mpz_init_set_int(y_mpz, y);
264 mpz_and(result, result, y_mpz);
265 return Int$from_mpz(result);
269 Int_t Int$slow_bit_or(Int_t x, Int_t y) {
271 mpz_init_set_int(result, x);
273 mpz_init_set_int(y_mpz, y);
274 mpz_ior(result, result, y_mpz);
275 return Int$from_mpz(result);
279 Int_t Int$slow_bit_xor(Int_t x, Int_t y) {
281 mpz_init_set_int(result, x);
283 mpz_init_set_int(y_mpz, y);
284 mpz_xor(result, result, y_mpz);
285 return Int$from_mpz(result);
289 Int_t Int$slow_negated(Int_t x) {
291 mpz_init_set_int(result, x);
292 mpz_neg(result, result);
293 mpz_sub_ui(result, result, 1);
294 return Int$from_mpz(result);
298 Int_t Int$slow_negative(Int_t x) {
299 if (likely(x.small & 1L)) return (Int_t){.small = 4L * -((x.small) >> 2L) + 1L};
302 mpz_init_set_int(result, x);
303 mpz_neg(result, result);
304 return Int$from_mpz(result);
308 Int_t Int$abs(Int_t x) {
309 if (likely(x.small & 1L)) return (Int_t){.small = 4L * labs((x.small) >> 2L) + 1L};
312 mpz_init_set_int(result, x);
313 mpz_abs(result, result);
314 return Int$from_mpz(result);
318 Int_t Int$power(Int_t base, Int_t exponent) {
319 int64_t exp = Int64$from_int(exponent, false);
320 if (unlikely(exp < 0)) fail("Cannot take a negative power of an integer!");
322 mpz_init_set_int(result, base);
323 mpz_pow_ui(result, result, (uint64_t)exp);
324 return Int$from_mpz(result);
328 Int_t Int$gcd(Int_t x, Int_t y) {
329 if (likely(x.small & y.small & 0x1L)) return I_small(Int32$gcd(x.small >> 2L, y.small >> 2L));
333 if (x.small & 0x1L) mpz_gcd_ui(result, y.big, (uint64_t)labs(x.small >> 2L));
334 else if (y.small & 0x1L) mpz_gcd_ui(result, x.big, (uint64_t)labs(y.small >> 2L));
335 else mpz_gcd(result, x.big, y.big);
336 return Int$from_mpz(result);
340 OptionalInt_t Int$sqrt(Int_t i) {
341 if (Int$compare_value(i, I(0)) < 0) return NONE_INT;
343 mpz_init_set_int(result, i);
344 mpz_sqrt(result, result);
345 return Int$from_mpz(result);
349 bool Int$get_bit(Int_t x, Int_t bit_index) {
351 mpz_init_set_int(i, x);
352 if (Int$compare_value(bit_index, I(1)) < 0) fail("Invalid bit index (expected 1 or higher): ", bit_index);
353 if (Int$compare_value(bit_index, Int$from_int64(INT64_MAX)) > 0) fail("Bit index is too large! ", bit_index);
355 int is_bit_set = mpz_tstbit(i, (mp_bitcnt_t)(Int64$from_int(bit_index, true) - 1));
356 return (bool)is_bit_set;
360 OptionalInt_t current, last;
364 static OptionalInt_t _next_int(IntRange_t *info) {
365 OptionalInt_t i = info->current;
366 if (!Int$is_none(&i, &Int$info)) {
367 Int_t next = Int$plus(i, info->step);
368 if (!Int$is_none(&info->last, &Int$info)
369 && Int$compare_value(next, info->last) == Int$compare_value(info->step, I(0)))
371 info->current = next;
377 PUREFUNC Closure_t Int$to(Int_t first, Int_t last, OptionalInt_t step) {
378 IntRange_t *range = GC_MALLOC(sizeof(IntRange_t));
379 range->current = first;
381 range->step = Int$is_none(&step, &Int$info) ? Int$compare_value(last, first) >= 0
382 ? (Int_t){.small = (1L << 2L) | 1L}
383 : (Int_t){.small = (-1L >> 2L) | 1L}
385 return (Closure_t){.fn = _next_int, .userdata = range};
389 PUREFUNC Closure_t Int$onward(Int_t first, Int_t step) {
390 IntRange_t *range = GC_MALLOC(sizeof(IntRange_t));
391 range->current = first;
392 range->last = NONE_INT;
394 return (Closure_t){.fn = _next_int, .userdata = range};
398 Int_t Int$from_str(const char *str) {
399 return Int$parse(Text$from_str(str), NONE_INT, NULL);
403 OptionalInt_t Int$parse(Text_t text, OptionalInt_t base, Text_t *remainder) {
404 const char *str = Text$as_c_string(text);
405 bool negative = (*str == '-');
406 if (negative || *str == '+') str += 1;
407 const char *end = str;
409 if (base.small != 0) {
410 base32 = Int32$from_int(base, false);
413 if (strncmp(str, "0x", 2) == 0) {
417 end = str + strspn(str, "0123456789abcdefABCDEF");
421 end = str + strspn(str, "0123456789");
424 if (strncmp(str, "0o", 2) == 0) {
428 end = str + strspn(str, "01234567");
431 if (strncmp(str, "0b", 2) == 0) {
435 end = str + strspn(str, "01");
438 str += strspn(str, "0");
439 size_t n = strspn(str, "1");
441 if (remainder) *remainder = Text$from_str(end);
442 else if (*end != '\0') return NONE_INT;
443 return Int$from_int64((int64_t)n);
446 if (base32 < 1 || base32 > 36) {
447 if (remainder) *remainder = text;
450 for (; *end; end++) {
453 if ('0' <= c && c <= '9') {
454 digit = (c - (int)'0');
455 } else if ('a' <= c && c <= 'z') {
456 digit = (c - (int)'a');
457 } else if ('A' <= c && c <= 'Z') {
458 digit = (c - (int)'A');
462 if (digit >= base32) break;
466 } else if (strncmp(str, "0x", 2) == 0) {
469 } else if (strncmp(str, "0o", 2) == 0) {
472 } else if (strncmp(str, "0b", 2) == 0) {
480 if (remainder) *remainder = Text$from_str(end);
481 else if (*end != '\0') return NONE_INT;
484 int result = mpz_init_set_str(i, String(string_slice(str, (size_t)(end - str))), base32);
486 if (remainder) *remainder = text;
492 return Int$from_mpz(i);
496 bool Int$is_prime(Int_t x, Int_t reps) {
498 mpz_init_set_int(p, x);
499 if (unlikely(Int$compare_value(reps, I(9999)) > 0))
500 fail("Number of prime-test repetitions should not be above 9999");
501 int reps_int = Int32$from_int(reps, false);
502 return (mpz_probab_prime_p(p, reps_int) != 0);
506 Int_t Int$next_prime(Int_t x) {
508 mpz_init_set_int(p, x);
510 return Int$from_mpz(p);
514 Int_t Int$choose(Int_t n, Int_t k) {
515 if unlikely (Int$compare_value(n, I_small(0)) < 0) fail("Negative inputs are not supported for choose()");
520 int64_t k_i64 = Int64$from_int(k, false);
521 if unlikely (k_i64 < 0) fail("Negative inputs are not supported for choose()");
523 if likely (n.small & 1L) {
524 mpz_bin_uiui(ret, (unsigned long)(n.small >> 2L), (unsigned long)k_i64);
527 mpz_init_set_int(n_mpz, n);
528 mpz_bin_ui(ret, n_mpz, (unsigned long)k_i64);
530 return Int$from_mpz(ret);
534 Int_t Int$factorial(Int_t n) {
537 int64_t n_i64 = Int64$from_int(n, false);
538 if unlikely (n_i64 < 0) fail("Factorials are not defined for negative numbers");
539 mpz_fac_ui(ret, (unsigned long)n_i64);
540 return Int$from_mpz(ret);
543 static void Int$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *info) {
545 Int_t i = *(Int_t *)obj;
546 if (likely(i.small & 1L)) {
548 int64_t i64 = i.small >> 2L;
549 Int64$serialize(&i64, out, pointers, &Int64$info);
553 mpz_init_set_int(n, *(Int_t *)obj);
558 static void Int$deserialize(FILE *in, void *obj, List_t *pointers, const TypeInfo_t *info) {
560 if (fgetc(in) == 0) {
562 Int64$deserialize(in, &i, pointers, &Int64$info);
563 *(Int_t *)obj = (Int_t){.small = (i << 2L) | 1L};
568 *(Int_t *)obj = Int$from_mpz(n);
573 const TypeInfo_t Int$info = {
574 .size = sizeof(Int_t),
575 .align = __alignof__(Int_t),
578 .compare = Int$compare,
581 .as_text = Int$as_text,
582 .is_none = Int$is_none,
583 .serialize = Int$serialize,
584 .deserialize = Int$deserialize,