use crate::InnerFloat::{Infinity, NaN};
use crate::arithmetic::log_base_rational_rational_base::rational_log_base_rational_rational_base;
use crate::basic::extended::ExtendedFloat;
use crate::{
Float, emulate_float_float_to_float_fn, float_infinity, float_nan, float_negative_infinity,
};
use core::cmp::Ordering::{self, *};
use malachite_base::num::arithmetic::traits::{
CeilingLogBase2, LogBaseOf1PlusX, LogBaseOf1PlusXAssign,
};
use malachite_base::num::basic::floats::PrimitiveFloat;
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::basic::traits::{NegativeZero, One, Zero as ZeroTrait};
use malachite_base::num::conversion::traits::{ExactFrom, RoundingFrom};
use malachite_base::num::logic::traits::SignificantBits;
use malachite_base::rounding_modes::RoundingMode::{self, *};
use malachite_nz::natural::arithmetic::float_extras::float_can_round;
use malachite_nz::platform::Limb;
use malachite_q::Rational;
pub(crate) fn log_base_float_base_1_plus_x_rational(
x: &Float,
base: &Float,
prec: u64,
) -> Option<Rational> {
let bound = prec.saturating_mul(64);
if i64::from(x.get_exponent()?).unsigned_abs() > bound
|| x.significant_bits() > bound
|| i64::from(base.get_exponent()?).unsigned_abs() > bound
|| base.significant_bits() > bound
{
return None;
}
let one_plus_x = Rational::exact_from(x) + Rational::ONE;
let br = Rational::exact_from(base);
if br > 1u32 {
rational_log_base_rational_rational_base(&one_plus_x, &br, prec)
} else {
rational_log_base_rational_rational_base(&one_plus_x, &(Rational::ONE / br), prec)
.map(|q| -q)
}
}
fn log_base_float_base_1_plus_x_normal(
x: &Float,
base: &Float,
prec: u64,
rm: RoundingMode,
) -> (Float, Ordering) {
if let Some(q) = log_base_float_base_1_plus_x_rational(x, base, prec) {
return Float::from_rational_prec_round(q, prec, rm);
}
assert_ne!(rm, Exact, "Inexact log_base_float_base_1_plus_x");
let mut working_prec = prec + 6 + prec.ceiling_log_base_2();
let mut increment = Limb::WIDTH;
loop {
let num = ExtendedFloat::from(x.log_base_2_1_plus_x_prec_ref(working_prec).0);
let den = ExtendedFloat::from(base.log_base_2_prec_ref(working_prec).0);
let (quotient, _) = num.div_prec_val_ref(&den, working_prec);
if float_can_round(
quotient.x.significand_ref().unwrap(),
working_prec - 6,
prec,
rm,
) {
let (rounded, o) = Float::from_float_prec_round(quotient.x, prec, rm);
let mut result = ExtendedFloat::from(rounded);
result.exp = result.exp.checked_add(quotient.exp).unwrap();
return result.into_float_helper(prec, rm, o);
}
working_prec += increment;
increment = working_prec >> 1;
}
}
fn log_base_float_base_1_plus_x_helper(
x: &Float,
base: &Float,
prec: u64,
rm: RoundingMode,
) -> (Float, Ordering) {
if x.is_nan() || base.is_nan() {
return (float_nan!(), Equal);
}
if *base < 0u32 {
return (float_nan!(), Equal); }
if *x < -1i32 {
return (float_nan!(), Equal); }
let x_neg = x.is_sign_negative();
if base.is_infinite() {
if x.is_infinite() || *x == -1i32 {
return (float_nan!(), Equal);
}
return if x_neg {
(Float::NEGATIVE_ZERO, Equal)
} else {
(Float::ZERO, Equal)
};
}
if *base == 0u32 {
if x.is_infinite() || *x == -1i32 {
return (float_nan!(), Equal);
}
return if x_neg {
(Float::ZERO, Equal)
} else {
(Float::NEGATIVE_ZERO, Equal)
};
}
if *base == 1u32 {
if *x == 0u32 {
return (float_nan!(), Equal);
}
return if x_neg {
(float_negative_infinity!(), Equal)
} else {
(float_infinity!(), Equal)
};
}
if x.is_infinite() {
return if *base < 1u32 {
(float_negative_infinity!(), Equal)
} else {
(float_infinity!(), Equal)
};
}
if *x == -1i32 {
return if *base < 1u32 {
(float_infinity!(), Equal)
} else {
(float_negative_infinity!(), Equal)
};
}
if *x == 0u32 {
return if x_neg == (*base < 1u32) {
(Float::ZERO, Equal)
} else {
(Float::NEGATIVE_ZERO, Equal)
};
}
log_base_float_base_1_plus_x_normal(x, base, prec, rm)
}
impl Float {
#[inline]
pub fn log_base_float_base_1_plus_x_prec_round(
self,
base: &Self,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
assert_ne!(prec, 0);
log_base_float_base_1_plus_x_helper(&self, base, prec, rm)
}
pub fn log_base_float_base_1_plus_x_prec_round_ref(
&self,
base: &Self,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
assert_ne!(prec, 0);
log_base_float_base_1_plus_x_helper(self, base, prec, rm)
}
#[inline]
pub fn log_base_float_base_1_plus_x_prec(self, base: &Self, prec: u64) -> (Self, Ordering) {
self.log_base_float_base_1_plus_x_prec_round(base, prec, Nearest)
}
#[inline]
pub fn log_base_float_base_1_plus_x_prec_ref(
&self,
base: &Self,
prec: u64,
) -> (Self, Ordering) {
self.log_base_float_base_1_plus_x_prec_round_ref(base, prec, Nearest)
}
#[inline]
pub fn log_base_float_base_1_plus_x_round(
self,
base: &Self,
rm: RoundingMode,
) -> (Self, Ordering) {
let prec = self.significant_bits();
self.log_base_float_base_1_plus_x_prec_round(base, prec, rm)
}
#[inline]
pub fn log_base_float_base_1_plus_x_round_ref(
&self,
base: &Self,
rm: RoundingMode,
) -> (Self, Ordering) {
self.log_base_float_base_1_plus_x_prec_round_ref(base, self.significant_bits(), rm)
}
#[inline]
pub fn log_base_float_base_1_plus_x_prec_round_assign(
&mut self,
base: &Self,
prec: u64,
rm: RoundingMode,
) -> Ordering {
let (result, o) =
core::mem::take(self).log_base_float_base_1_plus_x_prec_round(base, prec, rm);
*self = result;
o
}
#[inline]
pub fn log_base_float_base_1_plus_x_prec_assign(&mut self, base: &Self, prec: u64) -> Ordering {
self.log_base_float_base_1_plus_x_prec_round_assign(base, prec, Nearest)
}
#[inline]
pub fn log_base_float_base_1_plus_x_round_assign(
&mut self,
base: &Self,
rm: RoundingMode,
) -> Ordering {
let prec = self.significant_bits();
self.log_base_float_base_1_plus_x_prec_round_assign(base, prec, rm)
}
}
impl LogBaseOf1PlusX<Self> for Float {
type Output = Self;
#[inline]
fn log_base_1_plus_x(self, base: Self) -> Self {
let prec = self.significant_bits();
self.log_base_float_base_1_plus_x_prec_round(&base, prec, Nearest)
.0
}
}
impl LogBaseOf1PlusX<&Float> for &Float {
type Output = Float;
#[inline]
fn log_base_1_plus_x(self, base: &Float) -> Float {
self.log_base_float_base_1_plus_x_prec_round_ref(base, self.significant_bits(), Nearest)
.0
}
}
impl LogBaseOf1PlusXAssign<&Self> for Float {
#[inline]
fn log_base_1_plus_x_assign(&mut self, base: &Self) {
let prec = self.significant_bits();
self.log_base_float_base_1_plus_x_prec_round_assign(base, prec, Nearest);
}
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_log_base_float_base_1_plus_x<T: PrimitiveFloat>(x: T, base: T) -> T
where
Float: From<T> + PartialOrd<T>,
for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
{
emulate_float_float_to_float_fn(
|x, base, prec| x.log_base_float_base_1_plus_x_prec(&base, prec),
x,
base,
)
}