bignumbe_rs/
traits.rs

1//! This module contains additional traits I thought may be useful for BigNum usage.
2
3use crate::{Base, BigNumBase, SigRange};
4
5/// This trait gets the very next valid value of a type. Mainly for `BigNum`, since adding
6/// one often doesn't result in a changing value. This is provided for contexts where you
7/// need to increase the value easily
8pub trait Succ {
9    fn succ(self) -> Self;
10}
11
12/// This trait gets the previous valid value of a type. Mainly for `BigNum`, since subbing
13/// one often doesn't result in a changing value. This is provided for contexts where you
14/// need to decrease the value easily
15pub trait Pred {
16    fn pred(self) -> Self;
17}
18
19impl<T> Succ for BigNumBase<T>
20where
21    T: Base,
22{
23    fn succ(self) -> Self {
24        let SigRange(min_sig, max_sig) = self.base.sig_range();
25
26        if self.sig == max_sig {
27            Self {
28                sig: min_sig,
29                exp: self.exp + 1,
30                base: self.base,
31            }
32        } else {
33            Self {
34                sig: self.sig + 1,
35                ..self
36            }
37        }
38    }
39}
40
41impl<T> Pred for BigNumBase<T>
42where
43    T: Base,
44{
45    fn pred(self) -> Self {
46        let SigRange(min_sig, max_sig) = self.base.sig_range();
47
48        if self.exp == 0 {
49            if self.sig == 0 {
50                panic!("Cannot get the predecessor of 0");
51            }
52
53            Self {
54                sig: self.sig - 1,
55                ..self
56            }
57        } else if self.sig == min_sig {
58            Self {
59                sig: max_sig,
60                exp: self.exp - 1,
61                base: self.base,
62            }
63        } else {
64            Self {
65                sig: self.sig - 1,
66                ..self
67            }
68        }
69    }
70}
71
72pub trait BigNumPow<T>
73where
74    T: Base,
75{
76    fn pow(self, n: i32) -> BigNumBase<T>;
77}
78
79impl<T> BigNumPow<T> for f64
80where
81    T: Base,
82{
83    fn pow(self, n: i32) -> BigNumBase<T> {
84        let mut res: BigNumBase<T> = 1u64.into();
85
86        let max_pow = f64::MAX.log(T::NUMBER as f64).floor() as i32 - 1;
87
88        if n <= max_pow {
89            res *= self.powi(n);
90        } else {
91            let mut remaining_pow = n;
92            let mut divisions = 0;
93
94            loop {
95                if remaining_pow <= max_pow {
96                    //res *= self.powi(remaining_pow);
97                    res *= self.powi(remaining_pow);
98                    for _ in 0..divisions {
99                        res *= res;
100                    }
101                    break;
102                } else if remaining_pow <= max_pow * 25 {
103                    remaining_pow -= max_pow;
104                    res *= self.powi(max_pow);
105                } else {
106                    remaining_pow /= 2;
107                    divisions += 1;
108                }
109            }
110        }
111
112        res
113    }
114}
115
116#[cfg(test)]
117mod tests {
118    use super::*;
119
120    use crate::{
121        create_default_base, macros::test_macros::assert_eq_bignum, BigNumBase, BigNumBin, Binary,
122    };
123
124    // Other tests are in the normal macro so we can test it with many different bases
125    #[test]
126    fn test_succ() {
127        type BigNum = BigNumBase<Binary>;
128        let SigRange(min_sig, max_sig) = Binary::calculate_ranges().1;
129
130        assert_eq_bignum!(BigNum::new(0, 0).succ(), BigNum::new(1, 0));
131        assert_eq_bignum!(BigNum::new(max_sig, 0).succ(), BigNum::new(min_sig, 1));
132        assert_eq_bignum!(BigNum::new(max_sig, 1).succ(), BigNum::new(min_sig, 2));
133    }
134
135    #[test]
136    fn test_pred() {
137        type BigNum = BigNumBase<Binary>;
138        let SigRange(min_sig, max_sig) = Binary::calculate_ranges().1;
139
140        assert_eq_bignum!(BigNum::new(1, 0).pred(), BigNum::new(0, 0));
141        assert_eq_bignum!(BigNum::new(min_sig, 1).pred(), BigNum::new(max_sig, 0));
142        assert_eq_bignum!(BigNum::new(min_sig, 2).pred(), BigNum::new(max_sig, 1));
143    }
144
145    #[test]
146    fn test_bignum_pow() {
147        type BigNum = BigNumBase<Binary>;
148        create_default_base!(Base3, 3);
149        type BigNum3 = BigNumBase<Base3>;
150        create_default_base!(Base1422, 1422);
151        type BigNum1422 = BigNumBase<Base1422>;
152
153        let res: BigNum = 2.0.pow(1000);
154        let res3: BigNum3 = 3.0.pow(1000);
155        let res1422: BigNum1422 = 1422.0.pow(1000);
156
157        assert!(res.fuzzy_eq(BigNum::new(1, 1000), 1000));
158        assert!(res3.fuzzy_eq(BigNum3::new(1, 1000), 1000000));
159        assert!(
160            res1422.fuzzy_eq(BigNum1422::new(1, 1000), 1000000),
161            "{:?} {:?}",
162            res1422,
163            BigNum1422::new(1, 1000)
164        );
165    }
166
167    #[test]
168    fn test_bignum_pow2() {
169        type BigNum = BigNumBin;
170        assert_eq!(2f64.pow(10), BigNum::from(1024));
171        assert_eq!(2f64.pow(20), BigNum::from(1024 * 1024));
172
173        let act: BigNum = 2f64.pow(200_000);
174        let exp = BigNum::new(1, 200_000);
175        let (min, max) = if act > exp { (exp, act) } else { (act, exp) };
176
177        assert!(
178            min.fuzzy_eq(max, u64::MAX / 1_000_000),
179            "{:?} {:?}",
180            min,
181            max
182        );
183
184        let act: BigNum = 2f64.pow(2_000_000);
185        let exp = BigNum::new(1, 2_000_000);
186        let (min, max) = if act > exp { (exp, act) } else { (act, exp) };
187
188        assert!(
189            min.fuzzy_eq(max, u64::MAX / 1_000_000),
190            "{:?} {:?}",
191            min,
192            max
193        );
194
195        let act: BigNum = 2f64.pow(2_000_000_000);
196        let exp = BigNum::new(1, 2_000_000_000);
197        let (min, max) = if act > exp { (exp, act) } else { (act, exp) };
198
199        // With powers this large we give up a huge amount of precision so it's not recommended
200        assert!(
201            max.exp as f64 / (max.exp - min.exp) as f64 > 10_000.,
202            "{} {}",
203            min.exp,
204            max.exp
205        );
206    }
207}