cosmwasm_std/math/
fraction.rs

1/// A fraction `p`/`q` with integers `p` and `q`.
2///
3/// `p` is called the numerator and `q` is called the denominator.
4pub trait Fraction<T>: Sized {
5    /// Returns the numerator `p`
6    fn numerator(&self) -> T;
7    /// Returns the denominator `q`
8    fn denominator(&self) -> T;
9
10    /// Returns the multiplicative inverse `q/p` for fraction `p/q`.
11    ///
12    /// If `p` is zero, None is returned.
13    #[must_use = "this returns the result of the operation, without modifying the original"]
14    fn inv(&self) -> Option<Self>;
15}
16
17impl<T: Copy + From<u8> + PartialEq> Fraction<T> for (T, T) {
18    fn numerator(&self) -> T {
19        self.0
20    }
21
22    fn denominator(&self) -> T {
23        self.1
24    }
25
26    fn inv(&self) -> Option<Self> {
27        if self.numerator() == 0u8.into() {
28            None
29        } else {
30            Some((self.1, self.0))
31        }
32    }
33}
34
35#[macro_export]
36macro_rules! impl_mul_fraction {
37    ($Uint:ident) => {
38        impl $Uint {
39            /// Multiply `self` with a struct implementing [`Fraction`] (e.g. [`crate::Decimal`]).
40            /// Result is rounded down.
41            ///
42            /// ## Examples
43            ///
44            /// ```
45            /// use cosmwasm_std::Uint128;
46            /// let fraction = (8u128, 21u128);
47            /// let res = Uint128::new(123456).checked_mul_floor(fraction).unwrap();
48            /// assert_eq!(Uint128::new(47030), res); // 47030.8571 rounds down
49            /// ```
50            pub fn checked_mul_floor<F: Fraction<T>, T: Into<$Uint>>(
51                self,
52                rhs: F,
53            ) -> Result<Self, CheckedMultiplyFractionError> {
54                let divisor = rhs.denominator().into();
55                let res = self
56                    .full_mul(rhs.numerator().into())
57                    .checked_div(divisor.into())?;
58                Ok(res.try_into()?)
59            }
60
61            /// Same operation as `checked_mul_floor` except unwrapped
62            #[must_use = "this returns the result of the operation, without modifying the original"]
63            pub fn mul_floor<F: Fraction<T>, T: Into<$Uint>>(self, rhs: F) -> Self {
64                self.checked_mul_floor(rhs).unwrap()
65            }
66
67            /// Multiply `self` with a struct implementing [`Fraction`] (e.g. [`crate::Decimal`]).
68            /// Result is rounded up.
69            ///
70            /// ## Examples
71            ///
72            /// ```
73            /// use cosmwasm_std::Uint128;
74            /// let fraction = (8u128, 21u128);
75            /// let res = Uint128::new(123456).checked_mul_ceil(fraction).unwrap();
76            /// assert_eq!(Uint128::new(47031), res); // 47030.8571 rounds up
77            /// ```
78            pub fn checked_mul_ceil<F: Fraction<T>, T: Into<$Uint>>(
79                self,
80                rhs: F,
81            ) -> Result<Self, CheckedMultiplyFractionError> {
82                let dividend = self.full_mul(rhs.numerator().into());
83                let divisor = rhs.denominator().into().into();
84                let floor_result = dividend.checked_div(divisor)?.try_into()?;
85                let remainder = dividend.checked_rem(divisor)?;
86                if !remainder.is_zero() {
87                    Ok($Uint::one().checked_add(floor_result)?)
88                } else {
89                    Ok(floor_result)
90                }
91            }
92
93            /// Same operation as `checked_mul_ceil` except unwrapped
94            #[must_use = "this returns the result of the operation, without modifying the original"]
95            pub fn mul_ceil<F: Fraction<T>, T: Into<$Uint>>(self, rhs: F) -> Self {
96                self.checked_mul_ceil(rhs).unwrap()
97            }
98
99            /// Divide `self` with a struct implementing [`Fraction`] (e.g. [`crate::Decimal`]).
100            /// Result is rounded down.
101            ///
102            /// ## Examples
103            ///
104            /// ```
105            /// use cosmwasm_std::Uint128;
106            /// let fraction = (4u128, 5u128);
107            /// let res = Uint128::new(789).checked_div_floor(fraction).unwrap();
108            /// assert_eq!(Uint128::new(986), res); // 986.25 rounds down
109            /// ```
110            pub fn checked_div_floor<F: Fraction<T>, T: Into<$Uint>>(
111                self,
112                rhs: F,
113            ) -> Result<Self, CheckedMultiplyFractionError>
114            where
115                Self: Sized,
116            {
117                let divisor = rhs.numerator().into();
118                let res = self
119                    .full_mul(rhs.denominator().into())
120                    .checked_div(divisor.into())?;
121                Ok(res.try_into()?)
122            }
123
124            /// Same operation as `checked_div_floor` except unwrapped
125            #[must_use = "this returns the result of the operation, without modifying the original"]
126            pub fn div_floor<F: Fraction<T>, T: Into<$Uint>>(self, rhs: F) -> Self
127            where
128                Self: Sized,
129            {
130                self.checked_div_floor(rhs).unwrap()
131            }
132
133            /// Divide `self` with a struct implementing [`Fraction`] (e.g. [`crate::Decimal`]).
134            /// Result is rounded up.
135            ///
136            /// ## Examples
137            ///
138            /// ```
139            /// use cosmwasm_std::Uint128;
140            /// let fraction = (4u128, 5u128);
141            /// let res = Uint128::new(789).checked_div_ceil(fraction).unwrap();
142            /// assert_eq!(Uint128::new(987), res); // 986.25 rounds up
143            /// ```
144            pub fn checked_div_ceil<F: Fraction<T>, T: Into<$Uint>>(
145                self,
146                rhs: F,
147            ) -> Result<Self, CheckedMultiplyFractionError>
148            where
149                Self: Sized,
150            {
151                let dividend = self.full_mul(rhs.denominator().into());
152                let divisor = rhs.numerator().into().into();
153                let floor_result = dividend.checked_div(divisor)?.try_into()?;
154                let remainder = dividend.checked_rem(divisor)?;
155                if !remainder.is_zero() {
156                    Ok($Uint::one().checked_add(floor_result)?)
157                } else {
158                    Ok(floor_result)
159                }
160            }
161
162            /// Same operation as `checked_div_ceil` except unwrapped
163            #[must_use = "this returns the result of the operation, without modifying the original"]
164            pub fn div_ceil<F: Fraction<T>, T: Into<$Uint>>(self, rhs: F) -> Self
165            where
166                Self: Sized,
167            {
168                self.checked_div_ceil(rhs).unwrap()
169            }
170        }
171    };
172}
173
174#[cfg(test)]
175mod tests {
176    use crate::{Fraction, Uint128, Uint64};
177
178    #[test]
179    fn fraction_tuple_methods() {
180        let fraction = (Uint64::one(), Uint64::new(2));
181        assert_eq!(Uint64::one(), fraction.numerator());
182        assert_eq!(Uint64::new(2), fraction.denominator());
183        assert_eq!(Some((Uint64::new(2), Uint64::one())), fraction.inv());
184    }
185
186    #[test]
187    fn inverse_with_zero_denominator() {
188        let fraction = (Uint128::zero(), Uint128::one());
189        assert_eq!(None, fraction.inv());
190    }
191}