// 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::assert_panic; use malachite_base::num::arithmetic::traits::{Ceiling, Floor}; use malachite_base::num::basic::integers::PrimitiveInt; use malachite_base::num::basic::signeds::PrimitiveSigned; use malachite_base::num::basic::traits::OneHalf; use malachite_base::num::basic::unsigneds::PrimitiveUnsigned; use malachite_base::num::comparison::traits::PartialOrdAbs; use malachite_base::num::conversion::traits::{ ConvertibleFrom, ExactFrom, IsInteger, RoundingFrom, }; use malachite_base::rounding_modes::RoundingMode::*; use malachite_base::test_util::generators::{signed_gen, unsigned_gen}; use malachite_nz::integer::Integer; use malachite_q::conversion::primitive_int_from_rational::{ SignedFromRationalError, UnsignedFromRationalError, }; use malachite_q::test_util::generators::{ rational_gen, rational_gen_var_3, rational_rounding_mode_pair_gen_var_3, }; use malachite_q::Rational; use std::cmp::Ordering::*; use std::panic::catch_unwind; use std::str::FromStr; #[test] fn test_u32_try_from_rational() { let test = |s, out: Result| { let u = Rational::from_str(s).unwrap(); assert_eq!(u32::try_from(&u), out); }; test("0", Ok(0)); test("123", Ok(123)); test("-123", Err(UnsignedFromRationalError)); test("1000000000000", Err(UnsignedFromRationalError)); test("-1000000000000", Err(UnsignedFromRationalError)); test("22/7", Err(UnsignedFromRationalError)); test("-22/7", Err(UnsignedFromRationalError)); } #[test] fn test_i32_try_from_rational() { let test = |s, out: Result| { let u = Rational::from_str(s).unwrap(); assert_eq!(i32::try_from(&u), out); }; test("0", Ok(0)); test("123", Ok(123)); test("-123", Ok(-123)); test("1000000000000", Err(SignedFromRationalError)); test("-1000000000000", Err(SignedFromRationalError)); test("22/7", Err(SignedFromRationalError)); test("-22/7", Err(SignedFromRationalError)); } #[test] fn test_u32_convertible_from_rational() { let test = |s, out| { let u = Rational::from_str(s).unwrap(); assert_eq!(u32::convertible_from(&u), out); }; test("0", true); test("123", true); test("-123", false); test("1000000000000", false); test("-1000000000000", false); test("22/7", false); test("-22/7", false); } #[test] fn test_i32_convertible_from_rational() { let test = |s, out| { let u = Rational::from_str(s).unwrap(); assert_eq!(i32::convertible_from(&u), out); }; test("0", true); test("123", true); test("-123", true); test("1000000000000", false); test("-1000000000000", false); test("22/7", false); test("-22/7", false); } #[test] fn test_u32_rounding_from_rational() { let test = |s, rm, out: u32, o_out| { let r = Rational::from_str(s).unwrap(); let (u, o) = u32::rounding_from(&r, rm); assert_eq!(u, out); assert_eq!(o, o_out); }; test("123", Floor, 123, Equal); test("123", Ceiling, 123, Equal); test("123", Down, 123, Equal); test("123", Up, 123, Equal); test("123", Nearest, 123, Equal); test("123", Exact, 123, Equal); test("22/7", Floor, 3, Less); test("22/7", Ceiling, 4, Greater); test("22/7", Down, 3, Less); test("22/7", Up, 4, Greater); test("22/7", Nearest, 3, Less); test("7/2", Floor, 3, Less); test("7/2", Ceiling, 4, Greater); test("7/2", Down, 3, Less); test("7/2", Up, 4, Greater); test("7/2", Nearest, 4, Greater); test("9/2", Floor, 4, Less); test("9/2", Ceiling, 5, Greater); test("9/2", Down, 4, Less); test("9/2", Up, 5, Greater); test("9/2", Nearest, 4, Less); test("-123", Ceiling, 0, Greater); test("-123", Down, 0, Greater); test("-123", Nearest, 0, Greater); test("1000000000000", Floor, u32::MAX, Less); test("1000000000000", Down, u32::MAX, Less); test("1000000000000", Nearest, u32::MAX, Less); } #[test] fn test_i32_rounding_from_rational() { let test = |s, rm, out: i32, o_out| { let r = Rational::from_str(s).unwrap(); let (i, o) = i32::rounding_from(&r, rm); assert_eq!(i, out); assert_eq!(o, o_out); }; test("123", Floor, 123, Equal); test("123", Ceiling, 123, Equal); test("123", Down, 123, Equal); test("123", Up, 123, Equal); test("123", Nearest, 123, Equal); test("123", Exact, 123, Equal); test("22/7", Floor, 3, Less); test("22/7", Ceiling, 4, Greater); test("22/7", Down, 3, Less); test("22/7", Up, 4, Greater); test("22/7", Nearest, 3, Less); test("-22/7", Floor, -4, Less); test("-22/7", Ceiling, -3, Greater); test("-22/7", Down, -3, Greater); test("-22/7", Up, -4, Less); test("-22/7", Nearest, -3, Greater); test("7/2", Floor, 3, Less); test("7/2", Ceiling, 4, Greater); test("7/2", Down, 3, Less); test("7/2", Up, 4, Greater); test("7/2", Nearest, 4, Greater); test("9/2", Floor, 4, Less); test("9/2", Ceiling, 5, Greater); test("9/2", Down, 4, Less); test("9/2", Up, 5, Greater); test("9/2", Nearest, 4, Less); test("-1000000000000", Ceiling, i32::MIN, Greater); test("-1000000000000", Down, i32::MIN, Greater); test("-1000000000000", Nearest, i32::MIN, Greater); test("1000000000000", Floor, i32::MAX, Less); test("1000000000000", Down, i32::MAX, Less); test("1000000000000", Nearest, i32::MAX, Less); } #[test] fn rounding_from_rational_fail() { let x = Rational::from_str("22/7").unwrap(); assert_panic!(u32::rounding_from(&x, Exact)); let x = Rational::from_str("-123").unwrap(); assert_panic!(u32::rounding_from(&x, Floor)); assert_panic!(u32::rounding_from(&x, Up)); assert_panic!(u32::rounding_from(&x, Exact)); let x = Rational::from_str("1000000000000").unwrap(); assert_panic!(u32::rounding_from(&x, Ceiling)); assert_panic!(u32::rounding_from(&x, Up)); assert_panic!(u32::rounding_from(&x, Exact)); let x = Rational::from_str("22/7").unwrap(); assert_panic!(i32::rounding_from(&x, Exact)); let x = Rational::from_str("-1000000000000").unwrap(); assert_panic!(i32::rounding_from(&x, Floor)); assert_panic!(i32::rounding_from(&x, Up)); assert_panic!(i32::rounding_from(&x, Exact)); let x = Rational::from_str("1000000000000").unwrap(); assert_panic!(i32::rounding_from(&x, Ceiling)); assert_panic!(i32::rounding_from(&x, Up)); assert_panic!(i32::rounding_from(&x, Exact)); } fn try_from_rational_properties_helper< T: for<'a> TryFrom<&'a Rational> + for<'a> ConvertibleFrom<&'a Rational> + PrimitiveInt, >() where Rational: TryFrom + PartialOrd, { rational_gen().test_properties(|x| { let p_x = T::try_from(&x); assert_eq!(p_x.is_ok(), x >= T::MIN && x <= T::MAX && x.is_integer()); assert_eq!(p_x.is_ok(), T::convertible_from(&x)); if let Ok(n) = p_x { assert_eq!(n.to_string(), x.to_string()); assert_eq!(T::exact_from(&x), n); assert!(PartialEq::::eq(&Rational::exact_from(n), &x)); } }); } #[test] fn try_from_rational_properties() { apply_fn_to_primitive_ints!(try_from_rational_properties_helper); } fn convertible_from_rational_properties_helper< T: for<'a> ConvertibleFrom<&'a Rational> + PrimitiveInt, >() where Rational: PartialOrd, { rational_gen().test_properties(|x| { let convertible = T::convertible_from(&x); assert_eq!(convertible, x >= T::MIN && x <= T::MAX && x.is_integer()); }); } #[test] fn convertible_from_rational_properties() { apply_fn_to_primitive_ints!(convertible_from_rational_properties_helper); } fn rounding_from_rational_properties_helper< T: for<'a> ConvertibleFrom<&'a Rational> + PartialEq + PartialOrd + PrimitiveInt + for<'a> RoundingFrom<&'a Rational>, >() where Rational: From + PartialOrd, { rational_rounding_mode_pair_gen_var_3::().test_properties(|(x, rm)| { let (n, o) = T::rounding_from(&x, rm); if x >= T::MIN && x <= T::MAX { assert!((Rational::from(n) - &x).lt_abs(&1)); } assert_eq!(n.partial_cmp(&x), Some(o)); match (x >= T::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), _ => {} } }); // TODO use range rational_gen_var_3().test_properties(|x| { if x < T::MIN || x > T::MAX { return; } let floor = T::rounding_from(&x, Floor); assert_eq!(floor.0, (&x).floor()); assert!(floor.0 <= x); if floor.0 < T::MAX { assert!(floor.0 + T::ONE > x); } let ceiling = T::rounding_from(&x, Ceiling); assert_eq!(ceiling.0, (&x).ceiling()); assert!(ceiling.0 >= x); if ceiling.0 > T::MIN { assert!(ceiling.0 - T::ONE < x); } if x >= T::ZERO { assert_eq!(T::rounding_from(&x, Down), floor); assert_eq!(T::rounding_from(&x, Up), ceiling); } else { assert_eq!(T::rounding_from(&x, Down), ceiling); assert_eq!(T::rounding_from(&x, Up), floor); } let nearest = T::rounding_from(&x, Nearest); assert!(nearest == floor || nearest == ceiling); assert!((Rational::from(nearest.0) - x).le_abs(&Rational::ONE_HALF)); }); } fn rounding_from_rational_properties_unsigned_helper< T: PrimitiveUnsigned + for<'a> RoundingFrom<&'a Rational>, >() where Rational: From, { unsigned_gen::().test_properties(|n| { let no = (n, Equal); let x = Rational::from(n); assert_eq!(T::rounding_from(&x, Floor), no); assert_eq!(T::rounding_from(&x, Down), no); assert_eq!(T::rounding_from(&x, Ceiling), no); assert_eq!(T::rounding_from(&x, Up), no); assert_eq!(T::rounding_from(&x, Nearest), no); assert_eq!(T::rounding_from(&x, Exact), no); }); } fn rounding_from_rational_properties_signed_helper< T: PrimitiveSigned + for<'a> RoundingFrom<&'a Rational>, >() where Rational: From, { signed_gen::().test_properties(|n| { let no = (n, Equal); let x = Rational::from(n); assert_eq!(T::rounding_from(&x, Floor), no); assert_eq!(T::rounding_from(&x, Down), no); assert_eq!(T::rounding_from(&x, Ceiling), no); assert_eq!(T::rounding_from(&x, Up), no); assert_eq!(T::rounding_from(&x, Nearest), no); assert_eq!(T::rounding_from(&x, Exact), no); }); } #[test] fn rounding_from_rational_properties() { apply_fn_to_primitive_ints!(rounding_from_rational_properties_helper); apply_fn_to_unsigneds!(rounding_from_rational_properties_unsigned_helper); apply_fn_to_signeds!(rounding_from_rational_properties_signed_helper); }