fixed_point_math/
i128.rs

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