use crate::Float;
use core::cmp::Ordering::{self, *};
use malachite_base::num::arithmetic::traits::PowerOf2;
use malachite_base::num::conversion::traits::ExactFrom;
use malachite_base::rounding_modes::RoundingMode::{self, *};
use malachite_nz::natural::arithmetic::float_extras::float_can_round;
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_test! {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))
}}