Skip to main content

dashu_int/
div_const.rs

1//! Public interface for creating a constant divisor.
2
3use core::{
4    fmt::{Display, Formatter},
5    mem,
6    ops::{Div, DivAssign, Rem, RemAssign},
7};
8use dashu_base::{DivRem, DivRemAssign};
9use num_modular::{PreMulInv2by1, PreMulInv3by2};
10
11use crate::{
12    arch::word::{DoubleWord, Word},
13    buffer::Buffer,
14    div,
15    error::panic_divide_by_0,
16    helper_macros::debug_assert_zero,
17    math::{shl_dword, FastDivideNormalized2},
18    memory::MemoryAllocation,
19    primitive::{double_word, extend_word, shrink_dword},
20    repr::Repr,
21    repr::TypedRepr,
22    shift,
23    ubig::UBig,
24    IBig,
25};
26use alloc::boxed::Box;
27
28#[derive(Debug, PartialEq, Eq)]
29pub(crate) struct ConstSingleDivisor(pub(crate) PreMulInv2by1<Word>);
30
31#[derive(Debug, PartialEq, Eq)]
32pub(crate) struct ConstDoubleDivisor(pub(crate) PreMulInv3by2<Word, DoubleWord>);
33
34#[derive(Debug, PartialEq, Eq)]
35pub(crate) struct ConstLargeDivisor {
36    pub(crate) normalized_divisor: Box<[Word]>,
37    pub(crate) shift: u32,
38    pub(crate) fast_div_top: FastDivideNormalized2,
39}
40
41impl ConstSingleDivisor {
42    /// Create a single word const divisor
43    #[inline]
44    pub const fn new(n: Word) -> Self {
45        debug_assert!(n != 0);
46        Self(PreMulInv2by1::<Word>::new(n))
47    }
48
49    /// Get the original (unnormalized) divisor
50    #[inline]
51    pub const fn divisor(&self) -> Word {
52        self.0.divisor() >> self.0.shift()
53    }
54
55    #[inline]
56    pub const fn normalized_divisor(&self) -> Word {
57        self.0.divisor()
58    }
59    pub const fn shift(&self) -> u32 {
60        self.0.shift()
61    }
62
63    /// Calculate (word << self.shift) % self
64    #[inline]
65    pub const fn rem_word(&self, word: Word) -> Word {
66        if self.0.shift() == 0 {
67            self.0.divider().div_rem_1by1(word).1
68        } else {
69            self.0
70                .divider()
71                .div_rem_2by1(extend_word(word) << self.0.shift())
72                .1
73        }
74    }
75
76    /// Calculate (dword << self.shift) % self
77    #[inline]
78    pub const fn rem_dword(&self, dword: DoubleWord) -> Word {
79        if self.0.shift() == 0 {
80            self.0.divider().div_rem_2by1(dword).1
81        } else {
82            let (n0, n1, n2) = shl_dword(dword, self.0.shift());
83            let (_, r1) = self.0.divider().div_rem_2by1(double_word(n1, n2));
84            self.0.divider().div_rem_2by1(double_word(n0, r1)).1
85        }
86    }
87
88    /// Calculate (words << self.shift) % self
89    pub fn rem_large(&self, words: &[Word]) -> Word {
90        let mut rem = div::fast_rem_by_normalized_word(words, *self.0.divider());
91        if self.0.shift() != 0 {
92            rem = self
93                .0
94                .divider()
95                .div_rem_2by1(extend_word(rem) << self.0.shift())
96                .1
97        }
98        rem
99    }
100}
101
102impl ConstDoubleDivisor {
103    /// Create a double word const divisor
104    #[inline]
105    pub const fn new(n: DoubleWord) -> Self {
106        debug_assert!(n > Word::MAX as DoubleWord);
107        Self(PreMulInv3by2::<Word, DoubleWord>::new(n))
108    }
109
110    /// Get the original (unnormalized) divisor
111    #[inline]
112    pub const fn divisor(&self) -> DoubleWord {
113        self.0.divisor() >> self.0.shift()
114    }
115
116    #[inline]
117    pub const fn normalized_divisor(&self) -> DoubleWord {
118        self.0.divisor()
119    }
120    pub const fn shift(&self) -> u32 {
121        self.0.shift()
122    }
123
124    /// Calculate (dword << self.shift) % self
125    #[inline]
126    pub const fn rem_dword(&self, dword: DoubleWord) -> DoubleWord {
127        if self.0.shift() == 0 {
128            self.0.divider().div_rem_2by2(dword).1
129        } else {
130            let (n0, n1, n2) = shl_dword(dword, self.0.shift());
131            self.0.divider().div_rem_3by2(n0, double_word(n1, n2)).1
132        }
133    }
134
135    /// Calculate (words << self.shift) % self
136    pub fn rem_large(&self, words: &[Word]) -> DoubleWord {
137        let mut rem = div::fast_rem_by_normalized_dword(words, *self.0.divider());
138        if self.0.shift() != 0 {
139            let (r0, r1, r2) = shl_dword(rem, self.0.shift());
140            rem = self.0.divider().div_rem_3by2(r0, double_word(r1, r2)).1
141        }
142        rem
143    }
144}
145
146impl ConstLargeDivisor {
147    /// Create a const divisor with multiple words
148    pub fn new(mut n: Buffer) -> Self {
149        let (shift, fast_div_top) = crate::div::normalize(&mut n);
150        Self {
151            normalized_divisor: n.into_boxed_slice(),
152            shift,
153            fast_div_top,
154        }
155    }
156
157    /// Get the original (unnormalized) divisor
158    pub fn divisor(&self) -> Buffer {
159        let mut buffer = Buffer::from(self.normalized_divisor.as_ref());
160        debug_assert_zero!(shift::shr_in_place(&mut buffer, self.shift));
161        buffer
162    }
163
164    /// Calculate (words << self.shift) % self
165    #[inline]
166    pub fn rem_large(&self, mut words: Buffer) -> Buffer {
167        // shift
168        let carry = shift::shl_in_place(&mut words, self.shift);
169        words.push_resizing(carry);
170
171        // reduce
172        let modulus = &self.normalized_divisor;
173        if words.len() >= modulus.len() {
174            let mut allocation =
175                MemoryAllocation::new(div::memory_requirement_exact(words.len(), modulus.len()));
176            let _overflow = div::div_rem_in_place(
177                &mut words,
178                modulus,
179                self.fast_div_top,
180                &mut allocation.memory(),
181            );
182            words.truncate(modulus.len());
183        }
184        words
185    }
186
187    /// Calculate (x << self.shift) % self
188    #[inline]
189    pub fn rem_repr(&self, x: TypedRepr) -> Buffer {
190        match x {
191            TypedRepr::Small(dword) => {
192                let (lo, mid, hi) = shl_dword(dword, self.shift);
193                let mut buffer = Buffer::allocate_exact(self.normalized_divisor.len());
194                buffer.push(lo);
195                buffer.push(mid);
196                buffer.push(hi);
197
198                // because ConstLargeDivisor is used only for integer with more than two words,
199                // word << ring.shift() must be smaller than the normalized modulus
200                buffer
201            }
202            TypedRepr::Large(words) => self.rem_large(words),
203        }
204    }
205}
206
207#[derive(Debug, PartialEq, Eq)]
208pub(crate) enum ConstDivisorRepr {
209    Single(ConstSingleDivisor),
210    Double(ConstDoubleDivisor),
211    Large(ConstLargeDivisor),
212}
213
214/// An [UBig] with some pre-computed fields to support faster division.
215#[derive(Debug, PartialEq, Eq)]
216pub struct ConstDivisor(pub(crate) ConstDivisorRepr);
217
218impl ConstDivisor {
219    /// Create a [`ConstDivisor`] precomputing the division helper fields for `n`.
220    ///
221    /// # Panics
222    ///
223    /// Panics if `n` is zero.
224    pub fn new(n: UBig) -> ConstDivisor {
225        Self(match n.into_repr() {
226            TypedRepr::Small(0) => panic_divide_by_0(),
227            TypedRepr::Small(dword) => {
228                if let Some(word) = shrink_dword(dword) {
229                    ConstDivisorRepr::Single(ConstSingleDivisor::new(word))
230                } else {
231                    ConstDivisorRepr::Double(ConstDoubleDivisor::new(dword))
232                }
233            }
234            TypedRepr::Large(words) => ConstDivisorRepr::Large(ConstLargeDivisor::new(words)),
235        })
236    }
237
238    /// Create a [`ConstDivisor`] from a single word.
239    ///
240    /// # Panics
241    ///
242    /// Panics if `word` is zero.
243    #[inline]
244    pub const fn from_word(word: Word) -> Self {
245        if word == 0 {
246            panic_divide_by_0()
247        }
248        Self(ConstDivisorRepr::Single(ConstSingleDivisor::new(word)))
249    }
250
251    /// Create a [`ConstDivisor`] from a double word.
252    ///
253    /// # Panics
254    ///
255    /// Panics if `dword` is zero.
256    #[inline]
257    pub const fn from_dword(dword: DoubleWord) -> Self {
258        if dword == 0 {
259            panic_divide_by_0()
260        }
261
262        Self(if let Some(word) = shrink_dword(dword) {
263            ConstDivisorRepr::Single(ConstSingleDivisor::new(word))
264        } else {
265            ConstDivisorRepr::Double(ConstDoubleDivisor::new(dword))
266        })
267    }
268
269    /// Return the divisor value as a [`UBig`].
270    #[inline]
271    pub fn value(&self) -> UBig {
272        UBig(match &self.0 {
273            ConstDivisorRepr::Single(d) => Repr::from_word(d.divisor()),
274            ConstDivisorRepr::Double(d) => Repr::from_dword(d.divisor()),
275            ConstDivisorRepr::Large(d) => Repr::from_buffer(d.divisor()),
276        })
277    }
278}
279
280impl Display for ConstDivisor {
281    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
282        Display::fmt(&self.value(), f)
283    }
284}
285
286impl Div<&ConstDivisor> for UBig {
287    type Output = UBig;
288
289    #[inline]
290    fn div(self, rhs: &ConstDivisor) -> UBig {
291        UBig(self.into_repr() / &rhs.0)
292    }
293}
294impl Div<&ConstDivisor> for &UBig {
295    type Output = UBig;
296
297    #[inline]
298    fn div(self, rhs: &ConstDivisor) -> UBig {
299        UBig(self.clone().into_repr() / &rhs.0)
300    }
301}
302impl DivAssign<&ConstDivisor> for UBig {
303    #[inline]
304    fn div_assign(&mut self, rhs: &ConstDivisor) {
305        *self = mem::take(self) / rhs;
306    }
307}
308
309impl Rem<&ConstDivisor> for UBig {
310    type Output = UBig;
311
312    #[inline]
313    fn rem(self, rhs: &ConstDivisor) -> UBig {
314        UBig(self.into_repr() % &rhs.0)
315    }
316}
317impl Rem<&ConstDivisor> for &UBig {
318    type Output = UBig;
319
320    #[inline]
321    fn rem(self, rhs: &ConstDivisor) -> UBig {
322        UBig(self.repr() % &rhs.0)
323    }
324}
325impl RemAssign<&ConstDivisor> for UBig {
326    #[inline]
327    fn rem_assign(&mut self, rhs: &ConstDivisor) {
328        *self = mem::take(self) % rhs;
329    }
330}
331
332impl DivRem<&ConstDivisor> for UBig {
333    type OutputDiv = UBig;
334    type OutputRem = UBig;
335
336    #[inline]
337    fn div_rem(self, rhs: &ConstDivisor) -> (UBig, UBig) {
338        let (q, r) = self.into_repr().div_rem(&rhs.0);
339        (UBig(q), UBig(r))
340    }
341}
342impl DivRem<&ConstDivisor> for &UBig {
343    type OutputDiv = UBig;
344    type OutputRem = UBig;
345
346    #[inline]
347    fn div_rem(self, rhs: &ConstDivisor) -> (UBig, UBig) {
348        let (q, r) = self.clone().into_repr().div_rem(&rhs.0);
349        (UBig(q), UBig(r))
350    }
351}
352impl DivRemAssign<&ConstDivisor> for UBig {
353    type OutputRem = UBig;
354    #[inline]
355    fn div_rem_assign(&mut self, rhs: &ConstDivisor) -> UBig {
356        let (q, r) = mem::take(self).div_rem(rhs);
357        *self = q;
358        r
359    }
360}
361
362impl Div<&ConstDivisor> for IBig {
363    type Output = IBig;
364
365    #[inline]
366    fn div(self, rhs: &ConstDivisor) -> IBig {
367        let (sign, repr) = self.into_sign_repr();
368        IBig((repr / &rhs.0).with_sign(sign))
369    }
370}
371impl Div<&ConstDivisor> for &IBig {
372    type Output = IBig;
373
374    #[inline]
375    fn div(self, rhs: &ConstDivisor) -> IBig {
376        let (sign, repr) = self.clone().into_sign_repr();
377        IBig((repr / &rhs.0).with_sign(sign))
378    }
379}
380impl DivAssign<&ConstDivisor> for IBig {
381    #[inline]
382    fn div_assign(&mut self, rhs: &ConstDivisor) {
383        *self = mem::take(self) / rhs;
384    }
385}
386
387impl Rem<&ConstDivisor> for IBig {
388    type Output = IBig;
389
390    #[inline]
391    fn rem(self, rhs: &ConstDivisor) -> IBig {
392        let (sign, repr) = self.into_sign_repr();
393        IBig((repr % &rhs.0).with_sign(sign))
394    }
395}
396impl Rem<&ConstDivisor> for &IBig {
397    type Output = IBig;
398
399    #[inline]
400    fn rem(self, rhs: &ConstDivisor) -> IBig {
401        let (sign, repr) = self.as_sign_repr();
402        IBig((repr % &rhs.0).with_sign(sign))
403    }
404}
405impl RemAssign<&ConstDivisor> for IBig {
406    #[inline]
407    fn rem_assign(&mut self, rhs: &ConstDivisor) {
408        *self = mem::take(self) % rhs;
409    }
410}
411
412impl DivRem<&ConstDivisor> for IBig {
413    type OutputDiv = IBig;
414    type OutputRem = IBig;
415
416    #[inline]
417    fn div_rem(self, rhs: &ConstDivisor) -> (IBig, IBig) {
418        let (sign, repr) = self.into_sign_repr();
419        let (q, r) = repr.div_rem(&rhs.0);
420        (IBig(q.with_sign(sign)), IBig(r.with_sign(sign)))
421    }
422}
423impl DivRem<&ConstDivisor> for &IBig {
424    type OutputDiv = IBig;
425    type OutputRem = IBig;
426
427    #[inline]
428    fn div_rem(self, rhs: &ConstDivisor) -> (IBig, IBig) {
429        let (sign, repr) = self.clone().into_sign_repr();
430        let (q, r) = repr.div_rem(&rhs.0);
431        (IBig(q.with_sign(sign)), IBig(r.with_sign(sign)))
432    }
433}
434impl DivRemAssign<&ConstDivisor> for IBig {
435    type OutputRem = IBig;
436    #[inline]
437    fn div_rem_assign(&mut self, rhs: &ConstDivisor) -> IBig {
438        let (q, r) = mem::take(self).div_rem(rhs);
439        *self = q;
440        r
441    }
442}
443
444mod repr {
445    use super::*;
446    use crate::repr::{
447        Repr,
448        TypedRepr::{self, *},
449        TypedReprRef::{self, *},
450    };
451
452    impl Div<&ConstDivisorRepr> for TypedRepr {
453        type Output = Repr;
454        fn div(self, rhs: &ConstDivisorRepr) -> Repr {
455            match (self, rhs) {
456                (Small(dword), ConstDivisorRepr::Single(div)) => {
457                    Repr::from_dword(div_rem_small_single(dword, div).0)
458                }
459                (Small(dword), ConstDivisorRepr::Double(div)) => {
460                    Repr::from_word(div_rem_small_double(dword, div).0)
461                }
462                (Small(_), ConstDivisorRepr::Large(_)) => {
463                    // lhs must be less than rhs
464                    Repr::zero()
465                }
466                (Large(mut buffer), ConstDivisorRepr::Single(div)) => {
467                    let _rem = div::fast_div_by_word_in_place(
468                        &mut buffer,
469                        div.0.shift(),
470                        *div.0.divider(),
471                    );
472                    Repr::from_buffer(buffer)
473                }
474                (Large(mut buffer), ConstDivisorRepr::Double(div)) => {
475                    let _rem = div::fast_div_by_dword_in_place(
476                        &mut buffer,
477                        div.0.shift(),
478                        *div.0.divider(),
479                    );
480                    Repr::from_buffer(buffer)
481                }
482                (Large(mut buffer), ConstDivisorRepr::Large(div)) => {
483                    let div_len = div.normalized_divisor.len();
484                    if buffer.len() < div_len {
485                        Repr::zero()
486                    } else {
487                        let mut allocation = MemoryAllocation::new(div::memory_requirement_exact(
488                            buffer.len(),
489                            div_len,
490                        ));
491                        let q_top = div::div_rem_unshifted_in_place(
492                            &mut buffer,
493                            &div.normalized_divisor,
494                            div.shift,
495                            div.fast_div_top,
496                            &mut allocation.memory(),
497                        );
498                        buffer.erase_front(div_len);
499                        buffer.push_resizing(q_top);
500                        Repr::from_buffer(buffer)
501                    }
502                }
503            }
504        }
505    }
506
507    impl Rem<&ConstDivisorRepr> for TypedRepr {
508        type Output = Repr;
509
510        fn rem(self, rhs: &ConstDivisorRepr) -> Repr {
511            match (self, rhs) {
512                (Small(dword), ConstDivisorRepr::Single(div)) => {
513                    Repr::from_word(div.rem_dword(dword) >> div.0.shift())
514                }
515                (Small(dword), ConstDivisorRepr::Double(div)) => {
516                    Repr::from_dword(div.rem_dword(dword) >> div.0.shift())
517                }
518                (Small(dword), ConstDivisorRepr::Large(_)) => {
519                    // lhs must be less than rhs
520                    Repr::from_dword(dword)
521                }
522                (Large(buffer), ConstDivisorRepr::Single(div)) => {
523                    Repr::from_word(div.rem_large(&buffer) >> div.0.shift())
524                }
525                (Large(buffer), ConstDivisorRepr::Double(div)) => {
526                    Repr::from_dword(div.rem_large(&buffer) >> div.0.shift())
527                }
528                (Large(buffer), ConstDivisorRepr::Large(div)) => rem_large_large(buffer, div),
529            }
530        }
531    }
532
533    impl<'l, 'r> Rem<&'r ConstDivisorRepr> for TypedReprRef<'l> {
534        type Output = Repr;
535
536        fn rem(self, rhs: &ConstDivisorRepr) -> Repr {
537            match (self, rhs) {
538                (RefSmall(dword), ConstDivisorRepr::Single(div)) => {
539                    Repr::from_word(div.rem_dword(dword) >> div.0.shift())
540                }
541                (RefSmall(dword), ConstDivisorRepr::Double(div)) => {
542                    Repr::from_dword(div.rem_dword(dword) >> div.0.shift())
543                }
544                (RefSmall(dword), ConstDivisorRepr::Large(_)) => {
545                    // lhs must be less than rhs
546                    Repr::from_dword(dword)
547                }
548                (RefLarge(words), ConstDivisorRepr::Single(div)) => {
549                    Repr::from_word(div.rem_large(words) >> div.0.shift())
550                }
551                (RefLarge(words), ConstDivisorRepr::Double(div)) => {
552                    Repr::from_dword(div.rem_large(words) >> div.0.shift())
553                }
554                (RefLarge(words), ConstDivisorRepr::Large(div)) => {
555                    rem_large_large(words.into(), div)
556                }
557            }
558        }
559    }
560
561    impl DivRem<&ConstDivisorRepr> for TypedRepr {
562        type OutputDiv = Repr;
563        type OutputRem = Repr;
564
565        fn div_rem(self, rhs: &ConstDivisorRepr) -> (Repr, Repr) {
566            match (self, rhs) {
567                (Small(dword), ConstDivisorRepr::Single(div)) => {
568                    let (q, r) = div_rem_small_single(dword, div);
569                    (Repr::from_dword(q), Repr::from_word(r))
570                }
571                (Small(dword), ConstDivisorRepr::Double(div)) => {
572                    let (q, r) = div_rem_small_double(dword, div);
573                    (Repr::from_word(q), Repr::from_dword(r))
574                }
575                (Small(dword), ConstDivisorRepr::Large(_)) => {
576                    // lhs must be less than rhs
577                    (Repr::zero(), Repr::from_dword(dword))
578                }
579                (Large(mut buffer), ConstDivisorRepr::Single(div)) => {
580                    let r = div::fast_div_by_word_in_place(
581                        &mut buffer,
582                        div.0.shift(),
583                        *div.0.divider(),
584                    );
585                    (Repr::from_buffer(buffer), Repr::from_word(r))
586                }
587                (Large(mut buffer), ConstDivisorRepr::Double(div)) => {
588                    let r = div::fast_div_by_dword_in_place(
589                        &mut buffer,
590                        div.0.shift(),
591                        *div.0.divider(),
592                    );
593                    (Repr::from_buffer(buffer), Repr::from_dword(r))
594                }
595                (Large(mut buffer), ConstDivisorRepr::Large(div)) => {
596                    let div_len = div.normalized_divisor.len();
597                    if buffer.len() < div_len {
598                        (Repr::zero(), Repr::from_buffer(buffer))
599                    } else {
600                        let mut allocation = MemoryAllocation::new(div::memory_requirement_exact(
601                            buffer.len(),
602                            div_len,
603                        ));
604                        let q_top = div::div_rem_unshifted_in_place(
605                            &mut buffer,
606                            &div.normalized_divisor,
607                            div.shift,
608                            div.fast_div_top,
609                            &mut allocation.memory(),
610                        );
611
612                        let mut q = Buffer::from(&buffer[div_len..]);
613                        q.push_resizing(q_top);
614                        buffer.truncate(div_len);
615                        debug_assert_zero!(shift::shr_in_place(&mut buffer, div.shift));
616                        (Repr::from_buffer(q), Repr::from_buffer(buffer))
617                    }
618                }
619            }
620        }
621    }
622
623    fn div_rem_small_single(lhs: DoubleWord, rhs: &ConstSingleDivisor) -> (DoubleWord, Word) {
624        let (lo, mid, hi) = shl_dword(lhs, rhs.0.shift());
625        let (q1, r1) = rhs.0.divider().div_rem_2by1(double_word(mid, hi));
626        let (q0, r0) = rhs.0.divider().div_rem_2by1(double_word(lo, r1));
627        (double_word(q0, q1), r0 >> rhs.0.shift())
628    }
629
630    fn div_rem_small_double(lhs: DoubleWord, rhs: &ConstDoubleDivisor) -> (Word, DoubleWord) {
631        let (lo, mid, hi) = shl_dword(lhs, rhs.0.shift());
632        let (q, r) = rhs.0.divider().div_rem_3by2(lo, double_word(mid, hi));
633        (q, r >> rhs.0.shift())
634    }
635
636    fn rem_large_large(mut lhs: Buffer, rhs: &ConstLargeDivisor) -> Repr {
637        let modulus = &rhs.normalized_divisor;
638
639        // only reduce if lhs can be larger than rhs
640        if lhs.len() >= modulus.len() {
641            let mut allocation =
642                MemoryAllocation::new(div::memory_requirement_exact(lhs.len(), modulus.len()));
643            let _qtop = div::div_rem_unshifted_in_place(
644                &mut lhs,
645                modulus,
646                rhs.shift,
647                rhs.fast_div_top,
648                &mut allocation.memory(),
649            );
650
651            lhs.truncate(modulus.len());
652            debug_assert_zero!(shift::shr_in_place(&mut lhs, rhs.shift));
653        }
654        Repr::from_buffer(lhs)
655    }
656}