use crate::Rational;
use core::cmp::Ordering::*;
use malachite_base::num::arithmetic::traits::{
CeilingLogBase, CeilingLogBasePowerOf2, CheckedLogBase, CheckedLogBase2,
CheckedLogBasePowerOf2, FloorLogBase, FloorLogBasePowerOf2, Pow,
};
use malachite_base::num::comparison::traits::OrdAbs;
use malachite_base::num::conversion::traits::{ExactFrom, RoundingFrom, SciMantissaAndExponent};
use malachite_base::rounding_modes::RoundingMode::*;
use malachite_nz::natural::Natural;
fn approx_log_helper(x: &Rational) -> f64 {
let (mantissa, exponent): (f64, i64) = x.sci_mantissa_and_exponent();
libm::log(mantissa) + (exponent as f64) * core::f64::consts::LN_2
}
impl Rational {
#[inline]
pub fn approx_log(&self) -> f64 {
assert!(*self > 0u32);
approx_log_helper(self)
}
}
pub(crate) fn log_base_helper(x: &Rational, base: &Rational) -> (i64, bool) {
assert!(*base > 0u32);
assert_ne!(*base, 1u32);
if *x == 1u32 {
return (0, true);
}
let mut log = i64::rounding_from(approx_log_helper(x) / approx_log_helper(base), Floor).0;
let mut power = base.pow(log);
if *base > 1u32 {
match power.cmp_abs(x) {
Equal => (log, true),
Less => loop {
power *= base;
match power.cmp_abs(x) {
Equal => {
return (log + 1, true);
}
Less => {
log += 1;
}
Greater => {
return (log, false);
}
}
},
Greater => loop {
power /= base;
match power.cmp_abs(x) {
Equal => {
return (log - 1, true);
}
Less => {
return (log - 1, false);
}
Greater => {
log -= 1;
}
}
},
}
} else {
match power.cmp_abs(x) {
Equal => (log, true),
Less => loop {
power /= base;
match power.cmp_abs(x) {
Equal => {
return (log - 1, true);
}
Less => {
log -= 1;
}
Greater => {
return (log - 1, false);
}
}
},
Greater => loop {
power *= base;
match power.cmp_abs(x) {
Equal => {
return (log + 1, true);
}
Less => {
return (log, false);
}
Greater => {
log += 1;
}
}
},
}
}
}
fn log_base_u64_helper(x: &Rational, base: u64) -> (i64, bool) {
let n = x.numerator_ref();
let d = x.denominator_ref();
let base = Natural::from(base);
let log = i64::exact_from(n.floor_log_base(&base)) - i64::exact_from(d.floor_log_base(&base));
let cmp = if log >= 0 {
n.cmp(&(d * (&base).pow(u64::exact_from(log))))
} else {
(n * (&base).pow(u64::exact_from(-log))).cmp(d)
};
match cmp {
Greater => (log, false),
Equal => (log, true),
Less => (log - 1, false),
}
}
fn checked_power_exponent(bn: &Natural, bd: &Natural, xn: &Natural, xd: &Natural) -> Option<u64> {
let a = if *bn >= 2u32 {
xn.checked_log_base(bn)?
} else {
xd.checked_log_base(bd)?
};
if a != 0 && *xn == bn.pow(a) && *xd == bd.pow(a) {
Some(a)
} else {
None
}
}
impl FloorLogBase<&Rational> for &Rational {
type Output = i64;
fn floor_log_base(self, base: &Rational) -> i64 {
assert!(*self > 0u32);
if let Some(log_base) = base.checked_log_base_2() {
return self.floor_log_base_power_of_2(log_base);
}
log_base_helper(self, base).0
}
}
impl CeilingLogBase<&Rational> for &Rational {
type Output = i64;
fn ceiling_log_base(self, base: &Rational) -> i64 {
assert!(*self > 0u32);
if let Some(log_base) = base.checked_log_base_2() {
return self.ceiling_log_base_power_of_2(log_base);
}
let (log, exact) = log_base_helper(self, base);
if exact { log } else { log + 1 }
}
}
impl CheckedLogBase<&Rational> for &Rational {
type Output = i64;
fn checked_log_base(self, base: &Rational) -> Option<i64> {
assert!(*self > 0u32);
if let Some(log_base) = base.checked_log_base_2() {
return self.checked_log_base_power_of_2(log_base);
}
if *self == 1u32 {
return Some(0);
}
let n = self.numerator_ref();
let d = self.denominator_ref();
let p = base.numerator_ref();
let q = base.denominator_ref();
checked_power_exponent(p, q, n, d)
.map(i64::exact_from)
.or_else(|| checked_power_exponent(q, p, n, d).map(|m| -i64::exact_from(m)))
}
}
impl FloorLogBase<u64> for &Rational {
type Output = i64;
fn floor_log_base(self, base: u64) -> i64 {
assert!(*self > 0u32);
assert!(base > 1);
if let Some(log_base) = base.checked_log_base_2() {
return self.floor_log_base_power_of_2(i64::exact_from(log_base));
}
log_base_u64_helper(self, base).0
}
}
impl CeilingLogBase<u64> for &Rational {
type Output = i64;
fn ceiling_log_base(self, base: u64) -> i64 {
assert!(*self > 0u32);
assert!(base > 1);
if let Some(log_base) = base.checked_log_base_2() {
return self.ceiling_log_base_power_of_2(i64::exact_from(log_base));
}
let (log, exact) = log_base_u64_helper(self, base);
if exact { log } else { log + 1 }
}
}
impl CheckedLogBase<u64> for &Rational {
type Output = i64;
fn checked_log_base(self, base: u64) -> Option<i64> {
assert!(*self > 0u32);
assert!(base > 1);
if let Some(log_base) = base.checked_log_base_2() {
return self.checked_log_base_power_of_2(i64::exact_from(log_base));
}
let (log, exact) = log_base_u64_helper(self, base);
if exact { Some(log) } else { None }
}
}