use core::cmp::Ordering::{self, *};
use malachite_base::assert_panic;
use malachite_base::num::arithmetic::traits::Factorial;
use malachite_base::num::basic::traits::Infinity;
use malachite_base::rounding_modes::RoundingMode::{self, *};
use malachite_base::rounding_modes::exhaustive::exhaustive_rounding_modes;
use malachite_float::test_util::common::{rug_round_try_from_rounding_mode, to_hex_string};
use malachite_float::test_util::float::arithmetic::factorial::rug_factorial_prec_round;
use malachite_float::test_util::generators::unsigned_unsigned_rounding_mode_triple_gen_var_11;
use malachite_float::{ComparableFloat, ComparableFloatRef, Float};
use malachite_nz::natural::Natural;
use std::panic::catch_unwind;
#[test]
fn test_factorial() {
let test = |n: u64, prec, rm: RoundingMode, out: &str, out_hex: &str, o_out: Ordering| {
let (f, o) = Float::factorial_prec_round(n, prec, rm);
assert!(f.is_valid());
assert_eq!(f.to_string(), out);
assert_eq!(to_hex_string(&f), out_hex);
assert_eq!(o, o_out);
let (f_alt, o_alt) = Float::from_natural_prec_round(Natural::factorial(n), prec, rm);
assert_eq!(ComparableFloatRef(&f_alt), ComparableFloatRef(&f));
assert_eq!(o_alt, o);
if rm == Nearest {
let (f_alt, o_alt) = Float::factorial_prec(n, prec);
assert_eq!(ComparableFloatRef(&f_alt), ComparableFloatRef(&f));
assert_eq!(o_alt, o);
}
if let Ok(rug_rm) = rug_round_try_from_rounding_mode(rm) {
let (rug_f, rug_o) = rug_factorial_prec_round(u32::try_from(n).unwrap(), prec, rug_rm);
assert_eq!(
ComparableFloatRef(&Float::from(&rug_f)),
ComparableFloatRef(&f)
);
assert_eq!(rug_o, o);
}
};
test(0, 10, Nearest, "1.0000", "0x1.000#10", Equal);
test(1, 10, Nearest, "1.0000", "0x1.000#10", Equal);
test(2, 10, Nearest, "2.0000", "0x2.00#10", Equal);
test(3, 1, Nearest, "8.0", "0x8.0#1", Greater);
test(4, 10, Nearest, "24.000", "0x18.00#10", Equal);
test(5, 4, Floor, "120.0", "0x78.0#4", Equal);
test(5, 4, Ceiling, "120.0", "0x78.0#4", Equal);
test(5, 4, Nearest, "120.0", "0x78.0#4", Equal);
test(5, 4, Down, "120.0", "0x78.0#4", Equal);
test(5, 4, Up, "120.0", "0x78.0#4", Equal);
test(5, 5, Exact, "120.0", "0x78.0#5", Equal);
test(10, 20, Nearest, "3628800.0", "0x375f00.0#20", Equal);
test(
20,
30,
Nearest,
"2.4329020103e18",
"0x2.1c3677dE+15#30",
Greater,
);
test(100, 10, Floor, "9.3318e157", "0x1.b30E+131#10", Less);
test(100, 10, Ceiling, "9.3426e157", "0x1.b38E+131#10", Greater);
test(
100,
100,
Nearest,
"9.3326215443944152681699238856278e157",
"0x1.b30964ec395dc24069528d54cE+131#100",
Greater,
);
test(1000, 2, Floor, "3.1e2567", "0x2.0E+2132#2", Less);
test(1000, 2, Ceiling, "4.6e2567", "0x3.0E+2132#2", Greater);
}
#[test]
fn test_factorial_overflow() {
for (rm, inf) in [(Nearest, true), (Up, true), (Ceiling, true), (Floor, false), (Down, false)] {
let (f, o) = Float::factorial_prec_round(u64::MAX, 10, rm);
if inf {
assert_eq!(ComparableFloat(f), ComparableFloat(Float::INFINITY));
assert_eq!(o, Greater);
} else {
assert_eq!(
ComparableFloat(f),
ComparableFloat(Float::max_finite_value_with_prec(10))
);
assert_eq!(o, Less);
}
}
}
#[test]
fn factorial_overflow_window_high() {
let (f, o) = Float::factorial_prec_round(60_000_000, 10, Nearest);
assert_eq!(ComparableFloat(f), ComparableFloat(Float::INFINITY));
assert_eq!(o, Greater);
let (f, o) = Float::factorial_prec_round(60_000_000, 10, Down);
assert_eq!(
ComparableFloat(f),
ComparableFloat(Float::max_finite_value_with_prec(10))
);
assert_eq!(o, Less);
}
#[test]
fn factorial_prec_round_properties() {
unsigned_unsigned_rounding_mode_triple_gen_var_11().test_properties(|(n, prec, rm)| {
let (f, o) = Float::factorial_prec_round(n, prec, rm);
assert!(f.is_valid());
let (f_alt, o_alt) = Float::from_natural_prec_round(Natural::factorial(n), prec, rm);
assert_eq!(ComparableFloatRef(&f_alt), ComparableFloatRef(&f));
assert_eq!(o_alt, o);
if f.is_normal() {
assert_eq!(f.get_prec(), Some(prec));
}
assert!(f > 0u32);
if let (Ok(rug_rm), Ok(n32)) = (rug_round_try_from_rounding_mode(rm), u32::try_from(n)) {
let (rug_f, rug_o) = rug_factorial_prec_round(n32, prec, rug_rm);
assert_eq!(
ComparableFloatRef(&Float::from(&rug_f)),
ComparableFloatRef(&f)
);
assert_eq!(rug_o, o);
}
if rm == Nearest {
let (f_alt, o_alt) = Float::factorial_prec(n, prec);
assert_eq!(ComparableFloatRef(&f_alt), ComparableFloatRef(&f));
assert_eq!(o_alt, o);
}
if o == Equal {
for rm2 in exhaustive_rounding_modes() {
let (f_alt, o_alt) = Float::factorial_prec_round(n, prec, rm2);
assert_eq!(ComparableFloatRef(&f_alt), ComparableFloatRef(&f));
assert_eq!(o_alt, Equal);
}
} else {
assert_panic!(Float::factorial_prec_round(n, prec, Exact));
}
});
}
#[test]
fn factorial_fail() {
assert_panic!(Float::factorial_prec_round(5, 0, Nearest));
assert_panic!(Float::factorial_prec(5, 0));
assert_panic!(Float::factorial_prec_round(5, 3, Exact));
}
#[test]
fn test_primitive_float_factorial_tables() {
use malachite_base::num::conversion::traits::RoundingFrom;
use malachite_base::num::float::NiceFloat;
for n in 0u64..=200 {
assert_eq!(
NiceFloat(f64::factorial(n)),
NiceFloat(f64::rounding_from(&Natural::factorial(n), Nearest).0),
"{n}"
);
}
assert_eq!(f64::factorial(u64::MAX), f64::INFINITY);
for n in 0u64..=60 {
assert_eq!(
NiceFloat(f32::factorial(n)),
NiceFloat(f32::rounding_from(&Natural::factorial(n), Nearest).0),
"{n}"
);
}
assert_eq!(f32::factorial(u64::MAX), f32::INFINITY);
}