soroban_fixed_point_math/
i256.rs

1use soroban_sdk::{Env, I256};
2
3use crate::soroban_fixed_point::SorobanFixedPoint;
4
5impl SorobanFixedPoint for I256 {
6    fn fixed_mul_floor(&self, env: &Env, y: &I256, denominator: &I256) -> I256 {
7        mul_div_floor(env, &self, y, denominator)
8    }
9
10    fn fixed_mul_ceil(&self, env: &Env, y: &I256, denominator: &I256) -> I256 {
11        mul_div_ceil(env, &self, y, denominator)
12    }
13
14    fn fixed_div_floor(&self, env: &Env, y: &I256, denominator: &I256) -> I256 {
15        mul_div_floor(env, &self, denominator, y)
16    }
17
18    fn fixed_div_ceil(&self, env: &Env, y: &I256, denominator: &I256) -> I256 {
19        mul_div_ceil(env, &self, denominator, y)
20    }
21}
22
23/// Performs floor(x * y / z)
24pub(crate) fn mul_div_floor(env: &Env, x: &I256, y: &I256, z: &I256) -> I256 {
25    let zero = I256::from_i32(env, 0);
26    let r = x.mul(&y);
27    if r < zero || (r > zero && z.clone() < zero) {
28        // ceiling is taken by default for a negative result
29        let remainder = r.rem_euclid(&z);
30        let one = I256::from_i32(env, 1);
31        r.div(&z).sub(if remainder > zero { &one } else { &zero })
32    } else {
33        // floor taken by default for a positive or zero result
34        r.div(&z)
35    }
36}
37
38/// Performs ceil(x * y / z)
39pub(crate) fn mul_div_ceil(env: &Env, x: &I256, y: &I256, z: &I256) -> I256 {
40    let zero = I256::from_i32(env, 0);
41    let r = x.mul(&y);
42    if r <= zero || (r > zero && z.clone() < zero) {
43        // ceiling is taken by default for a negative or zero result
44        r.div(&z)
45    } else {
46        // floor taken by default for a positive result
47        let remainder = r.rem_euclid(&z);
48        let one = I256::from_i32(env, 1);
49        r.div(&z).add(if remainder > zero { &one } else { &zero })
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56
57    /********** fixed_mul_floor **********/
58
59    #[test]
60    fn test_fixed_mul_floor_rounds_down() {
61        let env = Env::default();
62        let x: I256 = I256::from_i128(&env, 1_5391283);
63        let y: I256 = I256::from_i128(&env, 314_1592653);
64        let denominator: I256 = I256::from_i128(&env, 1_0000001);
65
66        let result = x.fixed_mul_floor(&env, &y, &denominator);
67
68        assert_eq!(result, I256::from_i128(&env, 483_5313675));
69    }
70
71    #[test]
72    fn test_fixed_mul_floor_negative_rounds_down() {
73        let env = Env::default();
74        let x: I256 = I256::from_i128(&env, -1_5391283);
75        let y: I256 = I256::from_i128(&env, 314_1592653);
76        let denominator: I256 = I256::from_i128(&env, 1_0000001);
77
78        let result = x.fixed_mul_floor(&env, &y, &denominator);
79
80        assert_eq!(result, I256::from_i128(&env, -483_5313676));
81    }
82
83    #[test]
84    fn test_fixed_mul_floor_large_number() {
85        let env = Env::default();
86        let x: I256 = I256::from_i128(&env, i128::MAX);
87        let y: I256 = I256::from_i128(&env, 10i128.pow(38));
88        let denominator: I256 = I256::from_i128(&env, 10i128.pow(18));
89
90        let result = x.clone().fixed_mul_floor(&env, &y, &denominator);
91
92        let expected_result = x.mul(&I256::from_i128(&env, 10i128.pow(20)));
93        assert_eq!(result, expected_result);
94    }
95
96    #[test]
97    #[should_panic(expected = "attempt to multiply with overflow")]
98    fn test_fixed_mul_floor_phantom_overflow() {
99        let env = Env::default();
100        let x: I256 = I256::from_i128(&env, i128::MAX);
101        // 256 bit max ~= 5.8e76, 128 bit max ~= 1.7e38, need to multiply by at least 10^39
102        let y: I256 = I256::from_i128(&env, 10i128.pow(39));
103        let denominator: I256 = I256::from_i128(&env, 10i128.pow(18));
104
105        x.fixed_mul_floor(&env, &y, &denominator);
106    }
107
108    /********** fixed_mul_ceil **********/
109
110    #[test]
111    fn test_fixed_mul_ceil_rounds_up() {
112        let env = Env::default();
113        let x: I256 = I256::from_i128(&env, 1_5391283);
114        let y: I256 = I256::from_i128(&env, 314_1592653);
115        let denominator: I256 = I256::from_i128(&env, 1_0000001);
116
117        let result = x.fixed_mul_ceil(&env, &y, &denominator);
118
119        assert_eq!(result, I256::from_i128(&env, 483_5313676));
120    }
121
122    #[test]
123    fn test_fixed_mul_ceil_negative_rounds_up() {
124        let env = Env::default();
125        let x: I256 = I256::from_i128(&env, -1_5391283);
126        let y: I256 = I256::from_i128(&env, 314_1592653);
127        let denominator: I256 = I256::from_i128(&env, 1_0000001);
128
129        let result = x.fixed_mul_ceil(&env, &y, &denominator);
130
131        assert_eq!(result, I256::from_i128(&env, -483_5313675));
132    }
133
134    #[test]
135    fn test_fixed_mul_ceil_large_number() {
136        let env = Env::default();
137        let x: I256 = I256::from_i128(&env, i128::MAX);
138        let y: I256 = I256::from_i128(&env, 10i128.pow(38));
139        let denominator: I256 = I256::from_i128(&env, 10i128.pow(18));
140
141        let result = x.clone().fixed_mul_ceil(&env, &y, &denominator);
142
143        let expected_result = x.mul(&I256::from_i128(&env, 10i128.pow(20)));
144        assert_eq!(result, expected_result);
145    }
146
147    #[test]
148    #[should_panic(expected = "attempt to multiply with overflow")]
149    fn test_fixed_mul_ceil_phantom_overflow() {
150        let env = Env::default();
151        let x: I256 = I256::from_i128(&env, i128::MAX);
152        // 256 bit max ~= 5.8e76, 128 bit max ~= 1.7e38, need to multiply by at least 10^39
153        let y: I256 = I256::from_i128(&env, 10i128.pow(39));
154        let denominator: I256 = I256::from_i128(&env, 10i128.pow(18));
155
156        x.fixed_mul_ceil(&env, &y, &denominator);
157    }
158
159    /********** fixed_div_floor **********/
160
161    #[test]
162    fn test_fixed_div_floor_rounds_down() {
163        let env = Env::default();
164        let x: I256 = I256::from_i128(&env, 314_1592653);
165        let y: I256 = I256::from_i128(&env, 1_5391280);
166        let denominator: I256 = I256::from_i128(&env, 1_0000000);
167
168        let result = x.fixed_div_floor(&env, &y, &denominator);
169
170        assert_eq!(result, I256::from_i128(&env, 204_1150997));
171    }
172
173    #[test]
174    fn test_fixed_div_floor_negative_rounds_down() {
175        let env = Env::default();
176        let x: I256 = I256::from_i128(&env, 314_1592653);
177        let y: I256 = I256::from_i128(&env, -1_5391280);
178        let denominator: I256 = I256::from_i128(&env, 1_0000000);
179
180        let result = x.fixed_div_floor(&env, &y, &denominator);
181
182        assert_eq!(result, I256::from_i128(&env, -204_1150998));
183    }
184
185    #[test]
186    fn test_fixed_div_floor_large_number() {
187        let env = Env::default();
188        let x: I256 = I256::from_i128(&env, i128::MAX);
189        let y: I256 = I256::from_i128(&env, 10i128.pow(27));
190        let denominator: I256 = I256::from_i128(&env, 10i128.pow(38));
191
192        let result = x.clone().fixed_div_floor(&env, &y, &denominator);
193
194        let expected_result = x.mul(&I256::from_i128(&env, 10i128.pow(11)));
195        assert_eq!(result, expected_result);
196    }
197
198    #[test]
199    #[should_panic(expected = "attempt to multiply with overflow")]
200    fn test_fixed_div_floor_phantom_overflow() {
201        let env = Env::default();
202        let x: I256 = I256::from_i128(&env, i128::MAX);
203        let y: I256 = I256::from_i128(&env, 10i128.pow(27));
204        // 256 bit max ~= 5.8e76, 128 bit max ~= 1.7e38, need to multiply by at least 10^39
205        let denominator: I256 = I256::from_i128(&env, 10i128.pow(39));
206
207        x.fixed_div_floor(&env, &y, &denominator);
208    }
209
210    /********** fixed_div_ceil **********/
211
212    #[test]
213    fn test_fixed_div_ceil_rounds_down() {
214        let env = Env::default();
215        let x: I256 = I256::from_i128(&env, 314_1592653);
216        let y: I256 = I256::from_i128(&env, 1_5391280);
217        let denominator: I256 = I256::from_i128(&env, 1_0000000);
218
219        let result = x.fixed_div_ceil(&env, &y, &denominator);
220
221        assert_eq!(result, I256::from_i128(&env, 204_1150998));
222    }
223
224    #[test]
225    fn test_fixed_div_ceil_negative_rounds_down() {
226        let env = Env::default();
227        let x: I256 = I256::from_i128(&env, 314_1592653);
228        let y: I256 = I256::from_i128(&env, -1_5391280);
229        let denominator: I256 = I256::from_i128(&env, 1_0000000);
230
231        let result = x.fixed_div_ceil(&env, &y, &denominator);
232
233        assert_eq!(result, I256::from_i128(&env, -204_1150997));
234    }
235
236    #[test]
237    fn test_fixed_div_ceil_large_number() {
238        let env = Env::default();
239        let x: I256 = I256::from_i128(&env, i128::MAX);
240        let y: I256 = I256::from_i128(&env, 10i128.pow(27));
241        let denominator: I256 = I256::from_i128(&env, 10i128.pow(38));
242
243        let result = x.clone().fixed_div_ceil(&env, &y, &denominator);
244
245        let expected_result = x.mul(&I256::from_i128(&env, 10i128.pow(11)));
246        assert_eq!(result, expected_result);
247    }
248
249    #[test]
250    #[should_panic(expected = "attempt to multiply with overflow")]
251    fn test_fixed_div_ceil_phantom_overflow() {
252        let env = Env::default();
253        let x: I256 = I256::from_i128(&env, i128::MAX);
254        let y: I256 = I256::from_i128(&env, 10i128.pow(27));
255        // 256 bit max ~= 5.8e76, 128 bit max ~= 1.7e38, need to multiply by at least 10^39
256        let denominator: I256 = I256::from_i128(&env, 10i128.pow(39));
257
258        x.fixed_div_ceil(&env, &y, &denominator);
259    }
260}