use crate::{error::MathError, integer::Z, rational::Q};
use flint_sys::fmpz::fmpz_dlog;
impl Q {
pub fn ln(&self) -> Result<Self, MathError> {
if self <= &Q::ZERO {
Err(MathError::NonPositive(self.to_string()))
} else {
Ok(Q::from(
unsafe { fmpz_dlog(&self.value.num) } - unsafe { fmpz_dlog(&self.value.den) },
))
}
}
pub fn log(&self, base: impl Into<Z>) -> Result<Q, MathError> {
let base: Z = base.into();
if base <= Z::ONE {
return Err(MathError::InvalidIntegerInput(format!(
"The base must be greater than 1, but the provided is {base}"
)));
}
let ln_value = self.ln()?;
let ln_base = base.ln()?;
let log_b = ln_value / ln_base;
Ok(log_b)
}
}
#[cfg(test)]
mod test_natural_ln {
use crate::rational::Q;
use std::f64::consts::{LN_2, LN_10};
#[test]
fn value_too_small() {
assert!(Q::ZERO.ln().is_err());
assert!(Q::MINUS_ONE.ln().is_err());
assert!(Q::from(i64::MIN).ln().is_err());
}
#[test]
fn static_known_values() {
assert_eq!(Q::ZERO, Q::ONE.ln().unwrap());
assert_eq!(Q::from(LN_2), Q::from(2).ln().unwrap());
assert_eq!(Q::from(LN_10), Q::from(10).ln().unwrap());
assert_eq!(Q::ONE, Q::E.ln().unwrap());
}
}
#[cfg(test)]
mod test_log {
use crate::{integer::Z, rational::Q, traits::Distance};
#[test]
fn base_too_small() {
let value = Q::from(17);
assert!(value.log(Z::ZERO).is_err());
assert!(value.log(Z::ONE).is_err());
assert!(value.log(Z::MINUS_ONE).is_err());
assert!(value.log(Z::from(i64::MIN)).is_err());
}
#[test]
fn value_too_small() {
let base = Z::from(2);
assert!(Q::ZERO.log(&base).is_err());
assert!(Q::MINUS_ONE.log(&base).is_err());
assert!(Q::from(i64::MIN).log(&base).is_err());
}
#[test]
fn small_values() {
let z_0 = Q::from(1);
let z_1 = Q::from(2);
let z_2 = Q::from(1.25f64);
let z_3 = Q::from(20.75f64);
let cmp_0 = Q::from(1.25_f64.log2());
let cmp_1 = Q::from(20.75f64.log(3f64));
let max_distance = Q::from((1, 1_000_000_000));
let res_0 = z_0.log(2).unwrap();
let res_1 = z_1.log(2).unwrap();
let res_2 = z_2.log(2).unwrap();
let res_3 = z_3.log(3).unwrap();
assert_eq!(Q::ZERO, res_0);
assert_eq!(Q::ONE, res_1);
assert!(cmp_0.distance(res_2) < max_distance);
assert!(cmp_1.distance(res_3) < max_distance);
}
#[test]
fn large_values() {
let z_0 = Q::from(i64::MAX as u64 + 1);
let z_1 = Q::from(f64::MAX);
let z_2 = Q::from(i32::MAX);
let cmp_0 = Q::from((&63, &1));
let cmp_1 = Q::from(f64::MAX.log2());
let max_distance = Q::from((&1, &1_000_000_000));
let res_0 = z_0.log(2).unwrap();
let res_1 = z_1.log(2).unwrap();
let res_2 = z_2.log(i32::MAX).unwrap();
assert!(cmp_0.distance(res_0) < max_distance);
assert!(cmp_1.distance(res_1) < max_distance);
assert_eq!(Q::ONE, res_2);
}
#[test]
fn availability() {
let value = Z::from(5);
let _ = value.log(2u8).unwrap();
let _ = value.log(2u16).unwrap();
let _ = value.log(2u32).unwrap();
let _ = value.log(2u64).unwrap();
let _ = value.log(2i8).unwrap();
let _ = value.log(2i16).unwrap();
let _ = value.log(2i32).unwrap();
let _ = value.log(2i64).unwrap();
let _ = value.log(&value).unwrap();
}
}