use crate::InnerFloat::{Finite, Infinity, NaN, Zero};
use crate::float::arithmetic::exp::{
exp_overflow, exp_rational_near_one, exp_underflow, one_neighbor,
};
use crate::float::arithmetic::round_near_x::float_round_near_x;
use crate::{Float, emulate_float_to_float_fn, emulate_rational_to_float_fn, floor_and_ceiling};
use core::cmp::Ordering::{self, *};
use malachite_base::num::arithmetic::traits::{CeilingLogBase2, PowerOf2, PowerOf2Assign, Sign};
use malachite_base::num::basic::floats::PrimitiveFloat;
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::basic::traits::{
Infinity as InfinityTrait, NaN as NaNTrait, One, Zero as ZeroTrait,
};
use malachite_base::num::conversion::traits::{ExactFrom, IsInteger, RoundingFrom};
use malachite_base::num::logic::traits::SignificantBits;
use malachite_base::rounding_modes::RoundingMode::{self, *};
use malachite_nz::integer::Integer;
use malachite_nz::natural::arithmetic::float_extras::float_can_round;
use malachite_nz::platform::{Limb, SignedLimb};
use malachite_q::Rational;
fn power_of_2_of_float_prec_round_normal_helper(
xfrac: &Float,
xint: i64,
precy: u64,
rm: RoundingMode,
) -> (Float, Ordering) {
let ex = i64::from(xfrac.get_exponent().unwrap());
if let Some((mut y, o)) = float_round_near_x(
&Float::ONE,
u64::exact_from(1 - ex),
*xfrac > 0u32,
precy,
rm,
) {
let o = y.shl_prec_round_assign_helper(xint, precy, rm, o);
return (y, o);
}
let mut working_prec = precy + 5 + precy.ceiling_log_base_2();
let mut increment = Limb::WIDTH;
loop {
let ln_2 = Float::ln_2_prec_round(working_prec, Up).0;
let mut t = xfrac.mul_prec_round_ref_val(ln_2, working_prec, Up).0; let err = u64::exact_from(
i64::exact_from(working_prec) - (i64::from(t.get_exponent().unwrap()) + 2),
);
t.exp_prec_assign(working_prec); if float_can_round(t.significand_ref().unwrap(), err, precy, rm) {
return t.shl_prec_round(xint, precy, rm);
}
working_prec += increment;
increment = working_prec >> 1;
}
}
fn power_of_2_of_float_prec_round_normal(
x: &Float,
precy: u64,
rm: RoundingMode,
) -> (Float, Ordering) {
if *x >= const { Float::const_from_signed(Float::MAX_EXPONENT as SignedLimb) } {
return exp_overflow(precy, rm);
}
if *x <= const { Float::const_from_signed((Float::MIN_EXPONENT as SignedLimb) - 2) } {
return exp_underflow(precy, rm);
}
let xint = i64::exact_from(&Integer::rounding_from(x, Down).0); if x.is_integer() {
return Float::power_of_2_prec_round(xint, precy, rm);
}
assert_ne!(rm, Exact, "Inexact power_of_2_of_float");
let p = x.get_prec().unwrap();
if xint == 0 {
power_of_2_of_float_prec_round_normal_helper(x, 0, precy, rm)
} else {
let xint_f = Float::from_integer_prec(Integer::from(xint), p).0;
let xfrac = x.sub_prec_round_ref_val(xint_f, p, Floor).0;
power_of_2_of_float_prec_round_normal_helper(&xfrac, xint, precy, rm)
}
}
fn power_of_2_rational_near_one(
x: &Rational,
exp_x: i64,
prec: u64,
rm: RoundingMode,
) -> (Float, Ordering) {
let above = x.sign() == Greater;
let err = u64::exact_from(1 - exp_x);
if let Some(result) = float_round_near_x(&Float::ONE, err, above, prec, rm) {
return result;
}
let mut working_prec = (prec - u64::exact_from(-exp_x)) + Limb::WIDTH;
let mut increment = Limb::WIDTH;
loop {
let (ln_2_lo, ln_2_hi) = floor_and_ceiling(Float::ln_2_prec_round(working_prec, Floor));
let ln_2_lo = Rational::exact_from(&ln_2_lo);
let ln_2_hi = Rational::exact_from(&ln_2_hi);
let (lo, o_lo) = exp_rational_near_one(&(x * ln_2_lo), prec, rm);
let (hi, o_hi) = exp_rational_near_one(&(x * ln_2_hi), prec, rm);
if o_lo == o_hi && lo == hi {
return (lo, o_lo);
}
working_prec += increment;
increment = working_prec >> 1;
}
}
fn power_of_2_rational_helper(x: &Rational, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
assert_ne!(rm, Exact, "Inexact power_of_2");
let positive = x.sign() == Greater;
let exp_x = x.floor_log_base_2_abs() + 1; if exp_x >= Float::MAX_EXPONENT_I64 {
return if positive {
exp_overflow(prec, rm)
} else {
exp_underflow(prec, rm)
};
}
if exp_x <= Float::MIN_EXPONENT_I64 {
return power_of_2_rational_near_one(x, exp_x, prec, rm);
}
if -exp_x > i64::exact_from(prec) {
return match (positive, rm) {
(false, Down | Floor) => (one_neighbor(prec, false), Less), (true, Up | Ceiling) => (one_neighbor(prec, true), Greater), (true, _) => (Float::one_prec(prec), Less),
(false, _) => (Float::one_prec(prec), Greater),
};
}
let mut working_prec = prec + 10;
let mut increment = Limb::WIDTH;
loop {
let (x_lo, x_o) = Float::from_rational_prec_round_ref(x, working_prec, Floor);
if x_o == Equal {
return power_of_2_of_float_prec_round_normal(&x_lo, prec, rm);
}
let (x_lo, x_hi) = floor_and_ceiling((x_lo, x_o));
let (e_lo, o_lo) = power_of_2_of_float_prec_round_normal(&x_lo, prec, rm);
let (e_hi, o_hi) = power_of_2_of_float_prec_round_normal(&x_hi, prec, rm);
if o_lo == o_hi && e_lo == e_hi {
return (e_lo, o_lo);
}
working_prec += increment;
increment = working_prec >> 1;
}
}
impl Float {
#[allow(clippy::needless_pass_by_value)]
#[inline]
pub fn power_of_2_of_float_prec_round(
pow: Self,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
Self::power_of_2_of_float_prec_round_ref(&pow, prec, rm)
}
pub fn power_of_2_of_float_prec_round_ref(
pow: &Self,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
assert_ne!(prec, 0);
match &pow.0 {
NaN => (Self::NAN, Equal),
Infinity { sign } => {
if *sign {
(Self::INFINITY, Equal)
} else {
(Self::ZERO, Equal)
}
}
Zero { .. } => (Self::one_prec(prec), Equal),
Finite { .. } => power_of_2_of_float_prec_round_normal(pow, prec, rm),
}
}
#[allow(clippy::needless_pass_by_value)]
#[inline]
pub fn power_of_2_of_float_prec(pow: Self, prec: u64) -> (Self, Ordering) {
Self::power_of_2_of_float_prec_round_ref(&pow, prec, Nearest)
}
#[inline]
pub fn power_of_2_of_float_prec_ref(pow: &Self, prec: u64) -> (Self, Ordering) {
Self::power_of_2_of_float_prec_round_ref(pow, prec, Nearest)
}
#[allow(clippy::needless_pass_by_value)]
#[inline]
pub fn power_of_2_of_float_round(pow: Self, rm: RoundingMode) -> (Self, Ordering) {
let prec = pow.significant_bits();
Self::power_of_2_of_float_prec_round_ref(&pow, prec, rm)
}
#[inline]
pub fn power_of_2_of_float_round_ref(pow: &Self, rm: RoundingMode) -> (Self, Ordering) {
let prec = pow.significant_bits();
Self::power_of_2_of_float_prec_round_ref(pow, prec, rm)
}
#[inline]
pub fn power_of_2_of_float_prec_round_assign(
&mut self,
prec: u64,
rm: RoundingMode,
) -> Ordering {
let (result, o) = Self::power_of_2_of_float_prec_round_ref(self, prec, rm);
*self = result;
o
}
#[inline]
pub fn power_of_2_of_float_prec_assign(&mut self, prec: u64) -> Ordering {
self.power_of_2_of_float_prec_round_assign(prec, Nearest)
}
#[inline]
pub fn power_of_2_of_float_round_assign(&mut self, rm: RoundingMode) -> Ordering {
let prec = self.significant_bits();
self.power_of_2_of_float_prec_round_assign(prec, rm)
}
#[allow(clippy::needless_pass_by_value)]
#[inline]
pub fn power_of_2_rational_prec_round(
x: Rational,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
Self::power_of_2_rational_prec_round_ref(&x, prec, rm)
}
pub fn power_of_2_rational_prec_round_ref(
x: &Rational,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
assert_ne!(prec, 0);
if let Ok(n) = Integer::try_from(x) {
return if let Ok(pow) = i64::try_from(&n) {
Self::power_of_2_prec_round(pow, prec, rm)
} else if x.sign() == Greater {
exp_overflow(prec, rm)
} else {
exp_underflow(prec, rm)
};
}
power_of_2_rational_helper(x, prec, rm)
}
#[allow(clippy::needless_pass_by_value)]
#[inline]
pub fn power_of_2_rational_prec(x: Rational, prec: u64) -> (Self, Ordering) {
Self::power_of_2_rational_prec_round_ref(&x, prec, Nearest)
}
#[inline]
pub fn power_of_2_rational_prec_ref(x: &Rational, prec: u64) -> (Self, Ordering) {
Self::power_of_2_rational_prec_round_ref(x, prec, Nearest)
}
}
impl PowerOf2<Self> for Float {
#[inline]
fn power_of_2(pow: Self) -> Self {
Self::power_of_2_of_float_round(pow, Nearest).0
}
}
impl PowerOf2<&Self> for Float {
#[inline]
fn power_of_2(pow: &Self) -> Self {
Self::power_of_2_of_float_round_ref(pow, Nearest).0
}
}
impl PowerOf2Assign for Float {
#[inline]
fn power_of_2_assign(&mut self) {
self.power_of_2_of_float_round_assign(Nearest);
}
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_power_of_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::power_of_2_of_float_prec, x)
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_power_of_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::power_of_2_rational_prec_ref, x)
}