use core::cmp::Ordering::{self, *};
use malachite_base::num::arithmetic::traits::PowerOf2;
use malachite_base::num::conversion::traits::ExactFrom;
use malachite_base::num::logic::traits::SignificantBits;
use malachite_base::rounding_modes::RoundingMode::{self, *};
use malachite_float::test_util::common::{
parse_hex_string, rug_round_try_from_rounding_mode, to_hex_string,
};
use malachite_float::{ComparableFloat, ComparableFloatRef, Float};
use malachite_nz::natural::Natural;
#[test]
fn test_subnormalize_vs_rug() {
let normal_exp_min = -20i64;
for prec in [1u64, 2, 3, 5, 10, 24] {
let sub_exp_min = normal_exp_min - i64::exact_from(prec) + 1;
for exp in (sub_exp_min - 3)..=(normal_exp_min + 2) {
let mut significands = vec![
Natural::power_of_2(prec - 1),
(Natural::power_of_2(prec)) - Natural::power_of_2(0u64),
];
for tail in 0..prec {
significands.push(Natural::power_of_2(prec - 1) + Natural::power_of_2(tail));
}
if prec >= 3 {
significands.push(
Natural::power_of_2(prec - 1)
+ Natural::power_of_2(prec - 3)
+ Natural::power_of_2(0u64),
);
for tail in 1..prec - 1 {
significands.push(
Natural::power_of_2(prec - 1)
+ Natural::power_of_2(prec - 1 - tail)
+ Natural::power_of_2(prec - 2 - tail),
);
}
}
for significand in significands {
for sign in [false, true] {
for o in [Less, Equal, Greater] {
for rm in [Floor, Ceiling, Down, Up, Nearest] {
let x = Float::from_natural_prec(significand.clone(), prec).0
<< (exp - i64::exact_from(significand.significant_bits()));
let x = if sign { -x } else { x };
let (ours, o_ours) = x.subnormalize_ref(o, normal_exp_min, rm);
let (ours_val, o_val) = x.clone().subnormalize(o, normal_exp_min, rm);
assert_eq!(ComparableFloat(ours_val), ComparableFloat(ours.clone()));
assert_eq!(o_val, o_ours);
let mut ours_assign = x.clone();
let o_assign = ours_assign.subnormalize_assign(o, normal_exp_min, rm);
assert_eq!(ComparableFloat(ours_assign), ComparableFloat(ours.clone()));
assert_eq!(o_assign, o_ours);
let mut theirs = rug::Float::exact_from(&x);
let o_theirs = theirs.subnormalize_round(
i32::exact_from(normal_exp_min),
o,
rug_round_try_from_rounding_mode(rm).unwrap(),
);
assert_eq!(
ComparableFloat(
Float::from_float_prec(Float::from(&theirs), prec).0
),
ComparableFloat(ours.clone()),
"{prec} {exp} {significand} {sign} {o:?} {rm}"
);
assert_eq!(
o_theirs, o_ours,
"ternary: {prec} {exp} {significand} {sign} {o:?} {rm}"
);
assert!(ours.is_valid());
}
}
}
}
}
}
}
#[test]
fn subnormalize_fail() {
let x = Float::from_natural_prec(Natural::from(0b101u32), 3).0 >> 25u64;
assert!(std::panic::catch_unwind(|| x.subnormalize_ref(Equal, -20, Exact)).is_err());
}
#[test]
fn subnormalize_properties_like() {
let normal_exp_min = -20i64;
for prec in [1u64, 3, 10] {
let sub_exp_min = normal_exp_min - i64::exact_from(prec) + 1;
for exp in (sub_exp_min - 2)..=(normal_exp_min + 1) {
for tail in 0..prec {
let significand = Natural::power_of_2(prec - 1) + Natural::power_of_2(tail);
for rm in [Floor, Ceiling, Down, Up, Nearest] {
let x = Float::from_natural_prec(significand.clone(), prec).0
<< (exp - i64::exact_from(significand.significant_bits()));
let (y, o2) = x.subnormalize_ref(Less, normal_exp_min, rm);
if y != 0u32 {
assert_eq!(y.get_prec(), Some(prec));
}
let (z, o3) = y.subnormalize_ref(o2, normal_exp_min, rm);
assert_eq!(ComparableFloat(z), ComparableFloat(y.clone()));
assert_eq!(o3, o2);
if let Some(e) = y.get_exponent() {
let avail = i64::from(e) - sub_exp_min + 1;
assert!(
i64::exact_from(y.get_min_prec().unwrap())
<= avail.min(i64::exact_from(prec))
);
}
}
}
}
}
}
#[test]
fn test_subnormalize() {
let test = |s,
s_hex,
o_in: Ordering,
normal_exp_min: i64,
rm: RoundingMode,
out: &str,
out_hex: &str,
o_out: Ordering| {
let x = parse_hex_string(s_hex);
assert_eq!(x.to_string(), s);
let (r, o) = x.clone().subnormalize(o_in, normal_exp_min, rm);
assert!(r.is_valid());
assert_eq!(r.to_string(), out);
assert_eq!(to_hex_string(&r), out_hex);
assert_eq!(o, o_out);
let (r_alt, o_alt) = x.subnormalize_ref(o_in, normal_exp_min, rm);
assert!(r_alt.is_valid());
assert_eq!(ComparableFloatRef(&r_alt), ComparableFloatRef(&r));
assert_eq!(o_alt, o);
let mut x_alt = x.clone();
let o_alt = x_alt.subnormalize_assign(o_in, normal_exp_min, rm);
assert!(x_alt.is_valid());
assert_eq!(ComparableFloatRef(&x_alt), ComparableFloatRef(&r));
assert_eq!(o_alt, o);
};
test(
"1.5000",
"0x1.800#10",
Equal,
0,
Nearest,
"1.5000",
"0x1.800#10",
Equal,
);
test(
"1.5000",
"0x1.800#10",
Equal,
5,
Nearest,
"1.5000",
"0x1.800#10",
Equal,
);
test(
"1.5000",
"0x1.800#10",
Equal,
8,
Nearest,
"1.5000",
"0x1.800#10",
Equal,
);
test(
"1.5000",
"0x1.800#10",
Equal,
5,
Floor,
"1.5000",
"0x1.800#10",
Equal,
);
test(
"-1.5000",
"-0x1.800#10",
Equal,
5,
Nearest,
"-1.5000",
"-0x1.800#10",
Equal,
);
test(
"10.31", "0xa.50#9", Less, 6, Nearest, "10.38", "0xa.60#9", Greater,
);
test(
"10.31", "0xa.50#9", Greater, 6, Nearest, "10.25", "0xa.40#9", Less,
);
test("2.0", "0x2.0#1", Equal, 0, Nearest, "2.0", "0x2.0#1", Equal);
test("NaN", "NaN", Equal, 5, Nearest, "NaN", "NaN", Equal);
test(
"Infinity", "Infinity", Equal, 5, Nearest, "Infinity", "Infinity", Equal,
);
test("0.0", "0x0.0", Equal, 5, Nearest, "0.0", "0x0.0", Equal);
}