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, 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 >>= 1;
83        bit >>= 1;
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// ── DivAssign / RemAssign ──
136//
137// `RemAssign` is the used form; `DivAssign` is added for symmetry. Both
138// delegate to the same long-division kernel.
139
140impl<T, const CAP: usize> core::ops::DivAssign for HeaplessBigInt<T, CAP, Nct>
141where
142    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
143{
144    fn div_assign(&mut self, other: Self) {
145        *self = div_rem_impl::<T, CAP>(self, &other).0;
146    }
147}
148
149impl<T, const CAP: usize> core::ops::DivAssign<&HeaplessBigInt<T, CAP, Nct>>
150    for HeaplessBigInt<T, CAP, Nct>
151where
152    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
153{
154    fn div_assign(&mut self, other: &Self) {
155        *self = div_rem_impl::<T, CAP>(self, other).0;
156    }
157}
158
159impl<T, const CAP: usize> core::ops::RemAssign for HeaplessBigInt<T, CAP, Nct>
160where
161    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
162{
163    fn rem_assign(&mut self, other: Self) {
164        *self = div_rem_impl::<T, CAP>(self, &other).1;
165    }
166}
167
168impl<T, const CAP: usize> core::ops::RemAssign<&HeaplessBigInt<T, CAP, Nct>>
169    for HeaplessBigInt<T, CAP, Nct>
170where
171    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
172{
173    fn rem_assign(&mut self, other: &Self) {
174        *self = div_rem_impl::<T, CAP>(self, other).1;
175    }
176}