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