Skip to main content

fixed_bigint/fixeduint/
div_ceil_impl.rs

1// Copyright 2021 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Ceiling division for FixedUInt.
16
17use super::{const_div, const_is_zero, FixedUInt, MachineWord};
18use crate::const_numtraits::{ConstCheckedAdd, ConstDivCeil, ConstOne};
19use crate::machineword::ConstMachineWord;
20
21c0nst::c0nst! {
22    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst ConstDivCeil for FixedUInt<T, N> {
23        fn div_ceil(self, rhs: Self) -> Self {
24            match self.checked_div_ceil(rhs) {
25                Some(v) => v,
26                None => panic!("div_ceil: division by zero or overflow"),
27            }
28        }
29
30        fn checked_div_ceil(self, rhs: Self) -> Option<Self> {
31            if const_is_zero(&rhs.array) {
32                return None;
33            }
34            // Use const_div which computes both quotient and remainder in one pass
35            let mut quotient = self.array;
36            let remainder = const_div(&mut quotient, &rhs.array);
37            if const_is_zero(&remainder) {
38                Some(Self { array: quotient })
39            } else {
40                // Use checked_add to return None on overflow
41                ConstCheckedAdd::checked_add(&Self { array: quotient }, &Self::one())
42            }
43        }
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50
51    #[test]
52    fn test_div_ceil() {
53        type U16 = FixedUInt<u8, 2>;
54
55        // Exact division
56        assert_eq!(
57            ConstDivCeil::div_ceil(U16::from(10u8), U16::from(5u8)),
58            U16::from(2u8)
59        );
60        assert_eq!(
61            ConstDivCeil::div_ceil(U16::from(10u8), U16::from(2u8)),
62            U16::from(5u8)
63        );
64
65        // Rounds up
66        assert_eq!(
67            ConstDivCeil::div_ceil(U16::from(10u8), U16::from(3u8)),
68            U16::from(4u8)
69        ); // 10/3 = 3.33... -> 4
70        assert_eq!(
71            ConstDivCeil::div_ceil(U16::from(11u8), U16::from(3u8)),
72            U16::from(4u8)
73        ); // 11/3 = 3.66... -> 4
74        assert_eq!(
75            ConstDivCeil::div_ceil(U16::from(12u8), U16::from(3u8)),
76            U16::from(4u8)
77        ); // 12/3 = 4 exact
78
79        // Edge cases
80        assert_eq!(
81            ConstDivCeil::div_ceil(U16::from(0u8), U16::from(5u8)),
82            U16::from(0u8)
83        );
84        assert_eq!(
85            ConstDivCeil::div_ceil(U16::from(1u8), U16::from(5u8)),
86            U16::from(1u8)
87        ); // 1/5 = 0.2 -> 1
88        assert_eq!(
89            ConstDivCeil::div_ceil(U16::from(1u8), U16::from(1u8)),
90            U16::from(1u8)
91        );
92    }
93
94    #[test]
95    fn test_checked_div_ceil() {
96        type U16 = FixedUInt<u8, 2>;
97
98        assert_eq!(
99            ConstDivCeil::checked_div_ceil(U16::from(10u8), U16::from(3u8)),
100            Some(U16::from(4u8))
101        );
102
103        // Division by zero
104        assert_eq!(
105            ConstDivCeil::checked_div_ceil(U16::from(10u8), U16::from(0u8)),
106            None
107        );
108
109        // Edge case: MAX / 2 = 32767 remainder 1, ceil = 32768
110        assert_eq!(
111            ConstDivCeil::checked_div_ceil(U16::from(65535u16), U16::from(2u16)),
112            Some(U16::from(32768u16))
113        );
114
115        // Edge case: MAX / 1 = MAX exactly (no remainder, no +1 needed)
116        assert_eq!(
117            ConstDivCeil::checked_div_ceil(U16::from(65535u16), U16::from(1u16)),
118            Some(U16::from(65535u16))
119        );
120    }
121
122    c0nst::c0nst! {
123        pub c0nst fn const_div_ceil<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(
124            a: FixedUInt<T, N>,
125            b: FixedUInt<T, N>,
126        ) -> FixedUInt<T, N> {
127            ConstDivCeil::div_ceil(a, b)
128        }
129    }
130
131    #[test]
132    fn test_const_div_ceil() {
133        type U16 = FixedUInt<u8, 2>;
134
135        assert_eq!(
136            const_div_ceil(U16::from(10u8), U16::from(3u8)),
137            U16::from(4u8)
138        );
139
140        #[cfg(feature = "nightly")]
141        {
142            const TEN: U16 = FixedUInt { array: [10, 0] };
143            const THREE: U16 = FixedUInt { array: [3, 0] };
144            const RESULT: U16 = const_div_ceil(TEN, THREE);
145            assert_eq!(RESULT, FixedUInt { array: [4, 0] });
146        }
147    }
148}