use crate::InnerFloat::{Finite, Infinity, NaN, Zero};
use crate::{ComparableFloatRef, Float, significand_bits};
use malachite_base::num::arithmetic::traits::{Abs, PowerOf2};
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, NegativeInfinity, NegativeZero, Zero as ZeroTrait,
};
use malachite_base::num::conversion::traits::{ExactFrom, FromStringBase, RoundingFrom};
use malachite_base::num::logic::traits::{BitAccess, SignificantBits};
use malachite_base::rounding_modes::RoundingMode::{self, *};
use malachite_nz::natural::Natural;
use malachite_nz::platform::Limb;
use malachite_q::Rational;
use rug::float::{Round, Special};
use std::cmp::Ordering;
use std::sync::{Mutex, MutexGuard, PoisonError};
pub const fn rounding_mode_from_rug_round(rm: Round) -> RoundingMode {
match rm {
Round::Nearest => Nearest,
Round::Zero => Down,
Round::Up => Ceiling,
Round::Down => Floor,
Round::AwayZero => Up,
_ => panic!(),
}
}
#[allow(clippy::result_unit_err)]
pub const fn rug_round_try_from_rounding_mode(rm: RoundingMode) -> Result<Round, ()> {
match rm {
Floor => Ok(Round::Down),
Ceiling => Ok(Round::Up),
Down => Ok(Round::Zero),
Up => Ok(Round::AwayZero),
Nearest => Ok(Round::Nearest),
Exact => Err(()),
}
}
#[inline]
pub fn rug_round_exact_from_rounding_mode(rm: RoundingMode) -> Round {
rug_round_try_from_rounding_mode(rm).unwrap()
}
impl From<&rug::Float> for Float {
fn from(x: &rug::Float) -> Self {
if x.is_nan() {
Self::NAN
} else if x.is_infinite() {
if x.is_sign_positive() {
Self::INFINITY
} else {
Self::NEGATIVE_INFINITY
}
} else if x.is_zero() {
if x.is_sign_positive() {
Self::ZERO
} else {
Self::NEGATIVE_ZERO
}
} else {
let mut significand = Natural::exact_from(&*x.get_significand().unwrap());
let precision = u64::from(x.prec());
if significand.significant_bits() - precision >= Limb::WIDTH {
significand >>= Limb::WIDTH;
}
let result = Self(Finite {
sign: x.is_sign_positive(),
exponent: x.get_exp().unwrap(),
precision,
significand,
});
assert!(result.is_valid());
result
}
}
}
fn convert_prec(prec: u64) -> Result<u32, ()> {
u32::try_from(prec).map_err(|_| ())
}
#[allow(clippy::unnecessary_wraps)]
fn special_float(prec: u32, value: Special) -> Result<rug::Float, ()> {
Ok(rug::Float::with_val_round(prec, value, Round::Zero).0)
}
pub fn rug_float_significant_bits(x: &rug::Float) -> u64 {
if x.is_normal() {
u64::from(x.prec())
} else {
1
}
}
impl TryFrom<&Float> for rug::Float {
type Error = ();
fn try_from(x: &Float) -> Result<Self, ()> {
match x {
float_nan!() => special_float(1, Special::Nan),
float_infinity!() => special_float(1, Special::Infinity),
float_negative_infinity!() => special_float(1, Special::NegInfinity),
float_zero!() => special_float(1, Special::Zero),
float_negative_zero!() => special_float(1, Special::NegZero),
Float(Finite {
sign,
exponent,
precision,
significand,
}) => {
let bits = i64::exact_from(significand_bits(significand));
let e = i64::from(*exponent) - bits;
let prec = convert_prec(*precision)?;
let mut f = if bits < i64::from(Float::MAX_EXPONENT) && i32::try_from(-e).is_ok() {
let mut f =
Self::with_val_round(prec, rug::Integer::from(significand), Round::Zero).0;
f >>= i32::try_from(-e).map_err(|_| ())?;
f
} else {
let z = rug::Integer::from(significand);
let q = if e >= 0 {
rug::Rational::from(z << u32::try_from(e).map_err(|_| ())?)
} else {
rug::Rational::from((
z,
rug::Integer::from(1) << u32::try_from(-e).map_err(|_| ())?,
))
};
Self::with_val_round(prec, &q, Round::Zero).0
};
if !sign {
f = -f;
}
Ok(f)
}
}
}
}
pub fn assert_rounding_ordering_consistent(x: &Float, rm: RoundingMode, o: Ordering) {
if x.is_nan() {
assert_eq!(o, Ordering::Equal);
return;
}
assert_rounding_ordering_consistent_for_sign(!x.is_sign_negative(), rm, o);
}
pub fn assert_rounding_ordering_consistent_for_sign(
non_negative: bool,
rm: RoundingMode,
o: Ordering,
) {
match (non_negative, rm) {
(_, Floor) | (true, Down) | (false, Up) => assert_ne!(o, Ordering::Greater),
(_, Ceiling) | (true, Up) | (false, Down) => assert_ne!(o, Ordering::Less),
(_, Exact) => assert_eq!(o, Ordering::Equal),
_ => {}
}
}
pub const EXPONENT_GATE: i64 = 1 << 16;
pub fn exponent_in_gate(x: &Float) -> bool {
x.get_exponent()
.is_none_or(|e| i64::from(e).abs() < EXPONENT_GATE)
}
pub fn parse_hex_string(s_hex: &str) -> Float {
let x = Float::from_string_base(16, s_hex).unwrap();
assert_eq!(format!("{:#x}", ComparableFloatRef(&x)), s_hex);
x
}
pub fn to_hex_string(x: &Float) -> String {
format!("{:#x}", ComparableFloatRef(x))
}
pub const ORDERED_FLOAT_STRINGS: [&str; 21] = [
"-Infinity",
"-3.1415926535897931",
"-2.0",
"-1.4142135623730951",
"-1.0000000000000000000000000000000",
"-1.0",
"-1.0",
"-0.50",
"-0.33333333333333331",
"-0.0",
"NaN",
"0.0",
"0.33333333333333331",
"0.50",
"1.0",
"1.0",
"1.0000000000000000000000000000000",
"1.4142135623730951",
"2.0",
"3.1415926535897931",
"Infinity",
];
pub const ORDERED_FLOAT_HEX_STRINGS: [&str; 21] = [
"-Infinity",
"-0x3.243f6a8885a30#53",
"-0x2.0#1",
"-0x1.6a09e667f3bcd#53",
"-0x1.0000000000000000000000000#100",
"-0x1.0#2",
"-0x1.0#1",
"-0x0.8#1",
"-0x0.55555555555554#53",
"-0x0.0",
"NaN",
"0x0.0",
"0x0.55555555555554#53",
"0x0.8#1",
"0x1.0#1",
"0x1.0#2",
"0x1.0000000000000000000000000#100",
"0x1.6a09e667f3bcd#53",
"0x2.0#1",
"0x3.243f6a8885a30#53",
"Infinity",
];
pub const ORDERED_F32S: [f32; 17] = [
f32::NEGATIVE_INFINITY,
-std::f32::consts::PI,
-2.0,
-std::f32::consts::SQRT_2,
-1.0,
-0.5,
-1.0 / 3.0,
-0.0,
f32::NAN,
0.0,
1.0 / 3.0,
0.5,
1.0,
std::f32::consts::SQRT_2,
2.0,
std::f32::consts::PI,
f32::INFINITY,
];
pub const ORDERED_F64S: [f64; 17] = [
f64::NEGATIVE_INFINITY,
-std::f64::consts::PI,
-2.0,
-std::f64::consts::SQRT_2,
-1.0,
-0.5,
-1.0 / 3.0,
-0.0,
f64::NAN,
0.0,
1.0 / 3.0,
0.5,
1.0,
std::f64::consts::SQRT_2,
2.0,
std::f64::consts::PI,
f64::INFINITY,
];
pub fn test_constant<F: Fn(u64, RoundingMode) -> (Float, Ordering)>(f: F, limit: u64) {
let mut bit_index = Limb::WIDTH - 1;
let mut significand = Natural::ZERO;
for prec in 1..limit {
let x = f(prec, Floor).0;
let x_sig = x.significand_ref().unwrap();
if *x_sig != significand {
significand.set_bit(bit_index);
assert_eq!(*x_sig, significand);
}
if bit_index == 0 {
significand <<= Limb::WIDTH;
bit_index = Limb::WIDTH - 1;
} else {
bit_index -= 1;
}
}
}
pub fn rug_rational_fn_prec_round<F: Fn(&rug::Float, &mut rug::Float, Round) -> Ordering>(
x: &Rational,
prec: u64,
rm: Round,
exponent_multiplier: u64,
f: F,
) -> (rug::Float, Ordering) {
let exponent_bits = if *x == 0u32 {
0
} else {
x.floor_log_base_2_abs().unsigned_abs()
};
let rx = rug::Float::with_val(
u32::exact_from(prec + 128 + exponent_multiplier * exponent_bits),
rug::Rational::exact_from(x),
);
let mut c = rug::Float::with_val(u32::exact_from(prec), 0);
let o = f(&rx, &mut c, rm);
(c, o)
}
#[allow(clippy::type_repetition_in_bounds)]
pub fn round_once_to_primitive<T: PrimitiveFloat, F: FnMut(u64) -> Float>(mut f: F) -> T
where
for<'a> T: RoundingFrom<&'a Float>,
{
let approx = f(T::MANTISSA_WIDTH + 64);
if !approx.is_normal() {
return T::rounding_from(&approx, Nearest).0;
}
let e = i64::from(approx.get_exponent().unwrap());
if e <= T::MIN_EXPONENT {
let half = Float::power_of_2(T::MIN_EXPONENT - 1);
let mut p = T::MANTISSA_WIDTH + 64;
loop {
let v = f(p);
let v_abs = (&v).abs();
if v_abs != half {
let t = if v_abs > half {
T::MIN_POSITIVE_SUBNORMAL
} else {
T::ZERO
};
return if v.is_sign_negative() { -t } else { t };
}
p <<= 1;
}
}
let p = u64::exact_from((e - T::MIN_EXPONENT).min(i64::exact_from(T::MANTISSA_WIDTH + 1)));
T::rounding_from(&f(p), Nearest).0
}
static HUGE_PI_TEST_LOCK: Mutex<()> = Mutex::new(());
pub fn huge_pi_test_guard() -> MutexGuard<'static, ()> {
HUGE_PI_TEST_LOCK
.lock()
.unwrap_or_else(PoisonError::into_inner)
}