Skip to main content

fixed_bigint/heapless/
div_rem.rs

1//! `core::ops::Div` / `Rem` for `HeaplessBigInt<T, CAP, Nct>`.
2//!
3//! Nct-only. Division is data-dependent (early-exits on shift, on
4//! `rem >= shifted`) — the personality rule mirrors `FixedUInt`, where
5//! `Div`/`Rem` are `Nct` only. Ct callers that need `x mod modulus` use
6//! Montgomery reduction via the CIOS driver, not this path.
7//!
8//! Algorithm is shift-and-subtract long division: shift the divisor to
9//! the highest bit position where it might fit into the remaining
10//! dividend, subtract when it does, set the corresponding quotient bit.
11//! Uses only ops HeaplessBigInt already exposes (`Shl<usize>`,
12//! `wrapping_sub`, `wrapping_add`, `cmp`, `bit_length`) — no direct
13//! limb access, no new bit-poke helpers.
14
15use super::HeaplessBigInt;
16use crate::MachineWord;
17use const_num_traits::{CarryingMul, CheckedAdd, CheckedDiv, CheckedRem, DivCeil, Nct, One, Zero};
18
19fn div_rem_impl<T, const CAP: usize>(
20    dividend: &HeaplessBigInt<T, CAP, Nct>,
21    divisor: &HeaplessBigInt<T, CAP, Nct>,
22) -> (HeaplessBigInt<T, CAP, Nct>, HeaplessBigInt<T, CAP, Nct>)
23where
24    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
25{
26    use core::cmp::Ordering;
27    assert!(
28        !<HeaplessBigInt<T, CAP, Nct> as Zero>::is_zero(divisor),
29        "HeaplessBigInt: divide by zero"
30    );
31
32    // Both outputs are carried at the operands' width `max(len)`, the same
33    // as the general path below — the early returns must widen too, or
34    // `x / x` would come back at `len == 1` and later width-preserving ops
35    // would run narrower than the operands.
36    let work_len = core::cmp::max(dividend.len(), divisor.len());
37
38    match dividend.cmp(divisor) {
39        Ordering::Less => {
40            return (
41                <HeaplessBigInt<T, CAP, Nct> as Zero>::zero().widened(work_len),
42                dividend.widened(work_len),
43            );
44        }
45        Ordering::Equal => {
46            return (
47                <HeaplessBigInt<T, CAP, Nct> as One>::one().widened(work_len),
48                <HeaplessBigInt<T, CAP, Nct> as Zero>::zero().widened(work_len),
49            );
50        }
51        Ordering::Greater => {}
52    }
53
54    let d_bits = dividend.bit_length();
55    let dv_bits = divisor.bit_length();
56    let mut shift = d_bits - dv_bits;
57
58    // `Shl` is width-preserving, so the divisor and the quotient-bit unit
59    // must be carried at `work_len` for the up-shifts to have room. `shift
60    // <= d_bits - dv_bits`, so `divisor << shift <= dividend`, which fits
61    // in `dividend`'s words — `work_len` never needs to exceed the operands.
62    let mut rem = dividend.widened(work_len);
63    let wide_divisor = divisor.widened(work_len);
64    let one = <HeaplessBigInt<T, CAP, Nct> as One>::one().widened(work_len);
65    let mut quotient = <HeaplessBigInt<T, CAP, Nct> as Zero>::zero().widened(work_len);
66
67    // Compute the top shift once, then walk down one bit per iteration. A
68    // fresh `wide_divisor << shift` each step would be an O(W·shift) shift;
69    // `>> 1` is O(W). `divisor << shift <= dividend` fits in `work_len`, so
70    // no significant bit is lost on the initial shift and `>> 1` faithfully
71    // reconstructs `<< (shift-k)`.
72    let mut shifted = wide_divisor << shift;
73    let mut bit = one << shift;
74    loop {
75        if rem >= shifted {
76            rem = rem.wrapping_sub(&shifted);
77            quotient = quotient.wrapping_add(&bit);
78        }
79        if shift == 0 {
80            break;
81        }
82        shifted >>= 1usize;
83        bit >>= 1usize;
84        shift -= 1;
85    }
86
87    (quotient, rem)
88}
89
90macro_rules! div_impls {
91    ($lhs:ty, $rhs:ty, $out:ty) => {
92        impl<T, const CAP: usize> core::ops::Div<$rhs> for $lhs
93        where
94            T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
95        {
96            type Output = $out;
97            fn div(self, other: $rhs) -> Self::Output {
98                div_rem_impl::<T, CAP>(&self, &other).0
99            }
100        }
101
102        impl<T, const CAP: usize> core::ops::Rem<$rhs> for $lhs
103        where
104            T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
105        {
106            type Output = $out;
107            fn rem(self, other: $rhs) -> Self::Output {
108                div_rem_impl::<T, CAP>(&self, &other).1
109            }
110        }
111    };
112}
113
114div_impls!(
115    HeaplessBigInt<T, CAP, Nct>,
116    HeaplessBigInt<T, CAP, Nct>,
117    HeaplessBigInt<T, CAP, Nct>
118);
119div_impls!(
120    HeaplessBigInt<T, CAP, Nct>,
121    &HeaplessBigInt<T, CAP, Nct>,
122    HeaplessBigInt<T, CAP, Nct>
123);
124div_impls!(
125    &HeaplessBigInt<T, CAP, Nct>,
126    HeaplessBigInt<T, CAP, Nct>,
127    HeaplessBigInt<T, CAP, Nct>
128);
129div_impls!(
130    &HeaplessBigInt<T, CAP, Nct>,
131    &HeaplessBigInt<T, CAP, Nct>,
132    HeaplessBigInt<T, CAP, Nct>
133);
134
135// ── Checked division / remainder ──
136//
137// Nct-only, like `Div`/`Rem`. Return `None` on divide-by-zero instead of the
138// `div_rem_impl` assert; the quotient/remainder is the value-width result the
139// same-width `FixedUInt` gives.
140
141impl<T, const CAP: usize> HeaplessBigInt<T, CAP, Nct>
142where
143    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
144{
145    /// Quotient and remainder in one pass. Panics on divide-by-zero, like the
146    /// `Div`/`Rem` operators; use `checked_div`/`checked_rem` to avoid the panic.
147    pub fn div_rem(&self, other: &Self) -> (Self, Self) {
148        div_rem_impl(self, other)
149    }
150
151    /// Checked division. `None` on divide-by-zero.
152    pub fn checked_div(&self, other: &Self) -> Option<Self> {
153        if <Self as Zero>::is_zero(other) {
154            None
155        } else {
156            Some(div_rem_impl(self, other).0)
157        }
158    }
159
160    /// Checked remainder. `None` on divide-by-zero.
161    pub fn checked_rem(&self, other: &Self) -> Option<Self> {
162        if <Self as Zero>::is_zero(other) {
163            None
164        } else {
165            Some(div_rem_impl(self, other).1)
166        }
167    }
168
169    /// Ceiling division. `None` on divide-by-zero or when rounding up
170    /// overflows the value width. Result width is `max(self.len, other.len)`.
171    pub fn checked_div_ceil(&self, other: &Self) -> Option<Self> {
172        if <Self as Zero>::is_zero(other) {
173            return None;
174        }
175        let (q, r) = div_rem_impl(self, other);
176        if <Self as Zero>::is_zero(&r) {
177            Some(q)
178        } else {
179            CheckedAdd::checked_add(q, <Self as One>::one())
180        }
181    }
182}
183
184// Value-form trait bridges to the by-reference inherent methods (free,
185// `HeaplessBigInt: Copy`). Nct-only, matching Div/Rem.
186
187impl<T, const CAP: usize> CheckedDiv for HeaplessBigInt<T, CAP, Nct>
188where
189    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
190{
191    type Output = Self;
192    fn checked_div(self, v: Self) -> Option<Self> {
193        Self::checked_div(&self, &v)
194    }
195}
196
197impl<T, const CAP: usize> CheckedRem for HeaplessBigInt<T, CAP, Nct>
198where
199    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
200{
201    type Output = Self;
202    fn checked_rem(self, v: Self) -> Option<Self> {
203        Self::checked_rem(&self, &v)
204    }
205}
206
207impl<T, const CAP: usize> DivCeil for HeaplessBigInt<T, CAP, Nct>
208where
209    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
210{
211    type Output = Self;
212    fn div_ceil(self, rhs: Self) -> Self {
213        match Self::checked_div_ceil(&self, &rhs) {
214            Some(v) => v,
215            None => panic!("HeaplessBigInt::div_ceil: division by zero or overflow"),
216        }
217    }
218}
219
220// Reference-receiver mirrors: `(&h).checked_div(&g)` binds the same generic
221// trait bound as the value form. `HeaplessBigInt: Copy`, so the deref-and-
222// forward is a no-op at runtime. Nct-only, matching the value forms.
223
224impl<T, const CAP: usize> CheckedDiv for &HeaplessBigInt<T, CAP, Nct>
225where
226    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
227{
228    type Output = HeaplessBigInt<T, CAP, Nct>;
229    fn checked_div(self, v: Self) -> Option<Self::Output> {
230        <HeaplessBigInt<T, CAP, Nct> as CheckedDiv>::checked_div(*self, *v)
231    }
232}
233
234impl<T, const CAP: usize> CheckedRem for &HeaplessBigInt<T, CAP, Nct>
235where
236    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
237{
238    type Output = HeaplessBigInt<T, CAP, Nct>;
239    fn checked_rem(self, v: Self) -> Option<Self::Output> {
240        <HeaplessBigInt<T, CAP, Nct> as CheckedRem>::checked_rem(*self, *v)
241    }
242}
243
244impl<T, const CAP: usize> DivCeil for &HeaplessBigInt<T, CAP, Nct>
245where
246    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
247{
248    type Output = HeaplessBigInt<T, CAP, Nct>;
249    fn div_ceil(self, rhs: Self) -> Self::Output {
250        <HeaplessBigInt<T, CAP, Nct> as DivCeil>::div_ceil(*self, *rhs)
251    }
252}
253
254#[cfg(feature = "num-traits")]
255impl<T, const CAP: usize> num_traits::CheckedDiv for HeaplessBigInt<T, CAP, Nct>
256where
257    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
258{
259    fn checked_div(&self, v: &Self) -> Option<Self> {
260        Self::checked_div(self, v)
261    }
262}
263
264#[cfg(feature = "num-traits")]
265impl<T, const CAP: usize> num_traits::CheckedRem for HeaplessBigInt<T, CAP, Nct>
266where
267    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
268{
269    fn checked_rem(&self, v: &Self) -> Option<Self> {
270        Self::checked_rem(self, v)
271    }
272}
273
274// ── DivAssign / RemAssign ──
275//
276// `RemAssign` is the used form; `DivAssign` is added for symmetry. Both
277// delegate to the same long-division kernel.
278
279impl<T, const CAP: usize> core::ops::DivAssign for HeaplessBigInt<T, CAP, Nct>
280where
281    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
282{
283    fn div_assign(&mut self, other: Self) {
284        *self = div_rem_impl::<T, CAP>(self, &other).0;
285    }
286}
287
288impl<T, const CAP: usize> core::ops::DivAssign<&HeaplessBigInt<T, CAP, Nct>>
289    for HeaplessBigInt<T, CAP, Nct>
290where
291    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
292{
293    fn div_assign(&mut self, other: &Self) {
294        *self = div_rem_impl::<T, CAP>(self, other).0;
295    }
296}
297
298impl<T, const CAP: usize> core::ops::RemAssign for HeaplessBigInt<T, CAP, Nct>
299where
300    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
301{
302    fn rem_assign(&mut self, other: Self) {
303        *self = div_rem_impl::<T, CAP>(self, &other).1;
304    }
305}
306
307impl<T, const CAP: usize> core::ops::RemAssign<&HeaplessBigInt<T, CAP, Nct>>
308    for HeaplessBigInt<T, CAP, Nct>
309where
310    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
311{
312    fn rem_assign(&mut self, other: &Self) {
313        *self = div_rem_impl::<T, CAP>(self, other).1;
314    }
315}
316
317#[cfg(test)]
318mod div_ceil_tests {
319    use super::HeaplessBigInt;
320    use const_num_traits::{CheckedDiv, CheckedRem, DivCeil};
321
322    type H = HeaplessBigInt<u8, 4>;
323
324    #[test]
325    fn div_ceil_rounds_up() {
326        assert_eq!(DivCeil::div_ceil(H::from(10u8), H::from(5u8)), H::from(2u8));
327        assert_eq!(DivCeil::div_ceil(H::from(11u8), H::from(3u8)), H::from(4u8));
328        assert_eq!(DivCeil::div_ceil(H::from(1u8), H::from(5u8)), H::from(1u8));
329        assert_eq!(DivCeil::div_ceil(H::from(0u8), H::from(5u8)), H::from(0u8));
330    }
331
332    #[test]
333    fn checked_div_ceil_edges() {
334        // Divide-by-zero and rounding overflow both yield None.
335        assert_eq!(H::from(10u8).checked_div_ceil(&H::from(0u8)), None);
336        // MAX / 2 rounds to 2^31, still fits the 32-bit width.
337        assert_eq!(
338            H::from(u32::MAX).checked_div_ceil(&H::from(2u8)),
339            Some(H::from(0x8000_0000u32))
340        );
341    }
342
343    // Reference-receiver trait forms resolve to the same value as the by-value
344    // forms for the const_num_traits div/rem/ceil family.
345    #[test]
346    fn by_ref_matches_value() {
347        let a = H::from(100u8);
348        let b = H::from(7u8);
349        assert_eq!(
350            CheckedDiv::checked_div(&a, &b),
351            CheckedDiv::checked_div(a, b)
352        );
353        assert_eq!(
354            CheckedRem::checked_rem(&a, &b),
355            CheckedRem::checked_rem(a, b)
356        );
357        assert_eq!(DivCeil::div_ceil(&a, &b), DivCeil::div_ceil(a, b));
358    }
359}