Skip to main content

smart_big_rational/
denom_array.rs

1// Copyright 2023-2026 The SmartBigRational Authors
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//     https://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
15use crate::primes::{
16    ODD_PRIME_DIVIDERS_U16, ODD_PRIME_DIVIDERS_U32, ODD_PRIME_DIVIDERS_U64, ODD_PRIMES,
17    known_odd_prime_factor_indices,
18};
19use crate::util::OddDivider;
20use crate::{Denom, DenomRef};
21use num_bigint::{BigInt, BigUint, Sign};
22use num_integer::Integer;
23use num_traits::{One, Zero};
24use std::cmp::Ordering;
25use std::ops::{Div, DivAssign, Mul, MulAssign};
26
27/// Denominator representation that decomposes an integer as a product of the
28/// first `NUM_PRIMES` primes, multiplied by a regular big integer when that's
29/// not sufficient.
30#[derive(Clone, Debug, PartialEq, Eq)]
31pub struct DenomArray<const NUM_PRIMES: usize> {
32    // Invariant: the representation is in canonical form, i.e. powers of small primes must
33    // saturate the primes array before overflowing into the remainder.
34    primes: [u8; NUM_PRIMES],
35    remainder: Option<BigUint>,
36}
37
38/// Denominator representation that decomposes an integer as a product of the
39/// first 24 primes (up to 89), multiplied by a regular big integer when that's
40/// not sufficient.
41pub type DenomArray24 = DenomArray<24>;
42
43impl<const NUM_PRIMES: usize> DenomRef<DenomArray<NUM_PRIMES>> for &DenomArray<NUM_PRIMES> {}
44
45impl<const NUM_PRIMES: usize> Denom for DenomArray<NUM_PRIMES> {
46    const ONE: Self = Self {
47        primes: [0; NUM_PRIMES],
48        remainder: None,
49    };
50
51    fn into_biguint(self) -> BigUint {
52        self.into()
53    }
54
55    fn to_biguint(&self) -> BigUint {
56        self.into()
57    }
58
59    fn normalize(lnum: &mut BigInt, rnum: &mut BigInt, ldenom: &Self, rdenom: &Self) -> Self {
60        const {
61            assert!(NUM_PRIMES <= ODD_PRIMES.len() + 1);
62        }
63
64        let mut primes = [0; NUM_PRIMES];
65        let mut ltmp = 1_usize;
66        let mut rtmp = 1_usize;
67        for i in 0..NUM_PRIMES {
68            let p = if i == 0 {
69                2
70            } else {
71                ODD_PRIMES[i - 1] as usize
72            };
73            let lcount = ldenom.primes[i];
74            let rcount = rdenom.primes[i];
75            match lcount.cmp(&rcount) {
76                Ordering::Equal => {
77                    primes[i] = lcount;
78                }
79                Ordering::Less => {
80                    Self::accum_pow(lnum, &mut ltmp, p, rcount - lcount);
81                    primes[i] = rcount;
82                }
83                Ordering::Greater => {
84                    Self::accum_pow(rnum, &mut rtmp, p, lcount - rcount);
85                    primes[i] = lcount;
86                }
87            }
88        }
89
90        *lnum *= ltmp;
91        *rnum *= rtmp;
92        let remainder = match (&ldenom.remainder, &rdenom.remainder) {
93            (None, None) => None,
94            (None, Some(r)) => {
95                *lnum *= BigInt::from_biguint(Sign::Plus, r.clone());
96                Some(r.clone())
97            }
98            (Some(l), None) => {
99                *rnum *= BigInt::from_biguint(Sign::Plus, l.clone());
100                Some(l.clone())
101            }
102            (Some(l), Some(r)) => {
103                if l == r {
104                    Some(l.clone())
105                } else {
106                    *lnum *= BigInt::from_biguint(Sign::Plus, r.clone());
107                    *rnum *= BigInt::from_biguint(Sign::Plus, l.clone());
108                    Some(l * r)
109                }
110            }
111        };
112        Self { primes, remainder }
113    }
114
115    fn gcd_reduce(&mut self, num: &mut BigInt) {
116        const {
117            assert!(NUM_PRIMES <= ODD_PRIMES.len() + 1);
118        }
119
120        if let Some(remainder) = &mut self.remainder {
121            let gcd = remainder.gcd(num.magnitude());
122            if !gcd.is_one() {
123                *remainder /= &gcd;
124                if remainder.is_one() {
125                    self.remainder = None;
126                }
127                let gcd: BigInt = gcd.into();
128                *num /= gcd;
129            }
130        }
131
132        'outer: for i in 0..NUM_PRIMES {
133            let p = if i == 0 { 2 } else { ODD_PRIMES[i - 1] };
134            if self.primes[i] != 0 {
135                let p = BigInt::from(p);
136                while self.primes[i] != 0 {
137                    let (quo, rem) = num.div_rem(&p);
138                    if !rem.is_zero() {
139                        break;
140                    }
141                    *num = quo;
142                    self.primes[i] -= 1;
143                    if num.magnitude().is_one() {
144                        break 'outer;
145                    }
146                }
147            }
148        }
149    }
150}
151
152impl<const NUM_PRIMES: usize> DenomArray<NUM_PRIMES> {
153    fn decompose(mut x: BigUint) -> Self {
154        const {
155            assert!(NUM_PRIMES <= ODD_PRIMES.len() + 1);
156        }
157
158        let bits = x.bits();
159        if bits <= 8 {
160            return Self::decompose_u8(x.try_into().unwrap());
161        } else if bits <= 16 {
162            return Self::decompose_u16(x.try_into().unwrap());
163        } else if bits <= 32 {
164            return Self::decompose_u32(x.try_into().unwrap());
165        } else if bits <= 64 {
166            return Self::decompose_u64(x.try_into().unwrap());
167        } else if bits <= 128 {
168            return Self::decompose_u128(x.try_into().unwrap());
169        }
170
171        let mut primes = [0; NUM_PRIMES];
172
173        let mut count2 = x.trailing_zeros().unwrap();
174        if count2 != 0 {
175            x >>= count2;
176            if count2 <= u8::MAX as u64 {
177                primes[0] = count2 as u8;
178                count2 = 0;
179            } else {
180                primes[0] = u8::MAX;
181                count2 -= u8::MAX as u64;
182            }
183        }
184
185        'outer: for i in 1..NUM_PRIMES {
186            let p = BigUint::from(ODD_PRIMES[i - 1]);
187            while primes[i] != u8::MAX {
188                let (quo, rem) = x.div_rem(&p);
189                if !rem.is_zero() {
190                    break;
191                }
192                x = quo;
193
194                primes[i] += 1;
195                if x.is_one() {
196                    break 'outer;
197                }
198            }
199        }
200
201        let remainder = if x.is_one() && count2 == 0 {
202            None
203        } else {
204            Some(x << count2)
205        };
206        Self { primes, remainder }
207    }
208
209    fn decompose_known_factors(
210        mut primes: [u8; NUM_PRIMES],
211        factor_indices: impl Iterator<Item = (u16, u16)>,
212    ) -> Self {
213        const {
214            assert!(NUM_PRIMES <= ODD_PRIMES.len() + 1);
215        }
216
217        let mut remainder = 1_usize;
218        for (index, count) in factor_indices {
219            let index = index as usize;
220            if index < NUM_PRIMES {
221                primes[index] += count as u8;
222            } else {
223                let p = ODD_PRIMES[index - 1] as usize;
224                for _ in 0..count {
225                    remainder *= p;
226                }
227            }
228        }
229
230        let remainder = if remainder == 1 {
231            None
232        } else {
233            Some(remainder.into())
234        };
235        Self { primes, remainder }
236    }
237
238    fn decompose_u8(mut x: u8) -> Self {
239        let mut primes = [0; NUM_PRIMES];
240
241        let count2 = x.trailing_zeros();
242        if count2 != 0 {
243            x >>= count2;
244            primes[0] = count2 as u8;
245        }
246
247        // 8-bit integers always fit in the look-up table.
248        Self::decompose_known_factors(primes, known_odd_prime_factor_indices(x.into()).unwrap())
249    }
250
251    fn decompose_u16(mut x: u16) -> Self {
252        const {
253            assert!(NUM_PRIMES <= ODD_PRIMES.len() + 1);
254        }
255
256        let mut primes = [0; NUM_PRIMES];
257
258        let count2 = x.trailing_zeros();
259        if count2 != 0 {
260            x >>= count2;
261            primes[0] = count2 as u8;
262        }
263
264        'outer: for i in 1..NUM_PRIMES {
265            let p = ODD_PRIMES[i - 1];
266            let divider = OddDivider {
267                divisor: p,
268                multiplier: ODD_PRIME_DIVIDERS_U16[i - 1],
269                shift: p.ilog2(),
270            };
271            while primes[i] != u8::MAX {
272                // Use look-up table as soon as the remainder is small enough.
273                if let Some(iter) = known_odd_prime_factor_indices(x.into()) {
274                    return Self::decompose_known_factors(primes, iter);
275                }
276
277                let (quo, rem) = divider.div_rem(x);
278                if rem != 0 {
279                    break;
280                }
281                x = quo;
282
283                primes[i] += 1;
284                if x == 1 {
285                    break 'outer;
286                }
287            }
288        }
289
290        let remainder = if x == 1 { None } else { Some(x.into()) };
291        Self { primes, remainder }
292    }
293
294    fn decompose_u32(mut x: u32) -> Self {
295        const {
296            assert!(NUM_PRIMES <= ODD_PRIMES.len() + 1);
297        }
298
299        let mut primes = [0; NUM_PRIMES];
300
301        let count2 = x.trailing_zeros();
302        if count2 != 0 {
303            x >>= count2;
304            primes[0] = count2 as u8;
305        }
306
307        'outer: for i in 1..NUM_PRIMES {
308            let p = ODD_PRIMES[i - 1] as u32;
309            let divider = OddDivider {
310                divisor: p,
311                multiplier: ODD_PRIME_DIVIDERS_U32[i - 1],
312                shift: p.ilog2(),
313            };
314            while primes[i] != u8::MAX {
315                // Use look-up table as soon as the remainder is small enough.
316                if let Ok(xx) = x.try_into()
317                    && let Some(iter) = known_odd_prime_factor_indices(xx)
318                {
319                    return Self::decompose_known_factors(primes, iter);
320                }
321
322                let (quo, rem) = divider.div_rem(x);
323                if rem != 0 {
324                    break;
325                }
326                x = quo;
327
328                primes[i] += 1;
329                if x == 1 {
330                    break 'outer;
331                }
332            }
333        }
334
335        let remainder = if x == 1 { None } else { Some(x.into()) };
336        Self { primes, remainder }
337    }
338
339    fn decompose_u64(mut x: u64) -> Self {
340        const {
341            assert!(NUM_PRIMES <= ODD_PRIMES.len() + 1);
342        }
343
344        let mut primes = [0; NUM_PRIMES];
345
346        let count2 = x.trailing_zeros();
347        if count2 != 0 {
348            x >>= count2;
349            primes[0] = count2 as u8;
350        }
351
352        'outer: for i in 1..NUM_PRIMES {
353            let p = ODD_PRIMES[i - 1] as u64;
354            let divider = OddDivider {
355                divisor: p,
356                multiplier: ODD_PRIME_DIVIDERS_U64[i - 1],
357                shift: p.ilog2(),
358            };
359            while primes[i] != u8::MAX {
360                // Use look-up table as soon as the remainder is small enough.
361                if let Ok(xx) = x.try_into()
362                    && let Some(iter) = known_odd_prime_factor_indices(xx)
363                {
364                    return Self::decompose_known_factors(primes, iter);
365                }
366
367                let (quo, rem) = divider.div_rem(x);
368                if rem != 0 {
369                    break;
370                }
371                x = quo;
372
373                primes[i] += 1;
374                if x == 1 {
375                    break 'outer;
376                }
377            }
378        }
379
380        let remainder = if x == 1 { None } else { Some(x.into()) };
381        Self { primes, remainder }
382    }
383
384    fn decompose_u128(mut x: u128) -> Self {
385        const {
386            assert!(NUM_PRIMES <= ODD_PRIMES.len() + 1);
387        }
388
389        let mut primes = [0; NUM_PRIMES];
390
391        let count2 = x.trailing_zeros();
392        if count2 != 0 {
393            x >>= count2;
394            primes[0] = count2 as u8;
395        }
396
397        'outer: for i in 1..NUM_PRIMES {
398            let p = ODD_PRIMES[i - 1] as u128;
399            while primes[i] != u8::MAX {
400                // Use look-up table as soon as the remainder is small enough.
401                if let Ok(xx) = x.try_into()
402                    && let Some(iter) = known_odd_prime_factor_indices(xx)
403                {
404                    return Self::decompose_known_factors(primes, iter);
405                }
406
407                if !x.is_multiple_of(p) {
408                    break;
409                }
410                x /= p;
411
412                primes[i] += 1;
413                if x == 1 {
414                    break 'outer;
415                }
416            }
417        }
418
419        let remainder = if x == 1 { None } else { Some(x.into()) };
420        Self { primes, remainder }
421    }
422
423    fn decompose_usize(mut x: usize) -> Self {
424        const {
425            assert!(NUM_PRIMES <= ODD_PRIMES.len() + 1);
426        }
427
428        let mut primes = [0; NUM_PRIMES];
429
430        let count2 = x.trailing_zeros();
431        if count2 != 0 {
432            x >>= count2;
433            primes[0] = count2 as u8;
434        }
435
436        'outer: for i in 1..NUM_PRIMES {
437            let p = ODD_PRIMES[i - 1] as usize;
438            while primes[i] != u8::MAX {
439                // Use look-up table as soon as the remainder is small enough.
440                if let Some(iter) = known_odd_prime_factor_indices(x) {
441                    return Self::decompose_known_factors(primes, iter);
442                }
443
444                if !x.is_multiple_of(p) {
445                    break;
446                }
447                x /= p;
448
449                primes[i] += 1;
450                if x == 1 {
451                    break 'outer;
452                }
453            }
454        }
455
456        let remainder = if x == 1 { None } else { Some(x.into()) };
457        Self { primes, remainder }
458    }
459
460    /// Decomposes the given remainder using only primes whose bit mask is set.
461    fn decompose_mask(
462        remainder: &mut Option<BigUint>,
463        primes: &mut [u8; NUM_PRIMES],
464        mask: [bool; NUM_PRIMES],
465    ) {
466        const {
467            assert!(NUM_PRIMES <= ODD_PRIMES.len() + 1);
468        }
469
470        let x: &mut BigUint = match remainder {
471            None => return,
472            Some(x) => x,
473        };
474
475        'outer: for i in 0..NUM_PRIMES {
476            let p = if i == 0 { 2 } else { ODD_PRIMES[i - 1] };
477            if !mask[i] || primes[i] == u8::MAX {
478                continue;
479            }
480            let p = BigUint::from(p);
481            while primes[i] != u8::MAX {
482                let (quo, rem) = x.div_rem(&p);
483                if !rem.is_zero() {
484                    break;
485                }
486                *x = quo;
487
488                primes[i] += 1;
489                if x.is_one() {
490                    break 'outer;
491                }
492            }
493        }
494
495        if x.is_one() {
496            *remainder = None;
497        }
498    }
499
500    /// Computes `prime.pow(exponent)` and multiplies it into the accumulated
501    /// `(numerator, tmp)`.
502    fn accum_pow(numerator: &mut BigInt, tmp: &mut usize, prime: usize, exponent: u8) {
503        for _ in 0..exponent {
504            match tmp.checked_mul(prime) {
505                Some(prod) => *tmp = prod,
506                None => {
507                    *numerator *= *tmp;
508                    *tmp = prime;
509                }
510            }
511        }
512    }
513
514    fn pow_primes(
515        this: &[u8; NUM_PRIMES],
516        exponent: u32,
517        remainder: &mut Option<BigUint>,
518    ) -> [u8; NUM_PRIMES] {
519        const {
520            assert!(NUM_PRIMES <= ODD_PRIMES.len() + 1);
521        }
522
523        let mut primes = [0; NUM_PRIMES];
524        for i in 0..NUM_PRIMES {
525            let p = if i == 0 { 2 } else { ODD_PRIMES[i - 1] };
526            let product = (this[i] as u32).strict_mul(exponent);
527            if product <= u8::MAX as u32 {
528                primes[i] = product as u8;
529            } else {
530                primes[i] = u8::MAX;
531                let factor = BigUint::from(p).pow(product - u8::MAX as u32);
532                match remainder {
533                    None => *remainder = Some(factor),
534                    Some(r) => *r *= factor,
535                };
536            }
537        }
538        primes
539    }
540
541    fn mul_primes(
542        lhs: &[u8; NUM_PRIMES],
543        rhs: &[u8; NUM_PRIMES],
544        remainder: &mut Option<BigUint>,
545    ) -> [u8; NUM_PRIMES] {
546        const {
547            assert!(NUM_PRIMES <= ODD_PRIMES.len() + 1);
548        }
549
550        let mut primes = [0; NUM_PRIMES];
551        for i in 0..NUM_PRIMES {
552            let p = if i == 0 { 2 } else { ODD_PRIMES[i - 1] };
553            let sum = lhs[i] as u32 + rhs[i] as u32;
554            if sum <= u8::MAX as u32 {
555                primes[i] = sum as u8;
556            } else {
557                primes[i] = u8::MAX;
558                let factor = BigUint::from(p).pow(sum - u8::MAX as u32);
559                match remainder {
560                    None => *remainder = Some(factor),
561                    Some(r) => *r *= factor,
562                };
563            }
564        }
565        primes
566    }
567
568    fn div_primes(
569        num: &[u8; NUM_PRIMES],
570        denom: &[u8; NUM_PRIMES],
571        mut remainder: Option<BigUint>,
572    ) -> (Self, [bool; NUM_PRIMES]) {
573        const {
574            assert!(NUM_PRIMES <= ODD_PRIMES.len() + 1);
575        }
576
577        let mut primes = [0; NUM_PRIMES];
578        let mut mask = [false; NUM_PRIMES];
579        for i in 0..NUM_PRIMES {
580            let p = if i == 0 { 2 } else { ODD_PRIMES[i - 1] };
581            mask[i] = num[i] == u8::MAX;
582            if num[i] >= denom[i] {
583                primes[i] = num[i] - denom[i];
584            } else {
585                primes[i] = 0;
586                let factor = BigUint::from(p).pow(denom[i] as u32 - num[i] as u32);
587                remainder = match remainder {
588                    None => Some(factor),
589                    Some(r) => Some(r * factor),
590                };
591            }
592        }
593        (Self { primes, remainder }, mask)
594    }
595}
596
597impl<const NUM_PRIMES: usize> From<u8> for DenomArray<NUM_PRIMES> {
598    fn from(value: u8) -> Self {
599        if value == 0 {
600            panic!("Attempted to create a denominator of zero");
601        }
602        Self::decompose_u8(value)
603    }
604}
605
606impl<const NUM_PRIMES: usize> From<u16> for DenomArray<NUM_PRIMES> {
607    fn from(value: u16) -> Self {
608        if value == 0 {
609            panic!("Attempted to create a denominator of zero");
610        }
611        Self::decompose_u16(value)
612    }
613}
614
615impl<const NUM_PRIMES: usize> From<u32> for DenomArray<NUM_PRIMES> {
616    fn from(value: u32) -> Self {
617        if value == 0 {
618            panic!("Attempted to create a denominator of zero");
619        }
620        Self::decompose_u32(value)
621    }
622}
623
624impl<const NUM_PRIMES: usize> From<u64> for DenomArray<NUM_PRIMES> {
625    fn from(value: u64) -> Self {
626        if value == 0 {
627            panic!("Attempted to create a denominator of zero");
628        }
629        Self::decompose_u64(value)
630    }
631}
632
633impl<const NUM_PRIMES: usize> From<u128> for DenomArray<NUM_PRIMES> {
634    fn from(value: u128) -> Self {
635        if value == 0 {
636            panic!("Attempted to create a denominator of zero");
637        }
638        Self::decompose_u128(value)
639    }
640}
641
642impl<const NUM_PRIMES: usize> From<usize> for DenomArray<NUM_PRIMES> {
643    fn from(value: usize) -> Self {
644        if value == 0 {
645            panic!("Attempted to create a denominator of zero");
646        }
647        Self::decompose_usize(value)
648    }
649}
650
651impl<const NUM_PRIMES: usize> From<BigUint> for DenomArray<NUM_PRIMES> {
652    fn from(value: BigUint) -> Self {
653        if value.is_zero() {
654            panic!("Attempted to create a denominator of zero");
655        }
656        Self::decompose(value)
657    }
658}
659
660impl<const NUM_PRIMES: usize> From<&BigUint> for DenomArray<NUM_PRIMES> {
661    fn from(value: &BigUint) -> Self {
662        if value.is_zero() {
663            panic!("Attempted to create a denominator of zero");
664        }
665        Self::decompose(value.clone())
666    }
667}
668
669impl<const NUM_PRIMES: usize> From<DenomArray<NUM_PRIMES>> for BigUint {
670    fn from(value: DenomArray<NUM_PRIMES>) -> BigUint {
671        const {
672            assert!(NUM_PRIMES <= ODD_PRIMES.len() + 1);
673        }
674
675        let mut result = match value.remainder {
676            Some(x) => x,
677            None => BigUint::ONE,
678        };
679        let mut tmp = 1_usize;
680        for (i, &count) in value.primes.iter().enumerate() {
681            let p = if i == 0 {
682                2
683            } else {
684                ODD_PRIMES[i - 1] as usize
685            };
686            for _ in 0..count {
687                match tmp.checked_mul(p) {
688                    Some(prod) => tmp = prod,
689                    None => {
690                        result *= tmp;
691                        tmp = p;
692                    }
693                }
694            }
695        }
696        result * tmp
697    }
698}
699
700impl<const NUM_PRIMES: usize> From<&DenomArray<NUM_PRIMES>> for BigUint {
701    fn from(value: &DenomArray<NUM_PRIMES>) -> BigUint {
702        const {
703            assert!(NUM_PRIMES <= ODD_PRIMES.len() + 1);
704        }
705
706        let mut result = match &value.remainder {
707            Some(x) => x.clone(),
708            None => BigUint::ONE,
709        };
710        let mut tmp = 1_usize;
711        for (i, &count) in value.primes.iter().enumerate() {
712            let p = if i == 0 {
713                2
714            } else {
715                ODD_PRIMES[i - 1] as usize
716            };
717            for _ in 0..count {
718                match tmp.checked_mul(p) {
719                    Some(prod) => tmp = prod,
720                    None => {
721                        result *= tmp;
722                        tmp = p;
723                    }
724                }
725            }
726        }
727        result * tmp
728    }
729}
730
731impl<const NUM_PRIMES: usize> One for DenomArray<NUM_PRIMES> {
732    fn one() -> Self {
733        Self::ONE
734    }
735}
736
737impl<const NUM_PRIMES: usize> num_traits::Pow<u32> for DenomArray<NUM_PRIMES> {
738    type Output = Self;
739
740    fn pow(mut self, rhs: u32) -> Self {
741        if rhs == 0 {
742            return Self::ONE;
743        }
744        let primes = Self::pow_primes(&self.primes, rhs, &mut self.remainder);
745        Self {
746            primes,
747            remainder: self.remainder,
748        }
749    }
750}
751
752impl<const NUM_PRIMES: usize> num_traits::Pow<u32> for &DenomArray<NUM_PRIMES> {
753    type Output = DenomArray<NUM_PRIMES>;
754
755    fn pow(self, rhs: u32) -> DenomArray<NUM_PRIMES> {
756        if rhs == 0 {
757            return DenomArray::ONE;
758        }
759        let mut remainder = self.remainder.clone();
760        let primes = DenomArray::pow_primes(&self.primes, rhs, &mut remainder);
761        DenomArray { primes, remainder }
762    }
763}
764
765impl<const NUM_PRIMES: usize> Mul for DenomArray<NUM_PRIMES> {
766    type Output = Self;
767
768    fn mul(self, rhs: Self) -> Self {
769        let mut remainder = match (self.remainder, rhs.remainder) {
770            (None, None) => None,
771            (None, Some(r)) => Some(r),
772            (Some(l), None) => Some(l),
773            (Some(l), Some(r)) => Some(l * r),
774        };
775        let primes = Self::mul_primes(&self.primes, &rhs.primes, &mut remainder);
776        Self { primes, remainder }
777    }
778}
779
780impl<const NUM_PRIMES: usize> Mul<&Self> for DenomArray<NUM_PRIMES> {
781    type Output = Self;
782
783    fn mul(self, rhs: &Self) -> Self {
784        let mut remainder = match (self.remainder, &rhs.remainder) {
785            (None, None) => None,
786            (None, Some(r)) => Some(r.clone()),
787            (Some(l), None) => Some(l),
788            (Some(l), Some(r)) => Some(l * r),
789        };
790        let primes = Self::mul_primes(&self.primes, &rhs.primes, &mut remainder);
791        Self { primes, remainder }
792    }
793}
794
795impl<const NUM_PRIMES: usize> Mul for &DenomArray<NUM_PRIMES> {
796    type Output = DenomArray<NUM_PRIMES>;
797
798    fn mul(self, rhs: Self) -> DenomArray<NUM_PRIMES> {
799        let mut remainder = match (&self.remainder, &rhs.remainder) {
800            (None, None) => None,
801            (None, Some(r)) => Some(r.clone()),
802            (Some(l), None) => Some(l.clone()),
803            (Some(l), Some(r)) => Some(l * r),
804        };
805        let primes = DenomArray::mul_primes(&self.primes, &rhs.primes, &mut remainder);
806        DenomArray { primes, remainder }
807    }
808}
809
810impl<const NUM_PRIMES: usize> Mul<DenomArray<NUM_PRIMES>> for &DenomArray<NUM_PRIMES> {
811    type Output = DenomArray<NUM_PRIMES>;
812
813    fn mul(self, rhs: DenomArray<NUM_PRIMES>) -> DenomArray<NUM_PRIMES> {
814        let mut remainder = match (&self.remainder, rhs.remainder) {
815            (None, None) => None,
816            (None, Some(r)) => Some(r),
817            (Some(l), None) => Some(l.clone()),
818            (Some(l), Some(r)) => Some(l * r),
819        };
820        let primes = DenomArray::mul_primes(&self.primes, &rhs.primes, &mut remainder);
821        DenomArray { primes, remainder }
822    }
823}
824
825impl<const NUM_PRIMES: usize> MulAssign for DenomArray<NUM_PRIMES> {
826    fn mul_assign(&mut self, rhs: Self) {
827        match (&mut self.remainder, rhs.remainder) {
828            (_, None) => (),
829            (None, Some(r)) => self.remainder = Some(r),
830            (Some(l), Some(r)) => *l *= r,
831        };
832        self.primes = Self::mul_primes(&self.primes, &rhs.primes, &mut self.remainder);
833    }
834}
835
836impl<const NUM_PRIMES: usize> MulAssign<&Self> for DenomArray<NUM_PRIMES> {
837    fn mul_assign(&mut self, rhs: &Self) {
838        match (&mut self.remainder, &rhs.remainder) {
839            (_, None) => (),
840            (None, Some(r)) => self.remainder = Some(r.clone()),
841            (Some(l), Some(r)) => *l *= r,
842        };
843        self.primes = Self::mul_primes(&self.primes, &rhs.primes, &mut self.remainder);
844    }
845}
846
847impl<const NUM_PRIMES: usize> Div for DenomArray<NUM_PRIMES> {
848    type Output = Self;
849
850    /// Divides this denominator by the other one.
851    ///
852    /// This function panics if the other one doesn't divide this one.
853    fn div(self, rhs: Self) -> Self {
854        let (
855            Self {
856                mut primes,
857                remainder: rhs_remainder,
858            },
859            mask,
860        ) = Self::div_primes(&self.primes, &rhs.primes, rhs.remainder);
861
862        let mut remainder = match (self.remainder, rhs_remainder) {
863            (None, None) => None,
864            (None, Some(r)) => {
865                if !r.is_one() {
866                    panic!("Attempted to divide a denominator by a non-divisor");
867                }
868                None
869            }
870            (Some(l), None) => Some(l),
871            (Some(l), Some(r)) => {
872                let (quo, rem) = l.div_rem(&r);
873                if !rem.is_zero() {
874                    panic!("Attempted to divide a denominator by a non-divisor");
875                }
876                if quo.is_one() { None } else { Some(quo) }
877            }
878        };
879
880        Self::decompose_mask(&mut remainder, &mut primes, mask);
881        Self { primes, remainder }
882    }
883}
884
885impl<const NUM_PRIMES: usize> Div<&Self> for DenomArray<NUM_PRIMES> {
886    type Output = Self;
887
888    /// Divides this denominator by the other one.
889    ///
890    /// This function panics if the other one doesn't divide this one.
891    fn div(self, rhs: &Self) -> Self {
892        let (
893            Self {
894                mut primes,
895                remainder: rhs_remainder,
896            },
897            mask,
898        ) = Self::div_primes(&self.primes, &rhs.primes, rhs.remainder.clone());
899
900        let mut remainder = match (self.remainder, rhs_remainder) {
901            (None, None) => None,
902            (None, Some(r)) => {
903                if !r.is_one() {
904                    panic!("Attempted to divide a denominator by a non-divisor");
905                }
906                None
907            }
908            (Some(l), None) => Some(l),
909            (Some(l), Some(r)) => {
910                let (quo, rem) = l.div_rem(&r);
911                if !rem.is_zero() {
912                    panic!("Attempted to divide a denominator by a non-divisor");
913                }
914                if quo.is_one() { None } else { Some(quo) }
915            }
916        };
917
918        Self::decompose_mask(&mut remainder, &mut primes, mask);
919        Self { primes, remainder }
920    }
921}
922
923impl<const NUM_PRIMES: usize> Div for &DenomArray<NUM_PRIMES> {
924    type Output = DenomArray<NUM_PRIMES>;
925
926    /// Divides this denominator by the other one.
927    ///
928    /// This function panics if the other one doesn't divide this one.
929    fn div(self, rhs: Self) -> DenomArray<NUM_PRIMES> {
930        let (
931            DenomArray {
932                mut primes,
933                remainder: rhs_remainder,
934            },
935            mask,
936        ) = DenomArray::div_primes(&self.primes, &rhs.primes, rhs.remainder.clone());
937
938        let mut remainder = match (&self.remainder, rhs_remainder) {
939            (None, None) => None,
940            (None, Some(r)) => {
941                if !r.is_one() {
942                    panic!("Attempted to divide a denominator by a non-divisor");
943                }
944                None
945            }
946            (Some(l), None) => Some(l.clone()),
947            (Some(l), Some(r)) => {
948                let (quo, rem) = l.div_rem(&r);
949                if !rem.is_zero() {
950                    panic!("Attempted to divide a denominator by a non-divisor");
951                }
952                if quo.is_one() { None } else { Some(quo) }
953            }
954        };
955
956        DenomArray::decompose_mask(&mut remainder, &mut primes, mask);
957        DenomArray { primes, remainder }
958    }
959}
960
961impl<const NUM_PRIMES: usize> Div<DenomArray<NUM_PRIMES>> for &DenomArray<NUM_PRIMES> {
962    type Output = DenomArray<NUM_PRIMES>;
963
964    /// Divides this denominator by the other one.
965    ///
966    /// This function panics if the other one doesn't divide this one.
967    fn div(self, rhs: DenomArray<NUM_PRIMES>) -> DenomArray<NUM_PRIMES> {
968        let (
969            DenomArray {
970                mut primes,
971                remainder: rhs_remainder,
972            },
973            mask,
974        ) = DenomArray::div_primes(&self.primes, &rhs.primes, rhs.remainder);
975
976        let mut remainder = match (&self.remainder, rhs_remainder) {
977            (None, None) => None,
978            (None, Some(r)) => {
979                if !r.is_one() {
980                    panic!("Attempted to divide a denominator by a non-divisor");
981                }
982                None
983            }
984            (Some(l), None) => Some(l.clone()),
985            (Some(l), Some(r)) => {
986                let (quo, rem) = l.div_rem(&r);
987                if !rem.is_zero() {
988                    panic!("Attempted to divide a denominator by a non-divisor");
989                }
990                if quo.is_one() { None } else { Some(quo) }
991            }
992        };
993
994        DenomArray::decompose_mask(&mut remainder, &mut primes, mask);
995        DenomArray { primes, remainder }
996    }
997}
998
999impl<const NUM_PRIMES: usize> DivAssign for DenomArray<NUM_PRIMES> {
1000    /// Divides this denominator by the other one.
1001    ///
1002    /// This function panics if the other one doesn't divide this one.
1003    fn div_assign(&mut self, rhs: Self) {
1004        let (
1005            Self {
1006                primes,
1007                remainder: rhs_remainder,
1008            },
1009            mask,
1010        ) = Self::div_primes(&self.primes, &rhs.primes, rhs.remainder);
1011        self.primes = primes;
1012
1013        match (&mut self.remainder, rhs_remainder) {
1014            (_, None) => (),
1015            (None, Some(r)) => {
1016                if !r.is_one() {
1017                    panic!("Attempted to divide a denominator by a non-divisor");
1018                }
1019            }
1020            (Some(l), Some(r)) => {
1021                let (quo, rem) = l.div_rem(&r);
1022                if !rem.is_zero() {
1023                    panic!("Attempted to divide a denominator by a non-divisor");
1024                }
1025                if quo.is_one() {
1026                    self.remainder = None
1027                } else {
1028                    *l = quo;
1029                }
1030            }
1031        };
1032
1033        Self::decompose_mask(&mut self.remainder, &mut self.primes, mask);
1034    }
1035}
1036
1037impl<const NUM_PRIMES: usize> DivAssign<&Self> for DenomArray<NUM_PRIMES> {
1038    /// Divides this denominator by the other one.
1039    ///
1040    /// This function panics if the other one doesn't divide this one.
1041    fn div_assign(&mut self, rhs: &Self) {
1042        let (
1043            Self {
1044                primes,
1045                remainder: rhs_remainder,
1046            },
1047            mask,
1048        ) = Self::div_primes(&self.primes, &rhs.primes, rhs.remainder.clone());
1049        self.primes = primes;
1050
1051        match (&mut self.remainder, rhs_remainder) {
1052            (_, None) => (),
1053            (None, Some(r)) => {
1054                if !r.is_one() {
1055                    panic!("Attempted to divide a denominator by a non-divisor");
1056                }
1057            }
1058            (Some(l), Some(r)) => {
1059                let (quo, rem) = l.div_rem(&r);
1060                if !rem.is_zero() {
1061                    panic!("Attempted to divide a denominator by a non-divisor");
1062                }
1063                if quo.is_one() {
1064                    self.remainder = None
1065                } else {
1066                    *l = quo;
1067                }
1068            }
1069        };
1070
1071        Self::decompose_mask(&mut self.remainder, &mut self.primes, mask);
1072    }
1073}
1074
1075impl<const NUM_PRIMES: usize> Mul<DenomArray<NUM_PRIMES>> for BigInt {
1076    type Output = Self;
1077
1078    fn mul(mut self, rhs: DenomArray<NUM_PRIMES>) -> Self {
1079        const {
1080            assert!(NUM_PRIMES <= ODD_PRIMES.len() + 1);
1081        }
1082
1083        let mut tmp = 1_usize;
1084        for i in 0..NUM_PRIMES {
1085            let p = if i == 0 {
1086                2
1087            } else {
1088                ODD_PRIMES[i - 1] as usize
1089            };
1090            let count = rhs.primes[i];
1091            if count != 0 {
1092                DenomArray::<NUM_PRIMES>::accum_pow(&mut self, &mut tmp, p, count);
1093            }
1094        }
1095
1096        self *= tmp;
1097        if let Some(remainder) = rhs.remainder {
1098            self *= BigInt::from_biguint(Sign::Plus, remainder);
1099        }
1100        self
1101    }
1102}
1103
1104impl<const NUM_PRIMES: usize> Mul<&DenomArray<NUM_PRIMES>> for BigInt {
1105    type Output = Self;
1106
1107    fn mul(mut self, rhs: &DenomArray<NUM_PRIMES>) -> Self {
1108        const {
1109            assert!(NUM_PRIMES <= ODD_PRIMES.len() + 1);
1110        }
1111
1112        let mut tmp = 1_usize;
1113        for i in 0..NUM_PRIMES {
1114            let p = if i == 0 {
1115                2
1116            } else {
1117                ODD_PRIMES[i - 1] as usize
1118            };
1119            let count = rhs.primes[i];
1120            if count != 0 {
1121                DenomArray::<NUM_PRIMES>::accum_pow(&mut self, &mut tmp, p, count);
1122            }
1123        }
1124
1125        self *= tmp;
1126        if let Some(remainder) = &rhs.remainder {
1127            self *= BigInt::from_biguint(Sign::Plus, remainder.clone());
1128        }
1129        self
1130    }
1131}
1132
1133impl<const NUM_PRIMES: usize> Mul<DenomArray<NUM_PRIMES>> for &BigInt {
1134    type Output = BigInt;
1135
1136    fn mul(self, rhs: DenomArray<NUM_PRIMES>) -> BigInt {
1137        const {
1138            assert!(NUM_PRIMES <= ODD_PRIMES.len() + 1);
1139        }
1140
1141        let mut this = self.clone();
1142        let mut tmp = 1_usize;
1143        for i in 0..NUM_PRIMES {
1144            let p = if i == 0 {
1145                2
1146            } else {
1147                ODD_PRIMES[i - 1] as usize
1148            };
1149            let count = rhs.primes[i];
1150            if count != 0 {
1151                DenomArray::<NUM_PRIMES>::accum_pow(&mut this, &mut tmp, p, count);
1152            }
1153        }
1154
1155        this *= tmp;
1156        if let Some(remainder) = rhs.remainder {
1157            this *= BigInt::from_biguint(Sign::Plus, remainder);
1158        }
1159        this
1160    }
1161}
1162
1163impl<const NUM_PRIMES: usize> Mul<&DenomArray<NUM_PRIMES>> for &BigInt {
1164    type Output = BigInt;
1165
1166    fn mul(self, rhs: &DenomArray<NUM_PRIMES>) -> BigInt {
1167        const {
1168            assert!(NUM_PRIMES <= ODD_PRIMES.len() + 1);
1169        }
1170
1171        let mut this = self.clone();
1172        let mut tmp = 1_usize;
1173        for i in 0..NUM_PRIMES {
1174            let p = if i == 0 {
1175                2
1176            } else {
1177                ODD_PRIMES[i - 1] as usize
1178            };
1179            let count = rhs.primes[i];
1180            if count != 0 {
1181                DenomArray::<NUM_PRIMES>::accum_pow(&mut this, &mut tmp, p, count);
1182            }
1183        }
1184
1185        this *= tmp;
1186        if let Some(remainder) = &rhs.remainder {
1187            this *= BigInt::from_biguint(Sign::Plus, remainder.clone());
1188        }
1189        this
1190    }
1191}
1192
1193impl<const NUM_PRIMES: usize> Mul<BigInt> for DenomArray<NUM_PRIMES> {
1194    type Output = BigInt;
1195
1196    fn mul(self, mut rhs: BigInt) -> BigInt {
1197        const {
1198            assert!(NUM_PRIMES <= ODD_PRIMES.len() + 1);
1199        }
1200
1201        let mut tmp = 1_usize;
1202        for i in 0..NUM_PRIMES {
1203            let p = if i == 0 {
1204                2
1205            } else {
1206                ODD_PRIMES[i - 1] as usize
1207            };
1208            let count = self.primes[i];
1209            if count != 0 {
1210                Self::accum_pow(&mut rhs, &mut tmp, p, count);
1211            }
1212        }
1213
1214        rhs *= tmp;
1215        if let Some(remainder) = self.remainder {
1216            rhs *= BigInt::from_biguint(Sign::Plus, remainder);
1217        }
1218        rhs
1219    }
1220}
1221
1222impl<const NUM_PRIMES: usize> Mul<&BigInt> for DenomArray<NUM_PRIMES> {
1223    type Output = BigInt;
1224
1225    fn mul(self, rhs: &BigInt) -> BigInt {
1226        const {
1227            assert!(NUM_PRIMES <= ODD_PRIMES.len() + 1);
1228        }
1229
1230        let mut rhs = rhs.clone();
1231        let mut tmp = 1_usize;
1232        for i in 0..NUM_PRIMES {
1233            let p = if i == 0 {
1234                2
1235            } else {
1236                ODD_PRIMES[i - 1] as usize
1237            };
1238            let count = self.primes[i];
1239            if count != 0 {
1240                Self::accum_pow(&mut rhs, &mut tmp, p, count);
1241            }
1242        }
1243
1244        rhs *= tmp;
1245        if let Some(remainder) = self.remainder {
1246            rhs *= BigInt::from_biguint(Sign::Plus, remainder);
1247        }
1248        rhs
1249    }
1250}
1251
1252impl<const NUM_PRIMES: usize> Mul<BigInt> for &DenomArray<NUM_PRIMES> {
1253    type Output = BigInt;
1254
1255    fn mul(self, mut rhs: BigInt) -> BigInt {
1256        const {
1257            assert!(NUM_PRIMES <= ODD_PRIMES.len() + 1);
1258        }
1259
1260        let mut tmp = 1_usize;
1261        for i in 0..NUM_PRIMES {
1262            let p = if i == 0 {
1263                2
1264            } else {
1265                ODD_PRIMES[i - 1] as usize
1266            };
1267            let count = self.primes[i];
1268            if count != 0 {
1269                DenomArray::<NUM_PRIMES>::accum_pow(&mut rhs, &mut tmp, p, count);
1270            }
1271        }
1272
1273        rhs *= tmp;
1274        if let Some(remainder) = &self.remainder {
1275            rhs *= BigInt::from_biguint(Sign::Plus, remainder.clone());
1276        }
1277        rhs
1278    }
1279}
1280
1281impl<const NUM_PRIMES: usize> Mul<&BigInt> for &DenomArray<NUM_PRIMES> {
1282    type Output = BigInt;
1283
1284    fn mul(self, rhs: &BigInt) -> BigInt {
1285        const {
1286            assert!(NUM_PRIMES <= ODD_PRIMES.len() + 1);
1287        }
1288
1289        let mut rhs = rhs.clone();
1290        let mut tmp = 1_usize;
1291        for i in 0..NUM_PRIMES {
1292            let p = if i == 0 {
1293                2
1294            } else {
1295                ODD_PRIMES[i - 1] as usize
1296            };
1297            let count = self.primes[i];
1298            if count != 0 {
1299                DenomArray::<NUM_PRIMES>::accum_pow(&mut rhs, &mut tmp, p, count);
1300            }
1301        }
1302
1303        rhs *= tmp;
1304        if let Some(remainder) = &self.remainder {
1305            rhs *= BigInt::from_biguint(Sign::Plus, remainder.clone());
1306        }
1307        rhs
1308    }
1309}
1310
1311impl<const NUM_PRIMES: usize> MulAssign<DenomArray<NUM_PRIMES>> for BigInt {
1312    fn mul_assign(&mut self, rhs: DenomArray<NUM_PRIMES>) {
1313        const {
1314            assert!(NUM_PRIMES <= ODD_PRIMES.len() + 1);
1315        }
1316
1317        let mut tmp = 1_usize;
1318        for i in 0..NUM_PRIMES {
1319            let p = if i == 0 {
1320                2
1321            } else {
1322                ODD_PRIMES[i - 1] as usize
1323            };
1324            let count = rhs.primes[i];
1325            if count != 0 {
1326                DenomArray::<NUM_PRIMES>::accum_pow(self, &mut tmp, p, count);
1327            }
1328        }
1329
1330        *self *= tmp;
1331        if let Some(remainder) = rhs.remainder {
1332            *self *= BigInt::from_biguint(Sign::Plus, remainder);
1333        }
1334    }
1335}
1336
1337impl<const NUM_PRIMES: usize> MulAssign<&DenomArray<NUM_PRIMES>> for BigInt {
1338    fn mul_assign(&mut self, rhs: &DenomArray<NUM_PRIMES>) {
1339        const {
1340            assert!(NUM_PRIMES <= ODD_PRIMES.len() + 1);
1341        }
1342
1343        let mut tmp = 1_usize;
1344        for i in 0..NUM_PRIMES {
1345            let p = if i == 0 {
1346                2
1347            } else {
1348                ODD_PRIMES[i - 1] as usize
1349            };
1350            let count = rhs.primes[i];
1351            if count != 0 {
1352                DenomArray::<NUM_PRIMES>::accum_pow(self, &mut tmp, p, count);
1353            }
1354        }
1355
1356        *self *= tmp;
1357        if let Some(remainder) = &rhs.remainder {
1358            *self *= BigInt::from_biguint(Sign::Plus, remainder.clone());
1359        }
1360    }
1361}
1362
1363#[cfg(test)]
1364mod tests {
1365    use super::*;
1366
1367    macro_rules! tests {
1368        (
1369            $mod:ident,
1370            $num_primes:expr,
1371            $( $case:ident ,)*
1372        ) => {
1373            mod $mod {
1374                $(
1375                    #[test]
1376                    fn $case() {
1377                        $crate::denom_array::tests::$case::<$num_primes>();
1378                    }
1379                )*
1380            }
1381        };
1382    }
1383
1384    macro_rules! all_tests {
1385        (
1386            $mod:ident,
1387            $num_primes:expr
1388        ) => {
1389            tests!(
1390                $mod,
1391                $num_primes,
1392                test_decompose_is_correct,
1393                test_decompose_prime_powers,
1394                test_mul_prime_powers,
1395                test_div_prime_powers,
1396                test_to_biguint,
1397                test_product,
1398                test_normalize,
1399                test_gcd_reduce,
1400            );
1401        };
1402    }
1403
1404    all_tests!(denom24, 24);
1405    all_tests!(denom6542, 6542);
1406
1407    fn test_decompose_is_correct<const NUM_PRIMES: usize>() {
1408        for i in 1_usize..=1000 {
1409            let bigi = BigUint::from(i);
1410            let x = DenomArray::<NUM_PRIMES>::from(&bigi);
1411            let mut recomposed = x.remainder.unwrap_or_else(BigUint::one);
1412            for i in 0..NUM_PRIMES {
1413                let prime = if i == 0 { 2 } else { ODD_PRIMES[i - 1] };
1414                for _ in 0..x.primes[i] {
1415                    recomposed *= prime;
1416                }
1417            }
1418            assert_eq!(recomposed, bigi);
1419        }
1420    }
1421
1422    #[test]
1423    fn test_decompose_known_values() {
1424        assert_eq!(
1425            DenomArray24::from(BigUint::from(128_usize)),
1426            DenomArray24 {
1427                primes: [
1428                    7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1429                ],
1430                remainder: None,
1431            }
1432        );
1433        assert_eq!(
1434            DenomArray24::from(BigUint::from(89_usize)),
1435            DenomArray24 {
1436                primes: [
1437                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
1438                ],
1439                remainder: None,
1440            }
1441        );
1442        assert_eq!(
1443            DenomArray24::from(BigUint::from(97_usize)),
1444            DenomArray24 {
1445                primes: [
1446                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1447                ],
1448                remainder: Some(BigUint::from(97_usize)),
1449            }
1450        );
1451        assert_eq!(
1452            DenomArray24::from(BigUint::from(97000_usize)),
1453            DenomArray24 {
1454                primes: [
1455                    3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1456                ],
1457                remainder: Some(BigUint::from(97_usize)),
1458            }
1459        );
1460    }
1461
1462    fn test_decompose_prime_powers<const NUM_PRIMES: usize>() {
1463        for i in 0..24 {
1464            let p = if i == 0 { 2 } else { ODD_PRIMES[i - 1] };
1465            let p = BigUint::from(p);
1466            for power in 1..=255 {
1467                assert_eq!(
1468                    DenomArray::<NUM_PRIMES>::from(p.pow(power as u32)),
1469                    DenomArray {
1470                        primes: std::array::from_fn(|j| if i == j { power } else { 0 }),
1471                        remainder: None,
1472                    }
1473                );
1474            }
1475            for power in 1..=64 {
1476                assert_eq!(
1477                    DenomArray::<NUM_PRIMES>::from(p.pow(255 + power)),
1478                    DenomArray {
1479                        primes: std::array::from_fn(|j| if i == j { 255 } else { 0 }),
1480                        remainder: Some(p.pow(power)),
1481                    }
1482                );
1483            }
1484        }
1485    }
1486
1487    fn test_mul_prime_powers<const NUM_PRIMES: usize>() {
1488        let p = BigUint::from(2u32);
1489        for a in 1..=256 {
1490            let denom_a = DenomArray::<NUM_PRIMES>::from(p.pow(a));
1491            for b in 1..=256 {
1492                let denom_b = DenomArray::<NUM_PRIMES>::from(p.pow(b));
1493                let denom_ab = DenomArray::<NUM_PRIMES>::from(p.pow(a + b));
1494                assert_eq!(&denom_a * denom_b, denom_ab);
1495            }
1496        }
1497    }
1498
1499    fn test_div_prime_powers<const NUM_PRIMES: usize>() {
1500        let p = BigUint::from(2u32);
1501        for a in 1..=256 {
1502            let denom_a = DenomArray::<NUM_PRIMES>::from(p.pow(a));
1503            for b in 1..=256 {
1504                let denom_b = DenomArray::<NUM_PRIMES>::from(p.pow(b));
1505                let denom_ab = DenomArray::<NUM_PRIMES>::from(p.pow(a + b));
1506                assert_eq!(denom_ab / denom_b, denom_a);
1507            }
1508        }
1509    }
1510
1511    fn test_to_biguint<const NUM_PRIMES: usize>() {
1512        for i in 1_usize..=1000 {
1513            let bigi = BigUint::from(i);
1514            let x = DenomArray::<NUM_PRIMES>::from(&bigi);
1515            assert_eq!(x.to_biguint(), bigi);
1516        }
1517    }
1518
1519    fn test_product<const NUM_PRIMES: usize>() {
1520        let values = (100..200)
1521            .map(|i: usize| DenomArray::<NUM_PRIMES>::from(BigUint::from(i)))
1522            .collect::<Vec<_>>();
1523        for (i, x) in values.iter().enumerate().map(|(i, x)| (i + 100, x)) {
1524            for (j, y) in values.iter().enumerate().map(|(j, y)| (j + 100, y)) {
1525                let z = x * y;
1526                assert_eq!(z, DenomArray::<NUM_PRIMES>::from(BigUint::from(i * j)));
1527                for k in 0..NUM_PRIMES {
1528                    assert_eq!(z.primes[k], x.primes[k] + y.primes[k]);
1529                }
1530            }
1531        }
1532    }
1533
1534    fn test_normalize<const NUM_PRIMES: usize>() {
1535        let values = (100..200)
1536            .map(|i: usize| DenomArray::<NUM_PRIMES>::from(BigUint::from(i)))
1537            .collect::<Vec<_>>();
1538        for x in &values {
1539            for y in &values {
1540                let mut xnum = BigInt::one();
1541                let mut ynum = BigInt::one();
1542                let lcm = DenomArray::<NUM_PRIMES>::normalize(&mut xnum, &mut ynum, x, y);
1543                let lcm_bigint = lcm.to_biguint();
1544                let xnum = xnum.to_biguint().unwrap();
1545                let ynum = ynum.to_biguint().unwrap();
1546
1547                assert_eq!(xnum * x.to_biguint(), lcm_bigint);
1548                assert_eq!(ynum * y.to_biguint(), lcm_bigint);
1549                for k in 0..NUM_PRIMES {
1550                    assert_eq!(lcm.primes[k], std::cmp::max(x.primes[k], y.primes[k]));
1551                }
1552            }
1553        }
1554    }
1555
1556    fn test_gcd_reduce<const NUM_PRIMES: usize>() {
1557        let mut num = BigInt::from(-3 * 97);
1558        let mut denom = DenomArray::<NUM_PRIMES>::from(BigUint::from(3u32 * 5 * 97));
1559        denom.gcd_reduce(&mut num);
1560        assert_eq!(num, BigInt::from(-1));
1561        assert_eq!(denom, DenomArray::<NUM_PRIMES>::from(BigUint::from(5u32)));
1562    }
1563}