mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-27 04:37:22 +00:00
LibCrypto+LibJS: Move Power
to method of {Unsigned,Signed}BigInteger
Having it as a method instead of a free function is necessary for the next commits and generally allows for optimizations that require deeper access into the `UnsignedBigInteger` / `SignedBigInteger`. Also restrict the exponent to 32 bits to avoid huge memory allocations.
This commit is contained in:
parent
a952d000be
commit
5a4cfd05d0
Notes:
github-actions[bot]
2025-05-23 09:58:36 +00:00
Author: https://github.com/devgianlu
Commit: 5a4cfd05d0
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4507
Reviewed-by: https://github.com/gmta ✅
7 changed files with 53 additions and 46 deletions
|
@ -5,12 +5,11 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "BigFraction.h"
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Math.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <LibCrypto/BigFraction/BigFraction.h>
|
||||
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
|
||||
#include <LibCrypto/NumberTheory/ModularFunctions.h>
|
||||
|
||||
namespace Crypto {
|
||||
|
||||
|
@ -36,12 +35,11 @@ ErrorOr<BigFraction> BigFraction::from_string(StringView sv)
|
|||
|
||||
auto integer_part = TRY(SignedBigInteger::from_base(10, integer_part_view));
|
||||
auto fractional_part = TRY(SignedBigInteger::from_base(10, fraction_part_view));
|
||||
auto fraction_length = UnsignedBigInteger(static_cast<u64>(fraction_part_view.length()));
|
||||
|
||||
if (!sv.is_empty() && sv[0] == '-')
|
||||
fractional_part.negate();
|
||||
|
||||
return BigFraction(move(integer_part)) + BigFraction(move(fractional_part), NumberTheory::Power("10"_bigint, move(fraction_length)));
|
||||
return BigFraction(move(integer_part)) + BigFraction(move(fractional_part), "10"_bigint.pow(fraction_part_view.length()));
|
||||
}
|
||||
|
||||
BigFraction BigFraction::operator+(BigFraction const& rhs) const
|
||||
|
@ -129,7 +127,7 @@ BigFraction::BigFraction(double d)
|
|||
d -= digit * AK::pow(10.0, (double)current_pow);
|
||||
if (current_pow < 0) {
|
||||
++decimal_places;
|
||||
m_denominator.set_to(NumberTheory::Power("10"_bigint, UnsignedBigInteger { decimal_places }));
|
||||
m_denominator.set_to("10"_bigint.pow(decimal_places));
|
||||
}
|
||||
current_pow -= 1;
|
||||
}
|
||||
|
@ -205,7 +203,7 @@ BigFraction BigFraction::rounded(unsigned rounding_threshold) const
|
|||
auto res = m_numerator.divided_by(m_denominator);
|
||||
BigFraction result { move(res.quotient) };
|
||||
|
||||
auto const needed_power = NumberTheory::Power("10"_bigint, UnsignedBigInteger { rounding_threshold });
|
||||
auto const needed_power = "10"_bigint.pow(rounding_threshold);
|
||||
// We get one more digit to do proper rounding
|
||||
auto const fractional_value = res.remainder.multiplied_by(needed_power.multiplied_by("10"_bigint)).divided_by(m_denominator).quotient;
|
||||
|
||||
|
|
|
@ -314,6 +314,26 @@ FLATTEN SignedDivisionResult SignedBigInteger::divided_by(SignedBigInteger const
|
|||
};
|
||||
}
|
||||
|
||||
FLATTEN SignedBigInteger SignedBigInteger::pow(u32 exponent) const
|
||||
{
|
||||
UnsignedBigInteger ep { exponent };
|
||||
SignedBigInteger base { *this };
|
||||
SignedBigInteger exp { 1 };
|
||||
|
||||
while (!(ep < 1)) {
|
||||
if (ep.words()[0] % 2 == 1)
|
||||
exp.set_to(exp.multiplied_by(base));
|
||||
|
||||
// ep = ep / 2;
|
||||
ep.set_to(ep.shift_right(1));
|
||||
|
||||
// base = base * base
|
||||
base.set_to(base.multiplied_by(base));
|
||||
}
|
||||
|
||||
return exp;
|
||||
}
|
||||
|
||||
FLATTEN SignedBigInteger SignedBigInteger::negated_value() const
|
||||
{
|
||||
auto result { *this };
|
||||
|
|
|
@ -106,6 +106,7 @@ public:
|
|||
[[nodiscard]] SignedBigInteger shift_right(size_t num_bits) const;
|
||||
[[nodiscard]] SignedBigInteger multiplied_by(SignedBigInteger const& other) const;
|
||||
[[nodiscard]] SignedDivisionResult divided_by(SignedBigInteger const& divisor) const;
|
||||
[[nodiscard]] SignedBigInteger pow(u32 exponent) const;
|
||||
|
||||
[[nodiscard]] ErrorOr<SignedBigInteger> mod_power_of_two(size_t power_of_two) const;
|
||||
[[nodiscard]] ErrorOr<SignedBigInteger> try_shift_left(size_t num_bits) const;
|
||||
|
|
|
@ -547,6 +547,26 @@ FLATTEN UnsignedDivisionResult UnsignedBigInteger::divided_by(UnsignedBigInteger
|
|||
return UnsignedDivisionResult { quotient, remainder };
|
||||
}
|
||||
|
||||
FLATTEN UnsignedBigInteger UnsignedBigInteger::pow(u32 exponent) const
|
||||
{
|
||||
UnsignedBigInteger ep { exponent };
|
||||
UnsignedBigInteger base { *this };
|
||||
UnsignedBigInteger exp { 1 };
|
||||
|
||||
while (!(ep < 1 )) {
|
||||
if (ep.words()[0] % 2 == 1)
|
||||
exp.set_to(exp.multiplied_by(base));
|
||||
|
||||
// ep = ep / 2;
|
||||
ep.set_to(ep.shift_right(1));
|
||||
|
||||
// base = base * base
|
||||
base.set_to(base.multiplied_by(base));
|
||||
}
|
||||
|
||||
return exp;
|
||||
}
|
||||
|
||||
FLATTEN UnsignedBigInteger UnsignedBigInteger::gcd(UnsignedBigInteger const& other) const
|
||||
{
|
||||
UnsignedBigInteger temp_a { *this };
|
||||
|
|
|
@ -109,6 +109,7 @@ public:
|
|||
[[nodiscard]] UnsignedBigInteger as_n_bits(size_t n) const;
|
||||
[[nodiscard]] UnsignedBigInteger multiplied_by(UnsignedBigInteger const& other) const;
|
||||
[[nodiscard]] UnsignedDivisionResult divided_by(UnsignedBigInteger const& divisor) const;
|
||||
[[nodiscard]] UnsignedBigInteger pow(u32 exponent) const;
|
||||
[[nodiscard]] UnsignedBigInteger gcd(UnsignedBigInteger const& other) const;
|
||||
|
||||
[[nodiscard]] ErrorOr<UnsignedBigInteger> try_bitwise_not_fill_to_one_based_index(size_t) const;
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
|
||||
|
||||
namespace Crypto::NumberTheory {
|
||||
|
||||
// Note: This function _will_ generate extremely huge numbers, and in doing so,
|
||||
// it will allocate and free a lot of memory!
|
||||
// Please use |ModularPower| if your use-case is modexp.
|
||||
template<typename IntegerType>
|
||||
static IntegerType Power(IntegerType const& b, IntegerType const& e)
|
||||
{
|
||||
IntegerType ep { e };
|
||||
IntegerType base { b };
|
||||
IntegerType exp { 1 };
|
||||
|
||||
while (!(ep < IntegerType { 1 })) {
|
||||
if (ep.words()[0] % 2 == 1)
|
||||
exp.set_to(exp.multiplied_by(base));
|
||||
|
||||
// ep = ep / 2;
|
||||
ep.set_to(ep.shift_right(1));
|
||||
|
||||
// base = base * base
|
||||
base.set_to(base.multiplied_by(base));
|
||||
}
|
||||
|
||||
return exp;
|
||||
}
|
||||
|
||||
}
|
|
@ -15,7 +15,6 @@
|
|||
#include <AK/StringFloatingPointConversions.h>
|
||||
#include <AK/Utf8View.h>
|
||||
#include <LibCrypto/BigInt/SignedBigInteger.h>
|
||||
#include <LibCrypto/NumberTheory/ModularFunctions.h>
|
||||
#include <LibJS/Runtime/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Accessor.h>
|
||||
#include <LibJS/Runtime/Array.h>
|
||||
|
@ -1599,7 +1598,7 @@ ThrowCompletionOr<Value> left_shift(VM& vm, Value lhs, Value rhs)
|
|||
return vm.throw_completion<RangeError>(ErrorType::BigIntSizeExceeded);
|
||||
|
||||
// 6.1.6.2.9 BigInt::leftShift ( x, y ), https://tc39.es/ecma262/#sec-numeric-types-bigint-leftShift
|
||||
auto multiplier_divisor = Crypto::SignedBigInteger { Crypto::NumberTheory::Power(Crypto::UnsignedBigInteger(2), rhs_bigint) };
|
||||
auto multiplier_divisor = Crypto::SignedBigInteger { Crypto::UnsignedBigInteger(2).pow(rhs_bigint.to_u64()) };
|
||||
|
||||
// 1. If y < 0ℤ, then
|
||||
if (rhs_numeric.as_bigint().big_integer().is_negative()) {
|
||||
|
@ -2075,9 +2074,14 @@ ThrowCompletionOr<Value> exp(VM& vm, Value lhs, Value rhs)
|
|||
// 1. If exponent < 0ℤ, throw a RangeError exception.
|
||||
if (exponent.is_negative())
|
||||
return vm.throw_completion<RangeError>(ErrorType::NegativeExponent);
|
||||
|
||||
// AD-HOC: Prevent allocating huge amounts of memory.
|
||||
if (exponent.unsigned_value().byte_length() > sizeof(u32))
|
||||
return vm.throw_completion<RangeError>(ErrorType::BigIntSizeExceeded);
|
||||
|
||||
// 2. If base is 0ℤ and exponent is 0ℤ, return 1ℤ.
|
||||
// 3. Return the BigInt value that represents ℝ(base) raised to the power ℝ(exponent).
|
||||
return BigInt::create(vm, Crypto::NumberTheory::Power(base, exponent));
|
||||
return BigInt::create(vm, base.pow(exponent.to_u64()));
|
||||
}
|
||||
return vm.throw_completion<TypeError>(ErrorType::BigIntBadOperatorOtherType, "exponentiation");
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue