use crate::InnerFloat::{Infinity, NaN, Zero};
use crate::TWICE_WIDTH;
use crate::float::arithmetic::exp::{exp_overflow, one_neighbor};
use crate::float::arithmetic::exp_x_minus_1::exp_x_minus_1_rational_near_zero;
use crate::float::arithmetic::round_near_x::float_round_near_x;
use crate::{
Float, emulate_float_to_float_fn, emulate_rational_to_float_fn, float_infinity, float_nan,
float_zero, floor_and_ceiling,
};
use core::cmp::Ordering::{self, *};
use core::cmp::max;
use malachite_base::fail_on_untested_path;
use malachite_base::num::arithmetic::traits::{
CeilingLogBase2, Pow, PowerOf10XMinus1, PowerOf10XMinus1Assign, Reciprocal,
};
use malachite_base::num::basic::floats::PrimitiveFloat;
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::basic::traits::{NegativeOne, One};
use malachite_base::num::comparison::traits::PartialOrdAbs;
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;
use malachite_q::Rational;
const TEN: Rational = Rational::const_from_unsigned(10);
enum Small {
Round(Float),
NotSmall,
}
fn x_log_2_10_ge(x: &Rational, bound: i64) -> bool {
let bound = Rational::from(bound);
let positive = *x > 0u32;
let mut p = TWICE_WIDTH;
loop {
let (lo, hi) = floor_and_ceiling(Float::log_2_10_prec_round(p, Floor));
let lo = Rational::exact_from(lo);
let hi = Rational::exact_from(hi);
let (a_lo, a_hi) = if positive {
(x * lo, x * hi)
} else {
(x * hi, x * lo)
};
if a_lo >= bound {
return true;
}
if a_hi < bound {
return false;
}
p <<= 1;
}
}
fn power_of_10_x_minus_1_small(x: &Float, prec: u64, working_prec: u64, rm: RoundingMode) -> Small {
let ex = i64::from(x.get_exponent().unwrap());
if ex > -2 {
return Small::NotSmall;
}
let t = Float::ln_10_prec(working_prec)
.0
.mul_prec_val_ref(x, working_prec)
.0;
let exp_t = i64::from(t.get_exponent().unwrap());
let e = (ex << 1) + 2 + i64::exact_from(working_prec) - exp_t;
let e = if e <= 1 { 2 + i64::from(e == 1) } else { e + 1 };
if float_can_round(
t.significand_ref().unwrap(),
working_prec - u64::exact_from(e),
prec,
rm,
) {
Small::Round(t)
} else {
Small::NotSmall
}
}
fn power_of_10_x_minus_1_prec_round_normal(
x: &Float,
prec: u64,
rm: RoundingMode,
) -> (Float, Ordering) {
if x.is_sign_negative() && x.gt_abs(&(2 + (prec - 1) / 3)) {
return match rm {
Ceiling | Down => (-one_neighbor(prec, false), Greater),
Floor | Up | Nearest => (Float::negative_one_prec(prec), Less),
Exact => panic!("Inexact power_of_10_x_minus_1"),
};
}
if x.is_sign_negative()
&& 2 + (prec - 1) / 3 >= const { Float::MAX_EXPONENT_U64 >> 2 }
&& !x_log_2_10_ge(&Rational::exact_from(x), Float::MIN_EXPONENT_I64)
{
return power_of_10_x_minus_1_deep_negative(x, prec, rm);
}
let mut working_prec = prec + prec.ceiling_log_base_2() + 6;
let mut increment = Limb::WIDTH;
loop {
let (mut t, o1) = Float::power_of_10_of_float_prec_ref(x, working_prec);
if t.is_infinite() {
if prec < Float::MAX_EXPONENT_U64
|| x_log_2_10_ge(&Rational::exact_from(x), Float::MAX_EXPONENT_I64)
{
return exp_overflow(prec, rm);
}
working_prec += increment;
increment = working_prec >> 1;
continue;
}
if o1 == Equal {
return t.sub_prec_round(Float::ONE, prec, rm);
}
assert_ne!(rm, Exact, "Inexact power_of_10_x_minus_1");
let exp_te = i64::from(t.get_exponent().unwrap());
t.sub_prec_assign(Float::ONE, working_prec); if t != 0u32 {
let exp_t = i64::from(t.get_exponent().unwrap());
let err = u64::exact_from(max(exp_te - exp_t, 0) + 1);
if float_can_round(t.significand_ref().unwrap(), working_prec - err, prec, rm) {
return Float::from_float_prec_round(t, prec, rm);
}
}
match power_of_10_x_minus_1_small(x, prec, working_prec, rm) {
Small::Round(t) => return Float::from_float_prec_round(t, prec, rm),
Small::NotSmall => {}
}
working_prec += increment;
increment = working_prec >> 1;
}
}
fn power_of_10_x_minus_1_deep_negative(
x: &Float,
prec: u64,
rm: RoundingMode,
) -> (Float, Ordering) {
let xr = Rational::exact_from(x);
let neg_n = -Integer::rounding_from(&xr, Floor).0; let shift = u64::exact_from(&neg_n);
let ten_pow_s = TEN.pow(shift); let frac = xr + Rational::from(neg_n); if frac == 0u32 {
return Float::from_rational_prec_round(ten_pow_s.reciprocal() - Rational::ONE, prec, rm);
}
assert_ne!(rm, Exact, "Inexact power_of_10_x_minus_1");
let frac_prec = frac.numerator_ref().significant_bits();
let frac = Float::from_rational_prec_round(frac, frac_prec, Exact).0;
let mut working_prec = prec.saturating_sub(3 * shift) + Limb::WIDTH;
let mut increment = Limb::WIDTH;
loop {
let u = Float::power_of_10_of_float_prec_round_ref(&frac, working_prec, Floor);
let (u_lo, u_hi) = floor_and_ceiling(u);
let (f_lo, mut o_lo) = Float::from_rational_prec_round(
Rational::exact_from(u_lo) / &ten_pow_s - Rational::ONE,
prec,
rm,
);
let (f_hi, mut o_hi) = Float::from_rational_prec_round(
Rational::exact_from(u_hi) / &ten_pow_s - Rational::ONE,
prec,
rm,
);
if o_lo == Equal {
fail_on_untested_path(
"deep_negative o_lo == Equal: u_lo / 10^s has a 5^s factor in its denominator (the \
working-precision mantissa is smaller than 5^s in the deep regime), so it is \
non-dyadic and never rounds exactly",
);
o_lo = o_hi;
}
if o_hi == Equal {
fail_on_untested_path(
"deep_negative o_hi == Equal: as o_lo == Equal -- the bracket end is non-dyadic, \
so it never rounds exactly",
);
o_hi = o_lo;
}
if o_lo == o_hi && f_lo == f_hi {
return (f_lo, o_lo);
}
working_prec += increment;
increment = working_prec >> 1;
}
}
fn power_of_10_x_minus_1_rational_near_zero(
x: &Rational,
prec: u64,
rm: RoundingMode,
) -> (Float, Ordering) {
let positive = *x > 0u32;
let mut working_prec = prec + Limb::WIDTH;
let mut increment = Limb::WIDTH;
loop {
let (ln_10_lo, ln_10_hi) = floor_and_ceiling(Float::ln_10_prec_round(working_prec, Floor));
let ln_10_lo = Rational::exact_from(ln_10_lo);
let ln_10_hi = Rational::exact_from(ln_10_hi);
let (a_lo, a_hi) = if positive {
(x * ln_10_lo, x * ln_10_hi)
} else {
(x * ln_10_hi, x * ln_10_lo)
};
let (f_lo, o_lo) = exp_x_minus_1_rational_near_zero(&a_lo, prec, rm);
let (f_hi, o_hi) = exp_x_minus_1_rational_near_zero(&a_hi, prec, rm);
if o_lo == o_hi && f_lo == f_hi {
return (f_lo, o_lo);
}
working_prec += increment;
increment = working_prec >> 1;
}
}
fn power_of_10_x_minus_1_rational_helper(
x: &Rational,
prec: u64,
rm: RoundingMode,
) -> (Float, Ordering) {
if x.is_integer() {
let n = Integer::exact_from(x);
return if n > 0 {
if x_log_2_10_ge(x, Float::MAX_EXPONENT_I64) {
exp_overflow(prec, rm)
} else {
let value = TEN.pow(u64::exact_from(&n)) - Rational::ONE;
Float::from_rational_prec_round(value, prec, rm)
}
} else {
assert_ne!(rm, Exact, "Inexact power_of_10_x_minus_1");
if x_log_2_10_ge(x, -i64::exact_from(prec) - 2) {
let s = u64::exact_from(&-&n);
let value = TEN.pow(s).reciprocal() - Rational::ONE;
Float::from_rational_prec_round(value, prec, rm)
} else {
float_round_near_x(&Float::NEGATIVE_ONE, prec + 2, false, prec, rm).unwrap()
}
};
}
assert_ne!(rm, Exact, "Inexact power_of_10_x_minus_1");
let positive = *x > 0u32;
let exp_x = x.floor_log_base_2_abs() + 1; if exp_x <= Float::MIN_EXPONENT_I64 {
return power_of_10_x_minus_1_rational_near_zero(x, prec, rm);
}
if exp_x >= Float::MAX_EXPONENT_I64 {
if positive {
return exp_overflow(prec, rm);
}
if let Some(result) = float_round_near_x(
&Float::NEGATIVE_ONE,
Float::MAX_EXPONENT_U64,
false,
prec,
rm,
) {
return result;
}
return match rm {
Ceiling | Down => (-one_neighbor(prec, false), Greater), _ => (-Float::one_prec(prec), Less), };
}
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 x_lo.power_of_10_x_minus_1_prec_round(prec, rm);
}
let (x_lo, x_hi) = floor_and_ceiling((x_lo, x_o));
let (e_lo, o_lo) = x_lo.power_of_10_x_minus_1_prec_round_ref(prec, rm);
let (e_hi, o_hi) = x_hi.power_of_10_x_minus_1_prec_round_ref(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 {
#[inline]
pub fn power_of_10_x_minus_1_prec_round(self, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
self.power_of_10_x_minus_1_prec_round_ref(prec, rm)
}
pub fn power_of_10_x_minus_1_prec_round_ref(
&self,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
assert_ne!(prec, 0);
match self {
Self(NaN) => (float_nan!(), Equal),
float_infinity!() => (float_infinity!(), Equal),
Self(Infinity { sign: false }) => (Self::from_signed_prec(-1i32, prec).0, Equal),
Self(Zero { .. }) => (self.clone(), Equal),
_ => power_of_10_x_minus_1_prec_round_normal(self, prec, rm),
}
}
#[inline]
pub fn power_of_10_x_minus_1_prec(self, prec: u64) -> (Self, Ordering) {
self.power_of_10_x_minus_1_prec_round(prec, Nearest)
}
#[inline]
pub fn power_of_10_x_minus_1_prec_ref(&self, prec: u64) -> (Self, Ordering) {
self.power_of_10_x_minus_1_prec_round_ref(prec, Nearest)
}
#[inline]
pub fn power_of_10_x_minus_1_round(self, rm: RoundingMode) -> (Self, Ordering) {
let prec = self.significant_bits();
self.power_of_10_x_minus_1_prec_round(prec, rm)
}
#[inline]
pub fn power_of_10_x_minus_1_round_ref(&self, rm: RoundingMode) -> (Self, Ordering) {
self.power_of_10_x_minus_1_prec_round_ref(self.significant_bits(), rm)
}
#[inline]
pub fn power_of_10_x_minus_1_prec_round_assign(
&mut self,
prec: u64,
rm: RoundingMode,
) -> Ordering {
let (result, o) = core::mem::take(self).power_of_10_x_minus_1_prec_round(prec, rm);
*self = result;
o
}
#[inline]
pub fn power_of_10_x_minus_1_prec_assign(&mut self, prec: u64) -> Ordering {
self.power_of_10_x_minus_1_prec_round_assign(prec, Nearest)
}
#[inline]
pub fn power_of_10_x_minus_1_round_assign(&mut self, rm: RoundingMode) -> Ordering {
let prec = self.significant_bits();
self.power_of_10_x_minus_1_prec_round_assign(prec, rm)
}
#[allow(clippy::needless_pass_by_value)]
#[inline]
pub fn power_of_10_x_minus_1_rational_prec_round(
x: Rational,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
Self::power_of_10_x_minus_1_rational_prec_round_ref(&x, prec, rm)
}
#[inline]
pub fn power_of_10_x_minus_1_rational_prec_round_ref(
x: &Rational,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
assert_ne!(prec, 0);
if *x == 0u32 {
return (float_zero!(), Equal);
}
power_of_10_x_minus_1_rational_helper(x, prec, rm)
}
#[inline]
pub fn power_of_10_x_minus_1_rational_prec(x: Rational, prec: u64) -> (Self, Ordering) {
Self::power_of_10_x_minus_1_rational_prec_round(x, prec, Nearest)
}
#[inline]
pub fn power_of_10_x_minus_1_rational_prec_ref(x: &Rational, prec: u64) -> (Self, Ordering) {
Self::power_of_10_x_minus_1_rational_prec_round_ref(x, prec, Nearest)
}
}
impl PowerOf10XMinus1 for Float {
type Output = Self;
#[inline]
fn power_of_10_x_minus_1(self) -> Self {
let prec = self.significant_bits();
self.power_of_10_x_minus_1_prec(prec).0
}
}
impl PowerOf10XMinus1 for &Float {
type Output = Float;
#[inline]
fn power_of_10_x_minus_1(self) -> Float {
self.power_of_10_x_minus_1_prec_round_ref(self.significant_bits(), Nearest)
.0
}
}
impl PowerOf10XMinus1Assign for Float {
#[inline]
fn power_of_10_x_minus_1_assign(&mut self) {
let prec = self.significant_bits();
self.power_of_10_x_minus_1_prec_round_assign(prec, Nearest);
}
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_power_of_10_x_minus_1<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_10_x_minus_1_prec, x)
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_power_of_10_x_minus_1_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_10_x_minus_1_rational_prec_ref, x)
}