diff --git a/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp b/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp index 4dfbb30b1f2..2f1e4e5aa20 100644 --- a/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp +++ b/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp @@ -96,6 +96,21 @@ String UnsignedBigInteger::to_base10() const return builder.to_string(); } +void UnsignedBigInteger::set_to_0() +{ + m_words.clear_with_capacity(); + m_is_invalid = false; +} + +void UnsignedBigInteger::set_to(const UnsignedBigInteger& other) +{ + m_is_invalid = other.m_is_invalid; + m_words.clear_with_capacity(); + m_words.ensure_capacity(other.m_words.size()); + for (size_t i = 0; i < other.m_words.size(); ++i) + m_words.unchecked_append(other.m_words[i]); +} + size_t UnsignedBigInteger::trimmed_length() const { size_t num_leading_zeroes = 0; @@ -106,161 +121,61 @@ size_t UnsignedBigInteger::trimmed_length() const return length() - num_leading_zeroes; } -/** - * Complexity: O(N) where N is the number of words in the larger number - */ -UnsignedBigInteger UnsignedBigInteger::plus(const UnsignedBigInteger& other) const -{ - const UnsignedBigInteger* const longer = (length() > other.length()) ? this : &other; - const UnsignedBigInteger* const shorter = (longer == &other) ? this : &other; - UnsignedBigInteger result; - - u8 carry = 0; - - result.m_words.ensure_capacity(longer->length() + 1); - - for (size_t i = 0; i < shorter->length(); ++i) { - u32 word_addition_result = shorter->m_words[i] + longer->m_words[i]; - u8 carry_out = 0; - // if there was a carry, the result will be smaller than any of the operands - if (word_addition_result + carry < shorter->m_words[i]) { - carry_out = 1; - } - if (carry) { - word_addition_result++; - } - carry = carry_out; - result.m_words.unchecked_append(word_addition_result); - } - - for (size_t i = shorter->length(); i < longer->length(); ++i) { - u32 word_addition_result = longer->m_words[i] + carry; - - carry = 0; - if (word_addition_result < longer->m_words[i]) { - carry = 1; - } - result.m_words.unchecked_append(word_addition_result); - } - if (carry) { - result.m_words.unchecked_append(carry); - } - return result; -} - -/** - * Complexity: O(N) where N is the number of words in the larger number - */ -UnsignedBigInteger UnsignedBigInteger::minus(const UnsignedBigInteger& other) const +FLATTEN UnsignedBigInteger UnsignedBigInteger::plus(const UnsignedBigInteger& other) const { UnsignedBigInteger result; - if (*this < other) { - return UnsignedBigInteger::create_invalid(); - } - - u8 borrow = 0; - auto own_length = length(), other_length = other.length(); - - result.m_words.ensure_capacity(own_length); - - for (size_t i = 0; i < own_length; ++i) { - u32 other_word = (i < other_length) ? other.m_words[i] : 0; - i64 temp = static_cast(m_words[i]) - static_cast(other_word) - static_cast(borrow); - // If temp < 0, we had an underflow - borrow = (temp >= 0) ? 0 : 1; - if (temp < 0) { - temp += (UINT32_MAX + 1); - } - result.m_words.append(temp); - } - - // This assertion should not fail, because we verified that *this>=other at the beginning of the function - ASSERT(borrow == 0); + add_without_allocation(*this, other, result); return result; } -UnsignedBigInteger UnsignedBigInteger::shift_left(size_t num_bits) const +FLATTEN UnsignedBigInteger UnsignedBigInteger::minus(const UnsignedBigInteger& other) const { - // We can only do shift operations on individual words - // where the shift amount is <= size of word (32). - // But we do know how to shift by a multiple of word size (e.g 64=32*2) - // So we first shift the result by how many whole words fit in 'num_bits' - UnsignedBigInteger temp_result = shift_left_by_n_words(num_bits / UnsignedBigInteger::BITS_IN_WORD); + UnsignedBigInteger result; - // And now we shift by the leftover amount of bits - num_bits %= UnsignedBigInteger::BITS_IN_WORD; + subtract_without_allocation(*this, other, result); - UnsignedBigInteger result(temp_result); - - for (size_t i = 0; i < temp_result.length(); ++i) { - u32 current_word_of_temp_result = temp_result.shift_left_get_one_word(num_bits, i); - result.m_words[i] = current_word_of_temp_result; - } - - // Shifting the last word can produce a carry - u32 carry_word = temp_result.shift_left_get_one_word(num_bits, temp_result.length()); - if (carry_word != 0) { - result = result.plus(UnsignedBigInteger(carry_word).shift_left_by_n_words(temp_result.length())); - } return result; } -/** - * Complexity: O(N^2) where N is the number of words in the larger number - * Multiplcation method: - * An integer is equal to the sum of the powers of two - * according to the indexes of its 'on' bits. - * So to multiple x*y, we go over each '1' bit in x (say the i'th bit), - * and add y<= 0; --word_index) { - for (int bit_index = UnsignedBigInteger::BITS_IN_WORD - 1; bit_index >= 0; --bit_index) { + UnsignedBigInteger temp_shift_result; + UnsignedBigInteger temp_shift_plus; + UnsignedBigInteger temp_shift; + UnsignedBigInteger temp_minus; - const size_t shift_amount = word_index * UnsignedBigInteger::BITS_IN_WORD + bit_index; - UnsignedBigInteger divisor_shifted = divisor.shift_left(shift_amount); + divide_without_allocation(*this, divisor, temp_shift_result, temp_shift_plus, temp_shift, temp_minus, quotient, remainder); - UnsignedBigInteger temp_subtraction_result = leftover_dividend.minus(divisor_shifted); - if (!temp_subtraction_result.is_invalid()) { - leftover_dividend = temp_subtraction_result; - quotient.set_bit_inplace(shift_amount); - } - } - } - return UnsignedDivisionResult { quotient, leftover_dividend }; + return UnsignedDivisionResult { quotient, remainder }; } void UnsignedBigInteger::set_bit_inplace(size_t bit_index) @@ -320,39 +235,242 @@ bool UnsignedBigInteger::operator<(const UnsignedBigInteger& other) const return false; } -ALWAYS_INLINE UnsignedBigInteger UnsignedBigInteger::shift_left_by_n_words(const size_t number_of_words) const +/** + * Complexity: O(N) where N is the number of words in the larger number + */ +void UnsignedBigInteger::add_without_allocation( + const UnsignedBigInteger& left, + const UnsignedBigInteger& right, + UnsignedBigInteger& output) +{ + const UnsignedBigInteger* const longer = (left.length() > right.length()) ? &left : &right; + const UnsignedBigInteger* const shorter = (longer == &right) ? &left : &right; + + u8 carry = 0; + + output.set_to_0(); + output.m_words.ensure_capacity(longer->length() + 1); + + for (size_t i = 0; i < shorter->length(); ++i) { + u32 word_addition_result = shorter->m_words[i] + longer->m_words[i]; + u8 carry_out = 0; + // if there was a carry, the result will be smaller than any of the operands + if (word_addition_result + carry < shorter->m_words[i]) { + carry_out = 1; + } + if (carry) { + word_addition_result++; + } + carry = carry_out; + output.m_words.unchecked_append(word_addition_result); + } + + for (size_t i = shorter->length(); i < longer->length(); ++i) { + u32 word_addition_result = longer->m_words[i] + carry; + + carry = 0; + if (word_addition_result < longer->m_words[i]) { + carry = 1; + } + output.m_words.unchecked_append(word_addition_result); + } + if (carry) { + output.m_words.unchecked_append(carry); + } +} + +/** + * Complexity: O(N) where N is the number of words in the larger number + */ +void UnsignedBigInteger::subtract_without_allocation( + const UnsignedBigInteger& left, + const UnsignedBigInteger& right, + UnsignedBigInteger& output) +{ + if (left < right) { + output.invalidate(); + return; + } + + u8 borrow = 0; + auto own_length = left.length(); + auto other_length = right.length(); + + output.set_to_0(); + output.m_words.ensure_capacity(own_length); + + for (size_t i = 0; i < own_length; ++i) { + u32 other_word = (i < other_length) ? right.m_words[i] : 0; + i64 temp = static_cast(left.m_words[i]) - static_cast(other_word) - static_cast(borrow); + // If temp < 0, we had an underflow + borrow = (temp >= 0) ? 0 : 1; + if (temp < 0) { + temp += (UINT32_MAX + 1); + } + output.m_words.append(temp); + } + + // This assertion should not fail, because we verified that *this>=other at the beginning of the function + ASSERT(borrow == 0); +} + +/** + * Complexity : O(N + num_bits % 8) where N is the number of words in the number + * Shift method : + * Start by shifting by whole words in num_bits (by putting missing words at the start), + * then shift the number's words two by two by the remaining amount of bits. + */ +FLATTEN void UnsignedBigInteger::shift_left_without_allocation( + const UnsignedBigInteger& number, + size_t num_bits, + UnsignedBigInteger& temp_result, + UnsignedBigInteger& temp_plus, + UnsignedBigInteger& output) +{ + // We can only do shift operations on individual words + // where the shift amount is <= size of word (32). + // But we do know how to shift by a multiple of word size (e.g 64=32*2) + // So we first shift the result by how many whole words fit in 'num_bits' + shift_left_by_n_words(number, num_bits / UnsignedBigInteger::BITS_IN_WORD, temp_result); + + output.set_to(temp_result); + + // And now we shift by the leftover amount of bits + num_bits %= UnsignedBigInteger::BITS_IN_WORD; + + if (num_bits == 0) { + return; + } + + for (size_t i = 0; i < temp_result.length(); ++i) { + u32 current_word_of_temp_result = shift_left_get_one_word(temp_result, num_bits, i); + output.m_words[i] = current_word_of_temp_result; + } + + // Shifting the last word can produce a carry + u32 carry_word = shift_left_get_one_word(temp_result, num_bits, temp_result.length()); + if (carry_word != 0) { + + // output += (carry_word << temp_result.length()) + // FIXME : Using temp_plus this way to transform carry_word into a bigint is not + // efficient nor pretty. Maybe we should have an "add_with_shift" method ? + temp_plus.set_to_0(); + temp_plus.m_words.append(carry_word); + shift_left_by_n_words(temp_plus, temp_result.length(), temp_result); + add_without_allocation(output, temp_result, temp_plus); + output.set_to(temp_plus); + } +} + +/** + * Complexity: O(N^2) where N is the number of words in the larger number + * Multiplication method: + * An integer is equal to the sum of the powers of two + * according to the indexes of its 'on' bits. + * So to multiple x*y, we go over each '1' bit in x (say the i'th bit), + * and add y<= 0; --word_index) { + for (int bit_index = UnsignedBigInteger::BITS_IN_WORD - 1; bit_index >= 0; --bit_index) { + const size_t shift_amount = word_index * UnsignedBigInteger::BITS_IN_WORD + bit_index; + shift_left_without_allocation(denominator, shift_amount, temp_shift_result, temp_shift_plus, temp_shift); + + subtract_without_allocation(remainder, temp_shift, temp_minus); + if (!temp_minus.is_invalid()) { + remainder.set_to(temp_minus); + quotient.set_bit_inplace(shift_amount); + } + } + } +} + +ALWAYS_INLINE void UnsignedBigInteger::shift_left_by_n_words( + const UnsignedBigInteger& number, + const size_t number_of_words, + UnsignedBigInteger& output) { // shifting left by N words means just inserting N zeroes to the beginning of the words vector - UnsignedBigInteger result; - - result.m_words.ensure_capacity(number_of_words + length()); + output.set_to_0(); + output.m_words.ensure_capacity(number_of_words + number.length()); for (size_t i = 0; i < number_of_words; ++i) { - result.m_words.unchecked_append(0); + output.m_words.unchecked_append(0); } - for (size_t i = 0; i < length(); ++i) { - result.m_words.unchecked_append(m_words[i]); + for (size_t i = 0; i < number.length(); ++i) { + output.m_words.unchecked_append(number.m_words[i]); } - return result; } /** * Returns the word at a requested index in the result of a shift operation */ -ALWAYS_INLINE u32 UnsignedBigInteger::shift_left_get_one_word(const size_t num_bits, const size_t result_word_index) const +ALWAYS_INLINE u32 UnsignedBigInteger::shift_left_get_one_word( + const UnsignedBigInteger& number, + const size_t num_bits, + const size_t result_word_index) { // "<= length()" (rather than length() - 1) is intentional, // The result inedx of length() is used when calculating the carry word - ASSERT(result_word_index <= length()); + ASSERT(result_word_index <= number.length()); ASSERT(num_bits <= UnsignedBigInteger::BITS_IN_WORD); u32 result = 0; // we need to check for "num_bits != 0" since shifting right by 32 is apparently undefined behaviour! if (result_word_index > 0 && num_bits != 0) { - result += m_words[result_word_index - 1] >> (UnsignedBigInteger::BITS_IN_WORD - num_bits); + result += number.m_words[result_word_index - 1] >> (UnsignedBigInteger::BITS_IN_WORD - num_bits); } - if (result_word_index < length() && num_bits < 32) { - result += m_words[result_word_index] << num_bits; + if (result_word_index < number.length() && num_bits < 32) { + result += number.m_words[result_word_index] << num_bits; } return result; } diff --git a/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h b/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h index 0dd6be35640..72928c56168 100644 --- a/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h +++ b/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h @@ -64,6 +64,8 @@ public: const AK::Vector& words() const { return m_words; } + void set_to_0(); + void set_to(const UnsignedBigInteger& other); void invalidate() { m_is_invalid = true; } bool is_invalid() const { return m_is_invalid; } @@ -80,13 +82,19 @@ public: void set_bit_inplace(size_t bit_index); + static void add_without_allocation(const UnsignedBigInteger& left, const UnsignedBigInteger& right, UnsignedBigInteger& output); + static void subtract_without_allocation(const UnsignedBigInteger& left, const UnsignedBigInteger& right, UnsignedBigInteger& output); + static void shift_left_without_allocation(const UnsignedBigInteger& number, size_t bits_to_shift_by, UnsignedBigInteger& temp_result, UnsignedBigInteger& temp_plus, UnsignedBigInteger& output); + static void multiply_without_allocation(const UnsignedBigInteger& left, const UnsignedBigInteger& right, UnsignedBigInteger& temp_shift_result, UnsignedBigInteger& temp_shift_plus, UnsignedBigInteger& temp_shift, UnsignedBigInteger& temp_plus, UnsignedBigInteger& output); + static void divide_without_allocation(const UnsignedBigInteger& numerator, const UnsignedBigInteger& denominator, UnsignedBigInteger& temp_shift_result, UnsignedBigInteger& temp_shift_plus, UnsignedBigInteger& temp_shift, UnsignedBigInteger& temp_minus, UnsignedBigInteger& quotient, UnsignedBigInteger& remainder); + bool operator==(const UnsignedBigInteger& other) const; bool operator!=(const UnsignedBigInteger& other) const; bool operator<(const UnsignedBigInteger& other) const; private: - ALWAYS_INLINE UnsignedBigInteger shift_left_by_n_words(const size_t number_of_words) const; - ALWAYS_INLINE u32 shift_left_get_one_word(const size_t num_bits, const size_t result_word_index) const; + ALWAYS_INLINE static void shift_left_by_n_words(const UnsignedBigInteger& number, size_t number_of_words, UnsignedBigInteger& output); + ALWAYS_INLINE static u32 shift_left_get_one_word(const UnsignedBigInteger& number, size_t num_bits, size_t result_word_index); static constexpr size_t BITS_IN_WORD = 32; AK::Vector m_words;