Skip to content

Commit eddb6b3

Browse files
committed
extend macro to Uint64/256
1 parent 70fc135 commit eddb6b3

File tree

4 files changed

+333
-18
lines changed

4 files changed

+333
-18
lines changed

packages/std/src/math/fraction.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,38 +29,38 @@ impl<T: Clone> Fraction<T> for (T, T) {
2929

3030
#[macro_export]
3131
macro_rules! impl_mul_fraction {
32-
($UintBase:ident, $UintLarger:ident) => {
33-
impl $UintBase {
34-
pub fn checked_mul_floored<F: Fraction<T>, T: Into<$UintBase>>(
32+
($Uint:ident) => {
33+
impl $Uint {
34+
pub fn checked_mul_floored<F: Fraction<T>, T: Into<$Uint>>(
3535
self,
3636
rhs: F,
3737
) -> Result<Self, CheckedMultiplyFractionError> {
38+
let divisor = rhs.denominator().into();
3839
let res = self
3940
.full_mul(rhs.numerator().into())
40-
.checked_div($UintLarger::from(rhs.denominator().into()))?;
41+
.checked_div(divisor.into())?;
4142
Ok(res.try_into()?)
4243
}
4344

44-
pub fn mul_floored<F: Fraction<T>, T: Into<$UintBase>>(self, rhs: F) -> Self {
45+
pub fn mul_floored<F: Fraction<T>, T: Into<$Uint>>(self, rhs: F) -> Self {
4546
self.checked_mul_floored(rhs).unwrap()
4647
}
4748

48-
pub fn checked_mul_ceil<F: Fraction<T> + Clone, T: Into<$UintBase>>(
49+
pub fn checked_mul_ceil<F: Fraction<T> + Clone, T: Into<$Uint>>(
4950
self,
5051
rhs: F,
5152
) -> Result<Self, CheckedMultiplyFractionError> {
5253
let floor_result = self.checked_mul_floored(rhs.clone())?;
5354
let numerator = rhs.numerator().into();
5455
let denominator = rhs.denominator().into();
5556
if !numerator.checked_rem(denominator)?.is_zero() {
56-
let ceil_result = $UintLarger::one().checked_add(floor_result.into())?;
57-
Ok(ceil_result.try_into()?)
57+
Ok($Uint::one().checked_add(floor_result)?)
5858
} else {
5959
Ok(floor_result)
6060
}
6161
}
6262

63-
pub fn mul_ceil<F: Fraction<T> + Clone, T: Into<$UintBase>>(self, rhs: F) -> Self {
63+
pub fn mul_ceil<F: Fraction<T> + Clone, T: Into<$Uint>>(self, rhs: F) -> Self {
6464
self.checked_mul_ceil(rhs).unwrap()
6565
}
6666
}

packages/std/src/math/uint128.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ impl Uint128 {
232232
}
233233
}
234234

235-
impl_mul_fraction!(Uint128, Uint256);
235+
impl_mul_fraction!(Uint128);
236236

237237
// `From<u{128,64,32,16,8}>` is implemented manually instead of
238238
// using `impl<T: Into<u128>> From<T> for Uint128` because
@@ -1175,7 +1175,7 @@ mod tests {
11751175
Err(ConversionOverflow(ConversionOverflowError {
11761176
source_type: "Uint256",
11771177
target_type: "Uint128",
1178-
value: "893241213167463466591358344508391555069".to_string()
1178+
value: "893241213167463466591358344508391555069".to_string() // raises prior to rounding up
11791179
})),
11801180
);
11811181
}

packages/std/src/math/uint256.rs

Lines changed: 183 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ use std::ops::{
99
use std::str::FromStr;
1010

1111
use crate::errors::{
12-
CheckedMultiplyRatioError, ConversionOverflowError, DivideByZeroError, OverflowError,
13-
OverflowOperation, StdError,
12+
CheckedMultiplyFractionError, CheckedMultiplyRatioError, ConversionOverflowError,
13+
DivideByZeroError, OverflowError, OverflowOperation, StdError,
1414
};
15-
use crate::{Uint128, Uint512, Uint64};
15+
use crate::{impl_mul_fraction, Fraction, Uint128, Uint512, Uint64};
1616

1717
/// This module is purely a workaround that lets us ignore lints for all the code
1818
/// the `construct_uint!` macro generates.
@@ -336,6 +336,8 @@ impl Uint256 {
336336
}
337337
}
338338

339+
impl_mul_fraction!(Uint256);
340+
339341
impl From<Uint128> for Uint256 {
340342
fn from(val: Uint128) -> Self {
341343
val.u128().into()
@@ -666,7 +668,8 @@ impl PartialEq<Uint256> for &Uint256 {
666668
#[cfg(test)]
667669
mod tests {
668670
use super::*;
669-
use crate::{from_slice, to_vec};
671+
use crate::errors::CheckedMultiplyFractionError::{ConversionOverflow, DivideByZero};
672+
use crate::{from_slice, to_vec, Decimal, Decimal256};
670673

671674
#[test]
672675
fn size_of_works() {
@@ -1664,4 +1667,180 @@ mod tests {
16641667
assert_eq!(&lhs == &rhs, expected);
16651668
}
16661669
}
1670+
1671+
#[test]
1672+
fn mul_floored_works_with_zero() {
1673+
let fraction = (Uint256::zero(), Uint256::from(21u32));
1674+
let res = Uint256::from(123456u32).mul_floored(fraction);
1675+
assert_eq!(Uint256::zero(), res)
1676+
}
1677+
1678+
#[test]
1679+
fn mul_floored_does_nothing_with_one() {
1680+
let fraction = (Uint256::one(), Uint256::one());
1681+
let res = Uint256::from(123456u32).mul_floored(fraction);
1682+
assert_eq!(Uint256::from(123456u32), res)
1683+
}
1684+
1685+
#[test]
1686+
fn mul_floored_rounds_down_with_normal_case() {
1687+
let fraction = (Uint256::from(8u128), Uint256::from(21u128));
1688+
let res = Uint256::from(123456u32).mul_floored(fraction); // 47030.8571
1689+
assert_eq!(Uint256::from(47030u32), res)
1690+
}
1691+
1692+
#[test]
1693+
fn mul_floored_works_when_operation_temporarily_takes_above_max() {
1694+
let fraction = (8u128, 21u128);
1695+
let res = Uint256::MAX.mul_floored(fraction); // 44_111_272_090_406_169_685_169_899_050_928_726_801_245_708_444_053_548_205_507_651_050_633_573_196_165.71428571
1696+
assert_eq!(
1697+
Uint256::from_str(
1698+
"44111272090406169685169899050928726801245708444053548205507651050633573196165"
1699+
)
1700+
.unwrap(),
1701+
res
1702+
)
1703+
}
1704+
1705+
#[test]
1706+
fn mul_floored_works_with_decimal() {
1707+
let decimal = Decimal::from_ratio(8u128, 21u128);
1708+
let res = Uint256::from(123456u32).mul_floored(decimal); // 47030.8571
1709+
assert_eq!(Uint256::from(47030u32), res)
1710+
}
1711+
1712+
#[test]
1713+
fn mul_floored_works_with_decimal256() {
1714+
let decimal = Decimal256::from_ratio(8u128, 21u128);
1715+
let res = Uint256::from(123456u32).mul_floored(decimal); // 47030.8571
1716+
assert_eq!(Uint256::from(47030u32), res)
1717+
}
1718+
1719+
#[test]
1720+
#[should_panic(expected = "ConversionOverflowError")]
1721+
fn mul_floored_panics_on_overflow() {
1722+
let fraction = (21u128, 8u128);
1723+
Uint256::MAX.mul_floored(fraction);
1724+
}
1725+
1726+
#[test]
1727+
fn checked_mul_floored_does_not_panic_on_overflow() {
1728+
let fraction = (21u128, 8u128);
1729+
assert_eq!(
1730+
Uint256::MAX.checked_mul_floored(fraction),
1731+
Err(ConversionOverflow(ConversionOverflowError {
1732+
source_type: "Uint512",
1733+
target_type: "Uint256",
1734+
value:
1735+
"303954234247955012986873835647805758114833709747306480603576158020771965304829"
1736+
.to_string()
1737+
})),
1738+
);
1739+
}
1740+
1741+
#[test]
1742+
#[should_panic(expected = "DivideByZeroError")]
1743+
fn mul_floored_panics_on_zero_div() {
1744+
let fraction = (21u128, 0u128);
1745+
Uint256::from(123456u32).mul_floored(fraction);
1746+
}
1747+
1748+
#[test]
1749+
fn checked_mul_floored_does_not_panic_on_zero_div() {
1750+
let fraction = (21u128, 0u128);
1751+
assert_eq!(
1752+
Uint256::from(123456u32).checked_mul_floored(fraction),
1753+
Err(DivideByZero(DivideByZeroError {
1754+
operand: "2592576".to_string()
1755+
})),
1756+
);
1757+
}
1758+
1759+
#[test]
1760+
fn mul_ceil_works_with_zero() {
1761+
let fraction = (Uint256::zero(), Uint256::from(21u32));
1762+
let res = Uint256::from(123456u32).mul_ceil(fraction);
1763+
assert_eq!(Uint256::zero(), res)
1764+
}
1765+
1766+
#[test]
1767+
fn mul_ceil_does_nothing_with_one() {
1768+
let fraction = (Uint256::one(), Uint256::one());
1769+
let res = Uint256::from(123456u32).mul_ceil(fraction);
1770+
assert_eq!(Uint256::from(123456u32), res)
1771+
}
1772+
1773+
#[test]
1774+
fn mul_ceil_rounds_up_with_normal_case() {
1775+
let fraction = (8u128, 21u128);
1776+
let res = Uint256::from(123456u32).mul_ceil(fraction); // 47030.8571
1777+
assert_eq!(Uint256::from(47031u32), res)
1778+
}
1779+
1780+
#[test]
1781+
fn mul_ceil_works_when_operation_temporarily_takes_above_max() {
1782+
let fraction = (8u128, 21u128);
1783+
let res = Uint256::MAX.mul_ceil(fraction); // 44_111_272_090_406_169_685_169_899_050_928_726_801_245_708_444_053_548_205_507_651_050_633_573_196_165.71428571
1784+
assert_eq!(
1785+
Uint256::from_str(
1786+
"44111272090406169685169899050928726801245708444053548205507651050633573196166"
1787+
)
1788+
.unwrap(),
1789+
res
1790+
)
1791+
}
1792+
1793+
#[test]
1794+
fn mul_ceil_works_with_decimal() {
1795+
let decimal = Decimal::from_ratio(8u128, 21u128);
1796+
let res = Uint256::from(123456u32).mul_ceil(decimal); // 47030.8571
1797+
assert_eq!(Uint256::from(47031u32), res)
1798+
}
1799+
1800+
#[test]
1801+
fn mul_ceil_works_with_decimal256() {
1802+
let decimal = Decimal256::from_ratio(8u128, 21u128);
1803+
let res = Uint256::from(123456u32).mul_ceil(decimal); // 47030.8571
1804+
assert_eq!(Uint256::from(47031u32), res)
1805+
}
1806+
1807+
#[test]
1808+
#[should_panic(expected = "ConversionOverflowError")]
1809+
fn mul_ceil_panics_on_overflow() {
1810+
let fraction = (21u128, 8u128);
1811+
Uint256::MAX.mul_ceil(fraction);
1812+
}
1813+
1814+
#[test]
1815+
fn checked_mul_ceil_does_not_panic_on_overflow() {
1816+
let fraction = (21u128, 8u128);
1817+
assert_eq!(
1818+
Uint256::MAX.checked_mul_ceil(fraction),
1819+
Err(ConversionOverflow(ConversionOverflowError {
1820+
source_type: "Uint512",
1821+
target_type: "Uint256",
1822+
value:
1823+
"303954234247955012986873835647805758114833709747306480603576158020771965304829" // raises prior to rounding up
1824+
.to_string()
1825+
})),
1826+
);
1827+
}
1828+
1829+
#[test]
1830+
#[should_panic(expected = "DivideByZeroError")]
1831+
fn mul_ceil_panics_on_zero_div() {
1832+
let fraction = (21u128, 0u128);
1833+
Uint256::from(123456u32).mul_ceil(fraction);
1834+
}
1835+
1836+
#[test]
1837+
fn checked_mul_ceil_does_not_panic_on_zero_div() {
1838+
let fraction = (21u128, 0u128);
1839+
assert_eq!(
1840+
Uint256::from(123456u32).checked_mul_ceil(fraction),
1841+
Err(DivideByZero(DivideByZeroError {
1842+
operand: "2592576".to_string()
1843+
})),
1844+
);
1845+
}
16671846
}

0 commit comments

Comments
 (0)