soroban_fixed_point_math/
i64.rs

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