Skip to content

Commit

Permalink
feat: refactor and standardize conversions to Big numeric types
Browse files Browse the repository at this point in the history
Introduced traits `ToBig` and `FromBig` for consistent and extensible conversions between various numeric types. Replaced existing ad-hoc conversion functions with implementations of these traits. Updated dependent code to use the new standardized methods, improving clarity and maintainability.
  • Loading branch information
shuhuiluo committed Jan 5, 2025
1 parent bba761d commit 5e6f032
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 19 deletions.
3 changes: 2 additions & 1 deletion src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ pub enum Rounding {
}

/// Represents the maximum amount contained in a uint256
pub const MAX_UINT256: BigInt = to_big_int(U256::MAX);
pub const MAX_UINT256: BigInt =
BigInt::from_bits(BigUint::from_le_slice(&U256::MAX.to_le_bytes::<32>()).unwrap());
5 changes: 3 additions & 2 deletions src/entities/fractions/currency_amount.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,9 @@ impl<T: BaseCurrency> CurrencyAmount<T> {
/// Convert the currency amount to a string with exact precision
#[inline]
pub fn to_exact(&self) -> String {
to_big_decimal(self.quotient())
.div(to_big_decimal(self.decimal_scale))
self.quotient()
.to_big_decimal()
.div(self.decimal_scale.to_big_decimal())
.to_string()
}

Expand Down
2 changes: 1 addition & 1 deletion src/entities/fractions/fraction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ pub trait FractionBase<M: Clone>: Sized {
/// Converts the fraction to a [`BigDecimal`]
#[inline]
fn to_decimal(&self) -> BigDecimal {
to_big_decimal(self.numerator()) / to_big_decimal(self.denominator())
self.numerator().to_big_decimal() / self.denominator().to_big_decimal()
}

/// Converts the fraction to a string with a specified number of significant digits and rounding
Expand Down
187 changes: 172 additions & 15 deletions src/utils/types.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,181 @@
use crate::prelude::{BigDecimal, BigInt, BigUint};
use alloy_primitives::U256;
use alloy_primitives::{Signed, Uint};
use fastnum::decimal::{Context, Sign};

#[inline]
#[must_use]
pub const fn to_big_decimal(value: BigInt) -> BigDecimal {
match value.is_negative() {
false => BigDecimal::from_parts(value.to_bits(), 0, Sign::Plus, Context::default()),
true => BigDecimal::from_parts(value.to_bits(), 0, Sign::Minus, Context::default()),
pub trait ToBig: Sized {
fn to_big_uint(self) -> BigUint;

#[inline]
fn to_big_int(self) -> BigInt {
self.to_big_uint().to_big_int()
}

#[inline]
fn to_big_decimal(self) -> BigDecimal {
let x = self.to_big_int();
BigDecimal::from_parts(
x.to_bits(),
0,
match x.is_negative() {
false => Sign::Plus,
true => Sign::Minus,
},
Context::default(),
)
}
}

#[inline]
#[must_use]
pub const fn to_big_uint(x: U256) -> BigUint {
BigUint::from_le_slice(x.as_le_slice()).unwrap()
impl ToBig for BigInt {
#[inline]
fn to_big_uint(self) -> BigUint {
self.to_bits()
}

#[inline]
fn to_big_int(self) -> BigInt {
self
}
}

#[inline]
#[must_use]
pub const fn to_big_int(x: U256) -> BigInt {
BigInt::from_bits(to_big_uint(x))
impl ToBig for BigUint {
#[inline]
fn to_big_uint(self) -> BigUint {
self
}

#[inline]
fn to_big_int(self) -> BigInt {
BigInt::from_bits(self)
}

#[inline]
fn to_big_decimal(self) -> BigDecimal {
BigDecimal::from_parts(self, 0, Sign::Plus, Context::default())
}
}

impl<const BITS: usize, const LIMBS: usize> ToBig for Uint<BITS, LIMBS> {
#[inline]
fn to_big_uint(self) -> BigUint {
BigUint::from_le_slice(&self.as_le_bytes()).unwrap()
}
}

impl<const BITS: usize, const LIMBS: usize> ToBig for Signed<BITS, LIMBS> {
#[inline]
fn to_big_uint(self) -> BigUint {
self.into_raw().to_big_uint()
}

#[inline]
fn to_big_int(self) -> BigInt {
BigInt::from_le_slice(&self.into_raw().as_le_bytes()).unwrap()
}
}

pub trait FromBig: Sized {
fn from_big_uint(x: BigUint) -> Self;

#[inline]
#[must_use]
fn from_big_int(x: BigInt) -> Self {
Self::from_big_uint(x.to_bits())
}
}

impl<const BITS: usize, const LIMBS: usize> FromBig for Uint<BITS, LIMBS> {
#[inline]
fn from_big_uint(x: BigUint) -> Self {
Self::from_limbs_slice(&x.digits()[..LIMBS])
}
}

impl<const BITS: usize, const LIMBS: usize> FromBig for Signed<BITS, LIMBS> {
#[inline]
fn from_big_uint(x: BigUint) -> Self {
Self::from_raw(Uint::from_big_uint(x))
}
}

#[cfg(test)]
mod tests {
use super::*;
use alloy_primitives::{I256, U256};

#[test]
fn test_uint_to_big() {
let x = U256::from_limbs([1, 2, 3, 4]);
let y = BigUint::from(1_u64)
+ (BigUint::from(2_u64) << 64)
+ (BigUint::from(3_u64) << 128)
+ (BigUint::from(4_u64) << 192);
assert_eq!(x.to_big_uint(), y);
assert_eq!(x.to_big_int(), y.to_big_int());
assert_eq!(x.to_big_decimal(), y.to_big_decimal());

let x = -x;
let z = (BigUint::from(1_u64) << 256) - y;
assert_eq!(x.to_big_uint(), z);
assert_eq!(x.to_big_int(), z.to_big_int());
assert_eq!(x.to_big_decimal(), z.to_big_decimal());
}

#[test]
fn test_signed_to_big() {
let x = I256::from_raw(U256::from_limbs([1, 2, 3, 4]));
let y: BigInt = BigInt::from(1)
+ (BigInt::from(2) << 64)
+ (BigInt::from(3) << 128)
+ (BigInt::from(4) << 192);
assert_eq!(x.to_big_uint(), y.to_big_uint());
assert_eq!(x.to_big_int(), y);
assert_eq!(x.to_big_decimal(), y.to_big_decimal());

let x = -x;
let z: BigInt = (BigInt::from(1) << 256) - y;
assert_eq!(x.to_big_uint(), z.to_big_uint());
assert_eq!(x.to_big_int(), -y);
assert_eq!(x.to_big_decimal(), (-y).to_big_decimal());
}

#[test]
fn test_uint_from_big() {
let x = U256::from_limbs([1, 2, 3, 4]);
assert_eq!(U256::from_big_uint(x.to_big_uint()), x);
assert_eq!(U256::from_big_int(x.to_big_int()), x);

let x = -x;
assert_eq!(U256::from_big_uint(x.to_big_uint()), x);
assert_eq!(U256::from_big_int(x.to_big_int()), x);

assert_eq!(
U256::from_big_uint(BigInt::from(-1).to_big_uint()),
U256::MAX
);
assert_eq!(U256::from_big_int(BigInt::from(-1)), U256::MAX);

assert_eq!(
U256::from_big_uint(I256::MIN.to_big_uint()),
I256::MIN.into_raw()
);
assert_eq!(
U256::from_big_int(I256::MIN.to_big_int()),
I256::MIN.into_raw()
);
}

#[test]
fn test_signed_from_big() {
let x = I256::from_raw(U256::from_limbs([1, 2, 3, 4]));
assert_eq!(I256::from_big_uint(x.to_big_uint()), x);
assert_eq!(I256::from_big_int(x.to_big_int()), x);

let x = -x;
assert_eq!(I256::from_big_uint(x.to_big_uint()), x);
assert_eq!(I256::from_big_int(x.to_big_int()), x);
assert_eq!(
x.to_big_uint() + (-x.to_big_int()).to_big_uint(),
BigUint::from(1_u64) << 256
);
}
}

0 comments on commit 5e6f032

Please sign in to comment.