fixed_point_math/
u64.rs

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