diff options
author | Itamar <itamar8910@gmail.com> | 2020-04-08 19:04:36 +0300 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-05-02 12:24:10 +0200 |
commit | 2959c4a5e93cbdda3dd5bf44c6151fedb97a8d82 (patch) | |
tree | cb5a306dc97cd9809ed9a9f8289350258ef03fc0 /Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp | |
parent | 2843dce49847857e68c602dd37357c1c278d5cb9 (diff) | |
download | serenity-2959c4a5e93cbdda3dd5bf44c6151fedb97a8d82.zip |
LibCrypto: Add UnsignedBigInteger multiplication
Also added documentation for the runtime complexity of some operations.
Diffstat (limited to 'Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp')
-rw-r--r-- | Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp | 96 |
1 files changed, 94 insertions, 2 deletions
diff --git a/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp b/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp index 57c0bae64d..3b70ecdc2b 100644 --- a/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp +++ b/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp @@ -28,7 +28,10 @@ namespace Crypto { -UnsignedBigInteger UnsignedBigInteger::add(const UnsignedBigInteger& other) +/** + * Complexity: O(N) where N is the number of words in the larger number + */ +UnsignedBigInteger UnsignedBigInteger::add(const UnsignedBigInteger& other) const { const UnsignedBigInteger* const longer = (length() > other.length()) ? this : &other; const UnsignedBigInteger* const shorter = (longer == &other) ? this : &other; @@ -64,7 +67,10 @@ UnsignedBigInteger UnsignedBigInteger::add(const UnsignedBigInteger& other) return result; } -UnsignedBigInteger UnsignedBigInteger::sub(const UnsignedBigInteger& other) +/** + * Complexity: O(N) where N is the number of words in the larger number + */ +UnsignedBigInteger UnsignedBigInteger::sub(const UnsignedBigInteger& other) const { UnsignedBigInteger result; @@ -96,6 +102,92 @@ UnsignedBigInteger UnsignedBigInteger::sub(const UnsignedBigInteger& other) 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<<i to the result. + */ +UnsignedBigInteger UnsignedBigInteger::multiply(const UnsignedBigInteger& other) const +{ + UnsignedBigInteger result; + // iterate all bits + for (size_t word_index = 0; word_index < length(); ++word_index) { + for (size_t bit_index = 0; bit_index < UnsignedBigInteger::BITS_IN_WORD; ++bit_index) { + // If the bit is off - skip over it + if (!(m_words[word_index] & (1 << bit_index))) + continue; + + const size_t shift_amount = word_index * UnsignedBigInteger::BITS_IN_WORD + bit_index; + auto shift_result = other.shift_left(shift_amount); + result = result.add(shift_result); + } + } + return result; +} + +UnsignedBigInteger UnsignedBigInteger::shift_left(size_t num_bits) 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); + + // And now we shift by the leftover amount of bits + num_bits %= UnsignedBigInteger::BITS_IN_WORD; + + 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.add(UnsignedBigInteger(carry_word).shift_left_by_n_words(temp_result.length())); + } + return result; +} + +UnsignedBigInteger UnsignedBigInteger::shift_left_by_n_words(const size_t number_of_words) const +{ + // shifting left by N words means just inserting N zeroes to the beginning of the words vector + UnsignedBigInteger result; + for (size_t i = 0; i < number_of_words; ++i) { + result.m_words.append(0); + } + for (size_t i = 0; i < length(); ++i) { + result.m_words.append(m_words[i]); + } + return result; +} + +/** + * Returns the word at a requested index in the result of a shift operation + */ +u32 UnsignedBigInteger::shift_left_get_one_word(const size_t num_bits, const size_t result_word_index) const +{ + // "<= 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(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); + } + if (result_word_index < length() && num_bits < 32) { + result += m_words[result_word_index] << num_bits; + } + return result; +} + bool UnsignedBigInteger::operator==(const UnsignedBigInteger& other) const { if (trimmed_length() != other.trimmed_length()) { |