use crate::InnerFloat::{Finite, Infinity, NaN, Zero};
use crate::float::arithmetic::ln::{SliverOfOne, ln_1_plus_rational_brackets, sliver_of_one};
use crate::float::arithmetic::round_near_x::float_round_near_x;
use crate::float::basic::extended::ExtendedFloat;
use crate::{ComparableFloatRef, floor_and_ceiling};
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, LogBase2, LogBase2Assign, PowerOf2, Sign,
};
use malachite_base::num::basic::floats::PrimitiveFloat;
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::basic::traits::{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;
fn log_base_2_prec_round_normal(x: &Float, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
if *x == 1u32 {
return (Float::ZERO, Equal);
}
if x.is_power_of_2() {
return Float::from_signed_prec_round(i64::from(x.get_exponent().unwrap()) - 1, prec, rm);
}
match sliver_of_one(x) {
SliverOfOne::Representable(d) => return d.log_base_2_1_plus_x_prec_round(prec, rm),
SliverOfOne::Underflow => {
return Float::log_base_2_rational_prec_round(Rational::exact_from(x), prec, rm);
}
SliverOfOne::No => {}
}
assert_ne!(rm, Exact, "Inexact log_base_2");
let mut working_prec = prec + 3 + prec.ceiling_log_base_2();
let mut increment = Limb::WIDTH;
loop {
let t = x
.ln_prec_ref(working_prec)
.0
.div_prec(Float::ln_2_prec(working_prec).0, working_prec)
.0;
if float_can_round(t.significand_ref().unwrap(), working_prec - 3, prec, rm) {
return Float::from_float_prec_round(t, prec, rm);
}
working_prec += increment;
increment = working_prec >> 1;
}
}
pub(crate) fn log_2_rational_brackets(x: &Rational, wprec: u64) -> (Rational, Rational) {
let e = x - Rational::ONE;
if e == 0u32 {
return (Rational::ZERO, Rational::ZERO);
}
if e.floor_log_base_2_abs() >= -8 {
(
Rational::exact_from(
&Float::from_rational_prec_round_ref(x, wprec, Floor)
.0
.log_base_2_round(Floor)
.0,
),
Rational::exact_from(
&Float::from_rational_prec_round_ref(x, wprec, Ceiling)
.0
.log_base_2_round(Ceiling)
.0,
),
)
} else {
let (ln_lo, ln_hi) = ln_1_plus_rational_brackets(&e, wprec);
let (ln_2_lo, ln_2_hi) = floor_and_ceiling(Float::ln_2_prec_round(wprec, Floor));
let ln_2_lo = Rational::exact_from(&ln_2_lo);
let ln_2_hi = Rational::exact_from(&ln_2_hi);
if e > 0u32 {
(ln_lo / ln_2_hi, ln_hi / ln_2_lo)
} else {
(ln_lo / ln_2_lo, ln_hi / ln_2_hi)
}
}
}
fn log_base_2_rational_near_one(eps: &Rational, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
let x = Rational::ONE + eps;
let mut working_prec = prec + 3 + prec.ceiling_log_base_2();
let mut increment = Limb::WIDTH;
loop {
let (lo, hi) = log_2_rational_brackets(&x, working_prec);
let (f_lo, mut o_lo) = Float::from_rational_prec_round_ref(&lo, prec, rm);
let (f_hi, mut o_hi) = Float::from_rational_prec_round_ref(&hi, prec, rm);
if o_lo == Equal {
o_lo = o_hi;
}
if o_hi == Equal {
o_hi = o_lo;
}
if o_lo == o_hi && ComparableFloatRef(&f_lo) == ComparableFloatRef(&f_hi) {
return (f_lo, o_lo);
}
working_prec += increment;
increment = working_prec >> 1;
}
}
fn log_base_2_rational_near_power_of_2(
x: &Rational,
prec: u64,
rm: RoundingMode,
) -> Option<(Float, Ordering)> {
let m = x.floor_log_base_2_abs();
let pow_lo = Rational::power_of_2(m);
let pow_hi = &pow_lo << 1u32;
let dist_lo = x - &pow_lo;
let dist_hi = &pow_hi - x;
let (k, eps) = if dist_lo <= dist_hi {
(m, dist_lo / pow_lo)
} else {
(m + 1, -(dist_hi / pow_hi))
};
if k == 0 {
return Some(log_base_2_rational_near_one(&eps, prec, rm));
}
let eps_exp = eps.floor_log_base_2_abs();
let k_float = Float::from(k);
let exp_k = i64::from(k_float.get_exponent().unwrap());
let err = exp_k - eps_exp - 3;
if err <= 0 {
return None;
}
let dir = (eps > 0) == (k > 0);
float_round_near_x(&k_float, u64::exact_from(err), dir, prec, rm)
}
fn log_base_2_rational_prec_round_helper(
x: &Rational,
prec: u64,
rm: RoundingMode,
) -> (Float, Ordering) {
if let Some(result) = log_base_2_rational_near_power_of_2(x, prec, rm) {
return result;
}
let mut working_prec = prec + 3 + prec.ceiling_log_base_2();
let mut increment = Limb::WIDTH;
loop {
let t = Float::ln_rational_prec_ref(x, working_prec)
.0
.div_prec(Float::ln_2_prec(working_prec).0, working_prec)
.0;
if float_can_round(t.significand_ref().unwrap(), working_prec - 3, prec, rm) {
return Float::from_float_prec_round(t, prec, rm);
}
working_prec += increment;
increment = working_prec >> 1;
}
}
pub(crate) fn extended_log_base_2_of_rational(r: &Rational, prec: u64) -> ExtendedFloat {
let y = r - Rational::ONE;
if y.floor_log_base_2_abs() <= i64::from(Float::MIN_EXPONENT) + 1 {
let y_ext = ExtendedFloat::from_rational_prec_round(y, prec, Nearest).0;
let ln_2 = ExtendedFloat::from(Float::ln_2_prec(prec).0);
y_ext.div_prec_val_ref(&ln_2, prec).0
} else {
ExtendedFloat::from(Float::log_base_2_rational_prec_ref(r, prec).0)
}
}
impl Float {
#[inline]
pub fn log_base_2_prec_round(self, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
assert_ne!(prec, 0);
match self {
Self(NaN | Infinity { sign: false } | Finite { sign: false, .. }) => {
(float_nan!(), Equal)
}
float_either_zero!() => (float_negative_infinity!(), Equal),
float_infinity!() => (float_infinity!(), Equal),
_ => log_base_2_prec_round_normal(&self, prec, rm),
}
}
#[inline]
pub fn log_base_2_prec_round_ref(&self, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
assert_ne!(prec, 0);
match self {
Self(NaN | Infinity { sign: false } | Finite { sign: false, .. }) => {
(float_nan!(), Equal)
}
float_either_zero!() => (float_negative_infinity!(), Equal),
float_infinity!() => (float_infinity!(), Equal),
_ => log_base_2_prec_round_normal(self, prec, rm),
}
}
#[inline]
pub fn log_base_2_prec(self, prec: u64) -> (Self, Ordering) {
self.log_base_2_prec_round(prec, Nearest)
}
#[inline]
pub fn log_base_2_prec_ref(&self, prec: u64) -> (Self, Ordering) {
self.log_base_2_prec_round_ref(prec, Nearest)
}
#[inline]
pub fn log_base_2_round(self, rm: RoundingMode) -> (Self, Ordering) {
let prec = self.significant_bits();
self.log_base_2_prec_round(prec, rm)
}
#[inline]
pub fn log_base_2_round_ref(&self, rm: RoundingMode) -> (Self, Ordering) {
let prec = self.significant_bits();
self.log_base_2_prec_round_ref(prec, rm)
}
#[inline]
pub fn log_base_2_prec_round_assign(&mut self, prec: u64, rm: RoundingMode) -> Ordering {
let (result, o) = core::mem::take(self).log_base_2_prec_round(prec, rm);
*self = result;
o
}
#[inline]
pub fn log_base_2_prec_assign(&mut self, prec: u64) -> Ordering {
self.log_base_2_prec_round_assign(prec, Nearest)
}
#[inline]
pub fn log_base_2_round_assign(&mut self, rm: RoundingMode) -> Ordering {
let prec = self.significant_bits();
self.log_base_2_prec_round_assign(prec, rm)
}
#[allow(clippy::needless_pass_by_value)]
#[inline]
pub fn log_base_2_rational_prec_round(
x: Rational,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
Self::log_base_2_rational_prec_round_ref(&x, prec, rm)
}
pub fn log_base_2_rational_prec_round_ref(
x: &Rational,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
assert_ne!(prec, 0);
match x.sign() {
Equal => return (float_negative_infinity!(), Equal),
Less => return (float_nan!(), Equal),
Greater => {}
}
if let Some(k) = x.checked_log_base_2() {
return Self::from_signed_prec_round(k, prec, rm);
}
assert_ne!(rm, Exact, "Inexact log_base_2");
log_base_2_rational_prec_round_helper(x, prec, rm)
}
#[inline]
pub fn log_base_2_rational_prec(x: Rational, prec: u64) -> (Self, Ordering) {
Self::log_base_2_rational_prec_round(x, prec, Nearest)
}
#[inline]
pub fn log_base_2_rational_prec_ref(x: &Rational, prec: u64) -> (Self, Ordering) {
Self::log_base_2_rational_prec_round_ref(x, prec, Nearest)
}
}
impl LogBase2 for Float {
type Output = Self;
#[inline]
fn log_base_2(self) -> Self {
let prec = self.significant_bits();
self.log_base_2_prec_round(prec, Nearest).0
}
}
impl LogBase2 for &Float {
type Output = Float;
#[inline]
fn log_base_2(self) -> Float {
let prec = self.significant_bits();
self.log_base_2_prec_round_ref(prec, Nearest).0
}
}
impl LogBase2Assign for Float {
#[inline]
fn log_base_2_assign(&mut self) {
let prec = self.significant_bits();
self.log_base_2_prec_round_assign(prec, Nearest);
}
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_log_base_2<T: PrimitiveFloat>(x: T) -> T
where
Float: From<T> + PartialOrd<T>,
for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
{
emulate_float_to_float_fn(Float::log_base_2_prec, x)
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_log_base_2_rational<T: PrimitiveFloat>(x: &Rational) -> T
where
Float: PartialOrd<T>,
for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
{
emulate_rational_to_float_fn(Float::log_base_2_rational_prec_ref, x)
}