diff options
Diffstat (limited to 'prism/util/pm_integer.c')
| -rw-r--r-- | prism/util/pm_integer.c | 670 |
1 files changed, 0 insertions, 670 deletions
diff --git a/prism/util/pm_integer.c b/prism/util/pm_integer.c deleted file mode 100644 index 4170ecc58d..0000000000 --- a/prism/util/pm_integer.c +++ /dev/null @@ -1,670 +0,0 @@ -#include "prism/util/pm_integer.h" - -/** - * Pull out the length and values from the integer, regardless of the form in - * which the length/values are stored. - */ -#define INTEGER_EXTRACT(integer, length_variable, values_variable) \ - if ((integer)->values == NULL) { \ - length_variable = 1; \ - values_variable = &(integer)->value; \ - } else { \ - length_variable = (integer)->length; \ - values_variable = (integer)->values; \ - } - -/** - * Adds two positive pm_integer_t with the given base. - * Return pm_integer_t with values allocated. Not normalized. - */ -static void -big_add(pm_integer_t *destination, pm_integer_t *left, pm_integer_t *right, uint64_t base) { - size_t left_length; - uint32_t *left_values; - INTEGER_EXTRACT(left, left_length, left_values) - - size_t right_length; - uint32_t *right_values; - INTEGER_EXTRACT(right, right_length, right_values) - - size_t length = left_length < right_length ? right_length : left_length; - uint32_t *values = (uint32_t *) xmalloc(sizeof(uint32_t) * (length + 1)); - if (values == NULL) return; - - uint64_t carry = 0; - for (size_t index = 0; index < length; index++) { - uint64_t sum = carry + (index < left_length ? left_values[index] : 0) + (index < right_length ? right_values[index] : 0); - values[index] = (uint32_t) (sum % base); - carry = sum / base; - } - - if (carry > 0) { - values[length] = (uint32_t) carry; - length++; - } - - *destination = (pm_integer_t) { length, values, 0, false }; -} - -/** - * Internal use for karatsuba_multiply. Calculates `a - b - c` with the given - * base. Assume a, b, c, a - b - c all to be positive. - * Return pm_integer_t with values allocated. Not normalized. - */ -static void -big_sub2(pm_integer_t *destination, pm_integer_t *a, pm_integer_t *b, pm_integer_t *c, uint64_t base) { - size_t a_length; - uint32_t *a_values; - INTEGER_EXTRACT(a, a_length, a_values) - - size_t b_length; - uint32_t *b_values; - INTEGER_EXTRACT(b, b_length, b_values) - - size_t c_length; - uint32_t *c_values; - INTEGER_EXTRACT(c, c_length, c_values) - - uint32_t *values = (uint32_t*) xmalloc(sizeof(uint32_t) * a_length); - int64_t carry = 0; - - for (size_t index = 0; index < a_length; index++) { - int64_t sub = ( - carry + - a_values[index] - - (index < b_length ? b_values[index] : 0) - - (index < c_length ? c_values[index] : 0) - ); - - if (sub >= 0) { - values[index] = (uint32_t) sub; - carry = 0; - } else { - sub += 2 * (int64_t) base; - values[index] = (uint32_t) ((uint64_t) sub % base); - carry = sub / (int64_t) base - 2; - } - } - - while (a_length > 1 && values[a_length - 1] == 0) a_length--; - *destination = (pm_integer_t) { a_length, values, 0, false }; -} - -/** - * Multiply two positive integers with the given base using karatsuba algorithm. - * Return pm_integer_t with values allocated. Not normalized. - */ -static void -karatsuba_multiply(pm_integer_t *destination, pm_integer_t *left, pm_integer_t *right, uint64_t base) { - size_t left_length; - uint32_t *left_values; - INTEGER_EXTRACT(left, left_length, left_values) - - size_t right_length; - uint32_t *right_values; - INTEGER_EXTRACT(right, right_length, right_values) - - if (left_length > right_length) { - size_t temporary_length = left_length; - left_length = right_length; - right_length = temporary_length; - - uint32_t *temporary_values = left_values; - left_values = right_values; - right_values = temporary_values; - } - - if (left_length <= 10) { - size_t length = left_length + right_length; - uint32_t *values = (uint32_t *) xcalloc(length, sizeof(uint32_t)); - if (values == NULL) return; - - for (size_t left_index = 0; left_index < left_length; left_index++) { - uint32_t carry = 0; - for (size_t right_index = 0; right_index < right_length; right_index++) { - uint64_t product = (uint64_t) left_values[left_index] * right_values[right_index] + values[left_index + right_index] + carry; - values[left_index + right_index] = (uint32_t) (product % base); - carry = (uint32_t) (product / base); - } - values[left_index + right_length] = carry; - } - - while (length > 1 && values[length - 1] == 0) length--; - *destination = (pm_integer_t) { length, values, 0, false }; - return; - } - - if (left_length * 2 <= right_length) { - uint32_t *values = (uint32_t *) xcalloc(left_length + right_length, sizeof(uint32_t)); - - for (size_t start_offset = 0; start_offset < right_length; start_offset += left_length) { - size_t end_offset = start_offset + left_length; - if (end_offset > right_length) end_offset = right_length; - - pm_integer_t sliced_left = { - .length = left_length, - .values = left_values, - .value = 0, - .negative = false - }; - - pm_integer_t sliced_right = { - .length = end_offset - start_offset, - .values = right_values + start_offset, - .value = 0, - .negative = false - }; - - pm_integer_t product; - karatsuba_multiply(&product, &sliced_left, &sliced_right, base); - - uint32_t carry = 0; - for (size_t index = 0; index < product.length; index++) { - uint64_t sum = (uint64_t) values[start_offset + index] + product.values[index] + carry; - values[start_offset + index] = (uint32_t) (sum % base); - carry = (uint32_t) (sum / base); - } - - if (carry > 0) values[start_offset + product.length] += carry; - pm_integer_free(&product); - } - - *destination = (pm_integer_t) { left_length + right_length, values, 0, false }; - return; - } - - size_t half = left_length / 2; - pm_integer_t x0 = { half, left_values, 0, false }; - pm_integer_t x1 = { left_length - half, left_values + half, 0, false }; - pm_integer_t y0 = { half, right_values, 0, false }; - pm_integer_t y1 = { right_length - half, right_values + half, 0, false }; - - pm_integer_t z0 = { 0 }; - karatsuba_multiply(&z0, &x0, &y0, base); - - pm_integer_t z2 = { 0 }; - karatsuba_multiply(&z2, &x1, &y1, base); - - // For simplicity to avoid considering negative values, - // use `z1 = (x0 + x1) * (y0 + y1) - z0 - z2` instead of original karatsuba algorithm. - pm_integer_t x01 = { 0 }; - big_add(&x01, &x0, &x1, base); - - pm_integer_t y01 = { 0 }; - big_add(&y01, &y0, &y1, base); - - pm_integer_t xy = { 0 }; - karatsuba_multiply(&xy, &x01, &y01, base); - - pm_integer_t z1; - big_sub2(&z1, &xy, &z0, &z2, base); - - size_t length = left_length + right_length; - uint32_t *values = (uint32_t*) xcalloc(length, sizeof(uint32_t)); - - assert(z0.values != NULL); - memcpy(values, z0.values, sizeof(uint32_t) * z0.length); - - assert(z2.values != NULL); - memcpy(values + 2 * half, z2.values, sizeof(uint32_t) * z2.length); - - uint32_t carry = 0; - for(size_t index = 0; index < z1.length; index++) { - uint64_t sum = (uint64_t) carry + values[index + half] + z1.values[index]; - values[index + half] = (uint32_t) (sum % base); - carry = (uint32_t) (sum / base); - } - - for(size_t index = half + z1.length; carry > 0; index++) { - uint64_t sum = (uint64_t) carry + values[index]; - values[index] = (uint32_t) (sum % base); - carry = (uint32_t) (sum / base); - } - - while (length > 1 && values[length - 1] == 0) length--; - pm_integer_free(&z0); - pm_integer_free(&z1); - pm_integer_free(&z2); - pm_integer_free(&x01); - pm_integer_free(&y01); - pm_integer_free(&xy); - - *destination = (pm_integer_t) { length, values, 0, false }; -} - -/** - * The values of a hexadecimal digit, where the index is the ASCII character. - * Note that there's an odd exception here where _ is mapped to 0. This is - * because it's possible for us to end up trying to parse a number that has - * already had an error attached to it, and we want to provide _something_ to - * the user. - */ -static const int8_t pm_integer_parse_digit_values[256] = { -// 0 1 2 3 4 5 6 7 8 9 A B C D E F - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 1x - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 2x - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 3x - -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 4x - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, // 5x - -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 6x - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 7x - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 8x - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 9x - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Ax - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Bx - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Cx - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Dx - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Ex - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Fx -}; - -/** - * Return the value of a hexadecimal digit in a uint8_t. - */ -static uint8_t -pm_integer_parse_digit(const uint8_t character) { - int8_t value = pm_integer_parse_digit_values[character]; - assert(value != -1 && "invalid digit"); - - return (uint8_t) value; -} - -/** - * Create a pm_integer_t from uint64_t with the given base. It is assumed that - * the memory for the pm_integer_t pointer has been zeroed. - */ -static void -pm_integer_from_uint64(pm_integer_t *integer, uint64_t value, uint64_t base) { - if (value < base) { - integer->value = (uint32_t) value; - return; - } - - size_t length = 0; - uint64_t length_value = value; - while (length_value > 0) { - length++; - length_value /= base; - } - - uint32_t *values = (uint32_t *) xmalloc(sizeof(uint32_t) * length); - if (values == NULL) return; - - for (size_t value_index = 0; value_index < length; value_index++) { - values[value_index] = (uint32_t) (value % base); - value /= base; - } - - integer->length = length; - integer->values = values; -} - -/** - * Normalize pm_integer_t. - * Heading zero values will be removed. If the integer fits into uint32_t, - * values is set to NULL, length is set to 0, and value field will be used. - */ -static void -pm_integer_normalize(pm_integer_t *integer) { - if (integer->values == NULL) { - return; - } - - while (integer->length > 1 && integer->values[integer->length - 1] == 0) { - integer->length--; - } - - if (integer->length > 1) { - return; - } - - uint32_t value = integer->values[0]; - bool negative = integer->negative && value != 0; - - pm_integer_free(integer); - *integer = (pm_integer_t) { .values = NULL, .value = value, .length = 0, .negative = negative }; -} - -/** - * Convert base of the integer. - * In practice, it converts 10**9 to 1<<32 or 1<<32 to 10**9. - */ -static void -pm_integer_convert_base(pm_integer_t *destination, const pm_integer_t *source, uint64_t base_from, uint64_t base_to) { - size_t source_length; - const uint32_t *source_values; - INTEGER_EXTRACT(source, source_length, source_values) - - size_t bigints_length = (source_length + 1) / 2; - assert(bigints_length > 0); - - pm_integer_t *bigints = (pm_integer_t *) xcalloc(bigints_length, sizeof(pm_integer_t)); - if (bigints == NULL) return; - - for (size_t index = 0; index < source_length; index += 2) { - uint64_t value = source_values[index] + base_from * (index + 1 < source_length ? source_values[index + 1] : 0); - pm_integer_from_uint64(&bigints[index / 2], value, base_to); - } - - pm_integer_t base = { 0 }; - pm_integer_from_uint64(&base, base_from, base_to); - - while (bigints_length > 1) { - pm_integer_t next_base; - karatsuba_multiply(&next_base, &base, &base, base_to); - - pm_integer_free(&base); - base = next_base; - - size_t next_length = (bigints_length + 1) / 2; - pm_integer_t *next_bigints = (pm_integer_t *) xcalloc(next_length, sizeof(pm_integer_t)); - - for (size_t bigints_index = 0; bigints_index < bigints_length; bigints_index += 2) { - if (bigints_index + 1 == bigints_length) { - next_bigints[bigints_index / 2] = bigints[bigints_index]; - } else { - pm_integer_t multiplied = { 0 }; - karatsuba_multiply(&multiplied, &base, &bigints[bigints_index + 1], base_to); - - big_add(&next_bigints[bigints_index / 2], &bigints[bigints_index], &multiplied, base_to); - pm_integer_free(&bigints[bigints_index]); - pm_integer_free(&bigints[bigints_index + 1]); - pm_integer_free(&multiplied); - } - } - - xfree(bigints); - bigints = next_bigints; - bigints_length = next_length; - } - - *destination = bigints[0]; - destination->negative = source->negative; - pm_integer_normalize(destination); - - xfree(bigints); - pm_integer_free(&base); -} - -#undef INTEGER_EXTRACT - -/** - * Convert digits to integer with the given power-of-two base. - */ -static void -pm_integer_parse_powof2(pm_integer_t *integer, uint32_t base, const uint8_t *digits, size_t digits_length) { - size_t bit = 1; - while (base > (uint32_t) (1 << bit)) bit++; - - size_t length = (digits_length * bit + 31) / 32; - uint32_t *values = (uint32_t *) xcalloc(length, sizeof(uint32_t)); - - for (size_t digit_index = 0; digit_index < digits_length; digit_index++) { - size_t bit_position = bit * (digits_length - digit_index - 1); - uint32_t value = digits[digit_index]; - - size_t index = bit_position / 32; - size_t shift = bit_position % 32; - - values[index] |= value << shift; - if (32 - shift < bit) values[index + 1] |= value >> (32 - shift); - } - - while (length > 1 && values[length - 1] == 0) length--; - *integer = (pm_integer_t) { .length = length, .values = values, .value = 0, .negative = false }; - pm_integer_normalize(integer); -} - -/** - * Convert decimal digits to pm_integer_t. - */ -static void -pm_integer_parse_decimal(pm_integer_t *integer, const uint8_t *digits, size_t digits_length) { - const size_t batch = 9; - size_t length = (digits_length + batch - 1) / batch; - - uint32_t *values = (uint32_t *) xcalloc(length, sizeof(uint32_t)); - uint32_t value = 0; - - for (size_t digits_index = 0; digits_index < digits_length; digits_index++) { - value = value * 10 + digits[digits_index]; - - size_t reverse_index = digits_length - digits_index - 1; - if (reverse_index % batch == 0) { - values[reverse_index / batch] = value; - value = 0; - } - } - - // Convert base from 10**9 to 1<<32. - pm_integer_convert_base(integer, &((pm_integer_t) { .length = length, .values = values, .value = 0, .negative = false }), 1000000000, ((uint64_t) 1 << 32)); - xfree(values); -} - -/** - * Parse a large integer from a string that does not fit into uint32_t. - */ -static void -pm_integer_parse_big(pm_integer_t *integer, uint32_t multiplier, const uint8_t *start, const uint8_t *end) { - // Allocate an array to store digits. - uint8_t *digits = xmalloc(sizeof(uint8_t) * (size_t) (end - start)); - size_t digits_length = 0; - - for (; start < end; start++) { - if (*start == '_') continue; - digits[digits_length++] = pm_integer_parse_digit(*start); - } - - // Construct pm_integer_t from the digits. - if (multiplier == 10) { - pm_integer_parse_decimal(integer, digits, digits_length); - } else { - pm_integer_parse_powof2(integer, multiplier, digits, digits_length); - } - - xfree(digits); -} - -/** - * Parse an integer from a string. This assumes that the format of the integer - * has already been validated, as internal validation checks are not performed - * here. - */ -void -pm_integer_parse(pm_integer_t *integer, pm_integer_base_t base, const uint8_t *start, const uint8_t *end) { - // Ignore unary +. Unary - is parsed differently and will not end up here. - // Instead, it will modify the parsed integer later. - if (*start == '+') start++; - - // Determine the multiplier from the base, and skip past any prefixes. - uint32_t multiplier = 10; - switch (base) { - case PM_INTEGER_BASE_DEFAULT: - while (*start == '0') start++; // 01 -> 1 - break; - case PM_INTEGER_BASE_BINARY: - start += 2; // 0b - multiplier = 2; - break; - case PM_INTEGER_BASE_OCTAL: - start++; // 0 - if (*start == '_' || *start == 'o' || *start == 'O') start++; // o - multiplier = 8; - break; - case PM_INTEGER_BASE_DECIMAL: - if (*start == '0' && (end - start) > 1) start += 2; // 0d - break; - case PM_INTEGER_BASE_HEXADECIMAL: - start += 2; // 0x - multiplier = 16; - break; - case PM_INTEGER_BASE_UNKNOWN: - if (*start == '0' && (end - start) > 1) { - switch (start[1]) { - case '_': start += 2; multiplier = 8; break; - case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': start++; multiplier = 8; break; - case 'b': case 'B': start += 2; multiplier = 2; break; - case 'o': case 'O': start += 2; multiplier = 8; break; - case 'd': case 'D': start += 2; break; - case 'x': case 'X': start += 2; multiplier = 16; break; - default: assert(false && "unreachable"); break; - } - } - break; - } - - // It's possible that we've consumed everything at this point if there is an - // invalid integer. If this is the case, we'll just return 0. - if (start >= end) return; - - const uint8_t *cursor = start; - uint64_t value = (uint64_t) pm_integer_parse_digit(*cursor++); - - for (; cursor < end; cursor++) { - if (*cursor == '_') continue; - value = value * multiplier + (uint64_t) pm_integer_parse_digit(*cursor); - - if (value > UINT32_MAX) { - // If the integer is too large to fit into a single uint32_t, then - // we'll parse it as a big integer. - pm_integer_parse_big(integer, multiplier, start, end); - return; - } - } - - integer->value = (uint32_t) value; -} - -/** - * Compare two integers. This function returns -1 if the left integer is less - * than the right integer, 0 if they are equal, and 1 if the left integer is - * greater than the right integer. - */ -int -pm_integer_compare(const pm_integer_t *left, const pm_integer_t *right) { - if (left->negative != right->negative) return left->negative ? -1 : 1; - int negative = left->negative ? -1 : 1; - - if (left->values == NULL && right->values == NULL) { - if (left->value < right->value) return -1 * negative; - if (left->value > right->value) return 1 * negative; - return 0; - } - - if (left->values == NULL || left->length < right->length) return -1 * negative; - if (right->values == NULL || left->length > right->length) return 1 * negative; - - for (size_t index = 0; index < left->length; index++) { - size_t value_index = left->length - index - 1; - uint32_t left_value = left->values[value_index]; - uint32_t right_value = right->values[value_index]; - - if (left_value < right_value) return -1 * negative; - if (left_value > right_value) return 1 * negative; - } - - return 0; -} - -/** - * Reduce a ratio of integers to its simplest form. - */ -void pm_integers_reduce(pm_integer_t *numerator, pm_integer_t *denominator) { - // If either the numerator or denominator do not fit into a 32-bit integer, - // then this function is a no-op. In the future, we may consider reducing - // even the larger numbers, but for now we're going to keep it simple. - if ( - // If the numerator doesn't fit into a 32-bit integer, return early. - numerator->length != 0 || - // If the denominator doesn't fit into a 32-bit integer, return early. - denominator->length != 0 || - // If the numerator is 0, then return early. - numerator->value == 0 || - // If the denominator is 1, then return early. - denominator->value == 1 - ) return; - - // Find the greatest common divisor of the numerator and denominator. - uint32_t divisor = numerator->value; - uint32_t remainder = denominator->value; - - while (remainder != 0) { - uint32_t temporary = remainder; - remainder = divisor % remainder; - divisor = temporary; - } - - // Divide the numerator and denominator by the greatest common divisor. - numerator->value /= divisor; - denominator->value /= divisor; -} - -/** - * Convert an integer to a decimal string. - */ -PRISM_EXPORTED_FUNCTION void -pm_integer_string(pm_buffer_t *buffer, const pm_integer_t *integer) { - if (integer->negative) { - pm_buffer_append_byte(buffer, '-'); - } - - // If the integer fits into a single uint32_t, then we can just append the - // value directly to the buffer. - if (integer->values == NULL) { - pm_buffer_append_format(buffer, "%" PRIu32, integer->value); - return; - } - - // If the integer is two uint32_t values, then we can | them together and - // append the result to the buffer. - if (integer->length == 2) { - const uint64_t value = ((uint64_t) integer->values[0]) | ((uint64_t) integer->values[1] << 32); - pm_buffer_append_format(buffer, "%" PRIu64, value); - return; - } - - // Otherwise, first we'll convert the base from 1<<32 to 10**9. - pm_integer_t converted = { 0 }; - pm_integer_convert_base(&converted, integer, (uint64_t) 1 << 32, 1000000000); - - if (converted.values == NULL) { - pm_buffer_append_format(buffer, "%" PRIu32, converted.value); - pm_integer_free(&converted); - return; - } - - // Allocate a buffer that we'll copy the decimal digits into. - size_t digits_length = converted.length * 9; - char *digits = xcalloc(digits_length, sizeof(char)); - if (digits == NULL) return; - - // Pack bigdecimal to digits. - for (size_t value_index = 0; value_index < converted.length; value_index++) { - uint32_t value = converted.values[value_index]; - - for (size_t digit_index = 0; digit_index < 9; digit_index++) { - digits[digits_length - 9 * value_index - digit_index - 1] = (char) ('0' + value % 10); - value /= 10; - } - } - - size_t start_offset = 0; - while (start_offset < digits_length - 1 && digits[start_offset] == '0') start_offset++; - - // Finally, append the string to the buffer and free the digits. - pm_buffer_append_string(buffer, digits + start_offset, digits_length - start_offset); - xfree(digits); - pm_integer_free(&converted); -} - -/** - * Free the internal memory of an integer. This memory will only be allocated if - * the integer exceeds the size of a single uint32_t. - */ -PRISM_EXPORTED_FUNCTION void -pm_integer_free(pm_integer_t *integer) { - if (integer->values) { - xfree(integer->values); - } -} |
