number_theory/arithmetic/
mpz.rs

1use crate::arithmetic::conversion::*;
2use crate::arithmetic::inlineops::*;
3
4use crate::arithmetic::sign::Sign;
5use crate::arithmetic::sliceops::*;
6use std::cmp::Ordering;
7use crate::primitive::factorprim::poly_factor_128;
8use crate::ntrait::NumberTheory;
9
10/// Arbitrary precision integer (Z)
11#[derive(Debug, Default, Clone, PartialEq)]
12pub struct Mpz {
13    pub(crate) sign: Sign,
14    pub(crate) limbs: Vec<u64>,
15}
16
17impl std::fmt::Display for Mpz{
18      fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19    
20       let out = to_string(self.sign.clone(), self.limbs.clone());
21       write!(f,"{}",out)
22  }
23}
24
25impl std::convert::From<u64> for Mpz{
26   fn from(val: u64) -> Self{
27        Mpz::unchecked_new(Sign::Positive, vec![val])
28   }
29}
30
31impl std::convert::From<u32> for Mpz{
32   fn from(val: u32) -> Self{
33        Mpz::unchecked_new(Sign::Positive, vec![val.into()])
34   }
35}
36
37impl std::convert::From<i64> for Mpz{
38   fn from(val: i64) -> Self{
39        if val < 0i64 {
40            return Mpz::unchecked_new(Sign::Negative, vec![val.unsigned_abs()]);
41        }
42        Mpz::unchecked_new(Sign::Positive, vec![val as u64])
43      }  
44}
45
46impl std::convert::From<i32> for Mpz{
47   fn from(val: i32) -> Self{
48        if val < 0i32 {
49            return Mpz::unchecked_new(Sign::Negative, vec![val.unsigned_abs() as u64]);
50        }
51        Mpz::unchecked_new(Sign::Positive, vec![val as u64])
52      }  
53}
54
55impl std::convert::From<u128> for Mpz{
56   fn from(val: u128) -> Self{
57      let (x_lo, x_hi) = split(val);
58        if x_hi == 0 {
59            return Mpz::unchecked_new(Sign::Positive, vec![x_lo]);
60        }
61        Mpz::unchecked_new(Sign::Positive, vec![x_lo, x_hi])
62   }
63}
64
65impl std::convert::From<i128> for Mpz{
66
67   fn from(val: i128) -> Self {
68        if val < 0i128 {
69            let (x_lo, x_hi) = split(val.unsigned_abs());
70            if x_hi == 0 {
71                return Mpz::unchecked_new(Sign::Negative, vec![x_lo]);
72            }
73            Mpz::unchecked_new(Sign::Negative, vec![x_lo, x_hi])
74        } else {
75            Mpz::from(val as u128)
76        }
77    }
78  }
79
80impl std::convert::TryFrom<Mpz> for u64{
81      type Error = &'static str;
82
83   fn try_from(x: Mpz) ->  Result<Self, Self::Error>{
84        
85        match x.len() {
86         0 => Ok(0u64),
87         1 => Ok(x.limbs[0]),
88         _=> Err("Multiprecision value exceeds 2^64"),
89        }
90   }
91
92}  
93
94impl std::convert::TryFrom<Mpz> for i64{
95     type Error = &'static str;
96
97  fn try_from(x: Mpz) -> Result<Self,Self::Error>{
98     
99      match x.len(){
100          0 => Ok(0i64),
101          1 => {
102             let value = x.limbs[0];
103            if (value>>63)==1{
104               return  Err("Single precision value exceeds 2^63");
105             }
106            let mut res : i64 = value as i64;
107            if x.sign == Sign::Negative{
108               res = -res;
109            }
110            return Ok(res);
111           }
112          _=> Err("Multiprecision value exceeds 2^64"),
113      }
114  }
115}  
116  /*
117 impl std::str::FromStr for Mpz{
118 
119 } 
120
121*/
122impl Mpz {
123    /**
124
125    ```
126     use number_theory::Mpz;  // includes arbitrary-precision arithmetic
127     use number_theory::Sign; // allows sign
128     use number_theory::NumberTheory; // includes methods from NumberTheory trait
129
130     let bignum = Mpz::new(Sign::Positive, vec![5,5]);
131
132     let fortyfour = Mpz::from(44u128);
133
134     let fortyfour_neg = Mpz::from(-44i128);
135
136     let twopow = Mpz::from(128u64);
137
138
139     assert_eq!("92233720368547758085", bignum.to_string());
140     assert_eq!("44", fortyfour.to_string());
141     assert_eq!("-44", fortyfour_neg.to_string());
142     assert_eq!("128", twopow.to_string());
143
144    ```
145
146    */
147      /// Returns a positive x from a big-endian vector representation 
148    pub fn u_new(limbs: Vec<u64>) -> Self {
149        Mpz::from_slice(Sign::Positive, &limbs[..])
150    }
151      /// Returns a x from a big-endian vector representation and a sign 
152    pub fn new(sign: Sign, limbs: Vec<u64>) -> Self {
153        Mpz::from_slice(sign, &limbs[..])
154    }
155     /// Returns a positive x from a big-endian vector, this function does not eliminate extraneous leading zeros 
156    pub fn unchecked_u_new(limbs: Vec<u64>) -> Self {
157        Mpz {
158            sign: Sign::Positive,
159            limbs,
160        }
161    }
162
163  /// Returns a  x from a big-endian vector and sign, this function does not eliminate extraneous leading zeros 
164    pub fn unchecked_new(sign: Sign, limbs: Vec<u64>) -> Self {
165        Mpz { sign, limbs }
166    }
167/// Returns x from a big_endian slice and sign
168    pub fn from_slice(sign: Sign, x: &[u64]) -> Self {
169        let mut limbs = vec![];
170        limbs.extend_from_slice(&x[..sig_pos(x)]);
171        if limbs.is_empty() {
172            limbs.push(0u64)
173        }
174        Mpz { sign, limbs }
175    }
176    
177    /// Converts to 32-bit unsigned integer if it can fit 
178    pub fn to_u32(&self) ->  Option<u32>{
179       match self.len() {
180            0 => Some(0u32),
181            1 => {
182                   if self.limbs[0] > u32::MAX as u64 {
183                      return None
184                   } 
185                   Some(self.limbs[0] as u32)
186                 }
187            _ => None,
188        }
189    }
190    
191     /// Converts to 128-bit unsigned integer if it can fit
192    pub fn to_u128(&self) -> Option<u128> {
193        match self.len() {
194            0 => Some(0u128),
195            1 => Some(self.limbs[0] as u128),
196            2 => Some(fuse(self.limbs[1], self.limbs[0])),
197            _ => None,
198        }
199    }
200    /// Returns the vector representation of n
201    pub fn to_vector(&self) -> Vec<u64> {
202        self.limbs.clone()
203    }
204   
205     /// Set an integer between 2^(n-1);2^n
206    pub fn rand(len: usize) -> Self {
207      let mut k = len/64;
208      if len%64 != 0{
209        k+=1;
210      }
211      let mut interim = (0..k).map(|_| u64::rng()).collect::<Vec<u64>>();
212      
213      interim[k-1] &= (1<<(len%64))-1;
214      let mut result = Mpz::unchecked_new(Sign::Positive, interim);
215      result.set_bit(len-1);
216      result
217      
218      
219    }
220    
221    
222    /// Creates a integer from limbs generated by a function 
223    pub fn set_limbs(len: usize, gen: fn() -> u64) -> Self {
224        let interim = (0..len).map(|_| gen()).collect::<Vec<u64>>();
225        Mpz::unchecked_new(Sign::Positive, interim)
226    }
227    /// Converts n to a radix-10 string
228    pub fn to_string(&self) -> String {
229        to_string(self.sign.clone(), self.limbs.clone())
230    }
231    /// Converts n to a radix-16 string in linear time
232    pub fn to_hex_string(&self) -> String{
233       to_hex_string(self.sign.clone(),self.limbs.clone())
234    }
235    // temporary placeholder function to flip negative zeros
236    pub(crate) fn fix_zero(&mut self) {
237        if self.len() == 0 {
238            self.limbs.push(0u64)
239        }
240        if self.len() == 1 && self.limbs[0] == 0 && self.sign == Sign::Negative {
241            self.sign = Sign::Positive;
242        }
243    }
244    /**
245      Returns the polynomial representation of self in the form of the coefficient vector. Here we see that 50620 to radix-127 is 3x^2 + 17x + 74.
246      Accepts all radix in the interval 0;2^64-1.
247
248    ```
249    use number_theory::Mpz;
250
251     let value = Mpz::from(50620u64);
252     assert_eq!(value.to_radix_vec(127),vec![74,17,3])
253    ```
254    */
255
256    pub fn to_radix_vec(&self, radix: u64) -> Vec<u64> {
257        let mut k = vec![];
258        let mut x = self.clone().limbs;
259        loop {
260            let idx = sig_pos(&x[..]);
261
262            if idx == 0 {
263                break;
264            }
265
266            k.push(div_slice(&mut x[..idx], radix));
267        }
268        k.push(x[0] % radix);
269        remove_lead_zeros(&mut k);
270        k
271    }
272
273    /**
274      Conversion from radix-10^n string.
275
276    ```
277    use number_theory::Mpz;
278     let num = "-3141592653589793238462643383279502884197169399375105820974944592307816406286".to_string();
279     let machine = Mpz::from_string(&num).unwrap();
280
281     assert_eq!(machine.to_string(),num)
282    ```
283    */
284
285    pub fn from_string(x: &str) -> Option<Self> {
286        if x.is_empty() {
287            return None;
288        }
289
290        let ch = x.chars().next().unwrap();
291        let mut sign = Sign::Positive;
292        let mut k = x;
293        if ch == '-' {
294            sign = Sign::Negative;
295            let mut chars = x.chars();
296            chars.next();
297            k = chars.as_str();
298        }
299
300        if ch == '+' {
301            let mut chars = x.chars();
302            chars.next();
303            k = chars.as_str();
304        }
305
306        from_string(k).map(|y| Mpz::unchecked_new(sign, y))
307    }
308    
309    /// Returns 0
310    pub fn zero() -> Self {
311        Mpz::unchecked_new(Sign::Positive, vec![0])
312    }
313    /// Returns positive 1
314    pub fn one() -> Self {
315        Mpz::unchecked_new(Sign::Positive, vec![1])
316    }
317    /// Returns positive 2
318    pub fn two() -> Self{
319       Mpz::unchecked_new(Sign::Positive, vec![2])
320    }
321    /// Additive inverse of n, evaluated in-place 
322    pub fn neg(&mut self) {
323        self.sign = self.sign.neg();
324    }
325     /// Returns the absolute value of n  
326    pub fn abs(&self) -> Self {
327        Self::new(Sign::Positive, self.limbs.clone())
328    }
329    
330    /// Checks if n == 0
331    pub fn is_zero(&self) -> bool {
332       if self.len() == 0{
333         return true
334       }
335       if self.len() == 1 && self.limbs[0] == 0{
336         return true
337       }
338       false
339    }
340    /// Checks if n >= 0
341    pub fn is_positive(&self) -> bool {
342        self.sign == Sign::Positive
343    }
344     /// Checks if n in 2Z
345    pub fn is_even(&self) -> bool {
346        self.limbs[0] & 1 == 0
347    }
348    /// Checks if n in 2^Z
349    pub fn is_power_of_two(&self) -> bool {
350        if self.len() < 2 {
351            return self.to_u128().unwrap().is_power_of_two();
352        }
353        if !self.lead_digit().is_power_of_two() {
354            return false;
355        }
356        for i in self.limbs[..self.len() - 1].iter() {
357            if *i != 0 {
358                return false;
359            }
360        }
361        true
362    }
363
364    pub(crate) fn is_fermat(&self) -> bool {
365        if self.limbs[0] != 1 {
366            return false;
367        }
368
369        let lead = self.limbs[..].last().unwrap();
370
371        let mut flag = 0u64;
372        for i in 0..64 {
373            if *lead == 1u64 << i {
374                flag = 1; // set flag
375                break; // end loop
376            }
377        }
378
379        if flag == 0 {
380            return false;
381        } // if the flag is not set return false
382
383        for i in self.limbs[1..self.len() - 1].iter() {
384            if *i != 0u64 {
385                return false;
386            }
387        }
388        true
389    }
390
391    pub(crate) fn is_mersenne(&self) -> Option<u64> {
392        let mut flag = 0u64;
393        let mut start = 1u64;
394        let lead = self.limbs.last().unwrap();
395        for i in 0..64 {
396            if *lead == start {
397                flag = i;
398                break;
399            }
400            start = (start << 1) + 1;
401        }
402        if flag == 0 {
403            return None;
404        }
405        for i in self.limbs[..self.len() - 2].iter() {
406            if *i != u64::MAX {
407                return None;
408            }
409        }
410
411        Some(flag + 1 + 64u64 * (self.len() - 1) as u64)
412    }
413    /// Sets the bit at 2^k to 1, if it is already 1 then this does not change
414    pub fn set_bit(&mut self, k: usize) {
415        self.limbs[k / 64usize] |= 1 << (k % 64)
416    }
417    /// Flips the bit at 2^k
418    pub fn flip_bit(&mut self, k: usize){
419         self.limbs[k / 64usize] ^= 1 << (k % 64)
420    }
421   /// Check if the bit in the k-place is set  
422    pub fn check_bit(&self, index: usize) -> bool {
423        self.limbs[index / 64usize] & (1 << (index % 64)) > 0
424    }
425   /// Change the sign   
426    pub fn set_sign(&mut self, sign: Sign) {
427        self.sign = sign
428    }
429    /// Returns the number of 64-bit machine words used in this representation
430    pub fn len(&self) -> usize {
431        self.limbs.len()
432    }
433    /// Returns the lead digit in 2^64 representation
434    pub fn lead_digit(&self) -> u64 {
435        *self.limbs[..].last().unwrap()
436    }
437   /// Returns k such that  d*2^k = x
438    pub fn trailing_zeros(&self) -> u64 {
439        // Trailing zeros
440        let mut idx: u64 = 0;
441
442        for i in self.limbs.iter() {
443            if i == &0u64 {
444                idx += 1;
445            } else {
446                break;
447            }
448        }
449        if idx == self.len() as u64 {
450            64u64 * idx
451        } else {
452            self.limbs[idx as usize].trailing_zeros() as u64 + 64u64 * idx
453        }
454    }
455    /// Returns the number of extraneous zeros in bit representation
456    pub fn leading_zeros(&self) -> u64 {
457        let mut idx: u64 = 0;
458
459        for i in self.limbs.iter().rev() {
460            if i == &0u64 {
461                idx += 1;
462            } else {
463                break;
464            }
465        }
466        if idx == self.len() as u64 {
467            64u64 * idx
468        } else {
469            self.limbs[self.len() - 1 - idx as usize].leading_zeros() as u64 + 64u64 * idx
470        }
471    }
472  /// Returns the number of bits used to represent x 
473    pub fn bit_length(&self) -> u64 {
474        64u64 * self.len() as u64 - self.leading_zeros()
475    }
476   /// Removes extraneous zeros 
477    pub fn normalize(&mut self) {
478        remove_lead_zeros(&mut self.limbs);
479        if self.len() == 0 {
480            self.limbs.push(0u64)
481        };
482    }
483
484    /*
485    Equality Operations
486
487    */
488  /// Compares the magnitude of x and y, ignoring sign
489    pub fn u_cmp(&self, other: &Self) -> Ordering {
490        cmp_slice(&self.limbs[..], &other.limbs[..])
491    }
492   /// Checks if x mod n === c 
493    pub fn congruence_u64(&self, n: u64, c: u64) -> bool {
494        let mut interim = mod_slice(&self.limbs[..], n);
495        if self.sign == Sign::Negative {
496            interim = n - interim;
497        }
498        interim == c
499    }
500
501    pub(crate) fn add_modinv(&self, n: &Self) -> Self {
502        // additive modular inverse
503        let mut k = n.clone();
504        let mut selfie = self.clone();
505
506        if self.u_cmp(n) == Ordering::Greater {
507            selfie = selfie.ref_euclidean(n).1;
508        }
509
510        sub_slice(&mut k.limbs, &selfie.limbs);
511        k.normalize();
512        k.sign = Sign::Positive;
513        k
514    }
515
516    /**
517
518       ```
519             use number_theory::Mpz;
520         let mut one = Mpz::one();
521
522         one.successor();  //Applies the successor function ignoring sign
523
524         assert_eq!("2", one.to_string())
525
526       ```
527
528
529    */
530
531    pub fn successor(&mut self) {
532        if self.len() == 0 {
533            self.limbs.push(1)
534        }
535        let mut carry = 1u8;
536        for i in self.limbs.iter_mut() {
537            carry = adc(carry, *i, 0, i);
538            if carry == 0 {
539                break;
540            }
541        }
542        if carry > 0u8 {
543            self.limbs.push(1u64)
544        }
545    }
546     /// Increments by n amount
547    pub fn inc_by(&mut self, n: u64){
548      if self.len() == 0 {
549            self.limbs.push(n)
550        }
551        
552        add_slice(&mut self.limbs[..],&[n]);
553    }
554    
555    /// Inverse successor function. Subtracts 1 ignoring sign
556    pub fn predecessor(&mut self) {
557        if self.len() == 0 {
558            panic!("Value already zero")
559        }
560        let mut carry = 1u8;
561        for i in self.limbs.iter_mut() {
562            carry = sbb(carry, *i, 0, i);
563            if carry == 0 {
564                break;
565            }
566        }
567        if carry > 0u8 {
568            panic!("Value already zero")
569        }
570    }
571
572    /*
573      Precursor NT functions, unsigned
574    */
575
576    pub(crate) fn u_quadratic_residue(&self, n: &Self) -> Self {
577        self.ref_product(self).ref_euclidean(n).1
578    }
579
580    pub(crate) fn u_mul_mod(&self, other: &Self, n: &Self) -> Self {
581        self.ref_product(other).ref_euclidean(n).1
582    }
583
584    pub(crate) fn u_mod_pow(&self, y: &Self, modulo: &Self) -> Self {
585        if modulo == &Mpz::zero() {
586            return Mpz::zero();
587        }
588
589        let mut z = Mpz::one();
590        let mut base = self.clone().ref_euclidean(modulo).1;
591        let one = Mpz::one();
592
593        let mut pow = y.clone();
594
595        if pow == Mpz::zero(){
596            return z;
597        }
598
599        if pow == Mpz::one() {
600            return base;
601        }
602
603        while pow.u_cmp(&one) == Ordering::Greater {
604            if pow.len() == 0 {
605                break;
606            }
607
608            if pow.is_even() {
609                base = base.u_quadratic_residue(modulo);
610                pow.mut_shr(1);
611                remove_lead_zeros(&mut pow.limbs);
612            } else {
613                z = base.u_mul_mod(&z, modulo);
614                remove_lead_zeros(&mut base.limbs);
615                base = base.u_quadratic_residue(modulo);
616
617                sub_slice(&mut pow.limbs[..], &one.limbs[..]);
618                pow.mut_shr(1);
619                remove_lead_zeros(&mut pow.limbs);
620            }
621        }
622         base.u_mul_mod(&z, modulo)
623    }
624
625    /// Returns self^x
626    pub fn pow(&self, x: u64) -> Self {
627        let mut z = Mpz::one();
628        let mut base = self.clone();
629        let mut pow = x;
630
631        if pow == 0 {
632            return z;
633        }
634
635        while pow > 1 {
636            if pow % 2 == 0 {
637                base = base.ref_product(&base);
638                pow >>= 1;
639            } else {
640                z = base.ref_product(&z);
641                base = base.ref_product(&base);
642                pow = (pow - 1) >> 1;
643            }
644        }
645
646        base.ref_product(&z)
647    }
648
649    pub(crate) fn word_div(&self, x: u64) -> (Self, u64) {
650        let mut quotient = self.clone();
651        let remainder = div_slice(&mut quotient.limbs[..], x);
652        (quotient, remainder)
653    }
654
655    fn delta(&self, other: &Self) -> Self {
656        if self.u_cmp(other) == Ordering::Greater {
657            let mut k = self.clone();
658            sub_slice(&mut k.limbs[..], &other.limbs[..]);
659            return k;
660        }
661        if self.u_cmp(other) == Ordering::Less {
662            let mut k = other.clone();
663            sub_slice(&mut k.limbs[..], &self.limbs[..]);
664            k
665        } else {
666            Mpz::zero()
667        }
668    }
669
670    fn mod_sqr_1(&self, n: &Self) -> Self {
671        let mut k = self.ref_product(self);
672        k.successor();
673        k.ref_euclidean(n).1
674    }
675
676    fn gcd(&self, other: &Self) -> Self {
677        let mut a = self.clone();
678        let mut b = other.clone();
679
680        while b != Mpz::zero() {
681            let t = b.clone();
682
683            b = a.ref_euclidean(&b).1;
684
685            b.normalize();
686            a = t;
687        }
688        a
689    }
690    
691// FIXME 
692    pub(crate) fn rho_mpz(&self) -> Self {
693    
694     match self.to_u128(){
695      Some(x) => Mpz::from(poly_factor_128(x)),
696      None => {
697        let mut x = Mpz::two();
698        let mut y = Mpz::two();
699        let mut d = Mpz::one();
700        loop {
701            while d.is_unit(){
702                x = x.mod_sqr_1(self);
703                y = y.mod_sqr_1(self).mod_sqr_1(self).ref_euclidean(self).1;
704                d = x.delta(&y).gcd(self);
705            }
706
707            if d.is_prime() {
708                return d;
709            }
710            x = Mpz::from(u64::rng());
711            y = x.clone();
712            d = Mpz::one();
713        }
714       
715       },
716    }
717    }
718
719    /// Approximation of the natural logarithm ln(x)
720    pub fn ln(&self) -> f64 {
721        let mut iter_sqrt = 1u64;
722        let mut n = self.sqrt().0;
723        while n.len() > 1 {
724            n = n.sqrt().0;
725            iter_sqrt += 1;
726        }
727
728        (u64::try_from(n).unwrap() as f64).ln() * 2f64.powf(iter_sqrt as f64)
729    }
730    /// Approximation of the base-2 logarithm
731    pub fn log2(&self) -> f64 {
732        self.ln() * std::f64::consts::LOG2_E
733    }
734    /// Approximation of the base-10 logarithm
735    pub fn log10(&self) -> f64 {
736        self.ln() * 0.43429448190325176
737    }
738    /// Approximation of the base-k logarithm, where k is a Real.
739    pub fn log(&self, log: f64) -> f64 {
740        self.ln() * log.ln().recip()
741    }
742    /// Iterated logarithm
743    pub fn iter_log(&self, log: f64) -> u8 {
744        let mut first_log = self.log(log);
745        let mut count = 1u8;
746        // 1.444667861009766
747
748        while first_log > 1.0 {
749            first_log = first_log.log(log);
750            count += 1
751        }
752        count
753    }
754    
755    /// Finite ring variant of Extended Euclidean algorithm, see trait definition of extended gcd
756    pub fn eea(&self, other: Self) -> (Self, Self, Self) {
757    
758    let (gcd, bezout_1, bezout_2) = self.extended_gcd(other.clone());
759    
760    (gcd, bezout_1.residue(other.clone()), bezout_2.residue(self.clone()))
761     
762    }
763
764    /**
765    Sequential Interval Residue Product, a generalization of the k-factorials
766    
767      ```
768
769         // see the sirp crate for greater extension of this functionality
770          use number_theory::Mpz;
771          
772         let factorial_100 = Mpz::sirp(1,100,1,0);
773         
774         // Even. Odd double factorial would be sirp(1,100,2,1)
775         let doublefact_100 = Mpz::sirp(1,100,2,0);
776         assert_eq!(
777         "3424322470251197624824643289520818597\
778          5118675053719198827915654463488000000000000",doublefact_100.to_string())
779      ```
780    */
781
782    pub fn sirp(infimum: u64, supremum: u64, modulo: u64, residue: u64) -> Self {
783        let mut sirp = Mpz::one();
784        let mut acc = 1u64; // accumulator for factors
785
786        for i in infimum..supremum + 1 {
787            // inclusive range
788
789            if i % modulo == residue {
790                // if i is of the residue class modulo n then multiply   If you set residue as stop mod n then you get the k-factorials
791
792                if i >= 4294967296 {
793                    acc = i
794                }
795                if acc < 4294967296 {
796                    acc *= i
797                }
798                if acc >= 4294967296 {
799                    let carry = scale_slice(&mut sirp.limbs[..], acc);
800
801                    if carry > 0 {
802                        sirp.limbs.push(carry)
803                    }
804                    acc = 1u64;
805                } // end if
806            } // else
807        }
808
809        let carry = scale_slice(&mut sirp.limbs[..], acc);
810        if carry > 0 {
811            sirp.limbs.push(carry)
812        }
813        sirp
814    }
815    /**
816        Conditional Interval Product computes the product of integers satisfying an unary function. In this example the unary function is the
817         primality function. In practice it can be any function that borrows a u64 and returns a boolean. 
818
819      ```
820
821          use number_theory::Mpz;
822          use number_theory::NumberTheory;
823
824         let primorial_25 = Mpz::cip(1,100,u64::is_prime);
825
826
827      ```
828    */
829
830    // conditional interval product
831    pub fn cip(infimum: u64, supremum: u64, cond: fn(&u64) -> bool) -> Self {
832        let mut cip = Mpz::one();
833        let mut acc = 1u64; // accumulator for factors
834        for i in infimum..supremum + 1 {
835            if cond(&i) {
836                if i >= 4294967296 {
837                    acc = i
838                }
839                if acc < 4294967296 {
840                    acc *= i
841                }
842                if acc >= 4294967296 {
843                    let carry = scale_slice(&mut cip.limbs[..], acc);
844
845                    if carry > 0 {
846                        cip.limbs.push(carry)
847                    }
848                    acc = 1u64;
849                } // end if
850            } // else
851        }
852
853        let carry = scale_slice(&mut cip.limbs[..], acc);
854        if carry > 0 {
855            cip.limbs.push(carry)
856        }
857        cip
858    }
859}