1use crate::{
6 Choice, CtSelect, Limb, NonZero, Uint, WideWord, Word,
7 primitives::{addhilo, widening_mul},
8 word,
9};
10
11cpubits::cpubits! {
12 32 => {
13 pub const fn reciprocal(d: Word) -> Word {
15 debug_assert!(d >= (1 << (Word::BITS - 1)));
16
17 let d0 = d & 1;
18 let d10 = d >> 22;
19 let d21 = (d >> 11) + 1;
20 let d31 = (d >> 1) + d0;
21 let v0 = short_div((1 << 24) - (1 << 14) + (1 << 9), 24, d10, 10);
22 let (_lo, hi) = widening_mul(v0 * v0, d21);
23 let v1 = (v0 << 4) - hi - 1;
24
25 debug_assert!(widening_mul(v1, d31).1 == (1 << 16) - 1);
27 let e = Word::MAX - v1.wrapping_mul(d31) + 1 + (v1 >> 1) * d0;
28
29 let (_lo, hi) = widening_mul(v1, e);
30 let v2 = (v1 << 15).wrapping_add(hi >> 1);
34
35 let x = v2.wrapping_add(1);
39 let (_lo, hi) = widening_mul(x, d);
40 let hi = word::select(d, hi, Choice::from_u32_nz(x));
41
42 v2.wrapping_sub(hi).wrapping_sub(d)
43 }
44 }
45 64 => {
46 pub const fn reciprocal(d: Word) -> Word {
48 debug_assert!(d >= (1 << (Word::BITS - 1)));
49
50 let d0 = d & 1;
51 let d9 = d >> 55;
52 let d40 = (d >> 24) + 1;
53 let d63 = (d >> 1) + d0;
54 let v0 = short_div((1 << 19) - 3 * (1 << 8), 19, d9 as u32, 9) as u64;
55 let v1 = (v0 << 11) - ((v0 * v0 * d40) >> 40) - 1;
56 let v2 = (v1 << 13) + ((v1 * ((1 << 60) - v1 * d40)) >> 47);
57
58 debug_assert!(widening_mul(v2, d63).1 == (1 << 32) - 1);
60 let e = Word::MAX - v2.wrapping_mul(d63) + 1 + (v2 >> 1) * d0;
61
62 let (_lo, hi) = widening_mul(v2, e);
63 let v3 = (v2 << 31).wrapping_add(hi >> 1);
64
65 let x = v3.wrapping_add(1);
69 let (_lo, hi) = widening_mul(x, d);
70 let hi = word::select(d, hi, word::choice_from_nz(x));
71
72 v3.wrapping_sub(hi).wrapping_sub(d)
73 }
74 }
75}
76
77#[inline(always)]
80const fn short_div(mut dividend: u32, dividend_bits: u32, divisor: u32, divisor_bits: u32) -> u32 {
81 let mut divisor = divisor << (dividend_bits - divisor_bits);
91 let mut quotient: u32 = 0;
92 let mut i = dividend_bits - divisor_bits + 1;
93
94 while i > 0 {
95 i -= 1;
96 let bit = Choice::from_u32_lt(dividend, divisor);
97 dividend = bit.select_u32(dividend.wrapping_sub(divisor), dividend);
98 divisor >>= 1;
99 quotient |= bit.not().select_u32(0, 1 << i);
100 }
101
102 quotient
103}
104
105#[inline(always)]
108pub(crate) const fn div2by1(u1: Word, u0: Word, reciprocal: &Reciprocal) -> (Word, Word) {
109 let d = reciprocal.divisor_normalized;
110
111 debug_assert!(d >= (1 << (Word::BITS - 1)));
112 debug_assert!(u1 < d);
113
114 let (q0, q1) = widening_mul(reciprocal.reciprocal, u1);
115 let (q0, q1) = addhilo(q0, q1, u0, u1);
116 let q1 = q1.wrapping_add(1);
117 let r = u0.wrapping_sub(q1.wrapping_mul(d));
118
119 let r_gt_q0 = word::choice_from_lt(q0, r);
120 let q1 = word::select(q1, q1.wrapping_sub(1), r_gt_q0);
121 let r = word::select(r, r.wrapping_add(d), r_gt_q0);
122
123 debug_assert!(r < d || q1 < Word::MAX);
127 let r_ge_d = word::choice_from_le(d, r);
128 let q1 = word::select(q1, q1.wrapping_add(1), r_ge_d);
129 let r = word::select(r, r.wrapping_sub(d), r_ge_d);
130
131 (q1, r)
132}
133
134#[inline(always)]
140pub(crate) const fn div3by2(
141 u2: Word,
142 u1: Word,
143 u0: Word,
144 v1_reciprocal: &Reciprocal,
145 v0: Word,
146) -> Word {
147 debug_assert!(v1_reciprocal.shift == 0);
148 debug_assert!(u2 <= v1_reciprocal.divisor_normalized);
149
150 let q_maxed = word::choice_from_eq(u2, v1_reciprocal.divisor_normalized);
154 let (mut quo, rem) = div2by1(word::select(u2, 0, q_maxed), u1, v1_reciprocal);
155 quo = word::select(quo, Word::MAX, q_maxed);
158 let mut rem = word::select_wide(
159 rem as WideWord,
160 (u2 as WideWord) + (u1 as WideWord),
161 q_maxed,
162 );
163
164 let mut i = 0;
165 while i < 2 {
166 let qy = (quo as WideWord) * (v0 as WideWord);
167 let rx = (rem << Word::BITS) | (u0 as WideWord);
168 let done =
170 word::choice_from_nz((rem >> Word::BITS) as Word).or(word::choice_from_wide_le(qy, rx));
171 quo = word::select(quo.wrapping_sub(1), quo, done);
172 rem = word::select_wide(
173 rem + (v1_reciprocal.divisor_normalized as WideWord),
174 rem,
175 done,
176 );
177 i += 1;
178 }
179
180 quo
181}
182
183#[derive(Copy, Clone, Debug, PartialEq, Eq)]
185pub struct Reciprocal {
186 divisor_normalized: Word,
187 shift: u32,
188 reciprocal: Word,
189}
190
191impl Reciprocal {
192 #[must_use]
195 pub const fn new(divisor: NonZero<Limb>) -> Self {
196 let divisor = divisor.0;
197
198 let shift = divisor.0.leading_zeros();
200
201 let divisor_normalized = divisor.0 << shift;
203
204 Self {
205 divisor_normalized,
206 shift,
207 reciprocal: reciprocal(divisor_normalized),
208 }
209 }
210
211 #[must_use]
217 pub const fn default() -> Self {
218 Self {
219 divisor_normalized: Word::MAX,
220 shift: 0,
221 reciprocal: 1,
224 }
225 }
226
227 #[must_use]
229 pub const fn shift(&self) -> u32 {
230 self.shift
231 }
232}
233
234impl CtSelect for Reciprocal {
235 fn ct_select(&self, other: &Self, choice: Choice) -> Self {
236 Self {
237 divisor_normalized: Word::ct_select(
238 &self.divisor_normalized,
239 &other.divisor_normalized,
240 choice,
241 ),
242 shift: u32::ct_select(&self.shift, &other.shift, choice),
243 reciprocal: Word::ct_select(&self.reciprocal, &other.reciprocal, choice),
244 }
245 }
246}
247
248impl Default for Reciprocal {
251 fn default() -> Self {
252 Self::default()
253 }
254}
255
256#[inline(always)]
258pub(crate) const fn rem_limb_with_reciprocal<const L: usize>(
259 u: &Uint<L>,
260 reciprocal: &Reciprocal,
261) -> Limb {
262 let (u_shifted, u_hi) = u.shl_limb_with_carry(reciprocal.shift, Limb::ZERO);
263 let mut r = u_hi.0;
264
265 let mut j = L;
266 while j > 0 {
267 j -= 1;
268 let (_, rj) = div2by1(r, u_shifted.as_limbs()[j].0, reciprocal);
269 r = rj;
270 }
271 Limb(r >> reciprocal.shift)
272}
273
274#[inline(always)]
276pub(crate) const fn mul_rem(a: Limb, b: Limb, d: NonZero<Limb>) -> Limb {
277 let rec = Reciprocal::new(d);
278 let (lo, hi) = widening_mul(a.0, b.0);
279 rem_limb_with_reciprocal(&Uint::from_words([lo, hi]), &rec)
280}
281
282#[cfg(test)]
283mod tests {
284 use super::{Reciprocal, div2by1};
285 use crate::{Limb, NonZero, Word};
286 #[test]
287 fn div2by1_overflow() {
288 let r = Reciprocal::new(NonZero::new(Limb(Word::MAX - 1)).unwrap());
292 assert_eq!(
293 div2by1(Word::MAX - 2, Word::MAX - 63, &r),
294 (Word::MAX, Word::MAX - 65)
295 );
296 }
297}