diff --git a/.gitignore b/.gitignore index b21d3fd..c8a9632 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ *.pdb *.txt *.4c -*.bin \ No newline at end of file +*.bin diff --git a/big_int_c3.cpp b/big_int_c3.cpp new file mode 100644 index 0000000..c97cd09 --- /dev/null +++ b/big_int_c3.cpp @@ -0,0 +1,2211 @@ +// Copyright (c) 2019 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by the GNU LGPLv3.0 license +// a copy of which can be found in the LICENSE file. + +function void parsing_error(Token *token, const char *str, ...); +#define malloc_arena(x) exp_alloc(&pernament_arena, x) +#define ALLOC_DIGITS(_digits) (uint64_t *)((_digits) ? malloc_arena(sizeof(uint64_t) * (_digits)) : NULL) +#define FATAL_ERROR(x) parsing_error(0, x) + +struct BigInt +{ + unsigned digit_count; + bool is_negative; + union { + uint64_t digit; + uint64_t *digits; + }; +}; + + +#include +enum CmpRes +{ + CMP_LT, + CMP_GT, + CMP_EQ, +}; + +const char *bigint_to_error_string(Allocator *allocator, const BigInt *bigint, uint64_t base); + +void bigint_init_unsigned(BigInt *big_int, uint64_t value); +void bigint_init_signed(BigInt *big_int, int64_t value); +void bigint_init_bigint(BigInt *dest, const BigInt *src); +void bigint_init_data(BigInt *dest, const uint64_t *digits, unsigned int digit_count, bool is_negative); +void bigint_negate(BigInt *dest, const BigInt *source); +size_t bigint_clz(const BigInt *big_int, size_t bit_count); +size_t bigint_ctz(const BigInt *big_int, size_t bit_count); +bool bigint_fits_in_bits(const BigInt *big_int, size_t bit_count, bool is_signed); +void bigint_write_twos_complement(const BigInt *big_int, uint8_t *buf, size_t bit_count, bool is_big_endian); +void bigint_read_twos_complement(BigInt *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian, bool is_signed); +void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_add_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); +void bigint_sub(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_sub_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); +void bigint_mul(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_mul_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); +void bigint_rem(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_mod(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_shl(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_shl_int(BigInt *dest, const BigInt *op1, uint64_t shift); +void bigint_shl_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); +void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_div_floor(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_or(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_and(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_xor(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count); +void bigint_not(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed); +bool bigint_eql(BigInt a, BigInt b); +CmpRes bigint_cmp(const BigInt *op1, const BigInt *op2); +CmpRes bigint_cmp_zero(const BigInt *op); +uint32_t bigint_hash(BigInt x); +void bigint_print(BigInt *bigint, uint64_t base); +void bigint_fprint(FILE *file, BigInt *bigint, uint64_t base); +uint64_t bigint_as_unsigned(const BigInt *bigint); +int64_t bigint_as_signed(const BigInt *bigint); +double bigint_as_float(const BigInt *bigint); +void bigint_truncate(BigInt *dst, const BigInt *op, size_t bit_count, bool is_signed); +void bigint_incr(BigInt *x); +size_t bigint_popcount_signed(const BigInt *bi, size_t bit_count); +size_t bigint_popcount_unsigned(const BigInt *big_int); + +static inline uint32_t u32_min(uint32_t a, uint32_t b) +{ + return a < b ? a : b; +} + +static inline size_t size_max(size_t a, size_t b) +{ + return a > b ? a : b; +} + +static inline unsigned unsigned_max(unsigned a, unsigned b) +{ + return a > b ? a : b; +} + +static inline const uint64_t *bigint_ptr(const BigInt *big_int) +{ + return big_int->digit_count == 1 ? &big_int->digit : big_int->digits; +} + +static void normalize(BigInt *big_int) +{ + const uint64_t *digits = bigint_ptr(big_int); + unsigned last_non_zero = UINT32_MAX; + for (unsigned i = 0; i < big_int->digit_count; i++) + { + if (digits[i] != 0) + { + last_non_zero = i; + } + } + if (last_non_zero == UINT32_MAX) + { + big_int->is_negative = false; + big_int->digit_count = 0; + return; + } + big_int->digit_count = last_non_zero + 1; + if (!last_non_zero) + { + big_int->digit = digits[0]; + } +} + +static char digit_to_char(uint8_t digit, bool upper) +{ + if (digit <= 9) + { + return (char) (digit + '0'); + } + if (digit <= 35) + { + return (char) (digit + (upper ? 'A' : 'a') - 10); + } + invalid_return; +} + +static bool bit_at_index(const BigInt *big_int, size_t index) +{ + size_t digit_index = index / 64; + if (digit_index >= big_int->digit_count) + { + return false; + } + size_t digit_bit_index = index % 64; + const uint64_t *digits = bigint_ptr(big_int); + uint64_t digit = digits[digit_index]; + return ((digit >> digit_bit_index) & 0x1U) == 0x1U; +} + +uint32_t bigint_hash(BigInt x) +{ + if (x.digit_count == 0) return 0; + return (uint32_t) bigint_ptr(&x)[0]; +} + +static size_t bigint_bits_needed(const BigInt *big_int) +{ + size_t full_bits = big_int->digit_count * 64; + size_t leading_zero_count = bigint_clz(big_int, full_bits); + size_t bits_needed = full_bits - leading_zero_count; + return bits_needed + big_int->is_negative; +} + +void bigint_init_unsigned(BigInt *big_int, uint64_t value) +{ + if (value == 0) + { + big_int->digit_count = 0; + big_int->is_negative = false; + return; + } + big_int->digit_count = 1; + big_int->digit = value; + big_int->is_negative = false; +} + +void bigint_init_signed(BigInt *dest, int64_t value) +{ + if (value >= 0) + { + return bigint_init_unsigned(dest, (uint64_t)value); + } + dest->is_negative = true; + dest->digit_count = 1; + dest->digit = ((uint64_t) (-(value + 1))) + 1; +} + +void bigint_init_bigint(BigInt *dest, const BigInt *src) +{ + if (src->digit_count == 0) + { + return bigint_init_unsigned(dest, 0); + } + if (src->digit_count == 1) + { + dest->digit_count = 1; + dest->digit = src->digit; + dest->is_negative = src->is_negative; + return; + } + dest->is_negative = src->is_negative; + dest->digit_count = src->digit_count; + dest->digits = ALLOC_DIGITS(dest->digit_count); + memcpy(dest->digits, src->digits, sizeof(uint64_t) * dest->digit_count); +} + +void bigint_negate(BigInt *dest, const BigInt *source) +{ + bigint_init_bigint(dest, source); + dest->is_negative = !dest->is_negative; + normalize(dest); +} + +static void to_twos_complement(BigInt *dest, const BigInt *source, size_t bit_count) +{ + if (bit_count == 0 || source->digit_count == 0) + { + bigint_init_unsigned(dest, 0); + return; + } + if (source->is_negative) + { + BigInt negated = { 0 }; + bigint_negate(&negated, source); + + BigInt inverted = { 0 }; + bigint_not(&inverted, &negated, bit_count, false); + + BigInt one = { 0 }; + bigint_init_unsigned(&one, 1); + + bigint_add(dest, &inverted, &one); + return; + } + + dest->is_negative = false; + const uint64_t *source_digits = bigint_ptr(source); + if (source->digit_count == 1) + { + dest->digit = source_digits[0]; + if (bit_count < 64) + { + dest->digit &= (1ULL << bit_count) - 1; + } + dest->digit_count = 1; + normalize(dest); + return; + } + unsigned digits_to_copy = (unsigned int) (bit_count / 64); + unsigned leftover_bits = (unsigned int) (bit_count % 64); + dest->digit_count = digits_to_copy + ((leftover_bits == 0) ? 0 : 1); + if (dest->digit_count == 1 && leftover_bits == 0) + { + dest->digit = source_digits[0]; + if (dest->digit == 0) dest->digit_count = 0; + return; + } + dest->digits = (uint64_t *)malloc_arena(dest->digit_count * sizeof(uint64_t)); + for (size_t i = 0; i < digits_to_copy; i += 1) + { + uint64_t digit = (i < source->digit_count) ? source_digits[i] : 0; + dest->digits[i] = digit; + } + if (leftover_bits != 0) + { + uint64_t digit = (digits_to_copy < source->digit_count) ? source_digits[digits_to_copy] : 0; + dest->digits[digits_to_copy] = digit & ((1ULL << leftover_bits) - 1); + } + normalize(dest); +} + +size_t bigint_clz(const BigInt *big_int, size_t bit_count) +{ + if (big_int->is_negative || bit_count == 0) + { + return 0; + } + if (big_int->digit_count == 0) + { + return bit_count; + } + size_t count = 0; + for (size_t i = bit_count - 1;;) + { + if (bit_at_index(big_int, i)) + { + return count; + } + count++; + if (i == 0) break; + i--; + } + return count; +} + +bool bigint_eql(BigInt a, BigInt b) +{ + return bigint_cmp(&a, &b) == CMP_EQ; +} + +static void from_twos_complement(BigInt *dest, const BigInt *src, size_t bit_count, bool is_signed) +{ + assert(!src->is_negative); + + if (bit_count == 0 || src->digit_count == 0) + { + bigint_init_unsigned(dest, 0); + return; + } + + if (is_signed && bit_at_index(src, bit_count - 1)) + { + BigInt negative_one = { 0 }; + bigint_init_signed(&negative_one, -1); + + BigInt minus_one = { 0 }; + bigint_add(&minus_one, src, &negative_one); + + BigInt inverted = { 0 }; + bigint_not(&inverted, &minus_one, bit_count, false); + + bigint_negate(dest, &inverted); + return; + + } + + bigint_init_bigint(dest, src); +} + + +void bigint_init_data(BigInt *dest, const uint64_t *digits, unsigned int digit_count, bool is_negative) +{ + if (digit_count == 0) return bigint_init_unsigned(dest, 0); + + if (digit_count == 1) + { + dest->digit_count = 1; + dest->digit = digits[0]; + dest->is_negative = is_negative; + normalize(dest); + return; + } + + dest->digit_count = digit_count; + dest->is_negative = is_negative; + dest->digits = ALLOC_DIGITS(digit_count); + memcpy(dest->digits, digits, sizeof(uint64_t) * digit_count); + + normalize(dest); +} + +/* TODO +void bigint_init_bigfloat(BigInt *dest, const BigFloat *op) { + float128_t zero; + ui32_to_f128M(0, &zero); + + dest->is_negative = f128M_lt(&op->value, &zero); + float128_t abs_val; + if (dest->is_negative) { + f128M_sub(&zero, &op->value, &abs_val); + } else { + memcpy(&abs_val, &op->value, sizeof(float128_t)); + } + + float128_t max_u64; + ui64_to_f128M(UINT64_MAX, &max_u64); + if (f128M_le(&abs_val, &max_u64)) { + dest->digit_count = 1; + dest->data.digit = f128M_to_ui64(&op->value, softfloat_round_minMag, false); + bigint_normalize(dest); + return; + } + + float128_t amt; + f128M_div(&abs_val, &max_u64, &amt); + float128_t remainder; + f128M_rem(&abs_val, &max_u64, &remainder); + + dest->digit_count = 2; + dest->data.digits = allocate_nonzero(dest->digit_count); + dest->data.digits[0] = f128M_to_ui64(&remainder, softfloat_round_minMag, false); + dest->data.digits[1] = f128M_to_ui64(&amt, softfloat_round_minMag, false); + bigint_normalize(dest); +} +*/ + +bool bigint_fits_in_bits(const BigInt *big_int, size_t bit_count, bool is_signed) +{ + assert(big_int->digit_count != 1 || big_int->digit != 0); + if (bit_count == 0) + { + return bigint_cmp_zero(big_int) == CMP_EQ; + } + if (big_int->digit_count == 0) + { + return true; + } + + if (!is_signed) + { + size_t full_bits = big_int->digit_count * 64; + size_t leading_zero_count = bigint_clz(big_int, full_bits); + return bit_count >= full_bits - leading_zero_count; + } + + BigInt one = { 0 }; + bigint_init_unsigned(&one, 1); + + BigInt shl_amt = { 0 }; + bigint_init_unsigned(&shl_amt, bit_count - 1); + + BigInt max_value_plus_one = { 0 }; + bigint_shl(&max_value_plus_one, &one, &shl_amt); + + BigInt max_value = { 0 }; + bigint_sub(&max_value, &max_value_plus_one, &one); + + BigInt min_value = { 0 }; + bigint_negate(&min_value, &max_value_plus_one); + + CmpRes min_cmp = bigint_cmp(big_int, &min_value); + CmpRes max_cmp = bigint_cmp(big_int, &max_value); + + + return (min_cmp == CMP_GT || min_cmp == CMP_EQ) && (max_cmp == CMP_LT || max_cmp == CMP_EQ); +} + +void bigint_write_twos_complement(const BigInt *big_int, uint8_t *buf, size_t bit_count, bool is_big_endian) +{ + if (bit_count == 0) + { + return; + } + + BigInt twos_comp = { 0 }; + to_twos_complement(&twos_comp, big_int, bit_count); + + const uint64_t *twos_comp_digits = bigint_ptr(&twos_comp); + + size_t bits_in_last_digit = bit_count % 64; + if (bits_in_last_digit == 0) bits_in_last_digit = 64; + size_t bytes_in_last_digit = (bits_in_last_digit + 7) / 8; + size_t unwritten_byte_count = 8 - bytes_in_last_digit; + + if (is_big_endian) + { + size_t last_digit_index = (bit_count - 1) / 64; + size_t digit_index = last_digit_index; + size_t buf_index = 0; + for (;;) + { + uint64_t x = (digit_index < twos_comp.digit_count) ? twos_comp_digits[digit_index] : 0; + + for (size_t byte_index = 7;;) + { + uint8_t byte = (uint8_t) (x & 0xffU); + if (digit_index == last_digit_index) + { + buf[buf_index + byte_index - unwritten_byte_count] = byte; + if (byte_index == unwritten_byte_count) break; + } + else + { + buf[buf_index + byte_index] = byte; + } + + if (byte_index == 0) break; + byte_index -= 1; + x >>= 8U; + } + + if (digit_index == 0) break; + digit_index -= 1; + if (digit_index == last_digit_index) + { + buf_index += bytes_in_last_digit; + } + else + { + buf_index += 8; + } + } + } + else + { + size_t digit_count = (bit_count + 63) / 64; + size_t buf_index = 0; + for (size_t digit_index = 0; digit_index < digit_count; digit_index += 1) + { + uint64_t x = (digit_index < twos_comp.digit_count) ? twos_comp_digits[digit_index] : 0; + + for (size_t byte_index = 0; + byte_index < 8 && (digit_index + 1 < digit_count || byte_index < bytes_in_last_digit); + byte_index += 1) + { + uint8_t byte = (uint8_t) (x & 0xffU); + buf[buf_index] = byte; + buf_index += 1; + x >>= 8U; + } + } + } +} + +uint64_t bigint_as_unsigned(const BigInt *bigint) +{ + assert(!bigint->is_negative); + if (bigint->digit_count == 0) + { + return 0; + } + if (bigint->digit_count != 1) + { + FATAL_ERROR("Bigint exceeds u64"); + } + return bigint->digit; +} + +void bigint_read_twos_complement(BigInt *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian, + bool is_signed) +{ + if (bit_count == 0) + { + bigint_init_unsigned(dest, 0); + return; + } + + dest->digit_count = (unsigned int) ((bit_count + 63) / 64); + uint64_t *digits; + if (dest->digit_count == 1) + { + digits = &dest->digit; + } + else + { + digits = ALLOC_DIGITS(dest->digit_count); + dest->digits = digits; + } + + size_t bits_in_last_digit = bit_count % 64; + if (bits_in_last_digit == 0) + { + bits_in_last_digit = 64; + } + size_t bytes_in_last_digit = (bits_in_last_digit + 7) / 8; + size_t unread_byte_count = 8 - bytes_in_last_digit; + + if (is_big_endian) + { + size_t buf_index = 0; + uint64_t digit = 0; + for (size_t byte_index = unread_byte_count; byte_index < 8; byte_index += 1) + { + uint8_t byte = buf[buf_index]; + buf_index += 1; + digit <<= 8U; + digit |= byte; + } + digits[dest->digit_count - 1] = digit; + for (size_t digit_index = 1; digit_index < dest->digit_count; digit_index += 1) + { + digit = 0; + for (size_t byte_index = 0; byte_index < 8; byte_index += 1) + { + uint8_t byte = buf[buf_index]; + buf_index += 1; + digit <<= 8; + digit |= byte; + } + digits[dest->digit_count - 1 - digit_index] = digit; + } + } + else + { + size_t buf_index = 0; + for (size_t digit_index = 0; digit_index < dest->digit_count; digit_index += 1) + { + uint64_t digit = 0; + size_t end_byte_index = (digit_index == dest->digit_count - 1) ? bytes_in_last_digit : 8; + for (size_t byte_index = 0; byte_index < end_byte_index; byte_index += 1) + { + uint64_t byte = buf[buf_index]; + buf_index += 1; + + digit |= byte << (8 * byte_index); + } + digits[digit_index] = digit; + } + } + + if (is_signed) + { + normalize(dest); + BigInt tmp = { 0 }; + bigint_init_bigint(&tmp, dest); + from_twos_complement(dest, &tmp, bit_count, true); + } + else + { + dest->is_negative = false; + normalize(dest); + } +} + +#if defined(_MSC_VER) + +static bool add_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) +{ + *result = op1 + op2; + return *result < op1 || *result < op2; +} + +static bool sub_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) +{ + *result = op1 - op2; + return *result > op1; +} + +bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) +{ + *result = op1 * op2; + + if (op1 == 0 || op2 == 0) return false; + if (op1 > UINT64_MAX / op2) return true; + if (op2 > UINT64_MAX / op1) return true; + return false; +} + +#else + +static bool add_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) +{ + return __builtin_uaddll_overflow((unsigned long long) op1, (unsigned long long) op2, + (unsigned long long *) result); +} + +static bool sub_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) +{ + return __builtin_usubll_overflow((unsigned long long) op1, (unsigned long long) op2, + (unsigned long long *) result); +} + +bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) +{ + return __builtin_umulll_overflow((unsigned long long) op1, (unsigned long long) op2, + (unsigned long long *) result); +} + +#endif + +void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2) +{ + if (op1->digit_count == 0) + { + return bigint_init_bigint(dest, op2); + } + if (op2->digit_count == 0) + { + return bigint_init_bigint(dest, op1); + } + if (op1->is_negative == op2->is_negative) + { + dest->is_negative = op1->is_negative; + + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + bool overflow = add_u64_overflow(op1_digits[0], op2_digits[0], &dest->digit); + if (overflow == 0 && op1->digit_count == 1 && op2->digit_count == 1) + { + dest->digit_count = 1; + normalize(dest); + return; + } + unsigned i = 1; + uint64_t first_digit = dest->digit; + dest->digits = ALLOC_DIGITS(unsigned_max(op1->digit_count, op2->digit_count) + 1); + dest->digits[0] = first_digit; + + for (;;) + { + bool found_digit = false; + uint64_t x = (uint64_t) overflow; + overflow = 0; + + if (i < op1->digit_count) + { + found_digit = true; + uint64_t digit = op1_digits[i]; + overflow += add_u64_overflow(x, digit, &x); + } + + if (i < op2->digit_count) + { + found_digit = true; + uint64_t digit = op2_digits[i]; + overflow += add_u64_overflow(x, digit, &x); + } + + dest->digits[i] = x; + i += 1; + + if (!found_digit) + { + dest->digit_count = i; + normalize(dest); + return; + } + } + } + const BigInt *op_pos; + const BigInt *op_neg; + if (op1->is_negative) + { + op_neg = op1; + op_pos = op2; + } + else + { + op_pos = op1; + op_neg = op2; + } + + BigInt op_neg_abs = { 0 }; + bigint_negate(&op_neg_abs, op_neg); + const BigInt *bigger_op; + const BigInt *smaller_op; + switch (bigint_cmp(op_pos, &op_neg_abs)) + { + case CMP_EQ: + bigint_init_unsigned(dest, 0); + return; + case CMP_LT: + bigger_op = &op_neg_abs; + smaller_op = op_pos; + dest->is_negative = true; + break; + case CMP_GT: + bigger_op = op_pos; + smaller_op = &op_neg_abs; + dest->is_negative = false; + break; + default: + FATAL_ERROR("UNREACHABLE"); + } + const uint64_t *bigger_op_digits = bigint_ptr(bigger_op); + const uint64_t *smaller_op_digits = bigint_ptr(smaller_op); + uint64_t overflow = (uint64_t)sub_u64_overflow(bigger_op_digits[0], smaller_op_digits[0], &dest->digit); + if (overflow == 0 && bigger_op->digit_count == 1 && smaller_op->digit_count == 1) + { + dest->digit_count = 1; + normalize(dest); + return; + } + uint64_t first_digit = dest->digit; + dest->digits = ALLOC_DIGITS(bigger_op->digit_count); + dest->digits[0] = first_digit; + unsigned i = 1; + + for (;;) + { + bool found_digit = false; + uint64_t x = bigger_op_digits[i]; + uint64_t prev_overflow = overflow; + overflow = 0; + + if (i < smaller_op->digit_count) + { + found_digit = true; + uint64_t digit = smaller_op_digits[i]; + overflow += sub_u64_overflow(x, digit, &x); + } + if (sub_u64_overflow(x, prev_overflow, &x)) + { + found_digit = true; + overflow += 1; + } + dest->digits[i] = x; + i += 1; + + if (!found_digit || i >= bigger_op->digit_count) + { + break; + } + } + assert(overflow == 0); + dest->digit_count = i; + normalize(dest); +} + +void bigint_add_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) +{ + BigInt unwrapped = { 0 }; + bigint_add(&unwrapped, op1, op2); + bigint_truncate(dest, &unwrapped, bit_count, is_signed); +} + +void bigint_sub(BigInt *dest, const BigInt *op1, const BigInt *op2) +{ + BigInt op2_negated = { 0 }; + bigint_negate(&op2_negated, op2); + return bigint_add(dest, op1, &op2_negated); +} + +void bigint_sub_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) +{ + BigInt op2_negated = { 0 }; + bigint_negate(&op2_negated, op2); + return bigint_add_wrap(dest, op1, &op2_negated, bit_count, is_signed); +} + +static void mul_overflow(uint64_t op1, uint64_t op2, uint64_t *lo, uint64_t *hi) +{ + uint64_t u1 = (op1 & 0xffffffff); + uint64_t v1 = (op2 & 0xffffffff); + uint64_t t = (u1 * v1); + uint64_t w3 = (t & 0xffffffff); + uint64_t k = (t >> 32); + + op1 >>= 32; + t = (op1 * v1) + k; + k = (t & 0xffffffff); + uint64_t w1 = (t >> 32); + + op2 >>= 32; + t = (u1 * op2) + k; + k = (t >> 32); + + *hi = (op1 * op2) + w1 + k; + *lo = (t << 32) + w3; +} + +static void mul_scalar(BigInt *dest, const BigInt *op, uint64_t scalar) +{ + bigint_init_unsigned(dest, 0); + + BigInt bi_64; + bigint_init_unsigned(&bi_64, 64); + + const uint64_t *op_digits = bigint_ptr(op); + size_t i = op->digit_count - 1; + + while (1) + { + BigInt shifted; + bigint_shl(&shifted, dest, &bi_64); + + uint64_t result_scalar; + uint64_t carry_scalar; + mul_overflow(scalar, op_digits[i], &result_scalar, &carry_scalar); + + BigInt result; + bigint_init_unsigned(&result, result_scalar); + + BigInt carry; + bigint_init_unsigned(&carry, carry_scalar); + + BigInt carry_shifted; + bigint_shl(&carry_shifted, &carry, &bi_64); + + BigInt tmp; + bigint_add(&tmp, &shifted, &carry_shifted); + + bigint_add(dest, &tmp, &result); + + if (i == 0) + { + break; + } + i -= 1; + } +} + +void bigint_mul(BigInt *dest, const BigInt *op1, const BigInt *op2) +{ + if (op1->digit_count == 0 || op2->digit_count == 0) + { + return bigint_init_unsigned(dest, 0); + } + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + + uint64_t carry; + mul_overflow(op1_digits[0], op2_digits[0], &dest->digit, &carry); + if (carry == 0 && op1->digit_count == 1 && op2->digit_count == 1) + { + dest->is_negative = (op1->is_negative != op2->is_negative); + dest->digit_count = 1; + normalize(dest); + return; + } + + bigint_init_unsigned(dest, 0); + + BigInt bi_64; + bigint_init_unsigned(&bi_64, 64); + + size_t i = op2->digit_count - 1; + for (;;) + { + BigInt shifted; + bigint_shl(&shifted, dest, &bi_64); + + BigInt scalar_result; + mul_scalar(&scalar_result, op1, op2_digits[i]); + + bigint_add(dest, &scalar_result, &shifted); + + if (i == 0) + { + break; + } + i -= 1; + } + + dest->is_negative = (op1->is_negative != op2->is_negative); + normalize(dest); +} + +void bigint_mul_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) +{ + BigInt unwrapped = { 0 }; + bigint_mul(&unwrapped, op1, op2); + bigint_truncate(dest, &unwrapped, bit_count, is_signed); +} + +unsigned count_leading_zeros(uint32_t val) +{ + if (val == 0) return 32; + +#if defined(_MSC_VER) + unsigned long Index; + _BitScanReverse(&Index, val); + return Index ^ 31; +#else + return __builtin_clz(val); +#endif +} + +/// Make a 64-bit integer from a high / low pair of 32-bit integers. +static inline uint64_t make_64(uint32_t hi, uint32_t lo) +{ + return (((uint64_t) hi) << 32) | ((uint64_t) lo); +} + +/// Return the high 32 bits of a 64 bit value. +static inline uint32_t hi_32(uint64_t value) +{ + return (uint32_t) (value >> 32); +} + +/// Return the low 32 bits of a 64 bit value. +static inline uint32_t lo_32(uint64_t val) +{ + return (uint32_t) val; +} + +/// Implementation of Knuth's Algorithm D (Division of nonnegative integers) +/// from "Art of Computer Programming, Volume 2", section 4.3.1, p. 272. The +/// variables here have the same names as in the algorithm. Comments explain +/// the algorithm and any deviation from it. +static void knuth_div(uint32_t *u, uint32_t *v, uint32_t *q, uint32_t *r, unsigned m, unsigned n) +{ + assert(u && "Must provide dividend"); + assert(v && "Must provide divisor"); + assert(q && "Must provide quotient"); + assert(u != v && u != q && v != q && "Must use different memory"); + assert(n > 1 && "n must be > 1"); + + // b denotes the base of the number system. In our case b is 2^32. + const uint64_t b = ((uint64_t) 1) << 32; + + // D1. [Normalize.] Set d = b / (v[n-1] + 1) and multiply all the digits of + // u and v by d. Note that we have taken Knuth's advice here to use a power + // of 2 value for d such that d * v[n-1] >= b/2 (b is the base). A power of + // 2 allows us to shift instead of multiply and it is easy to determine the + // shift amount from the leading zeros. We are basically normalizing the u + // and v so that its high bits are shifted to the top of v's range without + // overflow. Note that this can require an extra word in u so that u must + // be of length m+n+1. + unsigned shift = count_leading_zeros(v[n - 1]); + uint32_t v_carry = 0; + uint32_t u_carry = 0; + if (shift) + { + for (unsigned i = 0; i < m + n; ++i) + { + uint32_t u_tmp = u[i] >> (32 - shift); + u[i] = (u[i] << shift) | u_carry; + u_carry = u_tmp; + } + for (unsigned i = 0; i < n; ++i) + { + uint32_t v_tmp = v[i] >> (32 - shift); + v[i] = (v[i] << shift) | v_carry; + v_carry = v_tmp; + } + } + u[m + n] = u_carry; + + // D2. [Initialize j.] Set j to m. This is the loop counter over the places. + int j = (int)m; + do + { + // D3. [Calculate q'.]. + // Set qp = (u[j+n]*b + u[j+n-1]) / v[n-1]. (qp=qprime=q') + // Set rp = (u[j+n]*b + u[j+n-1]) % v[n-1]. (rp=rprime=r') + // Now test if qp == b or qp*v[n-2] > b*rp + u[j+n-2]; if so, decrease + // qp by 1, increase rp by v[n-1], and repeat this test if rp < b. The test + // on v[n-2] determines at high speed most of the cases in which the trial + // value qp is one too large, and it eliminates all cases where qp is two + // too large. + uint64_t dividend = make_64(u[j + n], u[j + n - 1]); + uint64_t qp = dividend / v[n - 1]; + uint64_t rp = dividend % v[n - 1]; + if (qp == b || qp * v[n - 2] > b * rp + u[j + n - 2]) + { + qp--; + rp += v[n - 1]; + if (rp < b && (qp == b || qp * v[n - 2] > b * rp + u[j + n - 2])) + { + qp--; + } + } + + // D4. [Multiply and subtract.] Replace (u[j+n]u[j+n-1]...u[j]) with + // (u[j+n]u[j+n-1]..u[j]) - qp * (v[n-1]...v[1]v[0]). This computation + // consists of a simple multiplication by a one-place number, combined with + // a subtraction. + // The digits (u[j+n]...u[j]) should be kept positive; if the result of + // this step is actually negative, (u[j+n]...u[j]) should be left as the + // true value plus b**(n+1), namely as the b's complement of + // the true value, and a "borrow" to the left should be remembered. + int64_t borrow = 0; + for (unsigned i = 0; i < n; ++i) + { + uint64_t p = ((uint64_t) qp) * ((uint64_t) (v[i])); + int64_t subres = ((int64_t) (u[j + i])) - borrow - lo_32(p); + u[j + i] = lo_32((uint64_t) subres); + borrow = hi_32(p) - hi_32((uint64_t) subres); + } + bool is_neg = u[j + n] < borrow; + u[j + n] -= lo_32((uint64_t) borrow); + + // D5. [Test remainder.] Set q[j] = qp. If the result of step D4 was + // negative, go to step D6; otherwise go on to step D7. + q[j] = lo_32(qp); + if (is_neg) + { + // D6. [Add back]. The probability that this step is necessary is very + // small, on the order of only 2/b. Make sure that test data accounts for + // this possibility. Decrease q[j] by 1 + q[j]--; + // and add (0v[n-1]...v[1]v[0]) to (u[j+n]u[j+n-1]...u[j+1]u[j]). + // A carry will occur to the left of u[j+n], and it should be ignored + // since it cancels with the borrow that occurred in D4. + bool carry = false; + for (unsigned i = 0; i < n; i++) + { + uint32_t limit = u32_min(u[j + i], v[i]); + u[j + i] += v[i] + carry; + carry = u[j + i] < limit || (carry && u[j + i] == limit); + } + u[j + n] += carry; + } + + // D7. [Loop on j.] Decrease j by one. Now if j >= 0, go back to D3. + } while (--j >= 0); + + // D8. [Unnormalize]. Now q[...] is the desired quotient, and the desired + // remainder may be obtained by dividing u[...] by d. If r is non-null we + // compute the remainder (urem uses this). + if (r) + { + // The value d is expressed by the "shift" value above since we avoided + // multiplication by d by using a shift left. So, all we have to do is + // shift right here. + if (shift) + { + uint32_t carry = 0; + for (int i = (int)n - 1; i >= 0; i--) + { + r[i] = (u[i] >> shift) | carry; + carry = u[i] << (32 - shift); + } + } + else + { + for (int i = (int)n - 1; i >= 0; i--) + { + r[i] = u[i]; + } + } + } +} + +static void bigint_unsigned_division(const BigInt *op1, const BigInt *op2, BigInt *Quotient, BigInt *Remainder) +{ + CmpRes cmp = bigint_cmp(op1, op2); + if (cmp == CMP_LT) + { + if (!Quotient) + { + bigint_init_unsigned(Quotient, 0); + } + if (!Remainder) + { + bigint_init_bigint(Remainder, op1); + } + return; + } + if (cmp == CMP_EQ) + { + if (!Quotient) + { + bigint_init_unsigned(Quotient, 1); + } + if (!Remainder) + { + bigint_init_unsigned(Remainder, 0); + } + return; + } + + const uint64_t *lhs = bigint_ptr(op1); + const uint64_t *rhs = bigint_ptr(op2); + unsigned lhsWords = op1->digit_count; + unsigned rhsWords = op2->digit_count; + + // First, compose the values into an array of 32-bit words instead of + // 64-bit words. This is a necessity of both the "short division" algorithm + // and the Knuth "classical algorithm" which requires there to be native + // operations for +, -, and * on an m bit value with an m*2 bit result. We + // can't use 64-bit operands here because we don't have native results of + // 128-bits. Furthermore, casting the 64-bit values to 32-bit values won't + // work on large-endian machines. + unsigned n = rhsWords * 2; + unsigned m = (lhsWords * 2) - n; + + // Allocate space for the temporary values we need either on the stack, if + // it will fit, or on the heap if it won't. + uint32_t space[128]; + uint32_t *U = NULL; + uint32_t *V = NULL; + uint32_t *Q = NULL; + uint32_t *R = NULL; + if ((Remainder ? 4 : 3) * n + 2 * m + 1 <= 128) + { + U = &space[0]; + V = &space[m + n + 1]; + Q = &space[(m + n + 1) + n]; + if (Remainder) + { + R = &space[(m + n + 1) + n + (m + n)]; + } + } + else + { + U = (uint32_t *)malloc_arena(sizeof(uint32_t) * (m + n + 1)); + V = (uint32_t *)malloc_arena(sizeof(uint32_t) * n); + Q = (uint32_t *)malloc_arena(sizeof(uint32_t) * (m + n)); + if (Remainder) + { + R = (uint32_t *)malloc_arena(sizeof(uint32_t) * n); + } + } + + // Initialize the dividend + memset(U, 0, (m + n + 1) * sizeof(uint32_t)); + for (unsigned i = 0; i < lhsWords; ++i) + { + uint64_t tmp = lhs[i]; + U[i * 2] = lo_32(tmp); + U[i * 2 + 1] = hi_32(tmp); + } + U[m + n] = 0; // this extra word is for "spill" in the Knuth algorithm. + + // Initialize the divisor + memset(V, 0, (n) * sizeof(uint32_t)); + for (unsigned i = 0; i < rhsWords; ++i) + { + uint64_t tmp = rhs[i]; + V[i * 2] = lo_32(tmp); + V[i * 2 + 1] = hi_32(tmp); + } + + // initialize the quotient and remainder + memset(Q, 0, (m + n) * sizeof(uint32_t)); + if (Remainder) memset(R, 0, n * sizeof(uint32_t)); + + // Now, adjust m and n for the Knuth division. n is the number of words in + // the divisor. m is the number of words by which the dividend exceeds the + // divisor (i.e. m+n is the length of the dividend). These sizes must not + // contain any zero words or the Knuth algorithm fails. + for (unsigned i = n; i > 0 && V[i - 1] == 0; i--) + { + n--; + m++; + } + for (unsigned i = m + n; i > 0 && U[i - 1] == 0; i--) + { + m--; + } + + // If we're left with only a single word for the divisor, Knuth doesn't work + // so we implement the short division algorithm here. This is much simpler + // and faster because we are certain that we can divide a 64-bit quantity + // by a 32-bit quantity at hardware speed and short division is simply a + // series of such operations. This is just like doing short division but we + // are using base 2^32 instead of base 10. + assert(n != 0 && "Divide by zero?"); + if (n == 1) + { + uint32_t divisor = V[0]; + uint32_t rem = 0; + for (int i = (int)m; i >= 0; i--) + { + uint64_t partial_dividend = make_64(rem, U[i]); + if (partial_dividend == 0) + { + Q[i] = 0; + rem = 0; + } + else if (partial_dividend < divisor) + { + Q[i] = 0; + rem = lo_32(partial_dividend); + } + else if (partial_dividend == divisor) + { + Q[i] = 1; + rem = 0; + } + else + { + Q[i] = lo_32(partial_dividend / divisor); + rem = lo_32(partial_dividend - (Q[i] * divisor)); + } + } + if (R) + { + R[0] = rem; + } + } + else + { + // Now we're ready to invoke the Knuth classical divide algorithm. In this + // case n > 1. + knuth_div(U, V, Q, R, m, n); + } + + // If the caller wants the quotient + if (Quotient) + { + Quotient->is_negative = false; + Quotient->digit_count = lhsWords; + if (lhsWords == 1) + { + Quotient->digit = make_64(Q[1], Q[0]); + } + else + { + Quotient->digits = ALLOC_DIGITS(lhsWords); + for (size_t i = 0; i < lhsWords; i += 1) + { + Quotient->digits[i] = make_64(Q[i * 2 + 1], Q[i * 2]); + } + } + } + + // If the caller wants the remainder + if (Remainder) + { + Remainder->is_negative = false; + Remainder->digit_count = rhsWords; + if (rhsWords == 1) + { + Remainder->digit = make_64(R[1], R[0]); + } + else + { + Remainder->digits = ALLOC_DIGITS(rhsWords); + for (size_t i = 0; i < rhsWords; i += 1) + { + Remainder->digits[i] = make_64(R[i * 2 + 1], R[i * 2]); + } + } + } +} + + +void bigint_div_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2) +{ + assert(op2->digit_count != 0); // division by zero + if (op1->digit_count == 0) + { + bigint_init_unsigned(dest, 0); + return; + } + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + if (op1->digit_count == 1 && op2->digit_count == 1) + { + dest->digit = op1_digits[0] / op2_digits[0]; + dest->digit_count = 1; + dest->is_negative = op1->is_negative != op2->is_negative; + normalize(dest); + return; + } + if (op2->digit_count == 1 && op2_digits[0] == 1) + { + // X / 1 == X + bigint_init_bigint(dest, op1); + dest->is_negative = op1->is_negative != op2->is_negative; + normalize(dest); + return; + } + + const BigInt *op1_positive; + BigInt op1_positive_data; + if (op1->is_negative) + { + bigint_negate(&op1_positive_data, op1); + op1_positive = &op1_positive_data; + } + else + { + op1_positive = op1; + } + + const BigInt *op2_positive; + BigInt op2_positive_data; + if (op2->is_negative) + { + bigint_negate(&op2_positive_data, op2); + op2_positive = &op2_positive_data; + } + else + { + op2_positive = op2; + } + + bigint_unsigned_division(op1_positive, op2_positive, dest, NULL); + dest->is_negative = op1->is_negative != op2->is_negative; + normalize(dest); +} + +void bigint_div_floor(BigInt *dest, const BigInt *op1, const BigInt *op2) +{ + if (op1->is_negative != op2->is_negative) + { + bigint_div_trunc(dest, op1, op2); + BigInt mult_again = { 0 }; + bigint_mul(&mult_again, dest, op2); + mult_again.is_negative = op1->is_negative; + if (bigint_cmp(&mult_again, op1) != CMP_EQ) + { + BigInt tmp = { 0 }; + bigint_init_bigint(&tmp, dest); + BigInt neg_one = { 0 }; + bigint_init_signed(&neg_one, -1); + bigint_add(dest, &tmp, &neg_one); + } + normalize(dest); + } + else + { + bigint_div_trunc(dest, op1, op2); + } +} + +void bigint_rem(BigInt *dest, const BigInt *op1, const BigInt *op2) +{ + assert(op2->digit_count != 0); // division by zero + if (op1->digit_count == 0) + { + bigint_init_unsigned(dest, 0); + return; + } + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + + if (op1->digit_count == 1 && op2->digit_count == 1) + { + dest->digit = op1_digits[0] % op2_digits[0]; + dest->digit_count = 1; + dest->is_negative = op1->is_negative; + normalize(dest); + return; + } + if (op2->digit_count == 2 && op2_digits[0] == 0 && op2_digits[1] == 1) + { + // special case this divisor + bigint_init_unsigned(dest, op1_digits[0]); + dest->is_negative = op1->is_negative; + normalize(dest); + return; + } + + if (op2->digit_count == 1 && op2_digits[0] == 1) + { + // X % 1 == 0 + bigint_init_unsigned(dest, 0); + return; + } + + const BigInt *op1_positive; + BigInt op1_positive_data; + if (op1->is_negative) + { + bigint_negate(&op1_positive_data, op1); + op1_positive = &op1_positive_data; + } + else + { + op1_positive = op1; + } + + const BigInt *op2_positive; + BigInt op2_positive_data; + if (op2->is_negative) + { + bigint_negate(&op2_positive_data, op2); + op2_positive = &op2_positive_data; + } + else + { + op2_positive = op2; + } + + bigint_unsigned_division(op1_positive, op2_positive, NULL, dest); + dest->is_negative = op1->is_negative; + normalize(dest); +} + +void bigint_mod(BigInt *dest, const BigInt *op1, const BigInt *op2) +{ + if (op1->is_negative) + { + BigInt first_rem; + bigint_rem(&first_rem, op1, op2); + first_rem.is_negative = !op2->is_negative; + BigInt op2_minus_rem; + bigint_add(&op2_minus_rem, op2, &first_rem); + bigint_rem(dest, &op2_minus_rem, op2); + dest->is_negative = false; + } + else + { + bigint_rem(dest, op1, op2); + dest->is_negative = false; + } +} + +void bigint_or(BigInt *dest, const BigInt *op1, const BigInt *op2) +{ + if (op1->digit_count == 0) + { + return bigint_init_bigint(dest, op2); + } + if (op2->digit_count == 0) + { + return bigint_init_bigint(dest, op1); + } + if (op1->is_negative || op2->is_negative) + { + size_t big_bit_count = size_max(bigint_bits_needed(op1), bigint_bits_needed(op2)); + + BigInt twos_comp_op1 = { 0 }; + to_twos_complement(&twos_comp_op1, op1, big_bit_count); + + BigInt twos_comp_op2 = { 0 }; + to_twos_complement(&twos_comp_op2, op2, big_bit_count); + + BigInt twos_comp_dest = { 0 }; + bigint_or(&twos_comp_dest, &twos_comp_op1, &twos_comp_op2); + + from_twos_complement(dest, &twos_comp_dest, big_bit_count, true); + } + else + { + dest->is_negative = false; + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + if (op1->digit_count == 1 && op2->digit_count == 1) + { + dest->digit_count = 1; + dest->digit = op1_digits[0] | op2_digits[0]; + normalize(dest); + return; + } + dest->digit_count = unsigned_max(op1->digit_count, op2->digit_count); + dest->digits = ALLOC_DIGITS(dest->digit_count); + for (size_t i = 0; i < dest->digit_count; i += 1) + { + uint64_t digit = 0; + if (i < op1->digit_count) + { + digit |= op1_digits[i]; + } + if (i < op2->digit_count) + { + digit |= op2_digits[i]; + } + dest->digits[i] = digit; + } + normalize(dest); + } +} + +void bigint_and(BigInt *dest, const BigInt *op1, const BigInt *op2) +{ + if (op1->digit_count == 0 || op2->digit_count == 0) + { + return bigint_init_unsigned(dest, 0); + } + if (op1->is_negative || op2->is_negative) + { + size_t big_bit_count = size_max(bigint_bits_needed(op1), bigint_bits_needed(op2)); + + BigInt twos_comp_op1 = { 0 }; + to_twos_complement(&twos_comp_op1, op1, big_bit_count); + + BigInt twos_comp_op2 = { 0 }; + to_twos_complement(&twos_comp_op2, op2, big_bit_count); + + BigInt twos_comp_dest = { 0 }; + bigint_and(&twos_comp_dest, &twos_comp_op1, &twos_comp_op2); + + from_twos_complement(dest, &twos_comp_dest, big_bit_count, true); + } + else + { + dest->is_negative = false; + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + if (op1->digit_count == 1 && op2->digit_count == 1) + { + dest->digit_count = 1; + dest->digit = op1_digits[0] & op2_digits[0]; + normalize(dest); + return; + } + + dest->digit_count = unsigned_max(op1->digit_count, op2->digit_count); + dest->digits = ALLOC_DIGITS(dest->digit_count); + + size_t i = 0; + for (; i < op1->digit_count && i < op2->digit_count; i += 1) + { + dest->digits[i] = op1_digits[i] & op2_digits[i]; + } + for (; i < dest->digit_count; i += 1) + { + dest->digits[i] = 0; + } + normalize(dest); + } +} + +void bigint_xor(BigInt *dest, const BigInt *op1, const BigInt *op2) +{ + if (op1->digit_count == 0) + { + return bigint_init_bigint(dest, op2); + } + if (op2->digit_count == 0) + { + return bigint_init_bigint(dest, op1); + } + if (op1->is_negative || op2->is_negative) + { + size_t big_bit_count = size_max(bigint_bits_needed(op1), bigint_bits_needed(op2)); + + BigInt twos_comp_op1 = { 0 }; + to_twos_complement(&twos_comp_op1, op1, big_bit_count); + + BigInt twos_comp_op2 = { 0 }; + to_twos_complement(&twos_comp_op2, op2, big_bit_count); + + BigInt twos_comp_dest = { 0 }; + bigint_xor(&twos_comp_dest, &twos_comp_op1, &twos_comp_op2); + + from_twos_complement(dest, &twos_comp_dest, big_bit_count, true); + } + else + { + dest->is_negative = false; + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + + assert(op1->digit_count > 0 && op2->digit_count > 0); + if (op1->digit_count == 1 && op2->digit_count == 1) + { + dest->digit_count = 1; + dest->digit = op1_digits[0] ^ op2_digits[0]; + normalize(dest); + return; + } + dest->digit_count = unsigned_max(op1->digit_count, op2->digit_count); + dest->digits = ALLOC_DIGITS(dest->digit_count); + size_t i = 0; + for (; i < op1->digit_count && i < op2->digit_count; i += 1) + { + dest->digits[i] = op1_digits[i] ^ op2_digits[i]; + } + for (; i < dest->digit_count; i += 1) + { + if (i < op1->digit_count) + { + dest->digits[i] = op1_digits[i]; + } + else if (i < op2->digit_count) + { + dest->digits[i] = op2_digits[i]; + } + else + { + FATAL_ERROR("Unreachable"); + } + } + normalize(dest); + } +} + +void bigint_shl_int(BigInt *dest, const BigInt *op1, uint64_t shift); +void bigint_shl(BigInt *dest, const BigInt *op1, const BigInt *op2) +{ + assert(!op2->is_negative); + + if (op2->digit_count != 1) + { + FATAL_ERROR("Unsupported: shift left by amount greater than 64 bit integer"); + } + bigint_shl_int(dest, op1, bigint_as_unsigned(op2)); +} + +void bigint_shl_int(BigInt *dest, const BigInt *op1, uint64_t shift) +{ + if (shift == 0) + { + bigint_init_bigint(dest, op1); + return; + } + + if (op1->digit_count == 0) + { + bigint_init_unsigned(dest, 0); + return; + } + + const uint64_t *op1_digits = bigint_ptr(op1); + + if (op1->digit_count == 1 && shift < 64) + { + dest->digit = op1_digits[0] << shift; + if (dest->digit > op1_digits[0]) + { + dest->digit_count = 1; + dest->is_negative = op1->is_negative; + return; + } + } + + uint64_t digit_shift_count = shift / 64; + uint64_t leftover_shift_count = shift % 64; + + dest->digits = ALLOC_DIGITS(op1->digit_count + digit_shift_count + 1); + dest->digit_count = digit_shift_count; + uint64_t carry = 0; + for (size_t i = 0; i < op1->digit_count; i += 1) + { + uint64_t digit = op1_digits[i]; + dest->digits[dest->digit_count] = carry | (digit << leftover_shift_count); + dest->digit_count++; + if (leftover_shift_count > 0) + { + carry = digit >> (64 - leftover_shift_count); + } + else + { + carry = 0; + } + } + dest->digits[dest->digit_count] = carry; + dest->digit_count += 1; + dest->is_negative = op1->is_negative; + normalize(dest); +} + +void bigint_shl_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) +{ + BigInt unwrapped = { 0 }; + bigint_shl(&unwrapped, op1, op2); + bigint_truncate(dest, &unwrapped, bit_count, is_signed); +} + +void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) +{ + assert(!op2->is_negative); + + if (op1->digit_count == 0) + { + return bigint_init_unsigned(dest, 0); + } + + if (op2->digit_count == 0) + { + return bigint_init_bigint(dest, op1); + } + + if (op2->digit_count != 1) + { + FATAL_ERROR("Unsupported: shift right by amount greater than 64 bit integer"); + } + + const uint64_t *op1_digits = bigint_ptr(op1); + uint64_t shift_amt = bigint_as_unsigned(op2); + + if (op1->digit_count == 1) + { + dest->digit = shift_amt < 64 ? op1_digits[0] >> shift_amt : 0; + dest->digit_count = 1; + dest->is_negative = op1->is_negative; + normalize(dest); + return; + } + + uint64_t digit_shift_count = shift_amt / 64; + uint64_t leftover_shift_count = shift_amt % 64; + + if (digit_shift_count >= op1->digit_count) + { + return bigint_init_unsigned(dest, 0); + } + + dest->digit_count = op1->digit_count - digit_shift_count; + uint64_t *digits; + if (dest->digit_count == 1) + { + digits = &dest->digit; + } + else + { + digits = ALLOC_DIGITS(dest->digit_count); + dest->digits = digits; + } + + uint64_t carry = 0; + for (size_t op_digit_index = op1->digit_count - 1;;) + { + uint64_t digit = op1_digits[op_digit_index]; + size_t dest_digit_index = op_digit_index - digit_shift_count; + digits[dest_digit_index] = carry | (digit >> leftover_shift_count); + carry = digit << (64 - leftover_shift_count); + + if (dest_digit_index == 0) break; + op_digit_index -= 1; + } + dest->is_negative = op1->is_negative; + normalize(dest); +} + + +void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count) +{ + BigInt zero; + bigint_init_unsigned(&zero, 0); + bigint_sub_wrap(dest, &zero, op, bit_count, true); +} + +void bigint_not(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed) +{ + if (bit_count == 0) + { + bigint_init_unsigned(dest, 0); + return; + } + + if (is_signed) + { + BigInt twos_comp = { 0 }; + to_twos_complement(&twos_comp, op, bit_count); + + BigInt inverted = { 0 }; + bigint_not(&inverted, &twos_comp, bit_count, false); + + from_twos_complement(dest, &inverted, bit_count, true); + return; + } + + assert(!op->is_negative); + + dest->is_negative = false; + const uint64_t *op_digits = bigint_ptr(op); + if (bit_count <= 64) + { + dest->digit_count = 1; + if (op->digit_count == 0) + { + if (bit_count == 64) + { + dest->digit = UINT64_MAX; + } + else + { + dest->digit = (1ULL << bit_count) - 1; + } + } + else if (op->digit_count == 1) + { + dest->digit = ~op_digits[0]; + if (bit_count != 64) + { + uint64_t + mask = (1ULL << bit_count) - 1; + dest->digit &= mask; + } + } + normalize(dest); + return; + } + dest->digit_count = (unsigned int) ((bit_count + 63) / 64); + assert(dest->digit_count >= op->digit_count); + dest->digits = ALLOC_DIGITS(dest->digit_count); + size_t i = 0; + for (; i < op->digit_count; i += 1) + { + dest->digits[i] = ~op_digits[i]; + } + for (; i < dest->digit_count; i += 1) + { + dest->digits[i] = 0xffffffffffffffffULL; + } + size_t digit_index = dest->digit_count - 1; + size_t digit_bit_index = bit_count % 64; + if (digit_bit_index != 0) + { + uint64_t + mask = (1ULL << digit_bit_index) - 1; + dest->digits[digit_index] &= mask; + } + normalize(dest); +} + +void bigint_truncate(BigInt *dst, const BigInt *op, size_t bit_count, bool is_signed) +{ + BigInt twos_comp; + to_twos_complement(&twos_comp, op, bit_count); + from_twos_complement(dst, &twos_comp, bit_count, is_signed); +} + +CmpRes bigint_cmp(const BigInt *op1, const BigInt *op2) +{ + if (op1->is_negative && !op2->is_negative) return CMP_LT; + if (!op1->is_negative && op2->is_negative) return CMP_GT; + if (op1->digit_count > op2->digit_count) return op1->is_negative ? CMP_LT : CMP_GT; + if (op2->digit_count > op1->digit_count) return op1->is_negative ? CMP_GT : CMP_LT; + if (op1->digit_count == 0) return CMP_EQ; + + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + for (unsigned i = op1->digit_count - 1;; i--) + { + uint64_t op1_digit = op1_digits[i]; + uint64_t op2_digit = op2_digits[i]; + + if (op1_digit > op2_digit) + { + return op1->is_negative ? CMP_LT : CMP_GT; + } + if (op1_digit < op2_digit) + { + return op1->is_negative ? CMP_GT : CMP_LT; + } + if (i == 0) + { + return CMP_EQ; + } + } +} + +void bigint_print(BigInt *bigint, uint64_t base) +{ + if (bigint->digit_count == 0) + { + printf("0"); + return; + } + if (bigint->is_negative) + { + printf("-"); + } + if (bigint->digit_count == 1 && base == 10) + { + printf("%" PRIu64, bigint->digit); + return; + } + size_t len = bigint->digit_count * 64; + char *start = (char *)malloc_arena(len); + char *buf = start; + + BigInt digit_bi = { 0 }; + BigInt a1 = { 0 }; + BigInt a2 = { 0 }; + + BigInt *a = &a1; + BigInt *other_a = &a2; + bigint_init_bigint(a, bigint); + + BigInt base_bi = { 0 }; + bigint_init_unsigned(&base_bi, base); + + for (;;) + { + bigint_rem(&digit_bi, a, &base_bi); + uint8_t digit = (uint8_t)bigint_as_unsigned(&digit_bi); + *(buf++) = digit_to_char(digit, false); + bigint_div_trunc(other_a, a, &base_bi); + { + BigInt *tmp = a; + a = other_a; + other_a = tmp; + } + if (bigint_cmp_zero(a) == CMP_EQ) + { + break; + } + } + + // reverse + + for (char *ptr = buf - 1; ptr >= start; ptr--) + { + printf("%c", *ptr); + } +} + +const char *bigint_to_error_string(Allocator *allocator, const BigInt *bigint, uint64_t base) +{ + if (bigint->digit_count == 0) + { + return "0"; + } + if (bigint->digit_count == 1 && base == 10) + { + char *res = NULL; + if (bigint->is_negative) + { + String string = string_fmt(allocator, "-%" PRIu64, bigint->digit); + return (const char *)string.str; + } + else + { + String string = string_fmt(allocator, "%" PRIu64, bigint->digit); + return (const char *)string.str; + } + return res; + } + size_t len = bigint->digit_count * 64; + char *start = (char *)exp_alloc(allocator, len); + char *buf = start; + + BigInt digit_bi = { 0 }; + BigInt a1 = { 0 }; + BigInt a2 = { 0 }; + + BigInt *a = &a1; + BigInt *other_a = &a2; + bigint_init_bigint(a, bigint); + + BigInt base_bi = { 0 }; + bigint_init_unsigned(&base_bi, base); + + for (;;) + { + bigint_rem(&digit_bi, a, &base_bi); + uint8_t digit = (uint8_t)bigint_as_unsigned(&digit_bi); + *(buf++) = digit_to_char(digit, false); + bigint_div_trunc(other_a, a, &base_bi); + { + BigInt *tmp = a; + a = other_a; + other_a = tmp; + } + if (bigint_cmp_zero(a) == CMP_EQ) + { + break; + } + } + + // reverse + char *out = (char *)exp_alloc(allocator, buf - start + 2); + char *current = out; + if (bigint->is_negative) + { + *(current++) = '-'; + } + for (char *ptr = buf - 1; ptr >= start; ptr--) + { + *(current++) = *ptr; + } + *(current++) = '\0'; + return out; +} + +void bigint_fprint(FILE *file, BigInt *bigint, uint64_t base) +{ + if (bigint->digit_count == 0) + { + fprintf(file, "0"); + return; + } + if (bigint->is_negative) + { + fprintf(file, "-"); + } + if (bigint->digit_count == 1 && base == 10) + { + fprintf(file, "%" PRIu64, bigint->digit); + return; + } + size_t len = bigint->digit_count * 64; + char *start = (char *)malloc_arena(len); + char *buf = start; + + BigInt digit_bi = { 0 }; + BigInt a1 = { 0 }; + BigInt a2 = { 0 }; + + BigInt *a = &a1; + BigInt *other_a = &a2; + bigint_init_bigint(a, bigint); + + BigInt base_bi = { 0 }; + bigint_init_unsigned(&base_bi, base); + + for (;;) + { + bigint_rem(&digit_bi, a, &base_bi); + uint8_t digit = (uint8_t)bigint_as_unsigned(&digit_bi); + *(buf++) = digit_to_char(digit, false); + bigint_div_trunc(other_a, a, &base_bi); + { + BigInt *tmp = a; + a = other_a; + other_a = tmp; + } + if (bigint_cmp_zero(a) == CMP_EQ) + { + break; + } + } + + // reverse + + for (char *ptr = buf - 1; ptr >= start; ptr--) + { + fprintf(file, "%c", *ptr); + } +} +size_t bigint_popcount_unsigned(const BigInt *big_int) +{ + assert(!big_int->is_negative); + if (big_int->digit_count == 0) + { + return 0; + } + + unsigned count = 0; + size_t bit_count = big_int->digit_count * 64; + for (size_t i = 0; i < bit_count; i++) + { + if (bit_at_index(big_int, i)) + { + count += 1; + } + } + return count; +} + +size_t bigint_popcount_signed(const BigInt *bi, size_t bit_count) +{ + if (bit_count == 0) + { + return 0; + } + if (bi->digit_count == 0) + { + return 0; + } + + BigInt twos_comp = { 0 }; + to_twos_complement(&twos_comp, bi, bit_count); + + size_t count = 0; + for (size_t i = 0; i < bit_count; i += 1) + { + if (bit_at_index(&twos_comp, i)) + { + count += 1; + } + } + return count; +} + +size_t bigint_ctz(const BigInt *bi, size_t bit_count) +{ + if (bit_count == 0) + { + return 0; + } + if (bi->digit_count == 0) + { + return bit_count; + } + + BigInt twos_comp = { 0 }; + to_twos_complement(&twos_comp, bi, bit_count); + + size_t count = 0; + for (size_t i = 0; i < bit_count; i += 1) + { + if (bit_at_index(&twos_comp, i)) + { + return count; + } + count += 1; + } + return count; +} + + +int64_t bigint_as_signed(const BigInt *bigint) +{ + if (bigint->digit_count == 0) return 0; + if (bigint->digit_count != 1) + { + FATAL_ERROR("BigInt larger than i64"); + } + + if (bigint->is_negative) + { + // TODO this code path is untested + if (bigint->digit <= 9223372036854775808ULL) + { + return (-((int64_t) (bigint->digit - 1))) - 1; + } + FATAL_ERROR("BigInt does not fit in i64"); + } + return (int64_t)bigint->digit; +} + +CmpRes bigint_cmp_zero(const BigInt *op) +{ + if (op->digit_count == 0) + { + return CMP_EQ; + } + return op->is_negative ? CMP_LT : CMP_GT; +} + + +void bigint_incr(BigInt *x) +{ + if (!x->digit_count) + { + bigint_init_unsigned(x, 1); + return; + } + + if (x->digit_count == 1) + { + if (x->is_negative && x->digit != 0) + { + x->digit -= 1; + return; + } + if (!x->is_negative && x->digit != UINT64_MAX) + { + x->digit += 1; + return; + } + } + + BigInt copy; + bigint_init_bigint(©, x); + + BigInt one; + bigint_init_unsigned(&one, 1); + + bigint_add(x, ©, &one); +} + +double bigint_as_float(const BigInt *bigint) +{ + if (bigint_fits_in_bits(bigint, 64, bigint->is_negative)) + { + return bigint->is_negative ? (double)bigint_as_signed(bigint) : (double)bigint_as_unsigned(bigint); + } + BigInt div; + uint64_t mult = 0x100000000000ULL; + double mul = 1; + bigint_init_unsigned(&div, mult); + BigInt current; + bigint_init_bigint(¤t, bigint); + double f = 0; + do + { + BigInt temp; + bigint_mod(&temp, ¤t, &div); + f += bigint_as_signed(&temp) * mul; + mul *= mult; + bigint_div_trunc(&temp, ¤t, &div); + current = temp; + } + while (current.digit_count > 0); + return f; +} diff --git a/ccodegen.cpp b/ccodegen.cpp index 9ed26d8..0a9f7ef 100644 --- a/ccodegen.cpp +++ b/ccodegen.cpp @@ -73,10 +73,9 @@ gen_expr(Ast_Expr *ast){ } CASE(VALUE, Atom){ - if(is_int(node->type)) gen("%lld", node->int_val); + if(is_int(node->type)) gen("%lld", bigint_as_signed(&node->big_int_val)); else if(is_string(node->type)) gen("LIT(\"%s\")", node->intern_val.str); else if(is_bool(node->type)) node->bool_val ? gen("true"):gen("false"); - else if(is_f32(node->type)) gen("%f", node->f32_val); else if(is_float(node->type)) gen("%f", node->f64_val); else invalid_codepath; BREAK(); @@ -315,7 +314,7 @@ gen_ast(Ast *ast){ gen("// constant F64 %s = %f;", node->name.str, sym->f64_val); } else if(sym->type == untyped_int){ - gen("// constant int %s = %lld;", node->name.str, sym->int_val); + gen("// constant int %s = %lld;", node->name.str, bigint_as_signed(&sym->big_int_val)); } else if(sym->type == untyped_string){ gen("// const String %s = LIT(\"%s\");", node->name.str, sym->intern_val.str); @@ -356,7 +355,7 @@ gen_ast(Ast *ast){ genln("%s", it->name.str); gen(" = "); Sym *value_sym = resolved_get(it); - gen("%d", value_sym->int_val); + gen("%d", bigint_as_signed(&value_sym->big_int_val)); gen(","); } global_indent--; diff --git a/main.cpp b/main.cpp index 81ed87b..9c82477 100644 --- a/main.cpp +++ b/main.cpp @@ -63,6 +63,7 @@ For now I don't thing it should be overloadable. [x] - Test new operators, add constant eval for them [x] - lvalue, rvalue concept so we cant assign value to some arbitrary weird expression [x] - More basic types +[x] - Implementing required operations int128 [x] - Add basic support for floats [x] - Add basic setup for new type system [x] - Access through struct names to constants Arena.CONSTANT @@ -94,15 +95,17 @@ For now I don't thing it should be overloadable. #include "base_unicode.cpp" #include "new_lex.cpp" #include "types.h" +#include "big_int_c3.cpp" +// #include "big_int.cpp" #include "new_ast.cpp" #include "new_parse.cpp" #include "typecheck.h" #include "typecheck.cpp" #include "ccodegen.cpp" -#include "big_int.cpp" int main(){ - test_big_int(); + // test_big_int(); + test_os_memory(); thread_ctx_init(); @@ -114,6 +117,7 @@ int main(){ test_string_builder(); test_intern_table(); + String result = {}; #if 0 result = compile_file("globals.kl"_s); diff --git a/new_ast.cpp b/new_ast.cpp index bc1b9d4..d567341 100644 --- a/new_ast.cpp +++ b/new_ast.cpp @@ -141,22 +141,13 @@ Ast_Resolved_Type *type; \ union{ \ bool bool_val; \ F64 f64_val; \ - F32 f32_val; \ - S8 s8_val; \ - S16 s16_val; \ - S32 s32_val; \ - S64 s64_val; \ - U8 u8_val; \ - U16 u16_val; \ - U32 u32_val; \ - U64 u64_val; \ - S64 int_val; \ - U64 uint_val; \ - Intern_String intern_val; \ + Intern_String intern_val;\ + BigInt big_int_val;\ Ast_Resolved_Type *type_val; \ }; #define INLINE_VALUE_FIELDS union{Value value; struct{VALUE_FIELDS};} struct Value{VALUE_FIELDS}; + // BigInt big_int_val; struct Ast_Atom: Ast_Expr{ INLINE_VALUE_FIELDS; @@ -326,11 +317,9 @@ ast_float(Token *pos, F64 value){ function Ast_Atom * ast_int(Token *pos, U64 integer){ - assert(integer <= S64MAX); AST_NEW(Atom, VALUE, pos, AST_EXPR | AST_ATOM); result->type = untyped_int; - result->int_val = integer; - // result->big_int = big_int(pctx->perm, integer); // @todo: int arena?? + bigint_init_unsigned(&result->big_int_val, integer); return result; } diff --git a/typecheck.cpp b/typecheck.cpp index 93b0bb1..a827014 100644 --- a/typecheck.cpp +++ b/typecheck.cpp @@ -31,13 +31,6 @@ type_error(Token *token, Ast_Resolved_Type *expected, Ast_Resolved_Type *actual, //----------------------------------------------------------------------------- // Evaluating constant expressions //----------------------------------------------------------------------------- -#define CASE_CONVERT(pos, int_val, kind_val, new_val, TYPE, min, max) \ - case TYPE:{ \ - if(int_val > max) parsing_error(pos, "Overflow when converting from %s constant to %s, value out of range: %d, max is: %d", type_names[kind_val], type_names[TYPE], int_val, max); \ - if(int_val < min) parsing_error(pos, "Underflow when converting from %s constant to %s, value out of range: %d, min is: %d", type_names[kind_val], type_names[TYPE], int_val, min);\ - new_val = int_val;\ - }break; - function Value convert_untyped(Token *pos, Value a, Ast_Resolved_Type *new_type){ // assert(a.type); @@ -45,37 +38,18 @@ convert_untyped(Token *pos, Value a, Ast_Resolved_Type *new_type){ if(a.type == 0) return a; if(is_typed(a.type)) return a; - assert(a.int_val <= S64MAX); if(is_int(a.type) && is_int(new_type)){ assert(a.type == untyped_int); - switch(new_type->kind){ - case TYPE_UNTYPED_INT: break; - CASE_CONVERT(pos, a.int_val, a.type->kind, a.s64_val, TYPE_INT, TYPE_INT_MIN, TYPE_INT_MAX) - CASE_CONVERT(pos, a.int_val, a.type->kind, a.u64_val, TYPE_UINT, TYPE_UINT_MIN, TYPE_INT_MAX) // @todo big int - CASE_CONVERT(pos, a.int_val, a.type->kind, a.s8_val , TYPE_S8 , S8MIN, S8MAX) - CASE_CONVERT(pos, a.int_val, a.type->kind, a.s16_val, TYPE_S16, S16MIN, S16MAX) - CASE_CONVERT(pos, a.int_val, a.type->kind, a.s32_val, TYPE_S32, S32MIN, S32MAX) - CASE_CONVERT(pos, a.int_val, a.type->kind, a.s64_val, TYPE_S64, S64MIN, S64MAX) - CASE_CONVERT(pos, a.int_val, a.type->kind, a.u8_val , TYPE_U8 , U8MIN, U8MAX) - CASE_CONVERT(pos, a.int_val, a.type->kind, a.u16_val, TYPE_U16, U16MIN, U16MAX) - CASE_CONVERT(pos, a.int_val, a.type->kind, a.u32_val, TYPE_U32, U32MIN, U32MAX) - CASE_CONVERT(pos, a.int_val, a.type->kind, a.u64_val, TYPE_U64, U64MIN, U64MAX) // @todo big int - invalid_default_case; + if(!bigint_fits_in_bits(&a.big_int_val, new_type->size*8, is_signed_int(new_type))){ + parsing_error(pos, "Doesn't fit"); } } else if(is_int(a.type) && is_float(new_type)){ - assert(a.type == untyped_int); - switch(new_type->kind){ - case TYPE_F32: {a.f32_val = a.int_val;}break; - case TYPE_UNTYPED_FLOAT:{a.f64_val = a.int_val;}break; - case TYPE_F64: {a.f64_val = a.int_val;}break; - invalid_default_case; - } + a.f64_val = bigint_as_float(&a.big_int_val); // @leak bigint } else if(is_float(a.type) && is_float(new_type)){ - assert(a.type == untyped_float); - if(new_type == type_f32) a.f32_val = a.f64_val; + // nothing to do } else if(is_bool(a.type) && is_bool(new_type)) ; // nothing to do @@ -87,193 +61,208 @@ convert_untyped(Token *pos, Value a, Ast_Resolved_Type *new_type){ return a; } -function S64 -value_get_int(Value value){ - assert(value.int_val <= S64MAX); - assert(is_numeric(value.type)); - S64 result = 0; - switch(value.type->kind){ - case TYPE_BOOL: result = value.bool_val; break; - case TYPE_UNTYPED_BOOL: result = value.bool_val; break; - case TYPE_UNTYPED_FLOAT: result = (S64)value.f64_val; break; - case TYPE_F64: result = (S64)value.f64_val; break; - case TYPE_F32: result = (S64)value.f32_val; break; - case TYPE_UNTYPED_INT: result = value.int_val; break; - case TYPE_INT: result = value.s64_val; break; - case TYPE_S64:result = value.s64_val; break; - case TYPE_S32:result = value.s32_val; break; - case TYPE_S16:result = value.s16_val; break; - case TYPE_S8:result = value.s8_val; break; - case TYPE_U64:assert(value.u64_val <= S64MAX); result = value.u64_val; break; // @todo big int - case TYPE_U32:result = value.u32_val; break; - case TYPE_U16:result = value.u16_val; break; - case TYPE_U8:result = value.u8_val; break; - case TYPE_UINT:result = value.u64_val; break; // @todo big int - default: invalid_codepath; + +function Value +value_bool(B32 v){ + Value value; + value.bool_val = v; + value.type = untyped_bool; + return value; +} + +function Value +value_int(BigInt b){ + Value value; + value.big_int_val = b; + value.type = untyped_int; + return value; +} + +function Value +value_int(S64 s64){ + Value value; + value.type = untyped_int; + bigint_init_signed(&value.big_int_val, s64); + return value; +} + +function Value +value_float(F64 b){ + Value value; + value.f64_val = b; + value.type = untyped_float; + return value; +} + +function Value +value_float(BigInt a){ + Value value; + value.f64_val = bigint_as_float(&a); + value.type = untyped_float; + return value; +} + +function void +match_values(Value *a, Value *b){ + if(is_int(a->type) && is_float(b->type)){ + *a = value_float(a->big_int_val); + } + else if(is_float(a->type) && is_int(b->type)){ + *b = value_float(b->big_int_val); } - return result; } -function F64 -value_get_float(Value value){ - assert(is_numeric(value.type)); - F64 result = value.f64_val; - if(is_int(value.type) || is_bool(value.type)) - result = (F64)value_get_int(value); - else if(value.type == type_f32) - result = value.f32_val; - return result; +function Value +compare_values(Token *pos, Token_Kind op, Value a, Value b){ + if(!(is_numeric(a.type) && is_numeric(b.type))) parsing_error(pos, "Constant application of binary %s on values of type %s and %s is not allowed", name(op), docname(a.type), docname(a.type)); + match_values(&a, &b); + + B32 result = 0; + switch(a.type->kind){ + case TYPE_UNTYPED_INT: + case TYPE_S8:case TYPE_S16:case TYPE_S32:case TYPE_S64: + case TYPE_U8:case TYPE_U16:case TYPE_U32:case TYPE_U64:{ + CmpRes cmp = bigint_cmp(&a.big_int_val, &b.big_int_val); + switch(op){ + case TK_LesserThenOrEqual: result = (cmp == CMP_LT) || (cmp == CMP_EQ); break; + case TK_GreaterThenOrEqual: result = (cmp == CMP_GT) || (cmp == CMP_EQ); break; + case TK_GreaterThen: result = cmp == CMP_GT; break; + case TK_LesserThen: result = cmp == CMP_LT; break; + case TK_Equals: result = cmp == CMP_EQ; break; + case TK_NotEquals: result = cmp != CMP_EQ; break; + invalid_default_case; + } + }break; + case TYPE_UNTYPED_BOOL: + case TYPE_BOOL:{ + switch(op){ + case TK_Equals: result = a.bool_val == b.bool_val; break; + case TK_NotEquals: result = a.bool_val != b.bool_val; break; + invalid_default_case; + } + }break; + case TYPE_UNTYPED_FLOAT: + case TYPE_F32: case TYPE_F64:{ + switch(op){ + case TK_LesserThenOrEqual: result = a.f64_val <= b.f64_val; break; + case TK_GreaterThenOrEqual: result = a.f64_val >= b.f64_val; break; + case TK_GreaterThen: result = a.f64_val > b.f64_val; break; + case TK_LesserThen: result = a.f64_val < b.f64_val; break; + case TK_Equals: result = a.f64_val == b.f64_val; break; + case TK_NotEquals: result = a.f64_val != b.f64_val; break; + invalid_default_case; + } + }break; + case TYPE_UNTYPED_STRING: + case TYPE_STRING:{ + invalid_codepath; + }break; + invalid_default_case; + } + + return value_bool(result); } -#include // fmod function Value eval_binary(Token *pos, Token_Kind op, Value a, Value b){ if(!(is_numeric(a.type) && is_numeric(b.type))) parsing_error(pos, "Constant application of binary %s on values of type %s and %s is not allowed", name(op), docname(a.type), docname(a.type)); + match_values(&a, &b); - // @warning: this bool path returns early, always should return untyped bool - if(is_bool(a.type) || is_bool(b.type)){ - if(!is_bool(a.type)) parsing_error(pos, "Type mismatch in binary operation %s - left: %s right: %s", name(op), docname(a.type), docname(b.type)); - if(!is_bool(b.type)) parsing_error(pos, "Type mismatch in binary operation %s - left: %s right: %s", name(op), docname(a.type), docname(b.type)); - - Value result; - result.type = untyped_bool; - switch(op){ - case TK_And: result.bool_val = a.bool_val && b.bool_val; break; - case TK_Or: result.bool_val = a.bool_val || b.bool_val; break; - case TK_GreaterThen: result.bool_val = a.bool_val > b.bool_val; break; - case TK_GreaterThenOrEqual: result.bool_val = a.bool_val >= b.bool_val; break; - case TK_LesserThen: result.bool_val = a.bool_val < b.bool_val; break; - case TK_LesserThenOrEqual: result.bool_val = a.bool_val <= b.bool_val; break; - case TK_Equals: result.bool_val = a.bool_val == b.bool_val; break; - case TK_NotEquals: result.bool_val = a.bool_val != b.bool_val; break; - invalid_default_case; - } - return result; + if(token_is_compare(op)){ + return compare_values(pos, op, a, b); } - Ast_Resolved_Type *final_type = 0; - Value before_conversion; - if(is_int(a.type) && is_int(b.type)) { - before_conversion.type = untyped_int; - final_type = untyped_int; - } - else{ - before_conversion.type = untyped_float; - final_type = untyped_float; + Value result = {}; + result.type = a.type; + switch(a.type->kind){ + case TYPE_UNTYPED_INT: + case TYPE_S8:case TYPE_S16:case TYPE_S32:case TYPE_S64: + case TYPE_U8:case TYPE_U16:case TYPE_U32:case TYPE_U64:{ + switch(op){ + case TK_BitXor: bigint_xor(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; + case TK_BitAnd: bigint_and(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; + case TK_BitOr: bigint_or(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; + case TK_Add: bigint_add(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; + case TK_Sub: bigint_sub(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; + case TK_Mul: bigint_mul(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; + case TK_Div: bigint_div_trunc(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; + case TK_Mod: bigint_mod(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; + case TK_LeftShift: bigint_shl(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; + case TK_RightShift: bigint_shr(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; + invalid_default_case; + } + }break; + case TYPE_UNTYPED_BOOL: + case TYPE_BOOL:{ + switch(op){ + case TK_And: result.bool_val = a.bool_val && b.bool_val; break; + case TK_Or: result.bool_val = a.bool_val || b.bool_val; break; + case TK_BitAnd: result.bool_val = a.bool_val & b.bool_val; break; + case TK_BitOr: result.bool_val = a.bool_val | b.bool_val; break; + case TK_BitXor: result.bool_val = a.bool_val ^ b.bool_val; break; + invalid_default_case; + } + }break; + case TYPE_UNTYPED_FLOAT: + case TYPE_F32: case TYPE_F64:{ + switch(op){ + case TK_Add: result.f64_val = a.f64_val + b.f64_val; break; + case TK_Sub: result.f64_val = a.f64_val - b.f64_val; break; + case TK_Mul: result.f64_val = a.f64_val * b.f64_val; break; + case TK_Div: result.f64_val = a.f64_val / b.f64_val; break; + invalid_default_case; + } + }break; + case TYPE_UNTYPED_STRING: + case TYPE_STRING:{ + invalid_codepath; + }break; + invalid_default_case; } - if(is_typed(a.type) && is_typed(b.type)){ - if(a.type != b.type) parsing_error(pos, "Type mismatch in binary operation %s - left: %s right: %s", name(op), docname(a.type), docname(b.type)); - else final_type = a.type; - } - - else if(is_typed(a.type) || is_typed(b.type)){ - not_implemented; - } - - S64 left_int = value_get_int(a); - S64 right_int = value_get_int(b); - F64 left_float = value_get_float(a); - F64 right_float = value_get_float(b); - - assert(before_conversion.type == untyped_float || before_conversion.type == untyped_int); - // @note: for returning early with bools - Value c; c.type = untyped_bool; - switch(op){ - - // @note: These return early, they don't need type evaluation - // always should return untyped bool - case TK_And: { - if(final_type == untyped_int) c.bool_val = left_int && right_int; - else c.bool_val = left_float && right_float; - return c; - }break; - case TK_Or: { - if(final_type == untyped_int) c.bool_val = left_int || right_int; - else c.bool_val = left_float || right_float; - return c; - }break; - case TK_GreaterThen: { - if(final_type == untyped_int) c.bool_val = left_int > right_int; - else c.bool_val = left_float > right_float; - return c; - }break; - case TK_GreaterThenOrEqual: { - if(final_type == untyped_int) c.bool_val = left_int >= right_int; - else c.bool_val = left_float >= right_float; - return c; - }break; - case TK_LesserThen: { - if(final_type == untyped_int) c.bool_val = left_int < right_int; - else c.bool_val = left_float < right_float; - return c; - }break; - case TK_LesserThenOrEqual: { - if(final_type == untyped_int) c.bool_val = left_int <= right_int; - else c.bool_val = left_float <= right_float; - return c; - }break; - case TK_Equals: { - if(final_type == untyped_int) c.bool_val = left_int == right_int; - else c.bool_val = left_float == right_float; - return c; - }break; - case TK_NotEquals: { - if(final_type == untyped_int) c.bool_val = left_int != right_int; - else c.bool_val = left_float != right_float; - return c; - }break; - - // @note: These return at the end cause need type evaluation - case TK_Add: { - left_int = left_int + right_int; - left_float = left_float + right_float; - } break; - case TK_Sub: { - left_int = left_int - right_int; - left_float = left_float - right_float; - } break; - case TK_Mul: { - left_int = left_int * right_int; - left_float = left_float * right_float; - } break; - case TK_Div: { - left_int = left_int / right_int; - left_float = left_float / right_float; - } break; - case TK_Mod: { - left_int = left_int % right_int; - left_float = fmod(left_float, right_float); - } break; - - // @WARNING: When introducing big ints & | ^ will be problematic - case TK_BitAnd: { - left_int = left_int & right_int; - if(before_conversion.type == untyped_float) parsing_error(pos, "%s cant be performed on [Untyped_Float]", name(op)); - } break; - case TK_BitOr: { - left_int = left_int | right_int; - if(before_conversion.type == untyped_float) parsing_error(pos, "%s cant be performed on [Untyped_Float]", name(op)); - } break; - case TK_BitXor: { - left_int = left_int ^ right_int; - if(before_conversion.type == untyped_float) parsing_error(pos, "%s cant be performed on [Untyped_Float]", name(op)); - } break; - - default: parsing_error(pos, "Binary operation %s is not allowed on types: left: %s right: %s", name(op), docname(a.type), docname(b.type)); - } - - if(before_conversion.type == untyped_float){ - before_conversion.f64_val = left_float; - } else { - assert(before_conversion.type == untyped_int); - before_conversion.int_val = left_int; - } - - Value result = convert_untyped(pos, before_conversion, final_type); return result; } +function Value +eval_unary(Token *pos, Token_Kind op, Value a){ + if(!is_numeric(a.type)) parsing_error(pos, "Constant application of binary %s on values of type %s is not allowed", name(op), docname(a.type)); + + BigInt result = {}; + switch(op){ + case TK_Add:{ + return a; + } break; + case TK_Sub:{ + if(is_int(a.type)){ + bigint_negate(&result, &a.big_int_val); + return value_int(result); + } else if(is_float(a.type)){ + return value_float(-a.f64_val); + } else invalid_codepath; + } break; + case TK_Neg:{ + if(is_int(a.type)){ + bigint_not(&result, &a.big_int_val, 64, 1); // Invalid + return value_int(result); + } else invalid_codepath; + } break; + case TK_Not:{ + if(is_int(a.type)){ + if(CMP_EQ == bigint_cmp_zero(&a.big_int_val)){ + return value_bool(1); + } else return value_bool(0); + } else if(is_float(a.type)){ + return value_bool(!a.f64_val); + } else if(is_bool(a.type)){ + a.bool_val = !a.bool_val; + return a; + } + } break; + default: invalid_codepath; + } + invalid_return; +} + function void try_untyping(Operand *op){ if(!op) return; @@ -286,56 +275,6 @@ try_untyping(Operand *op){ } } -function Value -eval_unary(Token *pos, Token_Kind op, Value a){ - if(!is_numeric(a.type)) parsing_error(pos, "Constant application of binary %s on values of type %s is not allowed", name(op), docname(a.type)); - - S64 left_int = value_get_int(a); - F64 left_float = value_get_float(a); - - // @todo: bools go right through - switch(op){ - case TK_Not:{ - Value value; - value.type = type_bool; - value.bool_val = is_float(a.type) ? !left_float : !left_int; - return value; - }break; - case TK_Neg:{ - left_int = ~left_int; - if(is_float(a.type)) type_error(pos, type_int, a.type, "[~] doesn't work on floating point"); - // left_int = int_type_max(a.type) & left_int; - // @todo this doesn't work - }break; - case TK_Sub:{ - left_int = -left_int; - left_float = -left_float; - }break; - case TK_Add:{ - left_int = +left_int; - left_float = +left_float; - }break; - invalid_default_case; - } - // case TYPE_UINT: parsing_error(pos, "Application of unary [-] on type %s results in overflow", docname(v.type));break; - // default: parsing_error(pos, "Constant application of unary [-] on type of %s is unsupported", docname(v.type));break; - // default: parsing_error(pos, "Constant application of unary [+] on type of %s is unsupported", docname(v.type));break; - - Value before_conversion; - if(is_int(a.type)){ - before_conversion.type = untyped_int; - before_conversion.int_val = left_int; - } - else{ - assert(is_float(a.type)); - before_conversion.type = untyped_float; - before_conversion.f64_val = left_float; - } - - Value result = convert_untyped(pos, before_conversion, a.type); - return result; -} - #define rewrite_into_const(ast,T,s) _rewrite_into_const(ast,sizeof(T),s) function void _rewrite_into_const(Ast *node, U64 ast_size, Value value){ @@ -532,7 +471,7 @@ resolve_expr(Ast_Expr *ast, Ast_Resolved_Type *expected_type, Sym *lambda_to_res if(type.type != type_type) parsing_error(node->pos, "Prefix array operator is only allowed on types"); Operand expr = require_const_int(node->expr, AST_CANT_BE_NULL); - Ast_Resolved_Type *resolved = type_array(type.type_val, expr.int_val); + Ast_Resolved_Type *resolved = type_array(type.type_val, bigint_as_unsigned(&expr.big_int_val)); sym_type(resolved, node); return operand_type(resolved); BREAK(); @@ -572,7 +511,8 @@ resolve_expr(Ast_Expr *ast, Ast_Resolved_Type *expected_type, Sym *lambda_to_res if(i->name) parsing_error(i->pos, "Invalid indexing kind in a compound expression of type %s", type_names[type->kind]); if(i->index){ Operand index_op = require_const_int(i->index, AST_CANT_BE_NULL); - if(index_op.int_val > (type->arr.size - 1)) parsing_error(i->pos, "Invalid index in compound expression, larger then type can store"); + U64 index = bigint_as_unsigned(&index_op.big_int_val); + if(index > (type->arr.size - 1)) parsing_error(i->pos, "Invalid index in compound expression, larger then type can store"); } Operand expr = resolve_expr(i->item, item_type); expr.value = convert_untyped(i->pos, expr.value, item_type); @@ -895,11 +835,11 @@ resolve_const(Ast_Expr *ast, Sym *sym){ For(node->members){ Operand op = require_const_int(it->value, AST_CAN_BE_NULL); if(op.type){ - value = op.int_val + 1; + value = bigint_as_signed(&op.big_int_val) + 1; } else{ op.type = type_s64; - op.int_val = value++; + bigint_init_signed(&op.big_int_val, value++); } sym_const(it->name, op, it, INSERT_INTO_SCOPE); diff --git a/typecheck.h b/typecheck.h index 778a0ae..9be1c58 100644 --- a/typecheck.h +++ b/typecheck.h @@ -204,11 +204,10 @@ operand_type(Ast_Resolved_Type *type){ } function Operand -operand_int(S64 int_val){ +operand_int(BigInt big_int){ Operand result = {}; result.type = type_int; - result.int_val = int_val; - // result.big_int = int_val; + bigint_init_bigint(&result.big_int_val, &big_int); result.is_const = true; result.is_lvalue = false; return result; diff --git a/types.h b/types.h index 3c6eff4..89b9acb 100644 --- a/types.h +++ b/types.h @@ -271,4 +271,4 @@ int_type_max(Ast_Resolved_Type *type){ invalid_default_case; } invalid_return; -} \ No newline at end of file +}