mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-21 15:10:19 +00:00
AK: Move generalized internals of UFixedBigIntDivision to BigIntBase
We will reuse this in LibCrypto Co-Authored-By: Dan Klishch <danilklishch@gmail.com>
This commit is contained in:
parent
1af9fa1968
commit
877cfe1890
Notes:
sideshowbarker
2024-07-18 02:13:10 +09:00
Author: https://github.com/Hendiadyoin1 Commit: https://github.com/SerenityOS/serenity/commit/877cfe1890 Pull-request: https://github.com/SerenityOS/serenity/pull/23619 Issue: https://github.com/SerenityOS/serenity/issues/23575 Reviewed-by: https://github.com/ADKaster ✅ Reviewed-by: https://github.com/DanShaders ✅
2 changed files with 77 additions and 58 deletions
|
@ -147,6 +147,8 @@ requires(bit_size <= max_big_int_length * native_word_size) struct StaticStorage
|
||||||
{
|
{
|
||||||
return m_data;
|
return m_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr operator StorageSpan<NativeWord, is_signed>() { return { m_data, static_size }; }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct IntegerWrapper {
|
struct IntegerWrapper {
|
||||||
|
@ -267,6 +269,12 @@ ALWAYS_INLINE constexpr WordType sub_words(WordType word1, WordType word2, bool&
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename WordType>
|
||||||
|
ALWAYS_INLINE constexpr DoubleWord<WordType> wide_multiply(WordType word1, WordType word2)
|
||||||
|
{
|
||||||
|
return static_cast<DoubleWord<WordType>>(word1) * word2;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename WordType>
|
template<typename WordType>
|
||||||
constexpr DoubleWord<WordType> dword(WordType low, WordType high)
|
constexpr DoubleWord<WordType> dword(WordType low, WordType high)
|
||||||
{
|
{
|
||||||
|
@ -584,6 +592,74 @@ struct StorageOperations {
|
||||||
if (size2 < size && (sign1 ^ sign2))
|
if (size2 < size && (sign1 ^ sign2))
|
||||||
negate(result, result);
|
negate(result, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<bool restore_remainder = false>
|
||||||
|
static constexpr void div_mod_internal(
|
||||||
|
StorageSpan<WordType, false> dividend, StorageSpan<WordType, false> divisor,
|
||||||
|
StorageSpan<WordType, false> quotient, StorageSpan<WordType, false> remainder,
|
||||||
|
size_t dividend_len, size_t divisor_len)
|
||||||
|
{
|
||||||
|
// Knuth's algorithm D
|
||||||
|
// D1. Normalize
|
||||||
|
// FIXME: Investigate GCC producing bogus -Warray-bounds when dividing u128 by u32. This code
|
||||||
|
// should not be reachable at all in this case because fast paths above cover all cases
|
||||||
|
// when `operand2.size() == 1`.
|
||||||
|
AK_IGNORE_DIAGNOSTIC("-Warray-bounds", size_t shift = count_leading_zeroes(divisor[divisor_len - 1]);)
|
||||||
|
shift_left(dividend, shift, dividend);
|
||||||
|
shift_left(divisor, shift, divisor);
|
||||||
|
|
||||||
|
auto divisor_approx = divisor[divisor_len - 1];
|
||||||
|
|
||||||
|
for (size_t i = dividend_len + 1; i-- > divisor_len;) {
|
||||||
|
// D3. Calculate qhat
|
||||||
|
WordType qhat;
|
||||||
|
VERIFY(dividend[i] <= divisor_approx);
|
||||||
|
if (dividend[i] == divisor_approx) {
|
||||||
|
qhat = NumericLimits<WordType>::max();
|
||||||
|
} else {
|
||||||
|
WordType rhat;
|
||||||
|
qhat = div_mod_words(dividend[i - 1], dividend[i], divisor_approx, rhat);
|
||||||
|
|
||||||
|
auto is_qhat_too_large = [&] {
|
||||||
|
return wide_multiply(qhat, divisor[divisor_len - 2]) > dword(dividend[i - 2], rhat);
|
||||||
|
};
|
||||||
|
if (is_qhat_too_large()) {
|
||||||
|
--qhat;
|
||||||
|
bool carry = false;
|
||||||
|
rhat = add_words(rhat, divisor_approx, carry);
|
||||||
|
if (!carry && is_qhat_too_large())
|
||||||
|
--qhat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// D4. Multiply & subtract
|
||||||
|
WordType mul_carry = 0;
|
||||||
|
bool sub_carry = false;
|
||||||
|
for (size_t j = 0; j < divisor_len; ++j) {
|
||||||
|
auto mul_result = wide_multiply(qhat, divisor[j]) + mul_carry;
|
||||||
|
auto& output = dividend[i + j - divisor_len];
|
||||||
|
output = sub_words(output, static_cast<WordType>(mul_result), sub_carry);
|
||||||
|
mul_carry = mul_result >> word_size;
|
||||||
|
}
|
||||||
|
dividend[i] = sub_words(dividend[i], mul_carry, sub_carry);
|
||||||
|
|
||||||
|
if (sub_carry) {
|
||||||
|
// D6. Add back
|
||||||
|
auto dividend_part = StorageSpan<WordType, false> { dividend.slice(i - divisor_len, divisor_len + 1) };
|
||||||
|
auto overflow = add<false>(dividend_part, divisor, dividend_part);
|
||||||
|
VERIFY(overflow == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
quotient[i - divisor_len] = qhat - sub_carry;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = dividend_len - divisor_len + 1; i < quotient.size(); ++i)
|
||||||
|
quotient[i] = 0;
|
||||||
|
|
||||||
|
// D8. Unnormalize
|
||||||
|
if constexpr (restore_remainder)
|
||||||
|
shift_right(StorageSpan<WordType, false> { dividend.trim(remainder.size()) }, shift, remainder);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,64 +74,7 @@ constexpr void div_mod_internal(
|
||||||
Ops::copy(operand1, dividend);
|
Ops::copy(operand1, dividend);
|
||||||
auto divisor = operand2;
|
auto divisor = operand2;
|
||||||
|
|
||||||
// D1. Normalize
|
Ops::div_mod_internal<restore_remainder>(dividend, divisor, quotient, remainder, dividend_len, divisor_len);
|
||||||
// FIXME: Investigate GCC producing bogus -Warray-bounds when dividing u128 by u32. This code
|
|
||||||
// should not be reachable at all in this case because fast paths above cover all cases
|
|
||||||
// when `operand2.size() == 1`.
|
|
||||||
AK_IGNORE_DIAGNOSTIC("-Warray-bounds", size_t shift = count_leading_zeroes(divisor[divisor_len - 1]);)
|
|
||||||
Ops::shift_left(dividend, shift, dividend);
|
|
||||||
Ops::shift_left(divisor, shift, divisor);
|
|
||||||
|
|
||||||
auto divisor_approx = divisor[divisor_len - 1];
|
|
||||||
|
|
||||||
for (size_t i = dividend_len + 1; i-- > divisor_len;) {
|
|
||||||
// D3. Calculate qhat
|
|
||||||
NativeWord qhat;
|
|
||||||
VERIFY(dividend[i] <= divisor_approx);
|
|
||||||
if (dividend[i] == divisor_approx) {
|
|
||||||
qhat = max_native_word;
|
|
||||||
} else {
|
|
||||||
NativeWord rhat;
|
|
||||||
qhat = div_mod_words(dividend[i - 1], dividend[i], divisor_approx, rhat);
|
|
||||||
|
|
||||||
auto is_qhat_too_large = [&] {
|
|
||||||
return UFixedBigInt<native_word_size> { qhat }.wide_multiply(divisor[divisor_len - 2]) > UFixedBigInt<native_word_size * 2> { dividend[i - 2], rhat };
|
|
||||||
};
|
|
||||||
if (is_qhat_too_large()) {
|
|
||||||
--qhat;
|
|
||||||
bool carry = false;
|
|
||||||
rhat = add_words(rhat, divisor_approx, carry);
|
|
||||||
if (!carry && is_qhat_too_large())
|
|
||||||
--qhat;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// D4. Multiply & subtract
|
|
||||||
NativeWord mul_carry = 0;
|
|
||||||
bool sub_carry = false;
|
|
||||||
for (size_t j = 0; j < divisor_len; ++j) {
|
|
||||||
auto mul_result = UFixedBigInt<native_word_size> { qhat }.wide_multiply(divisor[j]) + mul_carry;
|
|
||||||
auto& output = dividend[i + j - divisor_len];
|
|
||||||
output = sub_words(output, mul_result.low(), sub_carry);
|
|
||||||
mul_carry = mul_result.high();
|
|
||||||
}
|
|
||||||
dividend[i] = sub_words(dividend[i], mul_carry, sub_carry);
|
|
||||||
|
|
||||||
if (sub_carry) {
|
|
||||||
// D6. Add back
|
|
||||||
auto dividend_part = UnsignedStorageSpan { dividend.data() + i - divisor_len, divisor_len + 1 };
|
|
||||||
VERIFY(Ops::add<false>(dividend_part, divisor, dividend_part));
|
|
||||||
}
|
|
||||||
|
|
||||||
quotient[i - divisor_len] = qhat - sub_carry;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = dividend_len - divisor_len + 1; i < quotient.size(); ++i)
|
|
||||||
quotient[i] = 0;
|
|
||||||
|
|
||||||
// D8. Unnormalize
|
|
||||||
if constexpr (restore_remainder)
|
|
||||||
Ops::shift_right(UnsignedStorageSpan { dividend.data(), remainder.size() }, shift, remainder);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue