use crate::Float;
use core::cmp::Ordering::{self, *};
use core::cmp::{max, min};
use malachite_base::num::arithmetic::traits::{Abs, PowerOf2, Reciprocal};
use malachite_base::num::conversion::traits::ExactFrom;
use malachite_base::num::logic::traits::SignificantBits;
use malachite_base::rounding_modes::RoundingMode::{self, *};
use malachite_nz::natural::arithmetic::float::round::float_can_round;
use malachite_q::Rational;
fn step_away_from_zero(y: Float, prec: u64) -> Float {
let mult = Float::one_prec(prec + 2)
.add_prec(Float::power_of_2(-i64::exact_from(prec) - 1), prec + 2)
.0;
y.mul_prec_round(mult, prec, Up).0
}
fn step_toward_zero(y: Float, prec: u64) -> Float {
let mult = Float::one_prec(prec + 1)
.sub_prec(Float::power_of_2(-i64::exact_from(prec) - 1), prec + 1)
.0;
y.mul_prec_round(mult, prec, Down).0
}
pub(crate) fn small_input_shortcut(
x: &Float,
err1: i64,
err2: u64,
dir: bool,
prec: u64,
rm: RoundingMode,
) -> Option<(Float, Ordering)> {
if err1 <= 0 {
return None;
}
let err = u64::exact_from(err1) + err2;
if err <= prec + 1 {
return None;
}
let cap = max(prec + 2, x.get_prec().unwrap() + 1);
float_round_near_x(x, min(err, cap), dir, prec, rm)
}
crate_test_fn! {float_round_near_x(
v: &Float,
err: u64,
dir: bool,
prec: u64,
rm: RoundingMode
) -> Option<(Float, Ordering)> {
assert_ne!(rm, Exact, "Inexact float_round_near_x");
let sign = if v > &0u32 { Greater } else { Less };
let prec_v = v.get_prec().unwrap();
if !(err > prec + 1
&& (err > prec_v || float_can_round(v.significand_ref().unwrap(), err, prec, rm)))
{
return None;
}
if rm == Nearest {
let (y_wide, o_wide) = Float::from_float_prec_round_ref(v, prec + 1, Down);
if o_wide == Equal {
let (y_trunc, o_trunc) = Float::from_float_prec_round(y_wide, prec, Down);
if o_trunc != Equal {
return Some(if dir {
(step_away_from_zero(y_trunc, prec), sign)
} else {
(y_trunc, sign.reverse())
});
}
}
}
let (mut y, mut o) = Float::from_float_prec_round_ref(v, prec, rm);
if o == Equal {
if dir {
o = sign.reverse();
let rounds_away = match rm {
Floor => sign == Less,
Ceiling => sign == Greater,
Up => true,
_ => false,
};
if rounds_away {
o = sign;
y = step_away_from_zero(y, prec);
}
} else {
o = sign;
let rounds_to_zero = match rm {
Floor => sign == Greater,
Ceiling => sign == Less,
Down => true,
_ => false,
};
if rounds_to_zero {
o = sign.reverse();
y = step_toward_zero(y, prec);
}
}
}
assert_ne!(o, Equal);
Some((y, o))
}}
pub(crate) const LEADING_TERM_MIN_EXPONENT: i64 = Float::MIN_EXPONENT_I64 + 4;
pub(crate) fn value_is_tie(wide: &Float, o_wide: Ordering, prec: u64) -> bool {
o_wide == Equal && Float::from_float_prec_round_ref(wide, prec, Down).1 != Equal
}
pub(crate) fn round_from_above(
t: Float,
o: Ordering,
tie: bool,
rm: RoundingMode,
) -> (Float, Ordering) {
if o == Equal {
return if rm == Floor || rm == Down {
let mut t = t;
t.decrement();
(t, Less)
} else {
(t, Greater)
};
}
if tie {
if o == Less {
return (t, Less);
}
let mut t = t;
t.decrement();
return (t, Less);
}
(t, o)
}
pub(crate) fn round_from_below(
t: Float,
o: Ordering,
tie: bool,
rm: RoundingMode,
) -> (Float, Ordering) {
if o == Equal {
return if rm == Ceiling || rm == Up {
let mut t = t;
t.increment();
(t, Greater)
} else {
(t, Less)
};
}
if tie {
if o == Greater {
return (t, Greater);
}
let mut t = t;
t.increment();
return (t, Greater);
}
(t, o)
}
pub(crate) fn round_near_reciprocal(
x: &Float,
beyond: bool,
prec: u64,
rm: RoundingMode,
) -> (Float, Ordering) {
let negative = x.is_sign_negative();
let rm_abs = if negative { -rm } else { rm };
let (r, o) = x.reciprocal_prec_round_ref(prec, rm);
let (r, o) = if negative { (-r, o.reverse()) } else { (r, o) };
let (r, o) = if beyond {
round_from_below(r, o, false, rm_abs)
} else {
round_from_above(r, o, false, rm_abs)
};
if negative { (-r, o.reverse()) } else { (r, o) }
}
pub(crate) fn round_rational_leading_term(
term_abs: Rational,
positive: bool,
beyond: bool,
prec: u64,
rm: RoundingMode,
) -> (Float, Ordering) {
let rm_abs = if positive { rm } else { -rm };
let (wide, o_wide) = Float::from_rational_prec_ref(&term_abs, prec + 1);
let tie = rm_abs == Nearest && value_is_tie(&wide, o_wide, prec);
let (t, o) = Float::from_rational_prec_round(term_abs, prec, rm_abs);
let (t, o) = if beyond {
round_from_below(t, o, tie, rm_abs)
} else {
round_from_above(t, o, tie, rm_abs)
};
if positive { (t, o) } else { (-t, o.reverse()) }
}
pub(crate) fn round_rational_reciprocal_leading_term(
x: &Rational,
exp_x: i64,
beyond: bool,
prec: u64,
rm: RoundingMode,
) -> Option<(Float, Ordering)> {
let n = i64::exact_from(x.numerator_ref().significant_bits());
(exp_x > LEADING_TERM_MIN_EXPONENT
&& -exp_x > n + 2
&& -(exp_x << 1) > i64::exact_from(prec) + n + 4)
.then(|| round_rational_leading_term(x.abs().reciprocal(), *x > 0u32, beyond, prec, rm))
}