1#[inline(always)]
2pub fn roundup(x: u64, round: u64) -> u64 {
3 debug_assert!(round.is_power_of_two());
4 x.wrapping_add((!x).wrapping_add(1) & (round.wrapping_sub(1)))
6}
7
8#[inline(always)]
9pub fn rounddown(x: u64, round: u64) -> u64 {
10 debug_assert!(round.is_power_of_two());
11 x & !(round.wrapping_sub(1))
13}
14
15#[cfg(test)]
16mod tests {
17 use super::*;
18 use proptest::prelude::*;
19
20 #[test]
21 fn test_roundup() {
22 assert_eq!(0, roundup(0, 16));
23 assert_eq!(16, roundup(1, 16));
24 assert_eq!(16, roundup(15, 16));
25 assert_eq!(16, roundup(16, 16));
26 assert_eq!(32, roundup(17, 16));
27 assert_eq!(u64::MAX - 15, roundup(u64::MAX - 15, 16));
28 assert_eq!(0, roundup(u64::MAX, 16));
29 }
30
31 #[test]
32 fn test_rounddown() {
33 assert_eq!(0, rounddown(0, 16));
34 assert_eq!(0, rounddown(1, 16));
35 assert_eq!(0, rounddown(15, 16));
36 assert_eq!(16, rounddown(16, 16));
37 assert_eq!(16, rounddown(17, 16));
38 assert_eq!(u64::MAX - 15, rounddown(u64::MAX, 16));
39 }
40
41 proptest! {
42 #[test]
43 #[cfg_attr(all(miri, feature = "miri-ci"), ignore)]
44 fn roundup_proptest(x: u64, round in (0u32..16).prop_map(|d| 2u64.pow(d))) {
45 prop_assume!(x.checked_add(round).is_some(), "avoid integer overflow");
46 let result = roundup(x, round);
47
48 assert_eq!(result % round, 0);
50
51 assert!(result >= x);
53
54 assert!(result < x + round);
56 }
57
58 #[test]
59 #[cfg_attr(all(miri, feature = "miri-ci"), ignore)]
60 fn rounddown_proptest(x: u64, round in (0u32..16).prop_map(|d| 2u64.pow(d))) {
61 let result = rounddown(x, round);
62
63 assert_eq!(result % round, 0);
65
66 assert!(result <= x);
68
69 if let Some(lower_bound) = x.checked_sub(round) {
71 assert!(result > lower_bound);
72 } else {
73 assert_eq!(result, 0);
74 }
75 }
76 }
77}