From 5e6f03280176a11677fdcff5adf1f925de7e54e4 Mon Sep 17 00:00:00 2001 From: Shuhui Luo <107524008+shuhuiluo@users.noreply.github.com> Date: Sun, 5 Jan 2025 08:29:33 -0500 Subject: [PATCH] feat: refactor and standardize conversions to Big numeric types 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. --- src/constants.rs | 3 +- src/entities/fractions/currency_amount.rs | 5 +- src/entities/fractions/fraction.rs | 2 +- src/utils/types.rs | 187 ++++++++++++++++++++-- 4 files changed, 178 insertions(+), 19 deletions(-) diff --git a/src/constants.rs b/src/constants.rs index 6078309..9c22ccf 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -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()); diff --git a/src/entities/fractions/currency_amount.rs b/src/entities/fractions/currency_amount.rs index ef9d325..8b54dff 100644 --- a/src/entities/fractions/currency_amount.rs +++ b/src/entities/fractions/currency_amount.rs @@ -80,8 +80,9 @@ impl CurrencyAmount { /// 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() } diff --git a/src/entities/fractions/fraction.rs b/src/entities/fractions/fraction.rs index 2d9c4f4..9e8f381 100644 --- a/src/entities/fractions/fraction.rs +++ b/src/entities/fractions/fraction.rs @@ -114,7 +114,7 @@ pub trait FractionBase: 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 diff --git a/src/utils/types.rs b/src/utils/types.rs index e9e921e..b4f081c 100644 --- a/src/utils/types.rs +++ b/src/utils/types.rs @@ -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 ToBig for Uint { + #[inline] + fn to_big_uint(self) -> BigUint { + BigUint::from_le_slice(&self.as_le_bytes()).unwrap() + } +} + +impl ToBig for Signed { + #[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 FromBig for Uint { + #[inline] + fn from_big_uint(x: BigUint) -> Self { + Self::from_limbs_slice(&x.digits()[..LIMBS]) + } +} + +impl FromBig for Signed { + #[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 + ); + } }