1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/// A fraction `p`/`q` with integers `p` and `q`.
///
/// `p` is called the numerator and `q` is called the denominator.
pub trait Fraction<T>: Sized {
    /// Returns the numerator `p`
    fn numerator(&self) -> T;
    /// Returns the denominator `q`
    fn denominator(&self) -> T;

    /// Returns the multiplicative inverse `q/p` for fraction `p/q`.
    ///
    /// If `p` is zero, None is returned.
    #[must_use = "this returns the result of the operation, without modifying the original"]
    fn inv(&self) -> Option<Self>;
}

impl<T: Copy + From<u8> + PartialEq> Fraction<T> for (T, T) {
    fn numerator(&self) -> T {
        self.0
    }

    fn denominator(&self) -> T {
        self.1
    }

    fn inv(&self) -> Option<Self> {
        if self.numerator() == 0u8.into() {
            None
        } else {
            Some((self.1, self.0))
        }
    }
}

#[macro_export]
macro_rules! impl_mul_fraction {
    ($Uint:ident) => {
        impl $Uint {
            /// Multiply `self` with a struct implementing [`Fraction`] (e.g. [`crate::Decimal`]).
            /// Result is rounded down.
            ///
            /// ## Examples
            ///
            /// ```
            /// use cosmwasm_std::Uint128;
            /// let fraction = (8u128, 21u128);
            /// let res = Uint128::new(123456).checked_mul_floor(fraction).unwrap();
            /// assert_eq!(Uint128::new(47030), res); // 47030.8571 rounds down
            /// ```
            pub fn checked_mul_floor<F: Fraction<T>, T: Into<$Uint>>(
                self,
                rhs: F,
            ) -> Result<Self, CheckedMultiplyFractionError> {
                let divisor = rhs.denominator().into();
                let res = self
                    .full_mul(rhs.numerator().into())
                    .checked_div(divisor.into())?;
                Ok(res.try_into()?)
            }

            /// Same operation as `checked_mul_floor` except unwrapped
            #[must_use = "this returns the result of the operation, without modifying the original"]
            pub fn mul_floor<F: Fraction<T>, T: Into<$Uint>>(self, rhs: F) -> Self {
                self.checked_mul_floor(rhs).unwrap()
            }

            /// Multiply `self` with a struct implementing [`Fraction`] (e.g. [`crate::Decimal`]).
            /// Result is rounded up.
            ///
            /// ## Examples
            ///
            /// ```
            /// use cosmwasm_std::Uint128;
            /// let fraction = (8u128, 21u128);
            /// let res = Uint128::new(123456).checked_mul_ceil(fraction).unwrap();
            /// assert_eq!(Uint128::new(47031), res); // 47030.8571 rounds up
            /// ```
            pub fn checked_mul_ceil<F: Fraction<T>, T: Into<$Uint>>(
                self,
                rhs: F,
            ) -> Result<Self, CheckedMultiplyFractionError> {
                let dividend = self.full_mul(rhs.numerator().into());
                let divisor = rhs.denominator().into().into();
                let floor_result = dividend.checked_div(divisor)?.try_into()?;
                let remainder = dividend.checked_rem(divisor)?;
                if !remainder.is_zero() {
                    Ok($Uint::one().checked_add(floor_result)?)
                } else {
                    Ok(floor_result)
                }
            }

            /// Same operation as `checked_mul_ceil` except unwrapped
            #[must_use = "this returns the result of the operation, without modifying the original"]
            pub fn mul_ceil<F: Fraction<T>, T: Into<$Uint>>(self, rhs: F) -> Self {
                self.checked_mul_ceil(rhs).unwrap()
            }

            /// Divide `self` with a struct implementing [`Fraction`] (e.g. [`crate::Decimal`]).
            /// Result is rounded down.
            ///
            /// ## Examples
            ///
            /// ```
            /// use cosmwasm_std::Uint128;
            /// let fraction = (4u128, 5u128);
            /// let res = Uint128::new(789).checked_div_floor(fraction).unwrap();
            /// assert_eq!(Uint128::new(986), res); // 986.25 rounds down
            /// ```
            pub fn checked_div_floor<F: Fraction<T>, T: Into<$Uint>>(
                self,
                rhs: F,
            ) -> Result<Self, CheckedMultiplyFractionError>
            where
                Self: Sized,
            {
                let divisor = rhs.numerator().into();
                let res = self
                    .full_mul(rhs.denominator().into())
                    .checked_div(divisor.into())?;
                Ok(res.try_into()?)
            }

            /// Same operation as `checked_div_floor` except unwrapped
            #[must_use = "this returns the result of the operation, without modifying the original"]
            pub fn div_floor<F: Fraction<T>, T: Into<$Uint>>(self, rhs: F) -> Self
            where
                Self: Sized,
            {
                self.checked_div_floor(rhs).unwrap()
            }

            /// Divide `self` with a struct implementing [`Fraction`] (e.g. [`crate::Decimal`]).
            /// Result is rounded up.
            ///
            /// ## Examples
            ///
            /// ```
            /// use cosmwasm_std::Uint128;
            /// let fraction = (4u128, 5u128);
            /// let res = Uint128::new(789).checked_div_ceil(fraction).unwrap();
            /// assert_eq!(Uint128::new(987), res); // 986.25 rounds up
            /// ```
            pub fn checked_div_ceil<F: Fraction<T>, T: Into<$Uint>>(
                self,
                rhs: F,
            ) -> Result<Self, CheckedMultiplyFractionError>
            where
                Self: Sized,
            {
                let dividend = self.full_mul(rhs.denominator().into());
                let divisor = rhs.numerator().into().into();
                let floor_result = dividend.checked_div(divisor)?.try_into()?;
                let remainder = dividend.checked_rem(divisor)?;
                if !remainder.is_zero() {
                    Ok($Uint::one().checked_add(floor_result)?)
                } else {
                    Ok(floor_result)
                }
            }

            /// Same operation as `checked_div_ceil` except unwrapped
            #[must_use = "this returns the result of the operation, without modifying the original"]
            pub fn div_ceil<F: Fraction<T>, T: Into<$Uint>>(self, rhs: F) -> Self
            where
                Self: Sized,
            {
                self.checked_div_ceil(rhs).unwrap()
            }
        }
    };
}

#[cfg(test)]
mod tests {
    use crate::{Fraction, Uint128, Uint64};

    #[test]
    fn fraction_tuple_methods() {
        let fraction = (Uint64::one(), Uint64::new(2));
        assert_eq!(Uint64::one(), fraction.numerator());
        assert_eq!(Uint64::new(2), fraction.denominator());
        assert_eq!(Some((Uint64::new(2), Uint64::one())), fraction.inv());
    }

    #[test]
    fn inverse_with_zero_denominator() {
        let fraction = (Uint128::zero(), Uint128::one());
        assert_eq!(None, fraction.inv());
    }
}