hyperreal 0.9.1

Exact rational and computable real arithmetic in Rust
Documentation
#[cfg(test)]
mod tests {
    use super::super::curve;
    use crate::{Problem, Rational, Real};

    #[test]
    fn zero() {
        assert_eq!(Real::zero(), Real::zero());
    }

    #[test]
    fn parse() {
        let counting: Real = "123456789".parse().unwrap();
        let answer = Real::new(Rational::new(123456789));
        assert_eq!(counting, answer);
    }

    #[test]
    fn parse_large() {
        let input: Real = "378089444731722233953867379643788100".parse().unwrap();
        let root = Rational::new(614889782588491410);
        let answer = Real::new(root.clone() * root);
        assert_eq!(input, answer);
    }

    #[test]
    fn parse_fraction() {
        let input: Real = "98760/123450".parse().unwrap();
        let answer = Real::new(Rational::fraction(9876, 12345).unwrap());
        assert_eq!(input, answer);
    }

    #[test]
    fn root_divide() {
        let twenty: Real = 20.into();
        let five: Real = 5.into();
        let a = twenty.sqrt().unwrap();
        let b = five.sqrt().unwrap().inverse().unwrap();
        let answer = a * b;
        let two: Real = 2.into();
        assert_eq!(answer, two);
    }

    #[test]
    fn rational() {
        let two: Real = 2.into();
        assert_ne!(two, Real::zero());
        let four: Real = 4.into();
        let answer = four - two;
        let two: Real = 2.into();
        assert_eq!(answer, two);
        let zero = answer - two;
        assert_eq!(zero, Real::zero());
        let six_half: Real = "13/2".parse().unwrap();
        let opposite = six_half.inverse().unwrap();
        let expected: Real = "2/13".parse().unwrap();
        assert_eq!(opposite, expected);
    }

    // https://devblogs.microsoft.com/oldnewthing/?p=93765
    // "Why does the Windows calculator generate tiny errors when calculating the square root of a
    // perfect square?" (fixed in 2018)
    #[test]
    fn perfect_square() {
        let four: Real = 4.into();
        let two: Real = 2.into();
        let calc = four.sqrt().unwrap() - two;
        assert_eq!(calc, Real::zero());
    }

    #[test]
    fn one_over_e() {
        let one: Real = 1.into();
        let e = Real::e();
        let e_inverse = Real::e().inverse().unwrap();
        let answer = e * e_inverse;
        assert_eq!(one, answer);
        let again = answer.sqrt().unwrap();
        assert_eq!(one, again);
    }

    #[test]
    fn unlike_sqrts() {
        let thirty: Real = 30.into();
        let ten: Real = 10.into();
        let answer = thirty.sqrt().unwrap() * ten.sqrt().unwrap();
        let ten: Real = 10.into();
        let three: Real = 3.into();
        let or = ten * three.sqrt().unwrap();
        assert_eq!(answer, or);
    }

    #[test]
    fn zero_pi() {
        let pi = Real::pi();
        let z1 = pi - Real::pi();
        let pi2 = Real::pi() + Real::pi();
        let z2 = pi2 * Real::zero();
        assert!(z1.definitely_zero());
        assert!(z2.definitely_zero());
        let two_pi = Real::pi() + Real::pi();
        let two: Real = 2.into();
        assert_eq!(two_pi, two * Real::pi());
        assert_ne!(two_pi, Rational::new(2));
    }

    #[test]
    fn ln_zero() {
        let zero = Real::zero();
        assert_eq!(zero.ln(), Err(Problem::NotANumber));
    }

    #[test]
    fn sqrt_exact() {
        let big: Real = 40_000.into();
        let small: Rational = Rational::new(200);
        let answer = big.sqrt().unwrap();
        assert_eq!(answer, small);
    }

    #[test]
    fn square_sqrt() {
        let two: Real = 2.into();
        let three: Real = 3.into();
        let small = three.sqrt().expect("Should be able to sqrt(n)");
        let a = small * two;
        let three: Real = 3.into();
        let small = three.sqrt().expect("Should be able to sqrt(n)");
        let three: Real = 3.into();
        let b = small * three;
        let answer = a * b;
        let eighteen: Rational = Rational::new(18);
        assert_eq!(answer, eighteen);
    }

    #[test]
    fn adding_one_works() {
        let pi = Real::pi();
        let one: Real = 1.into();
        let plus_one = pi + one;
        let float: f64 = plus_one.into();
        assert_eq!(float, 4.141592653589793);
    }

    #[test]
    fn sin_easy() {
        let pi = Real::pi();
        let zero = Real::zero();
        let two: Real = 2.into();
        let two_pi = pi.clone() * two;
        assert_eq!(zero.clone().sin(), zero);
        assert_eq!(pi.clone().sin(), zero);
        assert_eq!(two_pi.clone().sin(), zero);
    }

    #[test]
    fn cos_easy() {
        let pi = Real::pi();
        let zero = Real::zero();
        let one: Real = 1.into();
        let two: Real = 2.into();
        let two_pi = pi.clone() * two;
        let minus_one: Real = (-1).into();
        assert_eq!(zero.clone().cos(), one);
        assert_eq!(pi.clone().cos(), minus_one);
        assert_eq!(two_pi.clone().cos(), one);
    }

    fn pi_fraction(n: i64, d: u64) -> Real {
        Real::new(Rational::fraction(n, d).unwrap()) * Real::pi()
    }

    #[test]
    fn sin_pi_rational_multiples() {
        let zero = Real::zero();
        let one: Real = 1.into();
        let minus_one: Real = (-1).into();
        let half: Real = "1/2".parse().unwrap();
        let minus_half: Real = "-1/2".parse().unwrap();
        let sqrt_two_over_two = Real::new(Rational::fraction(1, 2).unwrap())
            * Real::new(Rational::new(2)).sqrt().unwrap();
        let sqrt_three_over_two = Real::new(Rational::fraction(1, 2).unwrap())
            * Real::new(Rational::new(3)).sqrt().unwrap();

        assert_eq!(pi_fraction(0, 1).sin(), zero);
        assert_eq!(pi_fraction(1, 6).sin(), half);
        assert_eq!(pi_fraction(1, 4).sin(), sqrt_two_over_two);
        assert_eq!(pi_fraction(1, 3).sin(), sqrt_three_over_two);
        assert_eq!(pi_fraction(1, 2).sin(), one);
        assert_eq!(pi_fraction(5, 6).sin(), half);
        assert_eq!(pi_fraction(7, 6).sin(), minus_half);
        assert_eq!(pi_fraction(3, 2).sin(), minus_one);
        assert_eq!(pi_fraction(-1, 6).sin(), minus_half);
        assert_eq!(pi_fraction(2, 1).sin(), zero);
    }

    #[test]
    fn sin_pi_rational_multiples_fold_to_same_curve() {
        assert_eq!(pi_fraction(1, 5).sin(), pi_fraction(4, 5).sin());
        assert_eq!(pi_fraction(6, 5).sin(), -pi_fraction(1, 5).sin());
        assert_eq!(pi_fraction(-4, 5).sin(), -pi_fraction(1, 5).sin());
        assert_eq!(pi_fraction(11, 5).sin(), pi_fraction(1, 5).sin());
    }

    #[test]
    fn cos_pi_rational_multiples_shift_through_sin() {
        let zero = Real::zero();
        let one: Real = 1.into();
        let minus_one: Real = (-1).into();
        let half: Real = "1/2".parse().unwrap();
        let minus_half: Real = "-1/2".parse().unwrap();
        let sqrt_two_over_two = Real::new(Rational::fraction(1, 2).unwrap())
            * Real::new(Rational::new(2)).sqrt().unwrap();
        let sqrt_three_over_two = Real::new(Rational::fraction(1, 2).unwrap())
            * Real::new(Rational::new(3)).sqrt().unwrap();

        assert_eq!(pi_fraction(0, 1).cos(), one);
        assert_eq!(pi_fraction(1, 6).cos(), sqrt_three_over_two);
        assert_eq!(pi_fraction(1, 4).cos(), sqrt_two_over_two);
        assert_eq!(pi_fraction(1, 3).cos(), half);
        assert_eq!(pi_fraction(1, 2).cos(), zero);
        assert_eq!(pi_fraction(2, 3).cos(), minus_half);
        assert_eq!(pi_fraction(1, 1).cos(), minus_one);
        assert_eq!(pi_fraction(3, 2).cos(), zero);
        assert_eq!(pi_fraction(-1, 3).cos(), half);
        assert_eq!(pi_fraction(2, 1).cos(), one);
    }

    #[test]
    fn tan_irrational_argument() {
        let sqrt_two = Real::new(Rational::new(2)).sqrt().unwrap();
        let answer = sqrt_two.tan().unwrap();
        let actual: f64 = answer.into();
        assert!((actual - 6.3341191670421955).abs() < 1e-12, "{actual}");
    }

    #[test]
    fn powi() {
        let base: Real = 4.into();
        let five_over_two: Real = "5/2".parse().unwrap();
        let answer = base.pow(five_over_two).unwrap();
        let correct: Real = 32.into();
        assert_eq!(answer, correct);
    }

    #[test]
    fn sqrt_3045512() {
        use crate::real::Class::Sqrt;

        let n: Real = 3045512.into();
        let sqrt = n.sqrt().unwrap();
        let root = Rational::new(1234);
        assert_eq!(sqrt.rational, root);
        let two = Rational::new(2);
        assert_eq!(sqrt.class, Sqrt(two));
    }

    fn closest_f64(r: Real, f: f64) -> bool {
        let left = f64::from_bits(f.to_bits() - 1);
        let right = f64::from_bits(f.to_bits() + 1);
        let f: f64 = r.into();
        if right > left {
            left < f && right > f
        } else {
            left > f && right < f
        }
    }

    #[test]
    fn pow_pi() {
        let pi = Real::pi();
        let sq = pi.pow(Real::pi()).unwrap();
        assert!(closest_f64(sq.clone(), 36.46215960720791));
        let sqsq = sq.pow(Real::pi()).unwrap();
        assert!(closest_f64(sqsq, 80662.6659385546));
    }

    #[test]
    fn pow_fract() {
        let frac: Real = "-1.3".parse().unwrap();
        let five: Real = 5.into();
        let answer = frac.pow(five).unwrap();
        assert!(closest_f64(answer, -3.7129299999999996));
    }

    #[test]
    fn pow_of_sine() {
        let sin_10 = Real::new(Rational::new(10)).sin();
        let answer = (sin_10.clone()).pow(Real::new(Rational::new(2))).unwrap();
        assert!(closest_f64(
            answer,
            // Value from wolframalpha.com
            0.29595896909330400696886606953617752145
        ));
    }

    #[test]
    fn curves() {
        let eighty = Rational::fraction(80, 100).unwrap();
        let twenty = Rational::fraction(20, 100).unwrap();
        assert_eq!(curve(eighty), (false, twenty.clone()));
        let forty = Rational::fraction(40, 100).unwrap();
        let sixty = Rational::fraction(60, 100).unwrap();
        assert_eq!(curve(sixty), (false, forty));
        let otf = Rational::fraction(124, 100).unwrap();
        let tf = Rational::fraction(24, 100).unwrap();
        assert_eq!(curve(otf), (true, tf.clone()));
        let minus_twenty = Rational::fraction(-20, 100).unwrap();
        assert_eq!(curve(minus_twenty), (true, twenty));
        let minus_otf = Rational::fraction(-124, 100).unwrap();
        assert_eq!(curve(minus_otf), (false, tf));
    }

    #[test]
    fn exp_pi() {
        let pi = Real::pi();
        assert_eq!(format!("{pi:.2e}"), "3.14e0");
        assert_eq!(format!("{pi:.4E}"), "3.1416E0");
        assert_eq!(format!("{pi:.8e}"), "3.14159265e0");
        assert_eq!(format!("{pi:.16E}"), "3.1415926535897932E0");
        assert_eq!(format!("{pi:.32e}"), "3.14159265358979323846264338327950e0");
        assert_eq!(format!("{pi:e}"), "3.1415926535897932384626433832795e0");
    }

    #[test]
    fn ln_division() {
        let fifth = Rational::fraction(2, 10).unwrap();
        let twenty_fifth = Rational::fraction(4, 100).unwrap();
        let ln_5th = Real::new(fifth).ln().unwrap();
        let ln_25th = Real::new(twenty_fifth).ln().unwrap();
        let answer = ln_25th / ln_5th;
        assert_eq!(answer.unwrap(), Rational::new(2));
    }

    #[test]
    fn integer_logs() {
        for (n, log) in [
            (1, 0),
            (10, 1),
            (10_000_000_000_000_000, 16),
            (100_000_000_000_000_000, 17),
            (1000_000_000_000_000_000, 18),
        ] {
            let n = Real::new(Rational::new(n));
            let answer = n.log10().unwrap();
            assert_eq!(answer, Rational::new(log));
        }
    }
}