// 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::{CoprimeWith, UnsignedAbs}; use malachite_base::num::basic::integers::PrimitiveInt; use malachite_base::num::basic::signeds::PrimitiveSigned; use malachite_base::num::basic::traits::{One, Zero}; use malachite_base::num::basic::unsigneds::PrimitiveUnsigned; use malachite_base::num::conversion::traits::ExactFrom; use malachite_base::test_util::generators::{ signed_gen, signed_pair_gen_var_6, unsigned_gen, unsigned_pair_gen_var_12, unsigned_unsigned_bool_triple_gen_var_2, }; use malachite_nz::integer::Integer; use malachite_nz::natural::Natural; use malachite_nz::platform::{Limb, SignedLimb}; use malachite_nz::test_util::generators::{ integer_gen, integer_pair_gen_var_1, natural_gen, natural_natural_bool_triple_gen_var_1, natural_pair_gen_var_5, }; use malachite_q::Rational; use num::{BigInt, BigRational}; use std::panic::catch_unwind; use std::str::FromStr; #[test] fn test_from_naturals() { let test = |n, d, out| { let n = Natural::from_str(n).unwrap(); let d = Natural::from_str(d).unwrap(); let x = Rational::from_naturals(n.clone(), d.clone()); assert!(x.is_valid()); assert_eq!(x.to_string(), out); let x = Rational::from_naturals_ref(&n, &d); assert!(x.is_valid()); assert_eq!(x.to_string(), out); }; test("0", "1", "0"); test("0", "5", "0"); test("3", "6", "1/2"); test("100", "101", "100/101"); } #[test] fn from_naturals_fail() { assert_panic!(Rational::from_naturals(Natural::ZERO, Natural::ZERO)); assert_panic!(Rational::from_naturals(Natural::ONE, Natural::ZERO)); assert_panic!(Rational::from_naturals_ref(&Natural::ZERO, &Natural::ZERO)); assert_panic!(Rational::from_naturals_ref(&Natural::ONE, &Natural::ZERO)); } #[test] fn from_naturals_properties() { natural_pair_gen_var_5().test_properties(|(n, d)| { let x = Rational::from_naturals(n.clone(), d.clone()); assert!(x.is_valid()); let x_alt = Rational::from_naturals_ref(&n, &d); assert!(x.is_valid()); assert_eq!(x, x_alt); if (&n).coprime_with(&d) { assert_eq!(x.into_numerator_and_denominator(), (n, d)); } }); natural_gen() .test_properties(|n| assert_eq!(Rational::from_naturals_ref(&n, &Natural::ONE), n)); } fn test_from_unsigneds_helper() where Natural: From, Limb: ExactFrom, { let test = |n: u8, d: u8, out| { let n = T::from(n); let d = T::from(d); let x = Rational::from_unsigneds(n, d); assert!(x.is_valid()); assert_eq!(x.to_string(), out); if T::WIDTH == Limb::WIDTH { let x_alt = Rational::const_from_unsigneds(Limb::exact_from(n), Limb::exact_from(d)); assert!(x_alt.is_valid()); assert!(PartialEq::::eq(&x_alt, &x)); } }; test(0, 1, "0"); test(0, 5, "0"); test(3, 6, "1/2"); test(100, 101, "100/101"); } fn from_unsigneds_fail_helper() where Natural: From, { assert_panic!(Rational::from_unsigneds(T::ZERO, T::ZERO)); assert_panic!(Rational::from_unsigneds(T::ONE, T::ZERO)); } #[test] fn from_unsigneds_fail() { apply_fn_to_unsigneds!(from_unsigneds_fail_helper); } #[test] fn test_from_unsigneds() { apply_fn_to_unsigneds!(test_from_unsigneds_helper); } #[test] fn test_const_from_unsigneds() { let test = |n: Limb, d: Limb, out| { let x = Rational::from_unsigneds(n, d); assert!(x.is_valid()); assert_eq!(x.to_string(), out); let x_alt = Rational::const_from_unsigneds(Limb::exact_from(n), Limb::exact_from(d)); assert!(x_alt.is_valid()); assert_eq!(x_alt.to_string(), out); }; test(0, 1, "0"); test(0, 5, "0"); test(3, 6, "1/2"); test(100, 101, "100/101"); // The following test case has the highest number of steps in the Euclidean GCD algorithm. #[cfg(not(feature = "32_bit_limbs"))] test( 7540113804746346429, 12200160415121876738, "7540113804746346429/12200160415121876738", ); #[cfg(not(feature = "32_bit_limbs"))] test( 7540113804746346430, 12200160415121876738, "3770056902373173215/6100080207560938369", ); } #[test] fn const_from_unsigneds_fail() { assert_panic!(Rational::const_from_unsigneds(0, 0)); assert_panic!(Rational::const_from_unsigneds(1, 0)); } fn from_unsigneds_properties_helper() where Natural: From + PartialEq, Rational: PartialEq, Limb: ExactFrom, { unsigned_pair_gen_var_12::().test_properties(|(n, d)| { let x = Rational::from_unsigneds(n, d); assert!(x.is_valid()); if n.coprime_with(d) { let (n_alt, d_alt) = x.clone().into_numerator_and_denominator(); assert_eq!(n_alt, n); assert_eq!(d_alt, d); } if T::WIDTH == Limb::WIDTH { let x_alt = Rational::const_from_unsigneds(Limb::exact_from(n), Limb::exact_from(d)); assert!(x_alt.is_valid()); assert!(PartialEq::::eq(&x_alt, &x)); } }); unsigned_gen::().test_properties(|n| assert_eq!(Rational::from_unsigneds(n, T::ONE), n)); } #[test] fn from_unsigneds_properties() { apply_fn_to_unsigneds!(from_unsigneds_properties_helper); } #[test] fn const_from_unsigneds_properties() { unsigned_pair_gen_var_12::().test_properties(|(n, d)| { let x = Rational::from_unsigneds(n, d); let x_alt = Rational::const_from_unsigneds(n, d); assert!(x_alt.is_valid()); assert_eq!(x_alt, x); }); } #[test] fn test_from_integers() { let test = |s, t, out| { let n = Integer::from_str(s).unwrap(); let d = Integer::from_str(t).unwrap(); let x = Rational::from_integers(n.clone(), d.clone()); assert!(x.is_valid()); assert_eq!(x.to_string(), out); let x = Rational::from_integers_ref(&n, &d); assert!(x.is_valid()); assert_eq!(x.to_string(), out); assert_eq!( BigRational::new(BigInt::from_str(s).unwrap(), BigInt::from_str(t).unwrap()) .to_string(), out ); assert_eq!( rug::Rational::from(( rug::Integer::from_str(s).unwrap(), rug::Integer::from_str(t).unwrap() )) .to_string(), out ); }; test("0", "1", "0"); test("0", "-1", "0"); test("0", "5", "0"); test("0", "-5", "0"); test("3", "6", "1/2"); test("3", "-6", "-1/2"); test("-3", "6", "-1/2"); test("-3", "-6", "1/2"); test("100", "101", "100/101"); test("100", "-101", "-100/101"); test("-100", "101", "-100/101"); test("-100", "-101", "100/101"); } #[test] fn from_integers_fail() { assert_panic!(Rational::from_integers(Integer::ZERO, Integer::ZERO)); assert_panic!(Rational::from_integers(Integer::ONE, Integer::ZERO)); assert_panic!(Rational::from_integers_ref(&Integer::ZERO, &Integer::ZERO)); assert_panic!(Rational::from_integers_ref(&Integer::ONE, &Integer::ZERO)); } #[test] fn from_integers_properties() { integer_pair_gen_var_1().test_properties(|(n, d)| { let x = Rational::from_integers(n.clone(), d.clone()); assert!(x.is_valid()); let x_alt = Rational::from_integers_ref(&n, &d); assert!(x.is_valid()); assert_eq!(x, x_alt); assert_eq!( Rational::from(&BigRational::new(BigInt::from(&n), BigInt::from(&d))), x ); assert_eq!( Rational::from(&rug::Rational::from(( rug::Integer::from(&n), rug::Integer::from(&d) ))), x ); if n != 0 { assert_eq!(x >= 0, (n >= 0) == (d >= 0)); } if n >= 0 && d > 0 && (n.unsigned_abs_ref()).coprime_with(d.unsigned_abs_ref()) { let (n_2, d_2) = x.into_numerator_and_denominator(); assert_eq!((Integer::from(n_2), Integer::from(d_2)), (n, d)); } }); integer_gen() .test_properties(|n| assert_eq!(Rational::from_integers_ref(&n, &Integer::ONE), n)); } fn test_from_signeds_helper() where Integer: From, SignedLimb: ExactFrom, { let test = |n: i8, d: i8, out| { let n = T::from(n); let d = T::from(d); let x = Rational::from_signeds(n, d); assert!(x.is_valid()); assert_eq!(x.to_string(), out); if T::WIDTH == Limb::WIDTH { let x_alt = Rational::const_from_signeds(SignedLimb::exact_from(n), SignedLimb::exact_from(d)); assert!(x_alt.is_valid()); assert!(PartialEq::::eq(&x_alt, &x)); } }; test(0, 1, "0"); test(0, -1, "0"); test(0, 5, "0"); test(0, -5, "0"); test(3, 6, "1/2"); test(3, -6, "-1/2"); test(-3, 6, "-1/2"); test(-3, -6, "1/2"); test(100, 101, "100/101"); test(100, -101, "-100/101"); test(-100, 101, "-100/101"); test(-100, -101, "100/101"); } #[test] fn test_from_signeds() { apply_fn_to_signeds!(test_from_signeds_helper); } fn from_signeds_fail_helper() where Integer: From, { assert_panic!(Rational::from_signeds(T::ZERO, T::ZERO)); assert_panic!(Rational::from_signeds(T::ONE, T::ZERO)); } #[test] fn from_signeds_fail() { apply_fn_to_signeds!(from_signeds_fail_helper); } #[test] fn const_from_signeds_fail() { assert_panic!(Rational::const_from_signeds(0, 0)); assert_panic!(Rational::const_from_signeds(1, 0)); } fn from_signeds_properties_helper() where Integer: From, rug::Rational: From<(T, T)>, ::Output: CoprimeWith, Natural: PartialEq, Rational: PartialEq, SignedLimb: ExactFrom, { signed_pair_gen_var_6::().test_properties(|(n, d)| { let x = Rational::from_signeds(n, d); assert!(x.is_valid()); let x_alt = Rational::from(&rug::Rational::from((n, d))); assert!(PartialEq::::eq(&x_alt, &x)); if n != T::ZERO { assert_eq!( PartialOrd::::ge(&x, &0u32), (n >= T::ZERO) == (d >= T::ZERO) ); } if n >= T::ZERO && d > T::ZERO && (n.unsigned_abs()).coprime_with(d.unsigned_abs()) { let (n_2, d_2) = x.clone().into_numerator_and_denominator(); assert_eq!(n_2, n); assert_eq!(d_2, d); } if T::WIDTH == Limb::WIDTH { let x_alt = Rational::const_from_signeds(SignedLimb::exact_from(n), SignedLimb::exact_from(d)); assert!(x_alt.is_valid()); assert!(PartialEq::::eq(&x_alt, &x)); } }); signed_gen::().test_properties(|n| assert_eq!(Rational::from_signeds(n, T::ONE), n)); } #[test] fn from_signeds_properties() { apply_fn_to_signeds!(from_signeds_properties_helper); } #[test] fn const_from_signeds_properties() { signed_pair_gen_var_6::().test_properties(|(n, d)| { let x = Rational::from_signeds(n, d); let x_alt = Rational::const_from_signeds(n, d); assert!(x_alt.is_valid()); assert_eq!(x_alt, x); }); } #[test] fn test_from_sign_and_naturals() { let test = |sign, n, d, out| { let n = Natural::from_str(n).unwrap(); let d = Natural::from_str(d).unwrap(); let x = Rational::from_sign_and_naturals(sign, n.clone(), d.clone()); assert!(x.is_valid()); assert_eq!(x.to_string(), out); let x = Rational::from_sign_and_naturals_ref(sign, &n, &d); assert!(x.is_valid()); assert_eq!(x.to_string(), out); }; test(false, "0", "1", "0"); test(true, "0", "1", "0"); test(false, "0", "5", "0"); test(true, "0", "5", "0"); test(false, "3", "6", "-1/2"); test(true, "3", "6", "1/2"); test(false, "100", "101", "-100/101"); test(true, "100", "101", "100/101"); } #[test] fn from_sign_and_naturals_properties() { natural_natural_bool_triple_gen_var_1().test_properties(|(n, d, sign)| { let x = Rational::from_sign_and_naturals(sign, n.clone(), d.clone()); assert!(x.is_valid()); let x_alt = Rational::from_sign_and_naturals_ref(sign, &n, &d); assert!(x.is_valid()); assert_eq!(x, x_alt); if n != 0 { assert_eq!(x >= 0, sign); } if (&n).coprime_with(&d) { assert_eq!(x.into_numerator_and_denominator(), (n, d)); } }); natural_pair_gen_var_5().test_properties(|(n, d)| { assert_eq!( Rational::from_naturals(n.clone(), d.clone()), Rational::from_sign_and_naturals(true, n, d) ); }); natural_gen().test_properties(|n| { assert_eq!( Rational::from_sign_and_naturals_ref(true, &n, &Natural::ONE), n ); }); } fn test_from_sign_and_unsigneds_helper() where Natural: From, { let test = |sign, n: u8, d: u8, out| { let n = T::from(n); let d = T::from(d); let x = Rational::from_sign_and_unsigneds(sign, n, d); assert!(x.is_valid()); assert_eq!(x.to_string(), out); }; test(false, 0, 1, "0"); test(true, 0, 1, "0"); test(false, 0, 5, "0"); test(true, 0, 5, "0"); test(false, 3, 6, "-1/2"); test(true, 3, 6, "1/2"); test(false, 100, 101, "-100/101"); test(true, 100, 101, "100/101"); } #[test] fn test_from_sign_and_unsigneds() { apply_fn_to_unsigneds!(test_from_sign_and_unsigneds_helper); } fn from_sign_and_unsigneds_properties_helper() where Natural: From + PartialEq, Rational: PartialEq, { unsigned_unsigned_bool_triple_gen_var_2::().test_properties(|(n, d, sign)| { let x = Rational::from_sign_and_unsigneds(sign, n, d); assert!(x.is_valid()); if n != T::ZERO { assert_eq!(PartialOrd::::ge(&x, &0u32), sign); } if n.coprime_with(d) { let (n_alt, d_alt) = x.into_numerator_and_denominator(); assert_eq!(n_alt, n); assert_eq!(d_alt, d); } }); unsigned_pair_gen_var_12::().test_properties(|(n, d)| { assert!(PartialEq::::eq( &Rational::from_unsigneds(n, d), &Rational::from_sign_and_unsigneds(true, n, d) )); }); unsigned_gen::() .test_properties(|n| assert_eq!(Rational::from_sign_and_unsigneds(true, n, T::ONE), n)); } #[test] fn from_sign_and_unsigneds_properties() { apply_fn_to_unsigneds!(from_sign_and_unsigneds_properties_helper); }