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}