// 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 ); }