// Copyright © 2024 Mikhail Hogrefe
//
// This file is part of Malachite.
//
// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
// 3 of the License, or (at your option) any later version. See .
use malachite_base::num::arithmetic::traits::Parity;
use malachite_base::num::basic::floats::PrimitiveFloat;
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::basic::signeds::PrimitiveSigned;
use malachite_base::num::basic::traits::NegativeInfinity;
use malachite_base::num::basic::unsigneds::PrimitiveUnsigned;
use malachite_base::num::conversion::traits::{ConvertibleFrom, RoundingFrom, WrappingFrom};
use malachite_base::num::float::NiceFloat;
use malachite_base::rounding_modes::exhaustive::exhaustive_rounding_modes;
use malachite_base::rounding_modes::RoundingMode::{self, *};
use malachite_base::test_util::generators::{
primitive_float_gen_var_13, primitive_float_gen_var_14, primitive_float_gen_var_15,
primitive_float_gen_var_16, primitive_float_gen_var_17,
primitive_float_rounding_mode_pair_gen_var_3, signed_gen_var_7, signed_gen_var_8,
signed_gen_var_9, signed_rounding_mode_pair_gen_var_4, unsigned_gen_var_18,
unsigned_gen_var_19, unsigned_gen_var_20, unsigned_rounding_mode_pair_gen_var_2,
};
use std::cmp::Ordering::{self, *};
use std::panic::catch_unwind;
#[test]
pub fn test_rounding_from() {
fn test_from_floating_point>(
n_in: T,
rm: RoundingMode,
n_out: U,
o: Ordering,
) {
assert_eq!(U::rounding_from(n_in, rm), (n_out, o));
}
test_from_floating_point::(0.0, Down, 0, Equal);
test_from_floating_point::(0.0, Floor, 0, Equal);
test_from_floating_point::(0.0, Up, 0, Equal);
test_from_floating_point::(0.0, Ceiling, 0, Equal);
test_from_floating_point::(0.0, Nearest, 0, Equal);
test_from_floating_point::(0.0, Exact, 0, Equal);
test_from_floating_point::(-0.0, Down, 0, Equal);
test_from_floating_point::(-0.0, Floor, 0, Equal);
test_from_floating_point::(-0.0, Up, 0, Equal);
test_from_floating_point::(-0.0, Ceiling, 0, Equal);
test_from_floating_point::(-0.0, Nearest, 0, Equal);
test_from_floating_point::(-0.0, Exact, 0, Equal);
test_from_floating_point::(100.0, Down, 100, Equal);
test_from_floating_point::(100.0, Floor, 100, Equal);
test_from_floating_point::(100.0, Up, 100, Equal);
test_from_floating_point::(100.0, Ceiling, 100, Equal);
test_from_floating_point::(100.0, Nearest, 100, Equal);
test_from_floating_point::(100.0, Exact, 100, Equal);
test_from_floating_point::(100.1, Down, 100, Less);
test_from_floating_point::(100.1, Floor, 100, Less);
test_from_floating_point::(100.1, Up, 101, Greater);
test_from_floating_point::(100.1, Ceiling, 101, Greater);
test_from_floating_point::(100.1, Nearest, 100, Less);
test_from_floating_point::(100.9, Down, 100, Less);
test_from_floating_point::(100.9, Floor, 100, Less);
test_from_floating_point::(100.9, Up, 101, Greater);
test_from_floating_point::(100.9, Ceiling, 101, Greater);
test_from_floating_point::(100.9, Nearest, 101, Greater);
test_from_floating_point::(100.5, Down, 100, Less);
test_from_floating_point::(100.5, Floor, 100, Less);
test_from_floating_point::(100.5, Up, 101, Greater);
test_from_floating_point::(100.5, Ceiling, 101, Greater);
test_from_floating_point::(100.5, Nearest, 100, Less);
test_from_floating_point::(101.5, Down, 101, Less);
test_from_floating_point::(101.5, Floor, 101, Less);
test_from_floating_point::(101.5, Up, 102, Greater);
test_from_floating_point::(101.5, Ceiling, 102, Greater);
test_from_floating_point::(101.5, Nearest, 102, Greater);
test_from_floating_point::(256.0, Down, 255, Less);
test_from_floating_point::(256.0, Floor, 255, Less);
test_from_floating_point::(256.0, Nearest, 255, Less);
test_from_floating_point::(-100.0, Down, 0, Greater);
test_from_floating_point::(-100.0, Ceiling, 0, Greater);
test_from_floating_point::(-100.0, Nearest, 0, Greater);
test_from_floating_point::(128.0, Down, 127, Less);
test_from_floating_point::(128.0, Floor, 127, Less);
test_from_floating_point::(128.0, Nearest, 127, Less);
test_from_floating_point::(-129.0, Down, -128, Greater);
test_from_floating_point::(-129.0, Ceiling, -128, Greater);
test_from_floating_point::(-129.0, Nearest, -128, Greater);
test_from_floating_point::(f32::INFINITY, Down, 255, Less);
test_from_floating_point::(f32::INFINITY, Floor, 255, Less);
test_from_floating_point::(f32::INFINITY, Nearest, 255, Less);
test_from_floating_point::(f32::NEGATIVE_INFINITY, Down, 0, Greater);
test_from_floating_point::(f32::NEGATIVE_INFINITY, Ceiling, 0, Greater);
test_from_floating_point::(f32::NEGATIVE_INFINITY, Nearest, 0, Greater);
test_from_floating_point::(f32::INFINITY, Down, 127, Less);
test_from_floating_point::(f32::INFINITY, Floor, 127, Less);
test_from_floating_point::(f32::INFINITY, Nearest, 127, Less);
test_from_floating_point::(f32::NEGATIVE_INFINITY, Down, -128, Greater);
test_from_floating_point::(f32::NEGATIVE_INFINITY, Ceiling, -128, Greater);
test_from_floating_point::(f32::NEGATIVE_INFINITY, Nearest, -128, Greater);
fn test_from_primitive_int>(
n_in: T,
rm: RoundingMode,
n_out: U,
o: Ordering,
) {
let (x, actual_o) = U::rounding_from(n_in, rm);
assert_eq!((NiceFloat(x), actual_o), (NiceFloat(n_out), o));
}
test_from_primitive_int::(0, Down, 0.0, Equal);
test_from_primitive_int::(0, Floor, 0.0, Equal);
test_from_primitive_int::(0, Up, 0.0, Equal);
test_from_primitive_int::(0, Ceiling, 0.0, Equal);
test_from_primitive_int::(0, Nearest, 0.0, Equal);
test_from_primitive_int::(0, Exact, 0.0, Equal);
test_from_primitive_int::(100, Down, 100.0, Equal);
test_from_primitive_int::(100, Floor, 100.0, Equal);
test_from_primitive_int::(100, Up, 100.0, Equal);
test_from_primitive_int::(100, Ceiling, 100.0, Equal);
test_from_primitive_int::(100, Nearest, 100.0, Equal);
test_from_primitive_int::(100, Exact, 100.0, Equal);
test_from_primitive_int::(-100, Down, -100.0, Equal);
test_from_primitive_int::(-100, Floor, -100.0, Equal);
test_from_primitive_int::(-100, Up, -100.0, Equal);
test_from_primitive_int::(-100, Ceiling, -100.0, Equal);
test_from_primitive_int::(-100, Nearest, -100.0, Equal);
test_from_primitive_int::(-100, Exact, -100.0, Equal);
test_from_primitive_int::(i32::MIN, Down, -2147483600.0, Equal);
test_from_primitive_int::(i32::MIN, Floor, -2147483600.0, Equal);
test_from_primitive_int::(i32::MIN, Up, -2147483600.0, Equal);
test_from_primitive_int::(i32::MIN, Ceiling, -2147483600.0, Equal);
test_from_primitive_int::(i32::MIN, Nearest, -2147483600.0, Equal);
test_from_primitive_int::(i32::MIN, Exact, -2147483600.0, Equal);
test_from_primitive_int::(i32::MAX, Down, 2147483500.0, Less);
test_from_primitive_int::(i32::MAX, Floor, 2147483500.0, Less);
test_from_primitive_int::(i32::MAX, Up, 2147483600.0, Greater);
test_from_primitive_int::(i32::MAX, Ceiling, 2147483600.0, Greater);
test_from_primitive_int::(i32::MAX, Nearest, 2147483600.0, Greater);
test_from_primitive_int::(u128::MAX, Down, 3.4028235e38, Less);
test_from_primitive_int::(u128::MAX, Floor, 3.4028235e38, Less);
test_from_primitive_int::(u128::MAX, Up, f32::INFINITY, Greater);
test_from_primitive_int::(u128::MAX, Ceiling, f32::INFINITY, Greater);
test_from_primitive_int::(u128::MAX, Nearest, 3.4028235e38, Less);
}
#[test]
fn exact_from_fail() {
assert_panic!(u8::rounding_from(100.1f32, Exact));
assert_panic!(u8::rounding_from(256.0f32, Exact));
assert_panic!(u8::rounding_from(256.0f32, Up));
assert_panic!(u8::rounding_from(256.0f32, Ceiling));
assert_panic!(u8::rounding_from(-100.0f32, Exact));
assert_panic!(u8::rounding_from(-100.0f32, Up));
assert_panic!(u8::rounding_from(-100.0f32, Floor));
assert_panic!(i8::rounding_from(128.0f32, Exact));
assert_panic!(i8::rounding_from(128.0f32, Up));
assert_panic!(i8::rounding_from(128.0f32, Ceiling));
assert_panic!(i8::rounding_from(-129.0f32, Exact));
assert_panic!(i8::rounding_from(-129.0f32, Up));
assert_panic!(i8::rounding_from(-129.0f32, Floor));
assert_panic!(u8::rounding_from(f32::NAN, Down));
assert_panic!(u8::rounding_from(f32::NAN, Floor));
assert_panic!(u8::rounding_from(f32::NAN, Up));
assert_panic!(u8::rounding_from(f32::NAN, Ceiling));
assert_panic!(u8::rounding_from(f32::NAN, Nearest));
assert_panic!(u8::rounding_from(f32::NAN, Exact));
assert_panic!(u8::rounding_from(f32::INFINITY, Up));
assert_panic!(u8::rounding_from(f32::INFINITY, Ceiling));
assert_panic!(u8::rounding_from(f32::INFINITY, Exact));
assert_panic!(u8::rounding_from(f32::NEGATIVE_INFINITY, Up));
assert_panic!(u8::rounding_from(f32::NEGATIVE_INFINITY, Floor));
assert_panic!(u8::rounding_from(f32::NEGATIVE_INFINITY, Exact));
assert_panic!(i8::rounding_from(f32::INFINITY, Up));
assert_panic!(i8::rounding_from(f32::INFINITY, Ceiling));
assert_panic!(i8::rounding_from(f32::INFINITY, Exact));
assert_panic!(i8::rounding_from(f32::NEGATIVE_INFINITY, Up));
assert_panic!(i8::rounding_from(f32::NEGATIVE_INFINITY, Floor));
assert_panic!(i8::rounding_from(f32::NEGATIVE_INFINITY, Exact));
assert_panic!(u8::rounding_from(f32::NAN, Down));
assert_panic!(f32::rounding_from(u32::MAX, Exact));
assert_panic!(f32::rounding_from(u128::MAX, Exact));
}
fn rounding_from_helper_unsigned_primitive_float<
T: ConvertibleFrom + PrimitiveUnsigned + RoundingFrom,
U: PrimitiveFloat + RoundingFrom,
>()
where
NiceFloat: TryFrom,
{
primitive_float_rounding_mode_pair_gen_var_3::().test_properties(|(f, rm)| {
let (rounded, o) = T::rounding_from(f, rm);
match (f >= U::ZERO, rm) {
(_, Floor) | (true, Down) | (false, Up) => {
assert_ne!(o, Greater);
}
(_, Ceiling) | (true, Up) | (false, Down) => {
assert_ne!(o, Less);
}
(_, Exact) => assert_eq!(o, Equal),
_ => {}
}
if o == Equal {
for rm in exhaustive_rounding_modes() {
assert_eq!(T::rounding_from(f, rm), (rounded, Equal));
}
} else {
assert_panic!(T::rounding_from(f, Exact));
}
});
primitive_float_gen_var_13::().test_properties(|f| {
let no = T::rounding_from(f, Exact);
assert_eq!(no, T::rounding_from(f, Floor));
assert_eq!(no, T::rounding_from(f, Ceiling));
assert_eq!(no, T::rounding_from(f, Down));
assert_eq!(no, T::rounding_from(f, Up));
assert_eq!(no, T::rounding_from(f, Nearest));
});
let f_max = U::rounding_from(T::MAX, Down).0;
primitive_float_gen_var_15::().test_properties(|f| {
if f >= U::ZERO && f <= f_max {
let n_floor = T::rounding_from(f, Floor);
assert_eq!(n_floor.1, Less);
if let Some(n_ceiling) = n_floor.0.checked_add(T::ONE) {
let n_ceiling = (n_ceiling, Greater);
assert_eq!(n_ceiling, T::rounding_from(f, Ceiling));
assert_eq!(n_floor, T::rounding_from(f, Down));
assert_eq!(n_ceiling, T::rounding_from(f, Up));
let n_nearest = T::rounding_from(f, Nearest);
assert!(n_nearest == n_floor || n_nearest == n_ceiling);
}
}
});
primitive_float_gen_var_16::().test_properties(|f| {
let floor = T::rounding_from(f, Floor);
let ceiling = (floor.0 + T::ONE, Greater);
let nearest = T::rounding_from(f, Nearest);
assert_eq!(nearest, if floor.0.even() { floor } else { ceiling });
});
}
fn rounding_from_helper_signed_primitive_float<
T: ConvertibleFrom + PrimitiveSigned + RoundingFrom,
U: PrimitiveFloat + RoundingFrom,
>()
where
NiceFloat: TryFrom,
{
primitive_float_rounding_mode_pair_gen_var_3::().test_properties(|(f, rm)| {
let (rounded, o) = T::rounding_from(f, rm);
match (f >= U::ZERO, rm) {
(_, Floor) | (true, Down) | (false, Up) => {
assert_ne!(o, Greater);
}
(_, Ceiling) | (true, Up) | (false, Down) => {
assert_ne!(o, Less);
}
(_, Exact) => assert_eq!(o, Equal),
_ => {}
}
if o == Equal {
for rm in exhaustive_rounding_modes() {
assert_eq!(T::rounding_from(f, rm), (rounded, Equal));
}
} else {
assert_panic!(T::rounding_from(f, Exact));
}
});
primitive_float_gen_var_14::().test_properties(|f| {
let no = T::rounding_from(f, Exact);
assert_eq!(no, T::rounding_from(f, Floor));
assert_eq!(no, T::rounding_from(f, Ceiling));
assert_eq!(no, T::rounding_from(f, Down));
assert_eq!(no, T::rounding_from(f, Up));
assert_eq!(no, T::rounding_from(f, Nearest));
});
let f_min = U::rounding_from(T::MIN, Down).0;
let f_max = U::rounding_from(T::MAX, Down).0;
primitive_float_gen_var_15::().test_properties(|f| {
if f >= f_min && f <= f_max {
let n_floor = T::rounding_from(f, Floor);
if let Some(n_ceiling) = n_floor.0.checked_add(T::ONE) {
let n_ceiling = (n_ceiling, Greater);
assert_eq!(n_ceiling, T::rounding_from(f, Ceiling));
if f >= U::ZERO {
assert_eq!(n_floor, T::rounding_from(f, Down));
assert_eq!(n_ceiling, T::rounding_from(f, Up));
} else {
assert_eq!(n_ceiling, T::rounding_from(f, Down));
assert_eq!(n_floor, T::rounding_from(f, Up));
}
let n_nearest = T::rounding_from(f, Nearest);
assert!(n_nearest == n_floor || n_nearest == n_ceiling);
}
}
});
primitive_float_gen_var_17::().test_properties(|f| {
let floor = T::rounding_from(f, Floor);
let ceiling = (floor.0 + T::ONE, Greater);
let nearest = T::rounding_from(f, Nearest);
assert_eq!(nearest, if floor.0.even() { floor } else { ceiling });
});
}
fn rounding_from_helper_primitive_float_unsigned<
T: ConvertibleFrom + PrimitiveFloat + RoundingFrom,
U: TryFrom> + PrimitiveUnsigned + RoundingFrom,
>() {
unsigned_rounding_mode_pair_gen_var_2::().test_properties(|(u, rm)| {
let (rounded, o) = T::rounding_from(u, rm);
match rm {
Floor | Down => assert_ne!(o, Greater),
Ceiling | Up => assert_ne!(o, Less),
Exact => assert_eq!(o, Equal),
_ => {}
}
if o == Equal {
for rm in exhaustive_rounding_modes() {
assert_eq!(T::rounding_from(u, rm), (rounded, Equal));
}
} else {
assert_panic!(T::rounding_from(u, Exact));
}
});
unsigned_gen_var_18::().test_properties(|u| {
let (f, o) = T::rounding_from(u, Exact);
let (f_alt, o_alt) = T::rounding_from(u, Floor);
assert_eq!(NiceFloat(f_alt), NiceFloat(f));
assert_eq!(o_alt, o);
let (f_alt, o_alt) = T::rounding_from(u, Ceiling);
assert_eq!(NiceFloat(f_alt), NiceFloat(f));
assert_eq!(o_alt, o);
let (f_alt, o_alt) = T::rounding_from(u, Down);
assert_eq!(NiceFloat(f_alt), NiceFloat(f));
assert_eq!(o_alt, o);
let (f_alt, o_alt) = T::rounding_from(u, Up);
assert_eq!(NiceFloat(f_alt), NiceFloat(f));
assert_eq!(o_alt, o);
let (f_alt, o_alt) = T::rounding_from(u, Nearest);
assert_eq!(NiceFloat(f_alt), NiceFloat(f));
assert_eq!(o_alt, o);
assert_eq!(U::rounding_from(f, Exact), (u, Equal));
});
if U::WIDTH > T::MANTISSA_WIDTH {
unsigned_gen_var_19::().test_properties(|u| {
let (f_below, o) = T::rounding_from(u, Floor);
assert_eq!(o, Less);
let f_above = f_below.next_higher();
let (f_alt, o) = T::rounding_from(u, Ceiling);
assert_eq!(NiceFloat(f_alt), NiceFloat(f_above));
assert_eq!(o, Greater);
let (f_alt, o) = T::rounding_from(u, Down);
assert_eq!(NiceFloat(f_alt), NiceFloat(f_below));
assert_eq!(o, Less);
let (f_alt, o) = T::rounding_from(u, Up);
assert_eq!(NiceFloat(f_alt), NiceFloat(f_above));
assert_eq!(o, Greater);
let (f_nearest, o) = T::rounding_from(u, Nearest);
assert!(
(NiceFloat(f_nearest), o) == (NiceFloat(f_below), Less)
|| (NiceFloat(f_nearest), o) == (NiceFloat(f_above), Greater)
);
});
unsigned_gen_var_20::().test_properties(|u| {
let (floor, o) = T::rounding_from(u, Floor);
assert_eq!(o, Less);
let ceiling = floor.next_higher();
let (nearest, o) = T::rounding_from(u, Nearest);
assert_eq!(
(NiceFloat(nearest), o),
if floor.to_bits().even() {
(NiceFloat(floor), Less)
} else {
(NiceFloat(ceiling), Greater)
}
);
});
}
}
fn rounding_from_helper_primitive_float_signed<
T: ConvertibleFrom + PrimitiveFloat + RoundingFrom,
U: PrimitiveUnsigned + WrappingFrom,
S: TryFrom> + PrimitiveSigned + RoundingFrom + WrappingFrom,
>() {
signed_rounding_mode_pair_gen_var_4::().test_properties(|(i, rm)| {
let (rounded, o) = T::rounding_from(i, rm);
match (i >= S::ZERO, rm) {
(_, Floor) | (true, Down) | (false, Up) => {
assert_ne!(o, Greater);
}
(_, Ceiling) | (true, Up) | (false, Down) => {
assert_ne!(o, Less);
}
(_, Exact) => assert_eq!(o, Equal),
_ => {}
}
if o == Equal {
for rm in exhaustive_rounding_modes() {
assert_eq!(T::rounding_from(i, rm), (rounded, Equal));
}
} else {
assert_panic!(T::rounding_from(i, Exact));
}
});
signed_gen_var_7::().test_properties(|i| {
let (f, o) = T::rounding_from(i, Exact);
let (f_alt, o_alt) = T::rounding_from(i, Floor);
assert_eq!(NiceFloat(f_alt), NiceFloat(f));
assert_eq!(o_alt, o);
let (f_alt, o_alt) = T::rounding_from(i, Ceiling);
assert_eq!(NiceFloat(f_alt), NiceFloat(f));
assert_eq!(o_alt, o);
let (f_alt, o_alt) = T::rounding_from(i, Down);
assert_eq!(NiceFloat(f_alt), NiceFloat(f));
assert_eq!(o_alt, o);
let (f_alt, o_alt) = T::rounding_from(i, Up);
assert_eq!(NiceFloat(f_alt), NiceFloat(f));
assert_eq!(o_alt, o);
let (f_alt, o_alt) = T::rounding_from(i, Nearest);
assert_eq!(NiceFloat(f_alt), NiceFloat(f));
assert_eq!(o_alt, o);
assert_eq!(S::rounding_from(f, Exact), (i, Equal));
});
if S::WIDTH > T::MANTISSA_WIDTH {
signed_gen_var_8::().test_properties(|i| {
let (f_below, o) = T::rounding_from(i, Floor);
assert_eq!(o, Less);
let f_above = f_below.next_higher();
let (f_alt, o_alt) = T::rounding_from(i, Ceiling);
assert_eq!(NiceFloat(f_alt), NiceFloat(f_above));
assert_eq!(o_alt, Greater);
if i >= S::ZERO {
let (f_alt, o_alt) = T::rounding_from(i, Down);
assert_eq!(NiceFloat(f_below), NiceFloat(f_alt));
assert_eq!(o_alt, Less);
let (f_alt, o_alt) = T::rounding_from(i, Up);
assert_eq!(NiceFloat(f_above), NiceFloat(f_alt));
assert_eq!(o_alt, Greater);
} else {
let (f_alt, o_alt) = T::rounding_from(i, Down);
assert_eq!(NiceFloat(f_above), NiceFloat(f_alt));
assert_eq!(o_alt, Greater);
let (f_alt, o_alt) = T::rounding_from(i, Up);
assert_eq!(NiceFloat(f_below), NiceFloat(f_alt));
assert_eq!(o_alt, Less);
}
let (f_nearest, o_alt) = T::rounding_from(i, Nearest);
assert!(
(NiceFloat(f_nearest), o_alt) == (NiceFloat(f_below), Less)
|| (NiceFloat(f_nearest), o_alt) == (NiceFloat(f_above), Greater)
);
});
signed_gen_var_9::().test_properties(|i| {
let (floor, o) = T::rounding_from(i, Floor);
assert_eq!(o, Less);
let ceiling = floor.next_higher();
let (nearest, o) = T::rounding_from(i, Nearest);
assert_eq!(
(NiceFloat(nearest), o),
if floor.to_bits().even() {
(NiceFloat(floor), Less)
} else {
(NiceFloat(ceiling), Greater)
}
);
});
}
}
#[test]
fn rounding_from_properties() {
apply_fn_to_unsigneds_and_primitive_floats!(rounding_from_helper_unsigned_primitive_float);
apply_fn_to_signeds_and_primitive_floats!(rounding_from_helper_signed_primitive_float);
apply_fn_to_primitive_floats_and_unsigneds!(rounding_from_helper_primitive_float_unsigned);
apply_fn_to_primitive_floats_and_unsigned_signed_pairs!(
rounding_from_helper_primitive_float_signed
);
}