soroban_fixed_point_math/
u128.rs1use soroban_sdk::{unwrap::UnwrapOptimized, Env, U256};
2
3use crate::{fixed_point::FixedPoint, SorobanFixedPoint};
4
5impl FixedPoint for u128 {
6 fn fixed_mul_floor(self, y: u128, denominator: u128) -> Option<u128> {
7 mul_div_floor(self, y, denominator)
8 }
9
10 fn fixed_mul_ceil(self, y: u128, denominator: u128) -> Option<u128> {
11 mul_div_ceil(self, y, denominator)
12 }
13
14 fn fixed_div_floor(self, y: u128, denominator: u128) -> Option<u128> {
15 mul_div_floor(self, denominator, y)
16 }
17
18 fn fixed_div_ceil(self, y: u128, denominator: u128) -> Option<u128> {
19 mul_div_ceil(self, denominator, y)
20 }
21}
22
23pub(crate) fn mul_div_floor(x: u128, y: u128, z: u128) -> Option<u128> {
25 let r = x.checked_mul(y)?;
26 r.checked_div(z)
27}
28
29pub(crate) fn mul_div_ceil(x: u128, y: u128, z: u128) -> Option<u128> {
31 let r = x.checked_mul(y)?;
32 div_ceil(r, z)
33}
34
35fn div_ceil(r: u128, z: u128) -> Option<u128> {
37 let remainder = r.checked_rem_euclid(z)?;
39 (r / z).checked_add(if remainder > 0 { 1 } else { 0 })
40}
41
42impl SorobanFixedPoint for u128 {
43 fn fixed_mul_floor(&self, env: &Env, y: &u128, denominator: &u128) -> u128 {
44 scaled_mul_div_floor(self, env, y, denominator)
45 }
46
47 fn fixed_mul_ceil(&self, env: &Env, y: &u128, denominator: &u128) -> u128 {
48 scaled_mul_div_ceil(&self, env, y, denominator)
49 }
50
51 fn fixed_div_floor(&self, env: &Env, y: &u128, denominator: &u128) -> u128 {
52 scaled_mul_div_floor(self, env, denominator, y)
53 }
54
55 fn fixed_div_ceil(&self, env: &Env, y: &u128, denominator: &u128) -> u128 {
56 scaled_mul_div_ceil(self, env, denominator, y)
57 }
58}
59
60fn scaled_mul_div_floor(x: &u128, env: &Env, y: &u128, z: &u128) -> u128 {
62 return match x.checked_mul(*y) {
63 Some(r) => r.checked_div(*z).unwrap_optimized(),
64 None => {
65 let res = crate::u256::mul_div_floor(
67 &U256::from_u128(&env, *x),
68 &U256::from_u128(&env, *y),
69 &U256::from_u128(&env, *z),
70 );
71 res.to_u128().unwrap_optimized()
73 }
74 };
75}
76
77fn scaled_mul_div_ceil(x: &u128, env: &Env, y: &u128, z: &u128) -> u128 {
79 return match x.checked_mul(*y) {
80 Some(r) => div_ceil(r, *z).unwrap_optimized(),
81 None => {
82 let res = crate::u256::mul_div_ceil(
84 &env,
85 &U256::from_u128(&env, *x),
86 &U256::from_u128(&env, *y),
87 &U256::from_u128(&env, *z),
88 );
89 res.to_u128().unwrap_optimized()
91 }
92 };
93}
94
95#[cfg(test)]
96mod test_fixed_point {
97
98 use crate::FixedPoint;
101
102 #[test]
103 fn test_fixed_mul_floor_rounds_down() {
104 let x: u128 = 1_5391283;
105 let y: u128 = 314_1592653;
106 let denominator: u128 = 1_0000001;
107
108 let result = x.fixed_mul_floor(y, denominator).unwrap();
109
110 assert_eq!(result, 483_5313675)
111 }
112
113 #[test]
114 fn test_fixed_mul_floor_large_number() {
115 let x: u128 = 340_282_366_920_938_463_463;
116 let y: u128 = 1_000_000_000_000_000_000;
117 let denominator: u128 = 1_000_000_000_000_000_000;
118
119 let result = x.fixed_mul_floor(y, denominator).unwrap();
120
121 assert_eq!(result, 340_282_366_920_938_463_463)
122 }
123
124 #[test]
125 fn test_fixed_mul_floor_phantom_overflow() {
126 let x: u128 = 340_282_366_920_938_463_463;
127 let y: u128 = 1_000_000_000_000_000_001;
128 let denominator: u128 = 1_000_000_000_000_000_000;
129
130 let result = x.fixed_mul_floor(y, denominator);
131
132 assert_eq!(None, result);
133 }
134
135 #[test]
138 fn test_fixed_mul_ceil_rounds_up() {
139 let x: u128 = 1_5391283;
140 let y: u128 = 314_1592653;
141 let denominator: u128 = 1_0000001;
142
143 let result = x.fixed_mul_ceil(y, denominator).unwrap();
144
145 assert_eq!(result, 483_5313676)
146 }
147
148 #[test]
149 fn test_fixed_mul_ceil_large_number() {
150 let x: u128 = 340_282_366_920_938_463_463;
151 let y: u128 = 1_000_000_000_000_000_000;
152 let denominator: u128 = 1_000_000_000_000_000_000;
153
154 let result = x.fixed_mul_ceil(y, denominator).unwrap();
155
156 assert_eq!(result, 340_282_366_920_938_463_463)
157 }
158
159 #[test]
160 fn test_fixed_mul_ceil_phantom_overflow() {
161 let x: u128 = 340_282_366_920_938_463_463;
162 let y: u128 = 1_000_000_000_000_000_001;
163 let denominator: u128 = 1_000_000_000_000_000_000;
164
165 let result = x.fixed_mul_ceil(y, denominator);
166
167 assert_eq!(None, result);
168 }
169
170 #[test]
173 fn test_fixed_div_floor_rounds_down() {
174 let x: u128 = 314_1592653;
175 let y: u128 = 1_5391280;
176 let denominator: u128 = 1_0000000;
177
178 let result = x.fixed_div_floor(y, denominator).unwrap();
179
180 assert_eq!(result, 204_1150997)
181 }
182
183 #[test]
184 fn test_fixed_div_floor_large_number() {
185 let x: u128 = 340_282_366_920_938_463_463;
186 let y: u128 = 1_000_000_000_000_000_000;
187 let denominator: u128 = 1_000_000_000_000_000_000;
188
189 let result = x.fixed_div_floor(y, denominator).unwrap();
190
191 assert_eq!(result, 340_282_366_920_938_463_463)
192 }
193
194 #[test]
195 fn test_fixed_div_floor_phantom_overflow() {
196 let x: u128 = 340_282_366_920_938_463_463;
197 let y: u128 = 1_000_000_000_000_000_000;
198 let denominator: u128 = 1_000_000_000_000_000_001;
199
200 let result = x.fixed_div_floor(y, denominator);
201
202 assert_eq!(None, result);
203 }
204
205 #[test]
208 fn test_fixed_div_ceil_rounds_down() {
209 let x: u128 = 314_1592653;
210 let y: u128 = 1_5391280;
211 let denominator: u128 = 1_0000000;
212
213 let result = x.fixed_div_ceil(y, denominator).unwrap();
214
215 assert_eq!(result, 204_1150998)
216 }
217
218 #[test]
219 fn test_fixed_div_ceil_large_number() {
220 let x: u128 = 340_282_366_920_938_463_463;
221 let y: u128 = 1_000_000_000_000_000_000;
222 let denominator: u128 = 1_000_000_000_000_000_000;
223
224 let result = x.fixed_div_ceil(y, denominator).unwrap();
225
226 assert_eq!(result, 340_282_366_920_938_463_463)
227 }
228
229 #[test]
230 fn test_fixed_div_ceil_phantom_overflow() {
231 let x: u128 = 340_282_366_920_938_463_463;
232 let y: u128 = 1_000_000_000_000_000_000;
233 let denominator: u128 = 1_000_000_000_000_000_001;
234
235 let result = x.fixed_div_ceil(y, denominator);
236
237 assert_eq!(None, result);
238 }
239}
240
241#[cfg(test)]
242mod test_soroban_fixed_point {
243 use crate::SorobanFixedPoint;
244 use soroban_sdk::Env;
245
246 #[test]
249 fn test_fixed_mul_floor_rounds_down() {
250 let env = Env::default();
251 let x: u128 = 1_5391283;
252 let y: u128 = 314_1592653;
253 let denominator: u128 = 1_0000001;
254
255 let result = x.fixed_mul_floor(&env, &y, &denominator);
256
257 assert_eq!(result, 483_5313675)
258 }
259
260 #[test]
261 fn test_fixed_mul_floor_phantom_overflow_scales() {
262 let env = Env::default();
263 let x: u128 = 340_282_366_920_938_463_463;
264 let y: u128 = 10u128.pow(27);
265 let denominator: u128 = 10u128.pow(18);
266
267 let result = x.fixed_mul_floor(&env, &y, &denominator);
268
269 assert_eq!(result, 340_282_366_920_938_463_463 * 10u128.pow(9));
270 }
271
272 #[test]
275 fn test_fixed_mul_ceil_rounds_up() {
276 let env = Env::default();
277 let x: u128 = 1_5391283;
278 let y: u128 = 314_1592653;
279 let denominator: u128 = 1_0000001;
280
281 let result = x.fixed_mul_ceil(&env, &y, &denominator);
282
283 assert_eq!(result, 483_5313676)
284 }
285
286 #[test]
287 fn test_fixed_mul_ceil_large_number() {
288 let env = Env::default();
289 let x: u128 = 340_282_366_920_938_463_463;
290 let y: u128 = 1_000_000_000_000_000_000;
291 let denominator: u128 = 1_000_000_000_000_000_000;
292
293 let result = x.fixed_mul_ceil(&env, &y, &denominator);
294
295 assert_eq!(result, 340_282_366_920_938_463_463)
296 }
297
298 #[test]
299 fn test_fixed_mul_ceil_phantom_overflow_scales() {
300 let env = Env::default();
301 let x: u128 = 340_282_366_920_938_463_463;
302 let y: u128 = 10u128.pow(27);
303 let denominator: u128 = 10u128.pow(18);
304
305 let result = x.fixed_mul_ceil(&env, &y, &denominator);
306
307 assert_eq!(result, 340_282_366_920_938_463_463 * 10u128.pow(9));
308 }
309
310 #[test]
313 fn test_fixed_div_floor_rounds_down() {
314 let env = Env::default();
315 let x: u128 = 314_1592653;
316 let y: u128 = 1_5391280;
317 let denominator: u128 = 1_0000000;
318
319 let result = x.fixed_div_floor(&env, &y, &denominator);
320
321 assert_eq!(result, 204_1150997)
322 }
323
324 #[test]
325 fn test_fixed_div_floor_phantom_overflow_scales() {
326 let env = Env::default();
327 let x: u128 = 340_282_366_920_938_463_463;
328 let y: u128 = 10u128.pow(18);
329 let denominator: u128 = 10u128.pow(27);
330
331 let result = x.fixed_div_floor(&env, &y, &denominator);
332
333 assert_eq!(result, 340_282_366_920_938_463_463 * 10u128.pow(9));
334 }
335
336 #[test]
339 fn test_fixed_div_ceil_rounds_down() {
340 let env = Env::default();
341 let x: u128 = 314_1592653;
342 let y: u128 = 1_5391280;
343 let denominator: u128 = 1_0000000;
344
345 let result = x.fixed_div_ceil(&env, &y, &denominator);
346
347 assert_eq!(result, 204_1150998)
348 }
349
350 #[test]
351 fn test_fixed_div_ceil_large_number() {
352 let env = Env::default();
353 let x: u128 = 340_282_366_920_938_463_463;
354 let y: u128 = 1_000_000_000_000_000_000;
355 let denominator: u128 = 1_000_000_000_000_000_000;
356
357 let result = x.fixed_div_ceil(&env, &y, &denominator);
358
359 assert_eq!(result, 340_282_366_920_938_463_463)
360 }
361
362 #[test]
363 fn test_fixed_div_ceil_phantom_overflow_scales() {
364 let env = Env::default();
365 let x: u128 = 340_282_366_920_938_463_463;
366 let y: u128 = 10u128.pow(18);
367 let denominator: u128 = 10u128.pow(27);
368
369 let result = x.fixed_div_floor(&env, &y, &denominator);
370
371 assert_eq!(result, 340_282_366_920_938_463_463 * 10u128.pow(9));
372 }
373}