@@ -8,9 +8,10 @@ use std::ops::{
8
8
use std:: str:: FromStr ;
9
9
10
10
use crate :: errors:: {
11
- CheckedMultiplyRatioError , DivideByZeroError , OverflowError , OverflowOperation , StdError ,
11
+ CheckedMultiplyFractionalError , CheckedMultiplyRatioError , DivideByZeroError , OverflowError ,
12
+ OverflowOperation , StdError ,
12
13
} ;
13
- use crate :: { ConversionOverflowError , Uint256 , Uint64 } ;
14
+ use crate :: { ConversionOverflowError , Fractional , Uint256 , Uint64 } ;
14
15
15
16
/// A thin wrapper around u128 that is using strings for JSON encoding/decoding,
16
17
/// such that the full u128 range can be used for clients that convert JSON numbers to floats,
@@ -136,6 +137,37 @@ impl Uint128 {
136
137
. unwrap ( )
137
138
}
138
139
140
+ pub fn mul_floored < F : Fractional < T > , T : Into < u128 > > ( self , rhs : F ) -> Self {
141
+ self . checked_mul_floored ( rhs) . unwrap ( )
142
+ }
143
+
144
+ pub fn checked_mul_floored < F : Fractional < T > , T : Into < u128 > > (
145
+ self ,
146
+ rhs : F ,
147
+ ) -> Result < Self , CheckedMultiplyFractionalError > {
148
+ let res = self
149
+ . full_mul ( rhs. numerator ( ) )
150
+ . checked_div ( Uint256 :: from ( rhs. denominator ( ) . into ( ) ) ) ?;
151
+ Ok ( res. try_into ( ) ?)
152
+ }
153
+
154
+ pub fn mul_ceil < F : Fractional < T > + Clone , T : Into < u128 > > ( self , rhs : F ) -> Self {
155
+ self . checked_mul_ceil ( rhs) . unwrap ( )
156
+ }
157
+
158
+ pub fn checked_mul_ceil < F : Fractional < T > + Clone , T : Into < u128 > > (
159
+ self ,
160
+ rhs : F ,
161
+ ) -> Result < Self , CheckedMultiplyFractionalError > {
162
+ let mut result = self . checked_mul_floored ( rhs. clone ( ) ) ?;
163
+ let numerator = Uint256 :: from ( rhs. numerator ( ) . into ( ) ) ;
164
+ let denominator = Uint256 :: from ( rhs. denominator ( ) . into ( ) ) ;
165
+ if !numerator. checked_rem ( denominator) ?. is_zero ( ) {
166
+ result += Uint128 :: one ( ) ;
167
+ } ;
168
+ Ok ( result)
169
+ }
170
+
139
171
pub fn checked_add ( self , other : Self ) -> Result < Self , OverflowError > {
140
172
self . 0
141
173
. checked_add ( other. 0 )
@@ -538,7 +570,9 @@ impl PartialEq<Uint128> for &Uint128 {
538
570
#[ cfg( test) ]
539
571
mod tests {
540
572
use super :: * ;
541
- use crate :: { from_slice, to_vec} ;
573
+ use crate :: errors:: CheckedMultiplyFractionalError :: { ConversionOverflow , DivideByZero } ;
574
+ use crate :: math:: fraction:: Fraction ;
575
+ use crate :: { from_slice, to_vec, Decimal } ;
542
576
543
577
#[ test]
544
578
fn size_of_works ( ) {
@@ -1039,4 +1073,136 @@ mod tests {
1039
1073
assert_eq ! ( & lhs == & rhs, expected) ;
1040
1074
}
1041
1075
}
1076
+
1077
+ #[ test]
1078
+ fn mul_floored_works_with_zero ( ) {
1079
+ let fraction = Fraction :: new ( Uint128 :: zero ( ) , Uint128 :: new ( 21 ) ) ;
1080
+ let res = Uint128 :: new ( 123456 ) . mul_floored ( fraction) ;
1081
+ assert_eq ! ( Uint128 :: zero( ) , res)
1082
+ }
1083
+
1084
+ #[ test]
1085
+ fn mul_floored_does_nothing_with_one ( ) {
1086
+ let fraction = Fraction :: new ( Uint128 :: one ( ) , Uint128 :: one ( ) ) ;
1087
+ let res = Uint128 :: new ( 123456 ) . mul_floored ( fraction) ;
1088
+ assert_eq ! ( Uint128 :: new( 123456 ) , res)
1089
+ }
1090
+
1091
+ #[ test]
1092
+ fn mul_floored_rounds_down_with_normal_case ( ) {
1093
+ let fraction = Fraction :: new ( 8u128 , 21u128 ) ;
1094
+ let res = Uint128 :: new ( 123456 ) . mul_floored ( fraction) ; // 47030.8571
1095
+ assert_eq ! ( Uint128 :: new( 47030 ) , res)
1096
+ }
1097
+
1098
+ #[ test]
1099
+ fn mul_floored_works_with_decimal ( ) {
1100
+ let decimal = Decimal :: from_ratio ( 8u128 , 21u128 ) ;
1101
+ let res = Uint128 :: new ( 123456 ) . mul_floored ( decimal) ; // 47030.8571
1102
+ assert_eq ! ( Uint128 :: new( 47030 ) , res)
1103
+ }
1104
+
1105
+ #[ test]
1106
+ #[ should_panic( expected = "ConversionOverflowError" ) ]
1107
+ fn mul_floored_panics_on_overflow ( ) {
1108
+ let fraction = Fraction :: new ( 21u128 , 8u128 ) ;
1109
+ Uint128 :: MAX . mul_floored ( fraction) ;
1110
+ }
1111
+
1112
+ #[ test]
1113
+ fn checked_mul_floored_does_not_panic_on_overflow ( ) {
1114
+ let fraction = Fraction :: new ( 21u128 , 8u128 ) ;
1115
+ assert_eq ! (
1116
+ Uint128 :: MAX . checked_mul_floored( fraction) ,
1117
+ Err ( ConversionOverflow ( ConversionOverflowError {
1118
+ source_type: "Uint256" ,
1119
+ target_type: "Uint128" ,
1120
+ value: "893241213167463466591358344508391555069" . to_string( )
1121
+ } ) ) ,
1122
+ ) ;
1123
+ }
1124
+
1125
+ #[ test]
1126
+ #[ should_panic( expected = "DivideByZeroError" ) ]
1127
+ fn mul_floored_panics_on_zero_div ( ) {
1128
+ let fraction = Fraction :: new ( 21u128 , 0u128 ) ;
1129
+ Uint128 :: new ( 123456 ) . mul_floored ( fraction) ;
1130
+ }
1131
+
1132
+ #[ test]
1133
+ fn checked_mul_floored_does_not_panic_on_zero_div ( ) {
1134
+ let fraction = Fraction :: new ( 21u128 , 0u128 ) ;
1135
+ assert_eq ! (
1136
+ Uint128 :: new( 123456 ) . checked_mul_floored( fraction) ,
1137
+ Err ( DivideByZero ( DivideByZeroError {
1138
+ operand: "2592576" . to_string( )
1139
+ } ) ) ,
1140
+ ) ;
1141
+ }
1142
+
1143
+ #[ test]
1144
+ fn mul_ceil_works_with_zero ( ) {
1145
+ let fraction = Fraction :: new ( Uint128 :: zero ( ) , Uint128 :: new ( 21 ) ) ;
1146
+ let res = Uint128 :: new ( 123456 ) . mul_ceil ( fraction) ;
1147
+ assert_eq ! ( Uint128 :: zero( ) , res)
1148
+ }
1149
+
1150
+ #[ test]
1151
+ fn mul_ceil_does_nothing_with_one ( ) {
1152
+ let fraction = Fraction :: new ( Uint128 :: one ( ) , Uint128 :: one ( ) ) ;
1153
+ let res = Uint128 :: new ( 123456 ) . mul_ceil ( fraction) ;
1154
+ assert_eq ! ( Uint128 :: new( 123456 ) , res)
1155
+ }
1156
+
1157
+ #[ test]
1158
+ fn mul_ceil_rounds_up_with_normal_case ( ) {
1159
+ let fraction = Fraction :: new ( 8u128 , 21u128 ) ;
1160
+ let res = Uint128 :: new ( 123456 ) . mul_ceil ( fraction) ; // 47030.8571
1161
+ assert_eq ! ( Uint128 :: new( 47031 ) , res)
1162
+ }
1163
+
1164
+ #[ test]
1165
+ fn mul_ceil_works_with_decimal ( ) {
1166
+ let decimal = Decimal :: from_ratio ( 8u128 , 21u128 ) ;
1167
+ let res = Uint128 :: new ( 123456 ) . mul_ceil ( decimal) ; // 47030.8571
1168
+ assert_eq ! ( Uint128 :: new( 47031 ) , res)
1169
+ }
1170
+
1171
+ #[ test]
1172
+ #[ should_panic( expected = "ConversionOverflowError" ) ]
1173
+ fn mul_ceil_panics_on_overflow ( ) {
1174
+ let fraction = Fraction :: new ( 21u128 , 8u128 ) ;
1175
+ Uint128 :: MAX . mul_ceil ( fraction) ;
1176
+ }
1177
+
1178
+ #[ test]
1179
+ fn checked_mul_ceil_does_not_panic_on_overflow ( ) {
1180
+ let fraction = Fraction :: new ( 21u128 , 8u128 ) ;
1181
+ assert_eq ! (
1182
+ Uint128 :: MAX . checked_mul_ceil( fraction) ,
1183
+ Err ( ConversionOverflow ( ConversionOverflowError {
1184
+ source_type: "Uint256" ,
1185
+ target_type: "Uint128" ,
1186
+ value: "893241213167463466591358344508391555069" . to_string( )
1187
+ } ) ) ,
1188
+ ) ;
1189
+ }
1190
+
1191
+ #[ test]
1192
+ #[ should_panic( expected = "DivideByZeroError" ) ]
1193
+ fn mul_ceil_panics_on_zero_div ( ) {
1194
+ let fraction = Fraction :: new ( 21u128 , 0u128 ) ;
1195
+ Uint128 :: new ( 123456 ) . mul_ceil ( fraction) ;
1196
+ }
1197
+
1198
+ #[ test]
1199
+ fn checked_mul_ceil_does_not_panic_on_zero_div ( ) {
1200
+ let fraction = Fraction :: new ( 21u128 , 0u128 ) ;
1201
+ assert_eq ! (
1202
+ Uint128 :: new( 123456 ) . checked_mul_ceil( fraction) ,
1203
+ Err ( DivideByZero ( DivideByZeroError {
1204
+ operand: "2592576" . to_string( )
1205
+ } ) ) ,
1206
+ ) ;
1207
+ }
1042
1208
}
0 commit comments