gpl_math/
precise_number.rs

1//! Defines PreciseNumber, a U256 wrapper with float-like operations
2
3use crate::uint::U256;
4
5// Allows for easy swapping between different internal representations
6type InnerUint = U256;
7
8/// The representation of the number one as a precise number as 10^12
9pub const ONE: u128 = 1_000_000_000_000;
10
11/// Struct encapsulating a fixed-point number that allows for decimal calculations
12#[derive(Clone, Debug, PartialEq)]
13pub struct PreciseNumber {
14    /// Wrapper over the inner value, which is multiplied by ONE
15    pub value: InnerUint,
16}
17
18/// The precise-number 1 as a InnerUint
19fn one() -> InnerUint {
20    InnerUint::from(ONE)
21}
22
23/// The number 0 as a PreciseNumber, used for easier calculations.
24fn zero() -> InnerUint {
25    InnerUint::from(0)
26}
27
28impl PreciseNumber {
29    /// Correction to apply to avoid truncation errors on division.  Since
30    /// integer operations will always floor the result, we artifically bump it
31    /// up by one half to get the expect result.
32    fn rounding_correction() -> InnerUint {
33        InnerUint::from(ONE / 2)
34    }
35
36    /// Desired precision for the correction factor applied during each
37    /// iteration of checked_pow_approximation.  Once the correction factor is
38    /// smaller than this number, or we reach the maxmium number of iterations,
39    /// the calculation ends.
40    fn precision() -> InnerUint {
41        InnerUint::from(100)
42    }
43
44    fn zero() -> Self {
45        Self { value: zero() }
46    }
47
48    fn one() -> Self {
49        Self { value: one() }
50    }
51
52    /// Maximum number iterations to apply on checked_pow_approximation.
53    const MAX_APPROXIMATION_ITERATIONS: u128 = 100;
54
55    /// Minimum base allowed when calculating exponents in checked_pow_fraction
56    /// and checked_pow_approximation.  This simply avoids 0 as a base.
57    fn min_pow_base() -> InnerUint {
58        InnerUint::from(1)
59    }
60
61    /// Maximum base allowed when calculating exponents in checked_pow_fraction
62    /// and checked_pow_approximation.  The calculation use a Taylor Series
63    /// approxmation around 1, which converges for bases between 0 and 2.  See
64    /// https://en.wikipedia.org/wiki/Binomial_series#Conditions_for_convergence
65    /// for more information.
66    fn max_pow_base() -> InnerUint {
67        InnerUint::from(2 * ONE)
68    }
69
70    /// Create a precise number from an imprecise u128, should always succeed
71    pub fn new(value: u128) -> Option<Self> {
72        let value = InnerUint::from(value).checked_mul(one())?;
73        Some(Self { value })
74    }
75
76    /// Convert a precise number back to u128
77    pub fn to_imprecise(&self) -> Option<u128> {
78        self.value
79            .checked_add(Self::rounding_correction())?
80            .checked_div(one())
81            .map(|v| v.as_u128())
82    }
83
84    /// Checks that two PreciseNumbers are equal within some tolerance
85    pub fn almost_eq(&self, rhs: &Self, precision: InnerUint) -> bool {
86        let (difference, _) = self.unsigned_sub(rhs);
87        difference.value < precision
88    }
89
90    /// Checks that a number is less than another
91    pub fn less_than(&self, rhs: &Self) -> bool {
92        self.value < rhs.value
93    }
94
95    /// Checks that a number is greater than another
96    pub fn greater_than(&self, rhs: &Self) -> bool {
97        self.value > rhs.value
98    }
99
100    /// Checks that a number is less than another
101    pub fn less_than_or_equal(&self, rhs: &Self) -> bool {
102        self.value <= rhs.value
103    }
104
105    /// Checks that a number is greater than another
106    pub fn greater_than_or_equal(&self, rhs: &Self) -> bool {
107        self.value >= rhs.value
108    }
109
110    /// Floors a precise value to a precision of ONE
111    pub fn floor(&self) -> Option<Self> {
112        let value = self.value.checked_div(one())?.checked_mul(one())?;
113        Some(Self { value })
114    }
115
116    /// Ceiling a precise value to a precision of ONE
117    pub fn ceiling(&self) -> Option<Self> {
118        let value = self
119            .value
120            .checked_add(one().checked_sub(InnerUint::from(1))?)?
121            .checked_div(one())?
122            .checked_mul(one())?;
123        Some(Self { value })
124    }
125
126    /// Performs a checked division on two precise numbers
127    pub fn checked_div(&self, rhs: &Self) -> Option<Self> {
128        if *rhs == Self::zero() {
129            return None;
130        }
131        match self.value.checked_mul(one()) {
132            Some(v) => {
133                let value = v
134                    .checked_add(Self::rounding_correction())?
135                    .checked_div(rhs.value)?;
136                Some(Self { value })
137            }
138            None => {
139                let value = self
140                    .value
141                    .checked_add(Self::rounding_correction())?
142                    .checked_div(rhs.value)?
143                    .checked_mul(one())?;
144                Some(Self { value })
145            }
146        }
147    }
148
149    /// Performs a multiplication on two precise numbers
150    pub fn checked_mul(&self, rhs: &Self) -> Option<Self> {
151        match self.value.checked_mul(rhs.value) {
152            Some(v) => {
153                let value = v
154                    .checked_add(Self::rounding_correction())?
155                    .checked_div(one())?;
156                Some(Self { value })
157            }
158            None => {
159                let value = if self.value >= rhs.value {
160                    self.value.checked_div(one())?.checked_mul(rhs.value)?
161                } else {
162                    rhs.value.checked_div(one())?.checked_mul(self.value)?
163                };
164                Some(Self { value })
165            }
166        }
167    }
168
169    /// Performs addition of two precise numbers
170    pub fn checked_add(&self, rhs: &Self) -> Option<Self> {
171        let value = self.value.checked_add(rhs.value)?;
172        Some(Self { value })
173    }
174
175    /// Subtracts the argument from self
176    pub fn checked_sub(&self, rhs: &Self) -> Option<Self> {
177        let value = self.value.checked_sub(rhs.value)?;
178        Some(Self { value })
179    }
180
181    /// Performs a subtraction, returning the result and whether the result is negative
182    pub fn unsigned_sub(&self, rhs: &Self) -> (Self, bool) {
183        match self.value.checked_sub(rhs.value) {
184            None => {
185                let value = rhs.value.checked_sub(self.value).unwrap();
186                (Self { value }, true)
187            }
188            Some(value) => (Self { value }, false),
189        }
190    }
191
192    /// Performs pow on a precise number
193    pub fn checked_pow(&self, exponent: u128) -> Option<Self> {
194        // For odd powers, start with a multiplication by base since we halve the
195        // exponent at the start
196        let value = if exponent.checked_rem(2)? == 0 {
197            one()
198        } else {
199            self.value
200        };
201        let mut result = Self { value };
202
203        // To minimize the number of operations, we keep squaring the base, and
204        // only push to the result on odd exponents, like a binary decomposition
205        // of the exponent.
206        let mut squared_base = self.clone();
207        let mut current_exponent = exponent.checked_div(2)?;
208        while current_exponent != 0 {
209            squared_base = squared_base.checked_mul(&squared_base)?;
210
211            // For odd exponents, "push" the base onto the value
212            if current_exponent.checked_rem(2)? != 0 {
213                result = result.checked_mul(&squared_base)?;
214            }
215
216            current_exponent = current_exponent.checked_div(2)?;
217        }
218        Some(result)
219    }
220
221    /// Approximate the nth root of a number using a Taylor Series around 1 on
222    /// x ^ n, where 0 < n < 1, result is a precise number.
223    /// Refine the guess for each term, using:
224    ///                                  1                    2
225    /// f(x) = f(a) + f'(a) * (x - a) + --- * f''(a) * (x - a)  + ...
226    ///                                  2!
227    /// For x ^ n, this gives:
228    ///  n    n         n-1           1                  n-2        2
229    /// x  = a  + n * a    (x - a) + --- * n * (n - 1) a     (x - a)  + ...
230    ///                               2!
231    ///
232    /// More simply, this means refining the term at each iteration with:
233    ///
234    /// t_k+1 = t_k * (x - a) * (n + 1 - k) / k
235    ///
236    /// where a = 1, n = power, x = precise_num
237    /// NOTE: this function is private because its accurate range and precision
238    /// have not been estbalished.
239    fn checked_pow_approximation(&self, exponent: &Self, max_iterations: u128) -> Option<Self> {
240        assert!(self.value >= Self::min_pow_base());
241        assert!(self.value <= Self::max_pow_base());
242        let one = Self::one();
243        if *exponent == Self::zero() {
244            return Some(one);
245        }
246        let mut precise_guess = one.clone();
247        let mut term = precise_guess.clone();
248        let (x_minus_a, x_minus_a_negative) = self.unsigned_sub(&precise_guess);
249        let exponent_plus_one = exponent.checked_add(&one)?;
250        let mut negative = false;
251        for k in 1..max_iterations {
252            let k = Self::new(k)?;
253            let (current_exponent, current_exponent_negative) = exponent_plus_one.unsigned_sub(&k);
254            term = term.checked_mul(&current_exponent)?;
255            term = term.checked_mul(&x_minus_a)?;
256            term = term.checked_div(&k)?;
257            if term.value < Self::precision() {
258                break;
259            }
260            if x_minus_a_negative {
261                negative = !negative;
262            }
263            if current_exponent_negative {
264                negative = !negative;
265            }
266            if negative {
267                precise_guess = precise_guess.checked_sub(&term)?;
268            } else {
269                precise_guess = precise_guess.checked_add(&term)?;
270            }
271        }
272        Some(precise_guess)
273    }
274
275    /// Get the power of a number, where the exponent is expressed as a fraction
276    /// (numerator / denominator)
277    /// NOTE: this function is private because its accurate range and precision
278    /// have not been estbalished.
279    #[allow(dead_code)]
280    fn checked_pow_fraction(&self, exponent: &Self) -> Option<Self> {
281        assert!(self.value >= Self::min_pow_base());
282        assert!(self.value <= Self::max_pow_base());
283        let whole_exponent = exponent.floor()?;
284        let precise_whole = self.checked_pow(whole_exponent.to_imprecise()?)?;
285        let (remainder_exponent, negative) = exponent.unsigned_sub(&whole_exponent);
286        assert!(!negative);
287        if remainder_exponent.value == InnerUint::from(0) {
288            return Some(precise_whole);
289        }
290        let precise_remainder = self
291            .checked_pow_approximation(&remainder_exponent, Self::MAX_APPROXIMATION_ITERATIONS)?;
292        precise_whole.checked_mul(&precise_remainder)
293    }
294
295    /// Approximate the nth root of a number using Newton's method
296    /// https://en.wikipedia.org/wiki/Newton%27s_method
297    /// NOTE: this function is private because its accurate range and precision
298    /// have not been established.
299    fn newtonian_root_approximation(
300        &self,
301        root: &Self,
302        mut guess: Self,
303        iterations: u128,
304    ) -> Option<Self> {
305        let zero = Self::zero();
306        if *self == zero {
307            return Some(zero);
308        }
309        if *root == zero {
310            return None;
311        }
312        let one = Self::new(1)?;
313        let root_minus_one = root.checked_sub(&one)?;
314        let root_minus_one_whole = root_minus_one.to_imprecise()?;
315        let mut last_guess = guess.clone();
316        let precision = Self::precision();
317        for _ in 0..iterations {
318            // x_k+1 = ((n - 1) * x_k + A / (x_k ^ (n - 1))) / n
319            let first_term = root_minus_one.checked_mul(&guess)?;
320            let power = guess.checked_pow(root_minus_one_whole);
321            let second_term = match power {
322                Some(num) => self.checked_div(&num)?,
323                None => Self::new(0)?,
324            };
325            guess = first_term.checked_add(&second_term)?.checked_div(root)?;
326            if last_guess.almost_eq(&guess, precision) {
327                break;
328            } else {
329                last_guess = guess.clone();
330            }
331        }
332        Some(guess)
333    }
334
335    /// Based on testing around the limits, this base is the smallest value that
336    /// provides an epsilon 11 digits
337    fn minimum_sqrt_base() -> Self {
338        Self {
339            value: InnerUint::from(0),
340        }
341    }
342
343    /// Based on testing around the limits, this base is the smallest value that
344    /// provides an epsilon of 11 digits
345    fn maximum_sqrt_base() -> Self {
346        Self::new(std::u128::MAX).unwrap()
347    }
348
349    /// Approximate the square root using Newton's method.  Based on testing,
350    /// this provides a precision of 11 digits for inputs between 0 and u128::MAX
351    pub fn sqrt(&self) -> Option<Self> {
352        if self.less_than(&Self::minimum_sqrt_base())
353            || self.greater_than(&Self::maximum_sqrt_base())
354        {
355            return None;
356        }
357        let two = PreciseNumber::new(2)?;
358        let one = PreciseNumber::new(1)?;
359        // A good initial guess is the average of the interval that contains the
360        // input number.  For all numbers, that will be between 1 and the given number.
361        let guess = self.checked_add(&one)?.checked_div(&two)?;
362        self.newtonian_root_approximation(&two, guess, Self::MAX_APPROXIMATION_ITERATIONS)
363    }
364}
365
366#[cfg(test)]
367mod tests {
368    use super::*;
369    use proptest::prelude::*;
370
371    fn check_pow_approximation(base: InnerUint, exponent: InnerUint, expected: InnerUint) {
372        let precision = InnerUint::from(5_000_000); // correct to at least 3 decimal places
373        let base = PreciseNumber { value: base };
374        let exponent = PreciseNumber { value: exponent };
375        let root = base
376            .checked_pow_approximation(&exponent, PreciseNumber::MAX_APPROXIMATION_ITERATIONS)
377            .unwrap();
378        let expected = PreciseNumber { value: expected };
379        assert!(root.almost_eq(&expected, precision));
380    }
381
382    #[test]
383    fn test_root_approximation() {
384        let one = one();
385        // square root
386        check_pow_approximation(one / 4, one / 2, one / 2); // 1/2
387        check_pow_approximation(one * 11 / 10, one / 2, InnerUint::from(1_048808848161u128)); // 1.048808848161
388
389        // 5th root
390        check_pow_approximation(one * 4 / 5, one * 2 / 5, InnerUint::from(914610103850u128));
391        // 0.91461010385
392
393        // 10th root
394        check_pow_approximation(one / 2, one * 4 / 50, InnerUint::from(946057646730u128));
395        // 0.94605764673
396    }
397
398    fn check_pow_fraction(
399        base: InnerUint,
400        exponent: InnerUint,
401        expected: InnerUint,
402        precision: InnerUint,
403    ) {
404        let base = PreciseNumber { value: base };
405        let exponent = PreciseNumber { value: exponent };
406        let power = base.checked_pow_fraction(&exponent).unwrap();
407        let expected = PreciseNumber { value: expected };
408        assert!(power.almost_eq(&expected, precision));
409    }
410
411    #[test]
412    fn test_pow_fraction() {
413        let one = one();
414        let precision = InnerUint::from(50_000_000); // correct to at least 3 decimal places
415        let less_precision = precision * 1_000; // correct to at least 1 decimal place
416        check_pow_fraction(one, one, one, precision);
417        check_pow_fraction(
418            one * 20 / 13,
419            one * 50 / 3,
420            InnerUint::from(1312_534484739100u128),
421            precision,
422        ); // 1312.5344847391
423        check_pow_fraction(one * 2 / 7, one * 49 / 4, InnerUint::from(2163), precision);
424        check_pow_fraction(
425            one * 5000 / 5100,
426            one / 9,
427            InnerUint::from(997802126900u128),
428            precision,
429        ); // 0.99780212695
430           // results get less accurate as the base gets further from 1, so allow
431           // for a greater margin of error
432        check_pow_fraction(
433            one * 2,
434            one * 27 / 5,
435            InnerUint::from(42_224253144700u128),
436            less_precision,
437        ); // 42.2242531447
438        check_pow_fraction(
439            one * 18 / 10,
440            one * 11 / 3,
441            InnerUint::from(8_629769290500u128),
442            less_precision,
443        ); // 8.629769290
444    }
445
446    #[test]
447    fn test_newtonian_approximation() {
448        // square root
449        let test = PreciseNumber::new(9).unwrap();
450        let nth_root = PreciseNumber::new(2).unwrap();
451        let guess = test.checked_div(&nth_root).unwrap();
452        let root = test
453            .newtonian_root_approximation(
454                &nth_root,
455                guess,
456                PreciseNumber::MAX_APPROXIMATION_ITERATIONS,
457            )
458            .unwrap()
459            .to_imprecise()
460            .unwrap();
461        assert_eq!(root, 3); // actually 3
462
463        let test = PreciseNumber::new(101).unwrap();
464        let nth_root = PreciseNumber::new(2).unwrap();
465        let guess = test.checked_div(&nth_root).unwrap();
466        let root = test
467            .newtonian_root_approximation(
468                &nth_root,
469                guess,
470                PreciseNumber::MAX_APPROXIMATION_ITERATIONS,
471            )
472            .unwrap()
473            .to_imprecise()
474            .unwrap();
475        assert_eq!(root, 10); // actually 10.049875
476
477        let test = PreciseNumber::new(1_000_000_000).unwrap();
478        let nth_root = PreciseNumber::new(2).unwrap();
479        let guess = test.checked_div(&nth_root).unwrap();
480        let root = test
481            .newtonian_root_approximation(
482                &nth_root,
483                guess,
484                PreciseNumber::MAX_APPROXIMATION_ITERATIONS,
485            )
486            .unwrap()
487            .to_imprecise()
488            .unwrap();
489        assert_eq!(root, 31_623); // actually 31622.7766
490
491        // 5th root
492        let test = PreciseNumber::new(500).unwrap();
493        let nth_root = PreciseNumber::new(5).unwrap();
494        let guess = test.checked_div(&nth_root).unwrap();
495        let root = test
496            .newtonian_root_approximation(
497                &nth_root,
498                guess,
499                PreciseNumber::MAX_APPROXIMATION_ITERATIONS,
500            )
501            .unwrap()
502            .to_imprecise()
503            .unwrap();
504        assert_eq!(root, 3); // actually 3.46572422
505    }
506
507    fn check_square_root(check: &PreciseNumber) {
508        let epsilon = PreciseNumber {
509            value: InnerUint::from(10),
510        }; // correct within 11 decimals
511        let one = PreciseNumber::one();
512        let one_plus_epsilon = one.checked_add(&epsilon).unwrap();
513        let one_minus_epsilon = one.checked_sub(&epsilon).unwrap();
514        let approximate_root = check.sqrt().unwrap();
515        let lower_bound = approximate_root
516            .checked_mul(&one_minus_epsilon)
517            .unwrap()
518            .checked_pow(2)
519            .unwrap();
520        let upper_bound = approximate_root
521            .checked_mul(&one_plus_epsilon)
522            .unwrap()
523            .checked_pow(2)
524            .unwrap();
525        assert!(check.less_than_or_equal(&upper_bound));
526        assert!(check.greater_than_or_equal(&lower_bound));
527    }
528
529    #[test]
530    fn test_square_root_min_max() {
531        let test_roots = [
532            PreciseNumber::minimum_sqrt_base(),
533            PreciseNumber::maximum_sqrt_base(),
534        ];
535        for i in test_roots.iter() {
536            check_square_root(i);
537        }
538    }
539
540    #[test]
541    fn test_floor() {
542        let whole_number = PreciseNumber::new(2).unwrap();
543        let mut decimal_number = PreciseNumber::new(2).unwrap();
544        decimal_number.value += InnerUint::from(1);
545        let floor = decimal_number.floor().unwrap();
546        let floor_again = floor.floor().unwrap();
547        assert_eq!(whole_number.value, floor.value);
548        assert_eq!(whole_number.value, floor_again.value);
549    }
550
551    #[test]
552    fn test_ceiling() {
553        let whole_number = PreciseNumber::new(2).unwrap();
554        let mut decimal_number = PreciseNumber::new(2).unwrap();
555        decimal_number.value -= InnerUint::from(1);
556        let ceiling = decimal_number.ceiling().unwrap();
557        let ceiling_again = ceiling.ceiling().unwrap();
558        assert_eq!(whole_number.value, ceiling.value);
559        assert_eq!(whole_number.value, ceiling_again.value);
560    }
561
562    proptest! {
563        #[test]
564        fn test_square_root(a in 0..u128::MAX) {
565            let a = PreciseNumber { value: InnerUint::from(a) };
566            check_square_root(&a);
567        }
568    }
569}