use crate::InnerFloat::{Infinity, NaN, Zero};
use crate::{Float, emulate_float_to_float_fn, float_infinity, float_nan, float_negative_infinity};
use core::cmp::Ordering::{self, *};
use malachite_base::num::arithmetic::traits::{
CeilingLogBase2, CheckedLogBase, IsPowerOf2, LogBaseOf1PlusX, LogBaseOf1PlusXAssign,
};
use malachite_base::num::basic::floats::PrimitiveFloat;
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::basic::traits::{One as OneTrait, Zero as ZeroTrait};
use malachite_base::num::comparison::traits::PartialOrdAbs;
use malachite_base::num::conversion::traits::{ExactFrom, RoundingFrom};
use malachite_base::num::factorization::traits::ExpressAsPower;
use malachite_base::num::logic::traits::SignificantBits;
use malachite_base::rounding_modes::RoundingMode::{self, *};
use malachite_nz::natural::Natural;
use malachite_nz::natural::arithmetic::float_extras::float_can_round;
use malachite_nz::platform::Limb;
use malachite_q::Rational;
pub(crate) fn log_base_1_plus_x_rational(x: &Float, base: u64) -> Option<Rational> {
if *x == 0u32 {
return Some(Rational::ZERO);
}
let e = i64::from(x.get_exponent()?);
if e < 1 || u64::exact_from(e) > x.get_prec()?.saturating_mul(64) {
return None;
}
let n = Natural::try_from(x).ok()?;
let (g, e_base) = base.express_as_power().unwrap_or((base, 1));
let m = (n + Natural::ONE).checked_log_base(&Natural::from(g))?;
Some(Rational::from_unsigneds(m, e_base))
}
fn log_base_1_plus_x_prec_round_normal(
x: &Float,
base: u64,
prec: u64,
rm: RoundingMode,
) -> (Float, Ordering) {
match x.partial_cmp(&-1i32).unwrap() {
Equal => return (float_negative_infinity!(), Equal),
Less => return (float_nan!(), Equal),
_ => {}
}
if let Some(q) = log_base_1_plus_x_rational(x, base) {
return Float::from_rational_prec_round(q, prec, rm);
}
assert_ne!(rm, Exact, "Inexact log_base_1_plus_x");
let base_float = Float::from(base);
let min_exp = i64::from(Float::MIN_EXPONENT);
let mut working_prec = prec + 4 + prec.ceiling_log_base_2();
let mut increment = Limb::WIDTH;
loop {
let num = x.log_base_2_1_plus_x_prec_ref(working_prec).0;
let den = base_float.log_base_2_prec_ref(working_prec).0;
let e_num = i64::from(num.get_exponent().unwrap());
let e_den = i64::from(den.get_exponent().unwrap());
if e_num - e_den + 1 < min_exp
|| (e_num - e_den < min_exp && (&num << u64::exact_from(1 - min_exp)).lt_abs(&den))
{
return num.div_prec_round(den, prec, rm);
}
let t = num.div_prec(den, working_prec).0;
if float_can_round(t.significand_ref().unwrap(), working_prec - 4, prec, rm) {
return Float::from_float_prec_round(t, prec, rm);
}
working_prec += increment;
increment = working_prec >> 1;
}
}
impl Float {
#[inline]
pub fn log_base_1_plus_x_prec_round(
self,
base: u64,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
assert_ne!(prec, 0);
assert!(base > 1, "Logarithm base must be greater than 1");
if base.is_power_of_2() {
return self.log_base_power_of_2_1_plus_x_prec_round(
i64::from(base.trailing_zeros()),
prec,
rm,
);
}
match self {
Self(NaN | Infinity { sign: false }) => (float_nan!(), Equal),
float_infinity!() => (float_infinity!(), Equal),
Self(Zero { .. }) => (self, Equal),
_ => log_base_1_plus_x_prec_round_normal(&self, base, prec, rm),
}
}
#[inline]
pub fn log_base_1_plus_x_prec_round_ref(
&self,
base: u64,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
assert_ne!(prec, 0);
assert!(base > 1, "Logarithm base must be greater than 1");
if base.is_power_of_2() {
return self.log_base_power_of_2_1_plus_x_prec_round_ref(
i64::from(base.trailing_zeros()),
prec,
rm,
);
}
match self {
Self(NaN | Infinity { sign: false }) => (float_nan!(), Equal),
float_infinity!() => (float_infinity!(), Equal),
Self(Zero { .. }) => (self.clone(), Equal),
_ => log_base_1_plus_x_prec_round_normal(self, base, prec, rm),
}
}
#[inline]
pub fn log_base_1_plus_x_prec(self, base: u64, prec: u64) -> (Self, Ordering) {
self.log_base_1_plus_x_prec_round(base, prec, Nearest)
}
#[inline]
pub fn log_base_1_plus_x_prec_ref(&self, base: u64, prec: u64) -> (Self, Ordering) {
self.log_base_1_plus_x_prec_round_ref(base, prec, Nearest)
}
#[inline]
pub fn log_base_1_plus_x_round(self, base: u64, rm: RoundingMode) -> (Self, Ordering) {
let prec = self.significant_bits();
self.log_base_1_plus_x_prec_round(base, prec, rm)
}
#[inline]
pub fn log_base_1_plus_x_round_ref(&self, base: u64, rm: RoundingMode) -> (Self, Ordering) {
self.log_base_1_plus_x_prec_round_ref(base, self.significant_bits(), rm)
}
#[inline]
pub fn log_base_1_plus_x_prec_round_assign(
&mut self,
base: u64,
prec: u64,
rm: RoundingMode,
) -> Ordering {
let (result, o) = core::mem::take(self).log_base_1_plus_x_prec_round(base, prec, rm);
*self = result;
o
}
#[inline]
pub fn log_base_1_plus_x_prec_assign(&mut self, base: u64, prec: u64) -> Ordering {
self.log_base_1_plus_x_prec_round_assign(base, prec, Nearest)
}
#[inline]
pub fn log_base_1_plus_x_round_assign(&mut self, base: u64, rm: RoundingMode) -> Ordering {
let prec = self.significant_bits();
self.log_base_1_plus_x_prec_round_assign(base, prec, rm)
}
}
impl LogBaseOf1PlusX<u64> for Float {
type Output = Self;
#[inline]
fn log_base_1_plus_x(self, base: u64) -> Self {
let prec = self.significant_bits();
self.log_base_1_plus_x_prec_round(base, prec, Nearest).0
}
}
impl LogBaseOf1PlusX<u64> for &Float {
type Output = Float;
#[inline]
fn log_base_1_plus_x(self, base: u64) -> Float {
self.log_base_1_plus_x_prec_round_ref(base, self.significant_bits(), Nearest)
.0
}
}
impl LogBaseOf1PlusXAssign<u64> for Float {
#[inline]
fn log_base_1_plus_x_assign(&mut self, base: u64) {
let prec = self.significant_bits();
self.log_base_1_plus_x_prec_round_assign(base, prec, Nearest);
}
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_log_base_1_plus_x<T: PrimitiveFloat>(x: T, base: u64) -> T
where
Float: From<T> + PartialOrd<T>,
for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
{
emulate_float_to_float_fn(|x, prec| Float::log_base_1_plus_x_prec(x, base, prec), x)
}