1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-18 15:05:09 +00:00

LibCrypto: Added BigInteger 'division by u16' operator

This commit is contained in:
DexesTTP 2020-05-03 10:59:00 +02:00 committed by Andreas Kling
parent 0efd58bf6d
commit 8aeccf4f02
3 changed files with 67 additions and 13 deletions

View file

@ -52,14 +52,17 @@ UnsignedBigInteger UnsignedBigInteger::import_data(const u8* ptr, size_t length)
size_t UnsignedBigInteger::export_data(AK::ByteBuffer& data)
{
UnsignedBigInteger copy { *this };
UnsignedBigInteger quotient;
UnsignedBigInteger remainder;
size_t size = trimmed_length() * sizeof(u32);
size_t i = 0;
for (; i < size; ++i) {
if (copy.length() == 0)
if (copy.trimmed_length() == 0)
break;
data[size - i - 1] = copy.m_words[0] & 0xff;
copy = copy.divided_by(256).quotient;
divide_u16_without_allocation(copy, 256, quotient, remainder);
copy.set_to(quotient);
}
return i;
}
@ -79,12 +82,14 @@ String UnsignedBigInteger::to_base10() const
{
StringBuilder builder;
UnsignedBigInteger temp(*this);
UnsignedBigInteger quotient;
UnsignedBigInteger remainder;
while (temp != UnsignedBigInteger { 0 }) {
auto div_result = temp.divided_by({ 10 });
ASSERT(div_result.remainder.words()[0] < 10);
builder.append(static_cast<char>(div_result.remainder.words()[0] + '0'));
temp = div_result.quotient;
divide_u16_without_allocation(temp, 10, quotient, remainder);
ASSERT(remainder.words()[0] < 10);
builder.append(static_cast<char>(remainder.words()[0] + '0'));
temp.set_to(quotient);
}
auto reversed_string = builder.to_string();
@ -102,6 +107,13 @@ void UnsignedBigInteger::set_to_0()
m_is_invalid = false;
}
void UnsignedBigInteger::set_to(u32 other)
{
m_is_invalid = false;
m_words.clear_with_capacity();
m_words.append(other);
}
void UnsignedBigInteger::set_to(const UnsignedBigInteger& other)
{
m_is_invalid = other.m_is_invalid;
@ -168,6 +180,13 @@ FLATTEN UnsignedDivisionResult UnsignedBigInteger::divided_by(const UnsignedBigI
UnsignedBigInteger quotient;
UnsignedBigInteger remainder;
// If we actually have a u16-compatible divisor, short-circuit to the
// less computationally-intensive "divide_u16_without_allocation" method.
if (divisor.trimmed_length() == 1 && divisor.m_words[0] < (1 << 16)) {
divide_u16_without_allocation(*this, divisor.m_words[0], quotient, remainder);
return UnsignedDivisionResult { quotient, remainder };
}
UnsignedBigInteger temp_shift_result;
UnsignedBigInteger temp_shift_plus;
UnsignedBigInteger temp_shift;
@ -434,6 +453,41 @@ FLATTEN void UnsignedBigInteger::divide_without_allocation(
}
}
/**
* Complexity : O(N) where N is the number of digits in the numerator
* Division method :
* Starting from the most significant one, for each half-word of the numerator, combine it
* with the existing remainder if any, divide the combined number as a u32 operation and
* update the quotient / remainder as needed.
*/
FLATTEN void UnsignedBigInteger::divide_u16_without_allocation(
const UnsignedBigInteger& numerator,
u32 denominator,
UnsignedBigInteger& quotient,
UnsignedBigInteger& remainder)
{
ASSERT(denominator < (1 << 16));
u32 remainder_word = 0;
auto numerator_length = numerator.trimmed_length();
quotient.set_to_0();
quotient.m_words.resize(numerator_length);
for (int word_index = numerator_length - 1; word_index >= 0; --word_index) {
auto word_high = numerator.m_words[word_index] >> 16;
auto word_low = numerator.m_words[word_index] & ((1 << 16) - 1);
auto number_to_divide_high = (remainder_word << 16) | word_high;
auto quotient_high = number_to_divide_high / denominator;
remainder_word = number_to_divide_high % denominator;
auto number_to_divide_low = remainder_word << 16 | word_low;
auto quotient_low = number_to_divide_low / denominator;
remainder_word = number_to_divide_low % denominator;
quotient.m_words[word_index] = (quotient_high << 16) | quotient_low;
}
remainder.set_to(remainder_word);
}
ALWAYS_INLINE void UnsignedBigInteger::shift_left_by_n_words(
const UnsignedBigInteger& number,
const size_t number_of_words,