From 9b49d19224bfb2531cd3ca2cccd2ad23f74661a6 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Tue, 1 Oct 2024 22:51:48 +0200 Subject: [PATCH] DString `reverse` and an initial BigInt implementation (untested), --- lib/std/core/dstring.c3 | 13 + lib/std/math/bigint.c3 | 1111 ++++++++++++++++++++++++++++++ releasenotes.md | 1 + test/unit/stdlib/core/dstring.c3 | 14 + test/unit/stdlib/math/bigint.c3 | 34 + 5 files changed, 1173 insertions(+) create mode 100644 lib/std/math/bigint.c3 create mode 100644 test/unit/stdlib/math/bigint.c3 diff --git a/lib/std/core/dstring.c3 b/lib/std/core/dstring.c3 index f369e9d28..f37cd609d 100644 --- a/lib/std/core/dstring.c3 +++ b/lib/std/core/dstring.c3 @@ -467,6 +467,19 @@ fn void! out_string_append_fn(void* data, char c) @private s.append_char(c); } +fn void DString.reverse(self) +{ + StringData *data = self.data(); + if (!data) return; + isz mid = data.len / 2; + for (isz i = 0; i < mid; i++) + { + char temp = data.chars[i]; + isz reverse_index = data.len - 1 - i; + data.chars[i] = data.chars[reverse_index]; + data.chars[reverse_index] = temp; + } +} fn StringData* DString.data(self) @inline @private { diff --git a/lib/std/math/bigint.c3 b/lib/std/math/bigint.c3 new file mode 100644 index 000000000..53a7ed925 --- /dev/null +++ b/lib/std/math/bigint.c3 @@ -0,0 +1,1111 @@ +/** + * Untested new big int implementation, missing unit tests. Etc + */ +module std::math::bigint; +import std::math::random; +import std::io; + +const MAX_LEN = 4096 * 2 / 32; + +const BigInt ZERO = { .len = 1 }; +const BigInt ONE = { .len = 1, .data[0] = 1 }; + +struct BigInt (Printable) +{ + uint[MAX_LEN] data; + uint len; +} + +fn BigInt from_int(int128 val) +{ + BigInt b @noinit; + b.init(val); + return b; +} + +fn BigInt* BigInt.init(&self, int128 value) +{ + self.data[..] = 0; + int128 tmp = value; + uint len = 0; + while (tmp != 0 && len < MAX_LEN) + { + self.data[len] = (uint)(tmp & 0xFFFFFFFF); + tmp >>= 32; + len++; + } + assert(value < 0 || tmp == 0 || !self.is_negative()); + assert(value > 0 || tmp == -1 || self.is_negative()); + self.len = len; + self.reduce_len(); + return self; +} + +fn BigInt* BigInt.init_with_u128(&self, uint128 value) +{ + self.data[..] = 0; + int128 tmp = value; + uint len = 0; + while (tmp != 0 && len < MAX_LEN) + { + self.data[len] = (uint)(tmp & 0xFFFFFFFF); + tmp >>= 32; + len++; + } + self.len = len; + assert(value == 0 || tmp == 0 || !self.is_negative()); + self.len = max(len, 1); + return self; +} + +/** + * @require values.len <= MAX_LEN + **/ +fn BigInt* BigInt.init_with_array(&self, uint[] values) +{ + self.data[..] = 0; + self.len = values.len; + + foreach_r(i, val : values) + { + values[values.len - 1 - i] = val; + } + while (self.len > 1 && self.data[self.len - 1] == 0) + { + self.len--; + } + return self; +} + +fn BigInt*! BigInt.init_string_radix(&self, String value, int radix) +{ + isz len = value.len; + isz limit = value[0] == '-' ? 1 : 0; + *self = ZERO; + BigInt multiplier = ONE; + BigInt radix_big = from_int(radix); + for (isz i = len - 1; i >= limit; i--) + { + int pos_val = value[i]; + switch (pos_val) + { + case '0'..'9': + pos_val -= '0'; + case 'A'..'Z': + pos_val -= 'A' + 10; + case 'a'..'z': + pos_val -= 'a' + 10; + default: + return NumberConversion.MALFORMED_INTEGER?; + } + if (pos_val >= radix) return NumberConversion.MALFORMED_INTEGER?; + if (limit == 1) pos_val = -pos_val; + self.set_add(multiplier.mult(from_int(pos_val))); + if (i - 1 >= limit) + { + multiplier.set_mult(radix_big); + } + } + switch + { + case limit && !self.is_negative(): + return NumberConversion.INTEGER_OVERFLOW?; + case !limit && self.is_negative(): + return NumberConversion.INTEGER_OVERFLOW?; + } + return self; +} + +fn bool BigInt.is_negative(&self) +{ + return self.data[MAX_LEN - 1] & 0x80000000 != 0; +} + +fn BigInt BigInt.add(self, BigInt other) +{ + self.set_add(other); + return self; +} + +fn void BigInt.set_add(&self, BigInt other) +{ + bool sign = self.is_negative(); + bool sign_arg = other.is_negative(); + + self.len = max(self.len, other.len); + + ulong carry = 0; + for (uint i = 0; i < self.len; i++) + { + ulong sum = (ulong)self.data[i] + (ulong)other.data[i] + carry; + carry = sum >> 32; + self.data[i] = (uint)(sum & 0xFFFFFFFF); + } + + if (carry != 0 && self.len < MAX_LEN) + { + self.data[self.len++] = (uint)carry; + } + + self.reduce_len(); + + assert(sign != sign_arg || sign == self.is_negative(), "Overflow in addition"); +} + +fn void BigInt.reduce_len(&self) @private +{ + self.len = max(find_length(&self.data, self.len), 1); +} + +macro uint find_length(uint* data, uint length) +{ + while (length > 1 && data[length - 1] == 0) + { + --length; + } + return length; +} + +fn void BigInt.set_mult(&self, BigInt bi2) +{ + *self = self.mult(bi2) @inline; +} + +fn BigInt BigInt.mult(&self, BigInt bi2) +{ + BigInt res = { .len = 1 }; + BigInt bi1 = *self; + + bool negative_sign = false; + + if (bi1.is_negative()) + { + bi1.negate(); + negative_sign = !negative_sign; + } + if (bi2.is_negative()) + { + bi2.negate(); + negative_sign = !negative_sign; + } + + // multiply the absolute values + for (uint i = 0; i < bi1.len; i++) + { + if (bi1.data[i] == 0) continue; + ulong mcarry = 0; + for (int j = 0, int k = i; j < bi2.len; j++, k++) + { + // k = i + j + + ulong bi1_val = (ulong)bi1.data[i]; + ulong bi2_val = (ulong)bi2.data[j]; + ulong res_val = (ulong)res.data[k]; + + ulong val = (bi1_val * bi2_val) + res_val + mcarry; + res.data[k] = (uint)(val & 0xFFFFFFFF); + mcarry = val >> 32; + } + + if (mcarry != 0) + { + res.data[i + bi2.len] = (uint)mcarry; + } + } + + res.len = min(bi1.len + bi2.len, (uint)MAX_LEN); + + res.reduce_len(); + + // overflow check (result is -ve) + assert(!res.is_negative(), "Multiplication overflow"); + + if (negative_sign) + { + res.negate(); + } + + return res; +} + +fn void BigInt.negate(&self) +{ + if (self.is_zero()) return; + + bool was_negative = self.is_negative(); + // 1's complement + for (uint i = 0; i < MAX_LEN; i++) + { + self.data[i] = (uint)~(self.data[i]); + } + // add one to result of 1's complement + ulong carry = 1; + int index = 0; + + while (carry != 0 && index < MAX_LEN) + { + ulong val = self.data[index]; + val++; + + self.data[index] = (uint)(val & 0xFFFFFFFF); + carry = val >> 32; + index++; + } + + assert(self.is_negative() != was_negative, "Overflow in negation"); + + self.len = MAX_LEN; + self.reduce_len(); +} + +macro bool BigInt.is_zero(&self) => self.len == 1 && self.data[0] == 0; + +fn BigInt BigInt.sub(self, BigInt other) +{ + self.set_sub(other); + return self; +} + +fn BigInt* BigInt.set_sub(&self, BigInt other) +{ + self.len = max(self.len, other.len); + + bool sign = self.is_negative(); + bool sign_arg = other.is_negative(); + + long carry_in = 0; + for (int i = 0; i < self.len; i++) + { + long diff = (long)self.data[i] - (long)other.data[i] - carry_in; + self.data[i] = (uint)(diff & 0xFFFFFFFF); + carry_in = diff < 0 ? 1 : 0; + } + + // roll over to negative + if (carry_in != 0) + { + for (uint i = self.len; i < MAX_LEN; i++) + { + self.data[i] = 0xFFFFFFFF; + } + self.len = MAX_LEN; + } + + self.reduce_len(); + + // overflow check + + assert(sign == sign_arg || sign == self.is_negative(), "Overflow in subtraction"); + return self; +} + +fn int BigInt.bitcount(&self) +{ + self.reduce_len(); + uint val = self.data[self.len - 1]; + uint mask = 0x80000000; + int bits = 32; + + while (bits > 0 && (val & mask) == 0) + { + bits--; + mask >>= 1; + } + bits += (self.len - 1) << 5; + return bits; +} + +fn BigInt BigInt.unary_minus(&self) +{ + if (self.is_zero()) return *self; + BigInt result = *self; + result.negate(); + return result; +} + + +macro void BigInt.set_div(&self, BigInt other) +{ + *self = self.div(other); +} + +fn BigInt BigInt.div(&self, BigInt other) +{ + BigInt bi1 = *self; + bool negate_answer = self.is_negative(); + + if (negate_answer) + { + bi1.negate(); + } + if (other.is_negative()) + { + negate_answer = !negate_answer; + other.negate(); + } + + if (bi1.less_than(other)) return ZERO; + + BigInt quotient = ZERO; + BigInt remainder = ZERO; + + if (other.len == 1) + { + bi1.single_byte_divide(&other, "ient, &remainder); + } + else + { + bi1.multi_byte_divide(&other, "ient, &remainder); + } + if (negate_answer) + { + quotient.negate(); + } + return quotient; +} + +fn void BigInt.set_mod(&self, BigInt bi2) +{ + *self = self.mod(bi2); +} + +fn BigInt BigInt.mod(&self, BigInt bi2) +{ + if (bi2.is_negative()) + { + bi2.negate(); + } + + bool negate_answer = self.is_negative(); + BigInt bi1 = *self; + if (negate_answer) + { + bi1.negate(); + } + + if (bi1.less_than(bi2)) + { + return *self; + } + + BigInt quotient = ZERO; + BigInt remainder = ZERO; + + if (bi2.len == 1) + { + bi1.single_byte_divide(&bi2, "ient, &remainder); + } + else + { + bi2.multi_byte_divide(&bi2, "ient, &remainder); + } + if (negate_answer) + { + remainder.negate(); + } + return remainder; +} + +fn BigInt BigInt.bit_negate(&self) +{ + BigInt result; + for (uint i = 0; i < MAX_LEN; i++) + { + result.data[i] = ~self.data[i]; + } + + result.len = MAX_LEN; + result.reduce_len(); + return result; +} + +fn BigInt BigInt.shr(self, int shift) +{ + self.set_shr(shift); + return self; +} + +fn void BigInt.set_shr(self, int shift) +{ + self.len = shift_right(&self.data, self.len, shift); +} + +fn BigInt BigInt.shl(self, int shift) +{ + self.set_shl(shift); + return self; +} + +macro bool BigInt.equals(&self, BigInt* &other) @safemacro +{ + if (self.len != other.len) return false; + return self.data[:self.len] == other.data[:self.len]; +} + +macro bool BigInt.greater_than(&self, BigInt* &other) @safemacro +{ + if (self.is_negative() && !other.is_negative()) return false; + if (!self.is_negative() && other.is_negative()) return true; + int pos; + // same sign + int len = max(self.len, other.len); + for (pos = len - 1; pos >= 0 && self.data[pos] == other.data[pos]; pos--); + return pos >= 0 && self.data[pos] > other.data[pos]; +} +macro bool BigInt.less_than(&self, BigInt* &other) @safemacro +{ + if (self.is_negative() && !other.is_negative()) return true; + if (!self.is_negative() && other.is_negative()) return false; + + // same sign + int len = max(self.len, other.len); + int pos; + for (pos = len - 1; pos >= 0 && self.data[pos] == other.data[pos]; pos--); + return pos >= 0 && self.data[pos] < other.data[pos]; +} + +fn bool BigInt.is_odd(&self) +{ + return (self.data[0] & 0x1) != 0; +} + + +fn bool BigInt.is_one(&self) +{ + return self.len == 1 && self.data[0] == 1; +} + + +macro bool BigInt.greater_or_equal(&self, BigInt* &other) @safemacro +{ + return self.greater_than(*other) || self.equals(*other); +} + +macro bool BigInt.less_or_equal(&self, BigInt* &other) @safemacro +{ + return self.less_than(*other) || self.equals(*other); +} + +fn BigInt BigInt.abs(&self) +{ + return self.is_negative() ? self.unary_minus() : *self; +} + +fn usz! BigInt.to_format(&self, Formatter* format) @dynamic +{ + @stack_mem(4100; Allocator mem) + { + return format.print(self.to_string_with_radix(10, mem)); + }; +} + +fn String BigInt.to_string(&self, Allocator allocator) @dynamic +{ + return self.to_string_with_radix(10, allocator); +} + +/** + * @require radix > 1 && radix <= 36 "Radix must be 2-36" + **/ +fn String BigInt.to_string_with_radix(&self, int radix, Allocator allocator) +{ + if (self.is_zero()) return "0".copy(allocator); + + const char[*] CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + @stack_mem(4100; Allocator mem) + { + BigInt a = *self; + DString str; + str.new_init(4096, allocator: mem); + bool negative = self.is_negative(); + if (negative) + { + a.negate(); + } + BigInt quotient = ZERO; + BigInt remainder = ZERO; + BigInt big_radix = from_int(radix); + + while (!a.is_zero()) + { + a.single_byte_divide(&big_radix, "ient, &remainder); + + if (remainder.data[0] < 10) + { + str.append((char)(remainder.data[0] + '0')); + } + else + { + str.append(CHARS[remainder.data[0] - 10]); + } + a = quotient; + } + if (negative) str.append("-"); + str.reverse(); + return str.copy_str(allocator); + }; +} + +/** + * @require !exp.is_negative() "Positive exponents only" + **/ +fn BigInt BigInt.mod_pow(&self, BigInt exp, BigInt mod) +{ + BigInt result_num = ONE; + + bool was_neg = self.is_negative(); + BigInt num = *self; + if (was_neg) + { + num.negate(); + } + + if (mod.is_negative()) + { + mod.negate(); + } + + num.set_mod(mod); + + // calculate constant = b^(2k) / m + BigInt constant = ZERO; + + uint i = mod.len << 1; + constant.data[i] = 0x00000001; + constant.len = i + 1; + + constant.set_div(mod); + int total_bits = exp.bitcount(); + int count = 0; + + // perform squaring and multiply exponentiation + for (int pos = 0; pos < exp.len; pos++) + { + uint mask = 0x01; + for (int index = 0; index < 32; index++) + { + if (exp.data[pos] & mask != 0) + { + result_num = barrett_reduction(result_num.mult(num), mod, constant); + } + + mask <<= 1; + + num = barrett_reduction(num.mult(num), mod, constant); + + if (num.len == 1 && num.data[0] == 1) + { + if (was_neg && (exp.data[0] & 0x1) != 0) + { + //odd exp + result_num.negate(); + } + return result_num; + } + count++; + if (count == total_bits) break; + } + } + + if (was_neg && exp.is_odd()) + { + //odd exp + result_num.negate(); + } + return result_num; +} + +/* + * Fast calculation of modular reduction using Barrett's reduction. + * Requires x < b^(2k), where b is the base. In this case, base is + * 2^32 (uint). + */ +fn BigInt barrett_reduction(BigInt x, BigInt n, BigInt constant) +{ + int k = n.len; + int k_plus_one = k + 1; + int k_minus_one = k - 1; + + BigInt q1 = ZERO; + // q1 = x / b^(k-1) + q1.len = x.len - k_minus_one; + if (q1.len == 0) + { + q1.len = 1; + } + else + { + q1.data[:q1.len] = x.data[k_minus_one:q1.len]; + } + + BigInt q2 = q1.mult(constant); + BigInt q3 = ZERO; + + // q3 = q2 / b^(k+1) + int length = q2.len - k_plus_one; + assert(length >= 0); + if (length) + { + q3.data[:length] = q2.data[k_plus_one:length]; + q3.len = length; + } + else + { + q3.len = 1; + } + + // r1 = x mod b^(k+1) + // i.e. keep the lowest (k+1) words + BigInt r1 = ZERO; + int length_to_copy = (x.len > k_plus_one) ? k_plus_one : x.len; + assert(length_to_copy >= 0); + r1.data[:length_to_copy] = x.data[0:length_to_copy]; + r1.len = length_to_copy; + + // r2 = (q3 * n) mod b^(k+1) + // partial multiplication of q3 and n + + BigInt r2 = ZERO; + for (int i = 0; i < q3.len; i++) + { + if (q3.data[i] == 0) continue; + + ulong mcarry = 0; + int t = i; + for (int j = 0; j < n.len && t < k_plus_one; j++, t++) + { + // t = i + j + ulong val = (ulong)q3.data[i] * n.data[j] + r2.data[t] + mcarry; + + r2.data[t] = (uint)(val & 0xFFFFFFFF); + mcarry = val >> 32; + } + + if (t < k_plus_one) + { + r2.data[t] = (uint)mcarry; + } + } + + r2.len = k_plus_one; + r2.reduce_len(); + + r1.set_sub(r2); + if (r1.is_negative()) + { + BigInt val = ZERO; + val.data[k_plus_one] = 0x00000001; + val.len = k_plus_one + 1; + r1.set_add(val); + } + + while (r1.greater_or_equal(n)) + { + r1.set_sub(n); + } + + return r1; +} + +fn BigInt BigInt.sqrt(&self) +{ + uint num_bits = self.bitcount(); + + num_bits = num_bits & 0x1 != 0 ? num_bits >> 1 + 1 : num_bits >> 1; + + uint byte_pos = num_bits >> 5; + int bit_pos = num_bits & 0x1F; + + uint mask; + BigInt result = ZERO; + + if (bit_pos == 0) + { + mask = 0x80000000; + } + else + { + mask = 1U << bit_pos; + byte_pos++; + } + result.len = byte_pos; + + for (int i = byte_pos - 1; i >= 0; i--) + { + while (mask != 0) + { + // guess + result.data[i] ^= mask; + + // undo the guess if its square is larger than this + if (result.mult(result).greater_than(*self)) + { + result.data[i] ^= mask; + } + mask >>= 1; + } + mask = 0x80000000; + } + return result; +} + +fn BigInt BigInt.bit_and(self, BigInt bi2) +{ + self.set_bit_and(bi2); + return self; +} + +fn void BigInt.set_bit_and(&self, BigInt bi2) +{ + uint len = max(self.len, bi2.len); + foreach (i, &ref : self.data[:len]) + { + *ref = *ref & bi2.data[i]; + } + self.len = MAX_LEN; + + self.reduce_len(); +} + +fn BigInt BigInt.bit_or(self, BigInt bi2) +{ + self.set_bit_or(bi2); + return self; +} + +fn void BigInt.set_bit_or(&self, BigInt bi2) +{ + uint len = max(self.len, bi2.len); + foreach (i, &ref : self.data[:len]) + { + *ref = *ref | bi2.data[i]; + } + self.len = MAX_LEN; + + self.reduce_len(); +} + +fn BigInt BigInt.bit_xor(self, BigInt bi2) +{ + self.set_bit_xor(bi2); + return self; +} + +fn void BigInt.set_bit_xor(&self, BigInt bi2) +{ + uint len = max(self.len, bi2.len); + foreach (i, &ref : self.data[:len]) + { + *ref = *ref ^ bi2.data[i]; + } + self.len = MAX_LEN; + + self.reduce_len(); +} + +fn void BigInt.set_shl(&self, int shift) +{ + self.len = shift_left(&self.data, self.len, shift); +} + +fn BigInt BigInt.gcd(&self, BigInt other) +{ + BigInt x = self.abs(); + BigInt y = other.abs(); + BigInt g = y; + while (!x.is_zero()) + { + g = x; + x = y.mod(x); + y = g; + } + return g; +} + +/** + * @require bits >> 5 < MAX_LEN "Required bits > maxlength" + **/ +fn void BigInt.randomize_bits(&self, Random random, int bits) +{ + int dwords = bits >> 5; + int rem_bits = bits & 0x1F; + + if (rem_bits != 0) + { + dwords++; + } + + for (int i = 0; i < dwords; i++) + { + self.data[i] = random.next_int(); + } + + for (int i = dwords; i < MAX_LEN; i++) + { + self.data[i] = 0; + } + + if (rem_bits != 0) + { + uint mask = (uint)(0x01 << (rem_bits - 1)); + self.data[dwords - 1] |= mask; + + mask = (uint)(0xFFFFFFFF >> (32 - rem_bits)); + self.data[dwords - 1] &= mask; + } + else + { + self.data[dwords - 1] |= 0x80000000; + } + + self.len = (int)dwords; + + if (self.len == 0) + { + self.len = 1; + } +} + +module std::math::bigint @private; + + +/** + * @param [&out] quotient + * @param [&in] bi2 + * @param [&out] remainder + **/ +fn void BigInt.single_byte_divide(&self, BigInt* bi2, BigInt* quotient, BigInt* remainder) +{ + uint[MAX_LEN] result; + int result_pos = 0; + + // copy dividend to reminder + *remainder = *self; + + while (remainder.len > 1 && remainder.data[remainder.len - 1] == 0) + { + remainder.len--; + } + + ulong divisor = bi2.data[0]; + int pos = remainder.len - 1; + ulong dividend = remainder.data[pos]; + + if (dividend >= divisor) + { + ulong q = dividend / divisor; + result[result_pos++] = (uint)q; + remainder.data[pos] = (uint)(dividend % divisor); + } + pos--; + + while (pos >= 0) + { + dividend = (ulong)remainder.data[pos + 1] << 32 + remainder.data[pos]; + ulong q = dividend / divisor; + result[result_pos++] = (uint)q; + + remainder.data[pos + 1] = 0; + remainder.data[pos--] = (uint)(dividend % divisor); + } + + quotient.len = result_pos; + uint j = 0; + for (int i = result_pos - 1; i >= 0; i--, j++) + { + quotient.data[j] = result[i]; + } + + quotient.data[j..] = 0; + quotient.reduce_len(); + remainder.reduce_len(); +} + +/** + * @param [&out] quotient + * @param [&in] other + * @param [&out] remainder + **/ +fn void BigInt.multi_byte_divide(&self, BigInt* other, BigInt* quotient, BigInt* remainder) +{ + uint[MAX_LEN] result; + uint[MAX_LEN] r; + uint[MAX_LEN] dividend_part; + + int remainder_len = self.len + 1; + + uint mask = 0x80000000; + uint val = other.data[other.len - 1]; + int shift = 0; + int result_pos = 0; + + while (mask != 0 && (val & mask) == 0) + { + shift++; + mask >>= 1; + } + + r[:self.len] = self.data[:self.len]; + + remainder_len = shift_left(&r, remainder_len, shift); + + BigInt bi2 = other.shl(shift); + + int j = remainder_len - bi2.len; + int pos = remainder_len - 1; + + ulong first_divisor_byte = bi2.data[bi2.len - 1]; + ulong second_divisor_byte = bi2.data[bi2.len - 2]; + + int divisor_len = bi2.len + 1; + + while (j > 0) + { + ulong dividend = (ulong)r[pos] << 32 + r[pos - 1]; + + ulong q_hat = dividend / first_divisor_byte; + ulong r_hat = dividend % first_divisor_byte; + + bool done = false; + while (!done) + { + done = true; + + if (q_hat == 0x10000000 || (q_hat * second_divisor_byte) > (r_hat << 32 + r[pos - 2])) + { + q_hat--; + r_hat += first_divisor_byte; + + if (r_hat < 0x10000000) done = false; + } + } + + for (int h = 0; h < divisor_len; h++) + { + dividend_part[h] = r[pos - h]; + } + + BigInt kk = { dividend_part, divisor_len }; + BigInt ss = bi2.mult(from_int(q_hat)); + + while (ss.greater_than(kk)) + { + q_hat--; + + ss.set_sub(bi2); + } + BigInt yy = kk.sub(ss); + + for (int h = 0; h < divisor_len; h++) + { + r[pos - h] = yy.data[bi2.len - h]; + } + + result[result_pos++] = (uint)q_hat; + + pos--; + j--; + + } + + quotient.len = result_pos; + uint y = 0; + + for (int x = quotient.len - 1; x >= 0; x--, y++) + { + quotient.data[y] = result[x]; + } + + for (; y < MAX_LEN; y++) + { + quotient.data[y] = 0; + } + + quotient.reduce_len(); + + remainder.len = shift_right(&r, remainder_len, shift); + + remainder.data[:remainder.len] = r[:remainder.len]; + + remainder.data[y..] = 0; +} + +fn int shift_left(uint* data, int len, int shift_val) @inline +{ + int shift_amount = 32; + int buf_len = len; + + while (buf_len > 1 && data[buf_len - 1] == 0) buf_len--; + + for (int count = shift_val; count > 0;) + { + if (count < shift_amount) shift_amount = count; + + ulong carry = 0; + for (int i = 0; i < buf_len; i++) + { + ulong val = (ulong)data[i] << shift_amount; + val |= carry; + + data[i] = (uint)(val & 0xFFFFFFFF); + carry = val >> 32; + } + + if (carry != 0) + { + if (buf_len + 1 <= len) + { + data[buf_len++] = (uint)carry; + } + } + count -= shift_amount; + } + return buf_len; +} + +fn int shift_right(uint* data, int len, int shift_val) @inline +{ + int shift_amount = 32; + int inv_shift = 0; + int buf_len = len; + + while (buf_len > 1 && data[buf_len - 1] == 0) + { + buf_len--; + } + + for (int count = shift_val; count > 0;) + { + if (count < shift_amount) + { + shift_amount = count; + inv_shift = 32 - shift_amount; + } + + ulong carry = 0; + for (int i = buf_len - 1; i >= 0; i--) + { + ulong val = (ulong)data[i] >> shift_amount; + val |= carry; + + carry = (ulong)data[i] << inv_shift; + data[i] = (uint)(val); + } + + count -= shift_amount; + } + return find_length(data, buf_len); +} + + + + + + + + + + + + + diff --git a/releasenotes.md b/releasenotes.md index eb25bcdb4..91840d54a 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -82,6 +82,7 @@ - Added MD5 and crypto::safe_compare. - Added generic HMAC. - Added generic PBKDF2 implementation. +- DString `reverse`. ## 0.6.2 Change list diff --git a/test/unit/stdlib/core/dstring.c3 b/test/unit/stdlib/core/dstring.c3 index d0b79ecd5..e9f9cb1eb 100644 --- a/test/unit/stdlib/core/dstring.c3 +++ b/test/unit/stdlib/core/dstring.c3 @@ -10,6 +10,20 @@ fn void test_replace() assert(hello.str_view() == "Hello world where are yoooo? Are yoooo here too?"); } +fn void test_reverse() +{ + DString a; + a.append("abcd"); + a.reverse(); + assert(a.str_view() == "dcba"); + a.reverse(); + assert(a.str_view() == "abcd"); + a.append("e"); + assert(a.str_view() == "abcde"); + a.reverse(); + assert(a.str_view() == "edcba"); +} + fn void test_delete() { { diff --git a/test/unit/stdlib/math/bigint.c3 b/test/unit/stdlib/math/bigint.c3 new file mode 100644 index 000000000..0c72614d9 --- /dev/null +++ b/test/unit/stdlib/math/bigint.c3 @@ -0,0 +1,34 @@ +module std::math::bigint @test; + +fn void test_plus() +{ + BigInt a = bigint::from_int(123); + BigInt b = bigint::from_int(234); + assert(a.add(b).equals(bigint::from_int(234 + 123))); + + a = bigint::from_int(12323400012311213314141414i128); + b = bigint::from_int(23400012311213314141414i128); + assert(a.add(b).equals(bigint::from_int(12323400012311213314141414i128 + 23400012311213314141414i128))); +} + +fn void test_minus() +{ + BigInt a = bigint::from_int(123); + BigInt b = bigint::from_int(234); + assert(a.sub(b).equals(bigint::from_int(123 - 234))); + + a = bigint::from_int(12323400012311213314141414i128); + b = bigint::from_int(23400012311213314141414i128); + assert(a.sub(b).equals(bigint::from_int(12323400012311213314141414i128 - 23400012311213314141414i128))); +} + +fn void test_init_string_radix() +{ + BigInt a; + a.init_string_radix("123", 10)!!; + assert(a.equals(bigint::from_int(123))); + a.init_string_radix("123", 8)!!; + assert(a.equals(bigint::from_int(0o123))); + a.init_string_radix("123", 16)!!; + assert(a.equals(bigint::from_int(0x123))); +} \ No newline at end of file