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}