soroban_fixed_point_math/
i128.rs1use soroban_sdk::{unwrap::UnwrapOptimized, Env, I256};
2
3use crate::{fixed_point::FixedPoint, SorobanFixedPoint};
4
5impl FixedPoint for i128 {
6    fn fixed_mul_floor(self, y: i128, denominator: i128) -> Option<i128> {
7        mul_div_floor(self, y, denominator)
8    }
9
10    fn fixed_mul_ceil(self, y: i128, denominator: i128) -> Option<i128> {
11        mul_div_ceil(self, y, denominator)
12    }
13
14    fn fixed_div_floor(self, y: i128, denominator: i128) -> Option<i128> {
15        mul_div_floor(self, denominator, y)
16    }
17
18    fn fixed_div_ceil(self, y: i128, denominator: i128) -> Option<i128> {
19        mul_div_ceil(self, denominator, y)
20    }
21}
22
23pub(crate) fn mul_div_floor(x: i128, y: i128, z: i128) -> Option<i128> {
25    let r = x.checked_mul(y)?;
26    div_floor(r, z)
27}
28
29fn div_floor(r: i128, z: i128) -> Option<i128> {
31    if r < 0 || (r > 0 && z < 0) {
32        let remainder = r.checked_rem_euclid(z)?;
34        (r / z).checked_sub(if remainder > 0 { 1 } else { 0 })
35    } else {
36        r.checked_div(z)
38    }
39}
40
41pub(crate) fn mul_div_ceil(x: i128, y: i128, z: i128) -> Option<i128> {
43    let r = x.checked_mul(y)?;
44    div_ceil(r, z)
45}
46
47fn div_ceil(r: i128, z: i128) -> Option<i128> {
49    if r <= 0 || (r > 0 && z < 0) {
50        r.checked_div(z)
52    } else {
53        let remainder = r.checked_rem_euclid(z)?;
55        (r / z).checked_add(if remainder > 0 { 1 } else { 0 })
56    }
57}
58
59impl SorobanFixedPoint for i128 {
60    fn fixed_mul_floor(&self, env: &Env, y: &i128, denominator: &i128) -> i128 {
61        scaled_mul_div_floor(&self, env, y, denominator)
62    }
63
64    fn fixed_mul_ceil(&self, env: &Env, y: &i128, denominator: &i128) -> i128 {
65        scaled_mul_div_ceil(&self, env, y, denominator)
66    }
67
68    fn fixed_div_floor(&self, env: &Env, y: &i128, denominator: &i128) -> i128 {
69        scaled_mul_div_floor(&self, env, denominator, y)
70    }
71
72    fn fixed_div_ceil(&self, env: &Env, y: &i128, denominator: &i128) -> i128 {
73        scaled_mul_div_ceil(&self, env, denominator, y)
74    }
75}
76
77fn scaled_mul_div_floor(x: &i128, env: &Env, y: &i128, z: &i128) -> i128 {
79    return match x.checked_mul(*y) {
80        Some(r) => div_floor(r, *z).unwrap_optimized(),
81        None => {
82            let res = crate::i256::mul_div_floor(
84                &env,
85                &I256::from_i128(&env, *x),
86                &I256::from_i128(&env, *y),
87                &I256::from_i128(&env, *z),
88            );
89            res.to_i128().unwrap_optimized()
91        }
92    };
93}
94
95fn scaled_mul_div_ceil(x: &i128, env: &Env, y: &i128, z: &i128) -> i128 {
97    return match x.checked_mul(*y) {
98        Some(r) => div_ceil(r, *z).unwrap_optimized(),
99        None => {
100            let res = crate::i256::mul_div_ceil(
102                &env,
103                &I256::from_i128(&env, *x),
104                &I256::from_i128(&env, *y),
105                &I256::from_i128(&env, *z),
106            );
107            res.to_i128().unwrap_optimized()
109        }
110    };
111}
112
113#[cfg(test)]
114mod test_fixed_point {
115
116    use crate::FixedPoint;
119
120    #[test]
121    fn test_fixed_mul_floor_rounds_down() {
122        let x: i128 = 1_5391283;
123        let y: i128 = 314_1592653;
124        let denominator: i128 = 1_0000001;
125
126        let result = x.fixed_mul_floor(y, denominator).unwrap();
127
128        assert_eq!(result, 483_5313675)
129    }
130
131    #[test]
132    fn test_fixed_mul_floor_negative_rounds_down() {
133        let x: i128 = -1_5391283;
134        let y: i128 = 314_1592653;
135        let denominator: i128 = 1_0000001;
136
137        let result = x.fixed_mul_floor(y, denominator).unwrap();
138
139        assert_eq!(result, -483_5313676)
140    }
141
142    #[test]
143    fn test_fixed_mul_floor_large_number() {
144        let x: i128 = 170_141_183_460_469_231_731;
145        let y: i128 = 1_000_000_000_000_000_000;
146        let denominator: i128 = 1_000_000_000_000_000_000;
147
148        let result = x.fixed_mul_floor(y, denominator).unwrap();
149
150        assert_eq!(result, 170_141_183_460_469_231_731)
151    }
152
153    #[test]
154    fn test_fixed_mul_floor_phantom_overflow() {
155        let x: i128 = 170_141_183_460_469_231_731;
156        let y: i128 = 1_000_000_000_000_000_001;
157        let denominator: i128 = 1_000_000_000_000_000_000;
158
159        let result = x.fixed_mul_floor(y, denominator);
160
161        assert_eq!(None, result);
162    }
163
164    #[test]
167    fn test_fixed_mul_ceil_rounds_up() {
168        let x: i128 = 1_5391283;
169        let y: i128 = 314_1592653;
170        let denominator: i128 = 1_0000001;
171
172        let result = x.fixed_mul_ceil(y, denominator).unwrap();
173
174        assert_eq!(result, 483_5313676)
175    }
176
177    #[test]
178    fn test_fixed_mul_ceil_negative_rounds_up() {
179        let x: i128 = -1_5391283;
180        let y: i128 = 314_1592653;
181        let denominator: i128 = 1_0000001;
182
183        let result = x.fixed_mul_ceil(y, denominator).unwrap();
184
185        assert_eq!(result, -483_5313675)
186    }
187
188    #[test]
189    fn test_fixed_mul_ceil_large_number() {
190        let x: i128 = 170_141_183_460_469_231_731;
191        let y: i128 = 1_000_000_000_000_000_000;
192        let denominator: i128 = 1_000_000_000_000_000_000;
193
194        let result = x.fixed_mul_ceil(y, denominator).unwrap();
195
196        assert_eq!(result, 170_141_183_460_469_231_731)
197    }
198
199    #[test]
200    fn test_fixed_mul_ceil_phantom_overflow() {
201        let x: i128 = 170_141_183_460_469_231_731;
202        let y: i128 = 1_000_000_000_000_000_001;
203        let denominator: i128 = 1_000_000_000_000_000_000;
204
205        let result = x.fixed_mul_ceil(y, denominator);
206
207        assert_eq!(None, result);
208    }
209
210    #[test]
213    fn test_fixed_div_floor_rounds_down() {
214        let x: i128 = 314_1592653;
215        let y: i128 = 1_5391280;
216        let denominator: i128 = 1_0000000;
217
218        let result = x.fixed_div_floor(y, denominator).unwrap();
219
220        assert_eq!(result, 204_1150997)
221    }
222
223    #[test]
224    fn test_fixed_div_floor_negative_rounds_down() {
225        let x: i128 = 314_1592653;
226        let y: i128 = -1_5391280;
227        let denominator: i128 = 1_0000000;
228
229        let result = x.fixed_div_floor(y, denominator).unwrap();
230
231        assert_eq!(result, -204_1150998)
232    }
233
234    #[test]
235    fn test_fixed_div_floor_large_number() {
236        let x: i128 = 170_141_183_460_469_231_731;
237        let y: i128 = 1_000_000_000_000_000_000;
238        let denominator: i128 = 1_000_000_000_000_000_000;
239
240        let result = x.fixed_div_floor(y, denominator).unwrap();
241
242        assert_eq!(result, 170_141_183_460_469_231_731)
243    }
244
245    #[test]
246    fn test_fixed_div_floor_phantom_overflow() {
247        let x: i128 = 170_141_183_460_469_231_732;
248        let y: i128 = 1_000_000_000_000_000_000;
249        let denominator: i128 = 1_000_000_000_000_000_000;
250
251        let result = x.fixed_div_floor(y, denominator);
252
253        assert_eq!(None, result);
254    }
255
256    #[test]
259    fn test_fixed_div_ceil_rounds_down() {
260        let x: i128 = 314_1592653;
261        let y: i128 = 1_5391280;
262        let denominator: i128 = 1_0000000;
263
264        let result = x.fixed_div_ceil(y, denominator).unwrap();
265
266        assert_eq!(result, 204_1150998)
267    }
268
269    #[test]
270    fn test_fixed_div_ceil_negative_rounds_down() {
271        let x: i128 = 314_1592653;
272        let y: i128 = -1_5391280;
273        let denominator: i128 = 1_0000000;
274
275        let result = x.fixed_div_ceil(y, denominator).unwrap();
276
277        assert_eq!(result, -204_1150997)
278    }
279
280    #[test]
281    fn test_fixed_div_ceil_large_number() {
282        let x: i128 = 170_141_183_460_469_231_731;
283        let y: i128 = 1_000_000_000_000_000_000;
284        let denominator: i128 = 1_000_000_000_000_000_000;
285
286        let result = x.fixed_div_ceil(y, denominator).unwrap();
287
288        assert_eq!(result, 170_141_183_460_469_231_731)
289    }
290
291    #[test]
292    fn test_fixed_div_ceil_phantom_overflow() {
293        let x: i128 = 170_141_183_460_469_231_732;
294        let y: i128 = 1_000_000_000_000_000_000;
295        let denominator: i128 = 1_000_000_000_000_000_000;
296
297        let result = x.fixed_div_ceil(y, denominator);
298
299        assert_eq!(None, result);
300    }
301}
302
303#[cfg(test)]
304mod test_soroban_fixed_point {
305    use crate::SorobanFixedPoint;
306    use soroban_sdk::Env;
307
308    #[test]
311    fn test_fixed_mul_floor_rounds_down() {
312        let env = Env::default();
313        let x: i128 = 1_5391283;
314        let y: i128 = 314_1592653;
315        let denominator: i128 = 1_0000001;
316
317        let result = x.fixed_mul_floor(&env, &y, &denominator);
318
319        assert_eq!(result, 483_5313675)
320    }
321
322    #[test]
323    fn test_fixed_mul_floor_negative_rounds_down() {
324        let env = Env::default();
325        let x: i128 = -1_5391283;
326        let y: i128 = 314_1592653;
327        let denominator: i128 = 1_0000001;
328
329        let result = x.fixed_mul_floor(&env, &y, &denominator);
330
331        assert_eq!(result, -483_5313676)
332    }
333
334    #[test]
335    fn test_fixed_mul_floor_phantom_overflow_scales() {
336        let env = Env::default();
337        let x: i128 = 170_141_183_460_469_231_731;
338        let y: i128 = 10i128.pow(27);
339        let denominator: i128 = 10i128.pow(18);
340
341        let result = x.fixed_mul_floor(&env, &y, &denominator);
342
343        assert_eq!(result, 170_141_183_460_469_231_731 * 10i128.pow(9));
344    }
345
346    #[test]
349    fn test_fixed_mul_ceil_rounds_up() {
350        let env = Env::default();
351        let x: i128 = 1_5391283;
352        let y: i128 = 314_1592653;
353        let denominator: i128 = 1_0000001;
354
355        let result = x.fixed_mul_ceil(&env, &y, &denominator);
356
357        assert_eq!(result, 483_5313676)
358    }
359
360    #[test]
361    fn test_fixed_mul_ceil_negative_rounds_up() {
362        let env = Env::default();
363        let x: i128 = -1_5391283;
364        let y: i128 = 314_1592653;
365        let denominator: i128 = 1_0000001;
366
367        let result = x.fixed_mul_ceil(&env, &y, &denominator);
368
369        assert_eq!(result, -483_5313675)
370    }
371
372    #[test]
373    fn test_fixed_mul_ceil_large_number() {
374        let env = Env::default();
375        let x: i128 = 170_141_183_460_469_231_731;
376        let y: i128 = 1_000_000_000_000_000_000;
377        let denominator: i128 = 1_000_000_000_000_000_000;
378
379        let result = x.fixed_mul_ceil(&env, &y, &denominator);
380
381        assert_eq!(result, 170_141_183_460_469_231_731)
382    }
383
384    #[test]
385    fn test_fixed_mul_ceil_phantom_overflow_scales() {
386        let env = Env::default();
387        let x: i128 = 170_141_183_460_469_231_731;
388        let y: i128 = 10i128.pow(27);
389        let denominator: i128 = 10i128.pow(18);
390
391        let result = x.fixed_mul_ceil(&env, &y, &denominator);
392
393        assert_eq!(result, 170_141_183_460_469_231_731 * 10i128.pow(9));
394    }
395
396    #[test]
399    fn test_fixed_div_floor_rounds_down() {
400        let env = Env::default();
401        let x: i128 = 314_1592653;
402        let y: i128 = 1_5391280;
403        let denominator: i128 = 1_0000000;
404
405        let result = x.fixed_div_floor(&env, &y, &denominator);
406
407        assert_eq!(result, 204_1150997)
408    }
409
410    #[test]
411    fn test_fixed_div_floor_negative_rounds_down() {
412        let env = Env::default();
413        let x: i128 = 314_1592653;
414        let y: i128 = -1_5391280;
415        let denominator: i128 = 1_0000000;
416
417        let result = x.fixed_div_floor(&env, &y, &denominator);
418
419        assert_eq!(result, -204_1150998)
420    }
421
422    #[test]
423    fn test_fixed_div_floor_phantom_overflow_scales() {
424        let env = Env::default();
425        let x: i128 = 170_141_183_460_469_231_731;
426        let y: i128 = 10i128.pow(18);
427        let denominator: i128 = 10i128.pow(27);
428
429        let result = x.fixed_div_floor(&env, &y, &denominator);
430
431        assert_eq!(result, 170_141_183_460_469_231_731 * 10i128.pow(9));
432    }
433
434    #[test]
437    fn test_fixed_div_ceil_rounds_down() {
438        let env = Env::default();
439        let x: i128 = 314_1592653;
440        let y: i128 = 1_5391280;
441        let denominator: i128 = 1_0000000;
442
443        let result = x.fixed_div_ceil(&env, &y, &denominator);
444
445        assert_eq!(result, 204_1150998)
446    }
447
448    #[test]
449    fn test_fixed_div_ceil_negative_rounds_down() {
450        let env = Env::default();
451        let x: i128 = 314_1592653;
452        let y: i128 = -1_5391280;
453        let denominator: i128 = 1_0000000;
454
455        let result = x.fixed_div_ceil(&env, &y, &denominator);
456
457        assert_eq!(result, -204_1150997)
458    }
459
460    #[test]
461    fn test_fixed_div_ceil_large_number() {
462        let env = Env::default();
463        let x: i128 = 170_141_183_460_469_231_731;
464        let y: i128 = 1_000_000_000_000_000_000;
465        let denominator: i128 = 1_000_000_000_000_000_000;
466
467        let result = x.fixed_div_ceil(&env, &y, &denominator);
468
469        assert_eq!(result, 170_141_183_460_469_231_731)
470    }
471
472    #[test]
473    fn test_fixed_div_ceil_phantom_overflow_scales() {
474        let env = Env::default();
475        let x: i128 = 170_141_183_460_469_231_731;
476        let y: i128 = 10i128.pow(18);
477        let denominator: i128 = 10i128.pow(27);
478
479        let result = x.fixed_div_floor(&env, &y, &denominator);
480
481        assert_eq!(result, 170_141_183_460_469_231_731 * 10i128.pow(9));
482    }
483}