use crate::InnerFloat::{Finite, Infinity, NaN, Zero};
use crate::{
Float, emulate_float_to_float_fn, emulate_rational_to_float_fn, float_either_zero,
float_infinity, float_nan, float_negative_infinity,
};
use core::cmp::Ordering::{self, *};
use malachite_base::num::arithmetic::traits::{
CeilingLogBase2, CheckedLogBase2, IsPowerOf2, LogBasePowerOf2, LogBasePowerOf2Assign, Sign,
};
use malachite_base::num::basic::floats::PrimitiveFloat;
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::basic::traits::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;
fn log_base_power_of_2_prec_round_normal(
x: &Float,
pow: i64,
prec: u64,
rm: RoundingMode,
) -> (Float, Ordering) {
if *x == 1u32 {
return (Float::ZERO, Equal);
}
if x.is_power_of_2() {
let m = i64::from(x.get_exponent().unwrap()) - 1;
return Float::from(m).div_prec_round(Float::from(pow), prec, rm);
}
assert_ne!(rm, Exact, "Inexact log_base_power_of_2");
let mut working_prec = prec + 3 + prec.ceiling_log_base_2();
let mut increment = Limb::WIDTH;
loop {
let t = x
.log_base_2_prec_ref(working_prec)
.0
.div_prec(Float::from(pow), working_prec)
.0;
if float_can_round(t.significand_ref().unwrap(), working_prec - 2, prec, rm) {
return Float::from_float_prec_round(t, prec, rm);
}
working_prec += increment;
increment = working_prec >> 1;
}
}
fn log_base_power_of_2_rational_prec_round_helper(
x: &Rational,
pow: i64,
prec: u64,
rm: RoundingMode,
) -> (Float, Ordering) {
let mut working_prec = prec + 3 + prec.ceiling_log_base_2();
let mut increment = Limb::WIDTH;
loop {
let t = Float::log_base_2_rational_prec_ref(x, working_prec)
.0
.div_prec(Float::from(pow), working_prec)
.0;
if float_can_round(t.significand_ref().unwrap(), working_prec - 2, 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_power_of_2_prec_round(
self,
pow: i64,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
assert_ne!(prec, 0);
assert_ne!(pow, 0, "Cannot take base-1 logarithm");
match self {
Self(NaN | Infinity { sign: false } | Finite { sign: false, .. }) => {
(float_nan!(), Equal)
}
float_either_zero!() => (
if pow > 0 {
float_negative_infinity!()
} else {
float_infinity!()
},
Equal,
),
float_infinity!() => (
if pow > 0 {
float_infinity!()
} else {
float_negative_infinity!()
},
Equal,
),
_ => log_base_power_of_2_prec_round_normal(&self, pow, prec, rm),
}
}
#[inline]
pub fn log_base_power_of_2_prec_round_ref(
&self,
pow: i64,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
assert_ne!(prec, 0);
assert_ne!(pow, 0, "Cannot take base-1 logarithm");
match self {
Self(NaN | Infinity { sign: false } | Finite { sign: false, .. }) => {
(float_nan!(), Equal)
}
float_either_zero!() => (
if pow > 0 {
float_negative_infinity!()
} else {
float_infinity!()
},
Equal,
),
float_infinity!() => (
if pow > 0 {
float_infinity!()
} else {
float_negative_infinity!()
},
Equal,
),
_ => log_base_power_of_2_prec_round_normal(self, pow, prec, rm),
}
}
#[inline]
pub fn log_base_power_of_2_prec(self, pow: i64, prec: u64) -> (Self, Ordering) {
self.log_base_power_of_2_prec_round(pow, prec, Nearest)
}
#[inline]
pub fn log_base_power_of_2_prec_ref(&self, pow: i64, prec: u64) -> (Self, Ordering) {
self.log_base_power_of_2_prec_round_ref(pow, prec, Nearest)
}
#[inline]
pub fn log_base_power_of_2_round(self, pow: i64, rm: RoundingMode) -> (Self, Ordering) {
let prec = self.significant_bits();
self.log_base_power_of_2_prec_round(pow, prec, rm)
}
#[inline]
pub fn log_base_power_of_2_round_ref(&self, pow: i64, rm: RoundingMode) -> (Self, Ordering) {
let prec = self.significant_bits();
self.log_base_power_of_2_prec_round_ref(pow, prec, rm)
}
#[inline]
pub fn log_base_power_of_2_prec_round_assign(
&mut self,
pow: i64,
prec: u64,
rm: RoundingMode,
) -> Ordering {
let (result, o) = core::mem::take(self).log_base_power_of_2_prec_round(pow, prec, rm);
*self = result;
o
}
#[inline]
pub fn log_base_power_of_2_prec_assign(&mut self, pow: i64, prec: u64) -> Ordering {
self.log_base_power_of_2_prec_round_assign(pow, prec, Nearest)
}
#[inline]
pub fn log_base_power_of_2_round_assign(&mut self, pow: i64, rm: RoundingMode) -> Ordering {
let prec = self.significant_bits();
self.log_base_power_of_2_prec_round_assign(pow, prec, rm)
}
#[allow(clippy::needless_pass_by_value)]
#[inline]
pub fn log_base_power_of_2_rational_prec_round(
x: Rational,
pow: i64,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
Self::log_base_power_of_2_rational_prec_round_ref(&x, pow, prec, rm)
}
pub fn log_base_power_of_2_rational_prec_round_ref(
x: &Rational,
pow: i64,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
assert_ne!(prec, 0);
assert_ne!(pow, 0, "Cannot take base-1 logarithm");
match x.sign() {
Equal => {
return (
if pow > 0 {
float_negative_infinity!()
} else {
float_infinity!()
},
Equal,
);
}
Less => return (float_nan!(), Equal),
Greater => {}
}
if let Some(m) = x.checked_log_base_2() {
return Self::from(m).div_prec_round(Self::from(pow), prec, rm);
}
assert_ne!(rm, Exact, "Inexact log_base_power_of_2");
log_base_power_of_2_rational_prec_round_helper(x, pow, prec, rm)
}
#[inline]
pub fn log_base_power_of_2_rational_prec(x: Rational, pow: i64, prec: u64) -> (Self, Ordering) {
Self::log_base_power_of_2_rational_prec_round(x, pow, prec, Nearest)
}
#[inline]
pub fn log_base_power_of_2_rational_prec_ref(
x: &Rational,
pow: i64,
prec: u64,
) -> (Self, Ordering) {
Self::log_base_power_of_2_rational_prec_round_ref(x, pow, prec, Nearest)
}
}
impl LogBasePowerOf2<i64> for Float {
type Output = Self;
#[inline]
fn log_base_power_of_2(self, pow: i64) -> Self {
let prec = self.significant_bits();
self.log_base_power_of_2_prec_round(pow, prec, Nearest).0
}
}
impl LogBasePowerOf2<i64> for &Float {
type Output = Float;
#[inline]
fn log_base_power_of_2(self, pow: i64) -> Float {
let prec = self.significant_bits();
self.log_base_power_of_2_prec_round_ref(pow, prec, Nearest)
.0
}
}
impl LogBasePowerOf2Assign<i64> for Float {
#[inline]
fn log_base_power_of_2_assign(&mut self, pow: i64) {
let prec = self.significant_bits();
self.log_base_power_of_2_prec_round_assign(pow, prec, Nearest);
}
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_log_base_power_of_2<T: PrimitiveFloat>(x: T, pow: i64) -> 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_power_of_2_prec(x, pow, prec), x)
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_log_base_power_of_2_rational<T: PrimitiveFloat>(x: &Rational, pow: i64) -> T
where
Float: PartialOrd<T>,
for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
{
emulate_rational_to_float_fn(
|x, prec| Float::log_base_power_of_2_rational_prec_ref(x, pow, prec),
x,
)
}