use crate::arithmetic::log_base_2::extended_log_base_2_of_rational;
use crate::basic::extended::ExtendedFloat;
use crate::{Float, emulate_rational_to_float_fn};
use core::cmp::Ordering::{self, *};
use malachite_base::num::arithmetic::traits::{CeilingLogBase2, CheckedLogBase, Sign};
use malachite_base::num::basic::floats::PrimitiveFloat;
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::basic::traits::{NaN, NegativeInfinity, Zero};
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::arithmetic::float_extras::float_can_round;
use malachite_nz::platform::Limb;
use malachite_q::Rational;
pub(crate) fn rational_log_base_rational_rational_base(
x: &Rational,
base: &Rational,
prec: u64,
) -> Option<Rational> {
let bound = prec.saturating_mul(64);
if x.significant_bits() > bound || base.significant_bits() > bound {
return None;
}
let (root, e_base) = base.express_as_power().unwrap_or_else(|| (base.clone(), 1));
let a = x.checked_log_base(&root)?;
Some(Rational::from_signeds(a, i64::exact_from(e_base)))
}
fn log_base_rational_rational_base_prec_round_normal(
x: &Rational,
base: &Rational,
prec: u64,
rm: RoundingMode,
) -> (Float, Ordering) {
if *x == 1u32 {
return (Float::ZERO, Equal);
}
if let Some(q) = rational_log_base_rational_rational_base(x, base, prec) {
return Float::from_rational_prec_round(q, prec, rm);
}
assert_ne!(rm, Exact, "Inexact log_base_rational_rational_base");
let mut working_prec = prec + 6 + prec.ceiling_log_base_2();
let mut increment = Limb::WIDTH;
loop {
let num = extended_log_base_2_of_rational(x, working_prec);
let den = extended_log_base_2_of_rational(base, working_prec);
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;
}
}
impl Float {
#[allow(clippy::needless_pass_by_value)]
#[inline]
pub fn log_base_rational_rational_base_prec_round(
x: Rational,
base: Rational,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
Self::log_base_rational_rational_base_prec_round_ref(&x, &base, prec, rm)
}
pub fn log_base_rational_rational_base_prec_round_ref(
x: &Rational,
base: &Rational,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
assert_ne!(prec, 0);
assert!(*base > 1u32, "Logarithm base must be greater than 1");
match x.sign() {
Less => (Self::NAN, Equal),
Equal => (Self::NEGATIVE_INFINITY, Equal),
Greater => log_base_rational_rational_base_prec_round_normal(x, base, prec, rm),
}
}
#[allow(clippy::needless_pass_by_value)]
#[inline]
pub fn log_base_rational_rational_base_prec(
x: Rational,
base: Rational,
prec: u64,
) -> (Self, Ordering) {
Self::log_base_rational_rational_base_prec_round_ref(&x, &base, prec, Nearest)
}
#[inline]
pub fn log_base_rational_rational_base_prec_ref(
x: &Rational,
base: &Rational,
prec: u64,
) -> (Self, Ordering) {
Self::log_base_rational_rational_base_prec_round_ref(x, base, prec, Nearest)
}
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_log_base_rational_rational_base<T: PrimitiveFloat>(
x: &Rational,
base: &Rational,
) -> T
where
Float: PartialOrd<T>,
for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
{
emulate_rational_to_float_fn(
|x, prec| Float::log_base_rational_rational_base_prec_ref(x, base, prec),
x,
)
}