Skip to content

Commit

Permalink
fix: reduce big int overflow (#101)
Browse files Browse the repository at this point in the history
* Refactor sqrt function and update dependencies

Replaced custom square root logic with num-integer's built-in `sqrt` method for simplicity and efficiency. Updated `fastnum` features and added `num-integer` as a new dependency in Cargo.toml. Adjusted `Token` macros to use fully qualified `alloy_primitives::Address`.

* Optimize `Fraction` arithmetic with GCD normalization

Refactored Fraction operations to include GCD-based normalization, improving efficiency and consistency. Introduced `bnum` and `num_integer` dependencies for better large number handling and casting. Updated arithmetic methods to ensure reduced fractions and avoid overflow by leveraging `I1024` and casting.

* Add `bnum` and `num_integer` to library exports

This change adds `bnum` and `num_integer::Integer` to the library's exports, making them available for external use. These additions enhance the library's functionality and compatibility with numeric operations and utilities.
  • Loading branch information
shuhuiluo authored Jan 16, 2025
1 parent 31ee3c2 commit e076788
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 31 deletions.
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ exclude = [".github", ".gitignore", "rustfmt.toml"]

[dependencies]
alloy-primitives = { version = ">=0.8.5", features = ["map-fxhash"] }
bnum = "0.12.0"
derive_more = { version = "1.0.0", features = ["deref"] }
eth_checksum = { version = "0.1.2", optional = true }
fastnum = { version = "0.1.9", default-features = false, features = ["libm"] }
fastnum = { version = "0.1.9", default-features = false, features = ["libm", "numtraits"] }
lazy_static = "1.5"
num-integer = "0.1.46"
regex = { version = "1.11", optional = true }
thiserror = { version = "2", default-features = false }

Expand Down
41 changes: 30 additions & 11 deletions src/entities/fractions/fraction.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use crate::prelude::*;
use alloc::string::ToString;
use bnum::cast::CastFrom;
use core::{
cmp::Ordering,
hash::{Hash, Hasher},
ops::{Add, Div, Mul, Sub},
};
use derive_more::Deref;
use fastnum::i512;
use fastnum::{i512, I1024};
use num_integer::Integer;

/// Struct representing a fraction with metadata
#[derive(Clone, Debug, Deref)]
Expand Down Expand Up @@ -194,7 +196,8 @@ impl<M: PartialEq> PartialEq for FractionLike<M> {
/// Checks if the current fraction is equal to another fraction
#[inline]
fn eq(&self, other: &Self) -> bool {
self.numerator * other.denominator == other.numerator * self.denominator
I1024::cast_from(self.numerator) * I1024::cast_from(other.denominator)
== I1024::cast_from(other.numerator) * I1024::cast_from(self.denominator)
&& self.meta == other.meta
}
}
Expand All @@ -214,7 +217,8 @@ impl<M: Hash> Hash for FractionLike<M> {
impl<M: PartialEq> Ord for FractionLike<M> {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
(self.numerator * other.denominator).cmp(&(other.numerator * self.denominator))
(I1024::cast_from(self.numerator) * I1024::cast_from(other.denominator))
.cmp(&(I1024::cast_from(other.numerator) * I1024::cast_from(self.denominator)))
}
}

Expand All @@ -233,15 +237,20 @@ macro_rules! impl_add_sub {
#[inline]
fn $method(self, other: $Rhs) -> Self::Output {
if self.denominator == other.denominator {
let numerator = self.numerator $op other.numerator;
let gcd = numerator.gcd(&self.denominator);
FractionBase::new(
self.numerator $op other.numerator,
self.denominator,
numerator / gcd,
self.denominator / gcd,
self.meta,
)
} else {
let numerator = I1024::cast_from(self.numerator) * I1024::cast_from(other.denominator) $op I1024::cast_from(other.numerator) * I1024::cast_from(self.denominator);
let denominator = I1024::cast_from(self.denominator) * I1024::cast_from(other.denominator);
let gcd = numerator.gcd(&denominator);
FractionBase::new(
self.numerator * other.denominator $op other.numerator * self.denominator,
self.denominator * other.denominator,
BigInt::cast_from(numerator / gcd),
BigInt::cast_from(denominator / gcd),
self.meta,
)
}
Expand All @@ -262,9 +271,14 @@ macro_rules! impl_mul {

#[inline]
fn $method(self, other: $Rhs) -> Self::Output {
let numerator =
I1024::cast_from(self.numerator) * I1024::cast_from(other.numerator);
let denominator =
I1024::cast_from(self.denominator) * I1024::cast_from(other.denominator);
let gcd = numerator.gcd(&denominator);
FractionBase::new(
self.numerator * other.numerator,
self.denominator * other.denominator,
BigInt::cast_from(numerator / gcd),
BigInt::cast_from(denominator / gcd),
self.meta,
)
}
Expand All @@ -282,9 +296,14 @@ macro_rules! impl_div {

#[inline]
fn $method(self, other: $Rhs) -> Self::Output {
let numerator =
I1024::cast_from(self.numerator) * I1024::cast_from(other.denominator);
let denominator =
I1024::cast_from(self.denominator) * I1024::cast_from(other.numerator);
let gcd = numerator.gcd(&denominator);
FractionBase::new(
self.numerator * other.denominator,
self.denominator * other.numerator,
BigInt::cast_from(numerator / gcd),
BigInt::cast_from(denominator / gcd),
self.meta,
)
}
Expand Down
15 changes: 12 additions & 3 deletions src/entities/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,10 @@ macro_rules! token {
($chain_id:expr, $address:expr, $decimals:expr) => {
Token::new(
$chain_id,
$address.to_string().parse::<Address>().unwrap(),
$address
.to_string()
.parse::<alloy_primitives::Address>()
.unwrap(),
$decimals,
None,
None,
Expand All @@ -164,7 +167,10 @@ macro_rules! token {
($chain_id:expr, $address:expr, $decimals:expr, $symbol:expr) => {
Token::new(
$chain_id,
$address.to_string().parse::<Address>().unwrap(),
$address
.to_string()
.parse::<alloy_primitives::Address>()
.unwrap(),
$decimals,
Some($symbol.to_string()),
None,
Expand All @@ -186,7 +192,10 @@ macro_rules! token {
($chain_id:expr, $address:expr, $decimals:expr, $symbol:expr, $name:expr) => {
Token::new(
$chain_id,
$address.to_string().parse::<Address>().unwrap(),
$address
.to_string()
.parse::<alloy_primitives::Address>()
.unwrap(),
$decimals,
Some($symbol.to_string()),
Some($name.to_string()),
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ pub mod prelude {

pub use alloc::{string::String, vec::Vec};
pub use alloy_primitives::{map::rustc_hash::FxHashMap, Address, Bytes, B256, U256};
pub use bnum;
pub use num_integer::Integer;

pub type BigInt = fastnum::I512;
pub type BigUint = fastnum::U512;
Expand Down
22 changes: 6 additions & 16 deletions src/utils/sqrt.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::prelude::*;
use fastnum::i512;
use num_integer::Roots;

/// Computes floor(sqrt(value))
///
Expand All @@ -10,27 +10,17 @@ use fastnum::i512;
/// returns: BigInt
#[inline]
pub fn sqrt(value: BigInt) -> Result<BigInt, Error> {
const ONE: BigInt = i512!(1);
const TWO: BigInt = i512!(2);
match value {
v if v < BigInt::ZERO => Err(Error::Invalid("NEGATIVE")),
BigInt::ZERO => Ok(BigInt::ZERO),
v if v <= TWO => Ok(ONE),
_ => {
let mut z = value;
let mut x = (value / TWO) + ONE;
while x < z {
z = x;
x = (value / x + x) / TWO;
}
Ok(z)
}
if value < BigInt::ZERO {
Err(Error::Invalid("NEGATIVE"))
} else {
Ok(value.sqrt())
}
}

#[cfg(test)]
mod tests {
use super::*;
use fastnum::i512;

#[test]
fn test_sqrt_0_1000() {
Expand Down

0 comments on commit e076788

Please sign in to comment.