use crate::{
rational::{PolyOverQ, Q},
traits::Evaluate,
};
use flint_sys::fmpq::fmpq_mul_2exp;
impl Q {
pub fn exp(&self) -> Self {
if self <= &Q::from(709) {
Q::from(f64::from(self).exp())
} else {
let log_2_of_e = Q::from(f64::log2(1f64.exp()));
let base_2_exponent = self * log_2_of_e;
let base_2_int_exponent: i64 = (&base_2_exponent.floor()).try_into().unwrap();
let base_2_remainder_exponent = base_2_exponent - base_2_int_exponent;
let mut out: Q = base_2_remainder_exponent + 1;
unsafe { fmpq_mul_2exp(&mut out.value, &out.value, base_2_int_exponent as u64) }
out
}
}
pub fn exp_taylor(&self, length_taylor_polynomial: impl Into<u32>) -> Self {
let exp_taylor_series = PolyOverQ::exp_function_taylor(length_taylor_polynomial);
exp_taylor_series.evaluate(self)
}
}
#[cfg(test)]
mod test_exp {
use super::*;
use flint_sys::fmpz::fmpz_get_d_2exp;
#[test]
fn zero() {
assert_eq!(Q::ONE, Q::ZERO.exp());
}
#[test]
fn one() {
let e = Q::ONE.exp();
assert_eq!(e, Q::from(1f64.exp()));
}
#[test]
fn large_negative_exponent() {
let small = Q::from(-745).exp();
let small_2 = Q::from((-7451, 10)).exp();
let zero = Q::from((-7452, 10)).exp();
assert_ne!(small, Q::ZERO);
assert_eq!(small, small_2);
assert_eq!(zero, Q::ZERO);
}
#[test]
fn algorithm_transition_correct() {
let result_less_than_709 = (Q::from(709) - Q::from((1, u64::MAX))).exp();
let result_709 = Q::from(709).exp();
assert_ne!(f64::from(&result_less_than_709), f64::INFINITY);
assert!(result_less_than_709 < result_709);
}
#[test]
fn medium_exponent() {
let a = Q::from(300).exp();
assert_eq!(a, Q::from(300f64.exp()));
}
#[test]
fn large_exponent() {
let large_1 = Q::from(u32::MAX).exp();
let large_2 = Q::from(u32::MAX - 1).exp();
assert!(large_1 > large_2);
}
#[test]
#[allow(clippy::unnecessary_mut_passed)]
fn large_exponent_error_bound() {
let large = Q::from(u32::MAX).exp();
let mut exponent = 0;
let mut value_prime = unsafe { fmpz_get_d_2exp(&mut exponent, &large.value.num) };
value_prime *= 2f64.powi((exponent - 6196328016) as i32);
let upper_bound = 1.06 * 2.42298514666;
let lower_bound = 0.94 * 2.42298514666;
assert!(lower_bound < value_prime);
assert!(upper_bound > value_prime);
}
}
#[cfg(test)]
mod test_exp_taylor {
use crate::rational::Q;
#[test]
fn zero_length() {
let q = Q::from((17, 3));
assert_eq!(Q::default(), q.exp_taylor(0_u32));
}
#[test]
fn ten_length_value() {
assert_eq!(Q::from((98641, 36288)), Q::ONE.exp_taylor(10_u32));
assert_eq!(
Q::from((2492063827u64, 1785641760)),
Q::from((1, 3)).exp_taylor(10_u32)
);
assert_eq!(
Q::from((5729869, 11160261)),
Q::from((-2, 3)).exp_taylor(10_u32)
);
}
}