mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-21 12:05:15 +00:00
LibCrypto: Add UnsignedBigInteger division
The division operation returns both the quotient and the remainder.
This commit is contained in:
parent
2959c4a5e9
commit
0d2777752e
Notes:
sideshowbarker
2024-07-19 07:05:43 +09:00
Author: https://github.com/itamar8910 Commit: https://github.com/SerenityOS/serenity/commit/0d2777752e4 Pull-request: https://github.com/SerenityOS/serenity/pull/1661 Reviewed-by: https://github.com/Dexesttp Reviewed-by: https://github.com/alimpfard Reviewed-by: https://github.com/awesomekling
3 changed files with 113 additions and 6 deletions
|
@ -75,12 +75,12 @@ UnsignedBigInteger UnsignedBigInteger::sub(const UnsignedBigInteger& other) cons
|
|||
UnsignedBigInteger result;
|
||||
|
||||
if (*this < other) {
|
||||
dbg() << "WARNING: bigint subtraction creates a negative number!";
|
||||
return UnsignedBigInteger::create_invalid();
|
||||
}
|
||||
|
||||
u8 borrow = 0;
|
||||
for (size_t i = 0; i < other.length(); ++i) {
|
||||
// This assertion should not fail, because we verified that *this>other at the beginning of the function
|
||||
ASSERT(!(borrow == 1 && m_words[i] == 0));
|
||||
|
||||
if (m_words[i] - borrow < other.m_words[i]) {
|
||||
|
@ -128,6 +128,47 @@ UnsignedBigInteger UnsignedBigInteger::multiply(const UnsignedBigInteger& other)
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Complexity: O(N^2) where N is the number of words in the larger number
|
||||
* Division method:
|
||||
* We loop over the bits of the divisor, attempting to subtract divisor<<i from the dividend.
|
||||
* If the result is non-negative, it means that divisor*2^i "fits" in the dividend,
|
||||
* so we set the ith bit in the quotient and reduce divisor<<i from the dividend.
|
||||
* When we're done, what's left from the dividend is the remainder.
|
||||
*/
|
||||
UnsignedDivisionResult UnsignedBigInteger::divide(const UnsignedBigInteger& divisor) const
|
||||
{
|
||||
UnsignedBigInteger leftover_dividend(*this);
|
||||
UnsignedBigInteger quotient;
|
||||
|
||||
// iterate all bits
|
||||
for (int word_index = trimmed_length() - 1; word_index >= 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;
|
||||
UnsignedBigInteger divisor_shifted = divisor.shift_left(shift_amount);
|
||||
|
||||
UnsignedBigInteger temp_subtraction_result = leftover_dividend.sub(divisor_shifted);
|
||||
if (!temp_subtraction_result.is_invalid()) {
|
||||
leftover_dividend = temp_subtraction_result;
|
||||
quotient.set_bit_inplace(shift_amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
return UnsignedDivisionResult { quotient, leftover_dividend };
|
||||
}
|
||||
|
||||
void UnsignedBigInteger::set_bit_inplace(size_t bit_index)
|
||||
{
|
||||
const size_t word_index = bit_index / UnsignedBigInteger::BITS_IN_WORD;
|
||||
const size_t inner_word_index = bit_index % UnsignedBigInteger::BITS_IN_WORD;
|
||||
|
||||
for (size_t i = length(); i <= word_index; ++i) {
|
||||
m_words.append(0);
|
||||
}
|
||||
m_words[word_index] |= (1 << inner_word_index);
|
||||
}
|
||||
|
||||
UnsignedBigInteger UnsignedBigInteger::shift_left(size_t num_bits) const
|
||||
{
|
||||
// We can only do shift operations on individual words
|
||||
|
@ -213,12 +254,16 @@ bool UnsignedBigInteger::operator<(const UnsignedBigInteger& other) const
|
|||
return false;
|
||||
}
|
||||
|
||||
size_t length = trimmed_length();
|
||||
int length = trimmed_length();
|
||||
if (length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_words[length - 1] < other.m_words[length - 1];
|
||||
for (int i = length - 1; i >= 0; --i) {
|
||||
if (m_words[i] == other.m_words[i])
|
||||
continue;
|
||||
return m_words[i] < other.m_words[i];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t UnsignedBigInteger::trimmed_length() const
|
||||
|
@ -237,5 +282,4 @@ UnsignedBigInteger UnsignedBigInteger::create_invalid()
|
|||
invalid.invalidate();
|
||||
return invalid;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -30,6 +30,9 @@
|
|||
#include <AK/Vector.h>
|
||||
|
||||
namespace Crypto {
|
||||
|
||||
struct UnsignedDivisionResult;
|
||||
|
||||
class UnsignedBigInteger {
|
||||
public:
|
||||
UnsignedBigInteger(u32 x) { m_words.append(x); }
|
||||
|
@ -49,7 +52,10 @@ public:
|
|||
UnsignedBigInteger sub(const UnsignedBigInteger& other) const;
|
||||
UnsignedBigInteger multiply(const UnsignedBigInteger& other) const;
|
||||
UnsignedBigInteger shift_left(size_t num_bits) const;
|
||||
UnsignedBigInteger shift_left_by_n_words(const size_t number_of_words) const;
|
||||
|
||||
UnsignedDivisionResult divide(const UnsignedBigInteger& divisor) const;
|
||||
|
||||
void set_bit_inplace(size_t bit_index);
|
||||
|
||||
size_t length() const { return m_words.size(); }
|
||||
|
||||
|
@ -63,6 +69,7 @@ public:
|
|||
bool is_invalid() const { return m_is_invalid; }
|
||||
|
||||
private:
|
||||
UnsignedBigInteger shift_left_by_n_words(const size_t number_of_words) const;
|
||||
u32 shift_left_get_one_word(const size_t num_bits, const size_t result_word_index) const;
|
||||
|
||||
static constexpr size_t BITS_IN_WORD = 32;
|
||||
|
@ -72,6 +79,11 @@ private:
|
|||
bool m_is_invalid { false };
|
||||
};
|
||||
|
||||
struct UnsignedDivisionResult {
|
||||
Crypto::UnsignedBigInteger quotient;
|
||||
Crypto::UnsignedBigInteger remainder;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
inline const LogStream& operator<<(const LogStream& stream, const Crypto::UnsignedBigInteger value)
|
||||
|
|
|
@ -306,6 +306,7 @@ void bigint_test_fibo500();
|
|||
void bigint_addition_edgecases();
|
||||
void bigint_subtraction();
|
||||
void bigint_multiplication();
|
||||
void bigint_division();
|
||||
|
||||
int aes_cbc_tests()
|
||||
{
|
||||
|
@ -801,6 +802,7 @@ int bigint_tests()
|
|||
bigint_addition_edgecases();
|
||||
bigint_subtraction();
|
||||
bigint_multiplication();
|
||||
bigint_division();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -905,6 +907,14 @@ void bigint_subtraction()
|
|||
FAIL(Incorrect Result);
|
||||
}
|
||||
}
|
||||
{
|
||||
I_TEST((BigInteger | Subtraction with large numbers 2));
|
||||
Crypto::UnsignedBigInteger num1(Vector<u32> { 1483061863, 446680044, 1123294122, 191895498, 3347106536, 16, 0, 0, 0 });
|
||||
Crypto::UnsignedBigInteger num2(Vector<u32> { 4196414175, 1117247942, 1123294122, 191895498, 3347106536, 16 });
|
||||
Crypto::UnsignedBigInteger result = num1.sub(num2);
|
||||
// this test only verifies that we don't crash on an assertion
|
||||
PASS;
|
||||
}
|
||||
}
|
||||
|
||||
void bigint_multiplication()
|
||||
|
@ -944,3 +954,44 @@ void bigint_multiplication()
|
|||
}
|
||||
}
|
||||
}
|
||||
void bigint_division()
|
||||
{
|
||||
{
|
||||
I_TEST((BigInteger | Simple Division));
|
||||
Crypto::UnsignedBigInteger num1(27194);
|
||||
Crypto::UnsignedBigInteger num2(251);
|
||||
auto result = num1.divide(num2);
|
||||
Crypto::UnsignedDivisionResult expected = { Crypto::UnsignedBigInteger(108), Crypto::UnsignedBigInteger(86) };
|
||||
if (result.quotient == expected.quotient && result.remainder == expected.remainder) {
|
||||
PASS;
|
||||
} else {
|
||||
FAIL(Incorrect Result);
|
||||
}
|
||||
}
|
||||
{
|
||||
I_TEST((BigInteger | Division with big numbers));
|
||||
Crypto::UnsignedBigInteger num1 = bigint_fibonacci(386);
|
||||
Crypto::UnsignedBigInteger num2 = bigint_fibonacci(238);
|
||||
auto result = num1.divide(num2);
|
||||
Crypto::UnsignedDivisionResult expected = {
|
||||
Crypto::UnsignedBigInteger(Vector<u32> { 2300984486, 2637503534, 2022805584, 107 }),
|
||||
Crypto::UnsignedBigInteger(Vector<u32> { 1483061863, 446680044, 1123294122, 191895498, 3347106536, 16, 0, 0, 0 })
|
||||
};
|
||||
if (result.quotient == expected.quotient && result.remainder == expected.remainder) {
|
||||
PASS;
|
||||
} else {
|
||||
FAIL(Incorrect Result);
|
||||
}
|
||||
}
|
||||
{
|
||||
I_TEST((BigInteger | Combined test));
|
||||
auto num1 = bigint_fibonacci(497);
|
||||
auto num2 = bigint_fibonacci(238);
|
||||
auto div_result = num1.divide(num2);
|
||||
if (div_result.quotient.multiply(num2).add(div_result.remainder) == num1) {
|
||||
PASS;
|
||||
} else {
|
||||
FAIL(Incorrect Result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue