use core::cmp::Ordering::{self, *};
use malachite_base::num::arithmetic::traits::{LogBase, LogBaseAssign};
use malachite_base::num::basic::floats::PrimitiveFloat;
use malachite_base::num::basic::traits::{Infinity, NaN, NegativeInfinity, One, Zero};
use malachite_base::num::conversion::traits::{ExactFrom, RoundingFrom};
use malachite_base::num::float::NiceFloat;
use malachite_base::num::logic::traits::SignificantBits;
use malachite_base::rounding_modes::RoundingMode::{self, *};
use malachite_base::rounding_modes::exhaustive::exhaustive_rounding_modes;
use malachite_base::test_util::generators::unsigned_rounding_mode_pair_gen_var_3;
use malachite_float::arithmetic::log_base::primitive_float_log_base;
use malachite_float::arithmetic::log_base_rational_base::primitive_float_log_base_rational_base;
use malachite_float::test_util::arithmetic::log_base_rational_base::{
rug_log_base_rational_base, rug_log_base_rational_base_prec,
rug_log_base_rational_base_prec_round, rug_log_base_rational_base_round,
};
use malachite_float::test_util::common::rug_round_try_from_rounding_mode;
use malachite_float::test_util::generators::{
float_rational_rounding_mode_triple_gen_var_12, float_rational_rounding_mode_triple_gen_var_13,
float_rational_unsigned_rounding_mode_quadruple_gen_var_11,
float_rational_unsigned_rounding_mode_quadruple_gen_var_12,
};
use malachite_float::{ComparableFloat, ComparableFloatRef, Float};
use malachite_q::Rational;
use std::panic::catch_unwind;
fn check(
x: &Float,
base: &Rational,
prec: u64,
rm: RoundingMode,
extreme: bool,
) -> (Float, Ordering) {
let (log, o) = x.clone().log_base_rational_base_prec_round(base, prec, rm);
assert!(log.is_valid());
let (log_alt, o_alt) = x.log_base_rational_base_prec_round_ref(base, prec, rm);
assert!(log_alt.is_valid());
assert_eq!(ComparableFloatRef(&log_alt), ComparableFloatRef(&log));
assert_eq!(o_alt, o);
let mut x_alt = x.clone();
let o_alt = x_alt.log_base_rational_base_prec_round_assign(base, prec, rm);
assert!(x_alt.is_valid());
assert_eq!(ComparableFloatRef(&x_alt), ComparableFloatRef(&log));
assert_eq!(o_alt, o);
if log.is_normal() {
assert_eq!(log.get_prec(), Some(prec));
}
if !extreme
&& base.significant_bits() <= 128
&& let Ok(rug_rm) = rug_round_try_from_rounding_mode(rm)
{
let (rug_log, rug_o) =
rug_log_base_rational_base_prec_round(&rug::Float::exact_from(x), base, prec, rug_rm);
assert_eq!(
ComparableFloatRef(&Float::from(&rug_log)),
ComparableFloatRef(&log),
);
assert_eq!(rug_o, o);
}
(log, o)
}
#[test]
fn test_log_base_rational_base_prec_round() {
let test = |n: i64, d: u64, bn: u64, bd: u64, prec: u64, rm: RoundingMode, out: &str, o_out| {
let x = Float::exact_from(Rational::from_signeds(n, i64::exact_from(d)));
let base = Rational::from_unsigneds(bn, bd);
let (log, o) = check(&x, &base, prec, rm, false);
assert_eq!(log.to_string(), out);
assert_eq!(o, o_out);
};
test(9, 1, 3, 1, 10, Exact, "2.0", Equal); test(8, 1, 2, 1, 10, Exact, "3.0", Equal); test(27, 1, 3, 1, 10, Exact, "3.0", Equal); test(8, 1, 4, 1, 10, Exact, "1.5", Equal); test(2, 1, 4, 1, 10, Exact, "0.5", Equal); test(9, 4, 3, 2, 10, Exact, "2.0", Equal); test(3, 2, 9, 4, 10, Exact, "0.5", Equal); test(1, 4, 4, 1, 10, Exact, "-1.0", Equal); test(1, 4, 2, 1, 10, Exact, "-2.0", Equal); test(1, 8, 2, 1, 10, Exact, "-3.0", Equal); test(2, 1, 3, 1, 20, Floor, "0.630929", Less); test(2, 1, 3, 1, 20, Ceiling, "0.63093", Greater);
test(2, 1, 3, 1, 20, Nearest, "0.63093", Greater);
test(2, 1, 3, 2, 20, Nearest, "1.709511", Less); test(1, 1, 3, 1, 10, Exact, "0.0", Equal);
test(1, 1, 7, 3, 10, Exact, "0.0", Equal);
}
#[test]
fn test_log_base_rational_base_specials() {
let three = Rational::from(3u32);
let nearest = |x: Float| x.log_base_rational_base_prec_round(&three, 10, Nearest);
assert_eq!(nearest(Float::NAN).0.to_string(), "NaN");
assert_eq!(nearest(Float::INFINITY), (Float::INFINITY, Equal));
assert_eq!(nearest(Float::NEGATIVE_INFINITY).0.to_string(), "NaN");
assert_eq!(nearest(Float::ZERO), (Float::NEGATIVE_INFINITY, Equal));
assert_eq!(nearest(-Float::ZERO), (Float::NEGATIVE_INFINITY, Equal));
assert_eq!(nearest(Float::ONE), (Float::ZERO, Equal)); assert_eq!(nearest(Float::from(-2)).0.to_string(), "NaN"); }
#[test]
fn test_log_base_rational_base_prec() {
let test = |n: i64, d: u64, bn: u64, bd: u64, prec: u64, out: &str, o_out| {
let x = Float::exact_from(Rational::from_signeds(n, i64::exact_from(d)));
let base = Rational::from_unsigneds(bn, bd);
let (log, o) = x.clone().log_base_rational_base_prec(&base, prec);
assert!(log.is_valid());
assert_eq!(log.to_string(), out);
assert_eq!(o, o_out);
let (log_alt, o_alt) = x.log_base_rational_base_prec_ref(&base, prec);
assert!(log_alt.is_valid());
assert_eq!(ComparableFloatRef(&log_alt), ComparableFloatRef(&log));
assert_eq!(o_alt, o);
let mut x_alt = x.clone();
let o_alt = x_alt.log_base_rational_base_prec_assign(&base, prec);
assert!(x_alt.is_valid());
assert_eq!(ComparableFloatRef(&x_alt), ComparableFloatRef(&log));
assert_eq!(o_alt, o);
let (rug_log, rug_o) =
rug_log_base_rational_base_prec(&rug::Float::exact_from(&x), &base, prec);
assert_eq!(
ComparableFloatRef(&Float::from(&rug_log)),
ComparableFloatRef(&log),
);
assert_eq!(rug_o, o);
};
test(9, 1, 3, 1, 10, "2.0", Equal); test(8, 1, 2, 1, 10, "3.0", Equal); test(8, 1, 4, 1, 10, "1.5", Equal); test(2, 1, 4, 1, 10, "0.5", Equal); test(9, 4, 3, 2, 10, "2.0", Equal); test(3, 2, 9, 4, 10, "0.5", Equal); test(1, 4, 4, 1, 10, "-1.0", Equal); test(1, 8, 2, 1, 10, "-3.0", Equal); test(2, 1, 3, 1, 20, "0.63093", Greater); test(2, 1, 3, 2, 20, "1.709511", Less); test(1, 1, 3, 1, 10, "0.0", Equal); }
#[test]
fn test_log_base_rational_base_round() {
let test = |n: i64, d: u64, bn: u64, bd: u64, rm: RoundingMode, out: &str, o_out| {
let x = Float::exact_from(Rational::from_signeds(n, i64::exact_from(d)));
let base = Rational::from_unsigneds(bn, bd);
let (log, o) = x.clone().log_base_rational_base_round(&base, rm);
assert!(log.is_valid());
assert_eq!(log.to_string(), out);
assert_eq!(o, o_out);
let (log_alt, o_alt) = x.log_base_rational_base_round_ref(&base, rm);
assert!(log_alt.is_valid());
assert_eq!(ComparableFloatRef(&log_alt), ComparableFloatRef(&log));
assert_eq!(o_alt, o);
if let Ok(rug_rm) = rug_round_try_from_rounding_mode(rm) {
let (rug_log, rug_o) =
rug_log_base_rational_base_round(&rug::Float::exact_from(&x), &base, rug_rm);
assert_eq!(
ComparableFloatRef(&Float::from(&rug_log)),
ComparableFloatRef(&log),
);
assert_eq!(rug_o, o);
}
};
test(9, 1, 3, 1, Floor, "2.0", Equal); test(9, 1, 3, 1, Ceiling, "2.0", Equal);
test(9, 1, 3, 1, Nearest, "2.0", Equal);
test(9, 1, 3, 1, Down, "2.0", Equal);
test(9, 1, 3, 1, Up, "2.0", Equal);
test(9, 1, 3, 1, Exact, "2.0", Equal);
test(81, 1, 3, 1, Exact, "4.0", Equal); test(2, 1, 4, 1, Exact, "0.5", Equal); test(1, 4, 4, 1, Exact, "-1.0", Equal); test(9, 4, 3, 2, Exact, "2.0", Equal); test(3, 2, 9, 4, Exact, "0.5", Equal); test(1, 1, 3, 1, Exact, "0.0", Equal); test(-3, 1, 3, 1, Nearest, "NaN", Equal); }
#[test]
fn test_log_base_rational_base() {
let test = |n: i64, d: u64, bn: u64, bd: u64, out: &str| {
let x = Float::exact_from(Rational::from_signeds(n, i64::exact_from(d)));
let base = Rational::from_unsigneds(bn, bd);
let log = x.clone().log_base(base.clone());
assert!(log.is_valid());
assert_eq!(log.to_string(), out);
let log_alt = (&x).log_base(&base);
assert_eq!(ComparableFloatRef(&log_alt), ComparableFloatRef(&log));
let mut x_alt = x.clone();
x_alt.log_base_assign(&base);
assert_eq!(ComparableFloatRef(&x_alt), ComparableFloatRef(&log));
assert_eq!(
ComparableFloatRef(&Float::from(&rug_log_base_rational_base(
&rug::Float::exact_from(&x),
&base
))),
ComparableFloatRef(&log),
);
};
test(9, 1, 3, 1, "2.0"); test(81, 1, 3, 1, "4.0"); test(2, 1, 4, 1, "0.5"); test(1, 4, 4, 1, "-1.0"); }
fn log_base_rational_base_prec_round_properties_helper(
x: &Float,
base: &Rational,
prec: u64,
rm: RoundingMode,
extreme: bool,
) {
let (log, o) = check(x, base, prec, rm, extreme);
if let Ok(n) = u64::try_from(base) {
let (alt, o_alt) = x.log_base_prec_round_ref(n, prec, rm);
assert_eq!(ComparableFloatRef(&alt), ComparableFloatRef(&log));
assert_eq!(o_alt, o);
}
if log.is_normal() {
if *x > 1u32 && o > Less {
assert!(log > 0u32);
} else if *x > 0u32 && *x < 1u32 && o < Greater {
assert!(log < 0u32);
}
}
if o == Equal {
for rm in exhaustive_rounding_modes() {
let (alt, o_alt) = x.log_base_rational_base_prec_round_ref(base, prec, rm);
assert_eq!(
ComparableFloat(alt.abs_negative_zero()),
ComparableFloat(log.abs_negative_zero_ref())
);
assert_eq!(o_alt, Equal);
}
} else {
assert_panic!(x.log_base_rational_base_prec_round_ref(base, prec, Exact));
}
}
#[test]
fn log_base_rational_base_prec_round_properties() {
float_rational_unsigned_rounding_mode_quadruple_gen_var_11().test_properties_with_limit(
20,
|(x, base, prec, rm)| {
log_base_rational_base_prec_round_properties_helper(&x, &base, prec, rm, false);
},
);
float_rational_unsigned_rounding_mode_quadruple_gen_var_12().test_properties_with_limit(
20,
|(x, base, prec, rm)| {
log_base_rational_base_prec_round_properties_helper(&x, &base, prec, rm, true);
},
);
unsigned_rounding_mode_pair_gen_var_3().test_properties(|(prec, rm)| {
for base in [Rational::from(3u32), Rational::from_unsigneds(3u32, 2)] {
let f = |x: Float| x.log_base_rational_base_prec_round(&base, prec, rm);
assert!(f(Float::NAN).0.is_nan());
assert_eq!(f(Float::INFINITY), (Float::INFINITY, Equal));
assert!(f(Float::NEGATIVE_INFINITY).0.is_nan());
assert_eq!(f(Float::ZERO), (Float::NEGATIVE_INFINITY, Equal));
assert_eq!(f(-Float::ZERO), (Float::NEGATIVE_INFINITY, Equal));
assert_eq!(f(Float::ONE), (Float::ZERO, Equal));
assert!(f(Float::from(-2)).0.is_nan());
}
});
}
#[test]
fn log_base_rational_base_prec_properties() {
let f = |x: Float, base: Rational, prec: u64, extreme: bool| {
let (log, o) = x.clone().log_base_rational_base_prec(&base, prec);
assert!(log.is_valid());
let (log_ref, o_ref) = x.log_base_rational_base_prec_ref(&base, prec);
assert_eq!(ComparableFloatRef(&log_ref), ComparableFloatRef(&log));
assert_eq!(o_ref, o);
let (expected, eo) = check(&x, &base, prec, Nearest, extreme);
assert_eq!(ComparableFloatRef(&log), ComparableFloatRef(&expected));
assert_eq!(o, eo);
};
float_rational_unsigned_rounding_mode_quadruple_gen_var_11()
.test_properties_with_limit(20, |(x, base, prec, _rm)| f(x, base, prec, false));
float_rational_unsigned_rounding_mode_quadruple_gen_var_12()
.test_properties_with_limit(20, |(x, base, prec, _rm)| f(x, base, prec, true));
}
#[test]
fn log_base_rational_base_round_properties() {
let f = |x: Float, base: Rational, rm: RoundingMode, extreme: bool| {
let (log, o) = x.clone().log_base_rational_base_round(&base, rm);
assert!(log.is_valid());
let (log_ref, o_ref) = x.log_base_rational_base_round_ref(&base, rm);
assert_eq!(ComparableFloatRef(&log_ref), ComparableFloatRef(&log));
assert_eq!(o_ref, o);
let (expected, eo) = check(&x, &base, x.significant_bits(), rm, extreme);
assert_eq!(ComparableFloatRef(&log), ComparableFloatRef(&expected));
assert_eq!(o, eo);
};
float_rational_rounding_mode_triple_gen_var_12()
.test_properties_with_limit(20, |(x, base, rm)| f(x, base, rm, false));
float_rational_rounding_mode_triple_gen_var_13()
.test_properties_with_limit(20, |(x, base, rm)| f(x, base, rm, true));
}
#[test]
fn log_base_rational_base_properties() {
let f = |x: Float, base: Rational, extreme: bool| {
let (expected, _) = check(&x, &base, x.significant_bits(), Nearest, extreme);
let log = x.clone().log_base(base.clone());
assert!(log.is_valid());
assert_eq!(ComparableFloatRef(&log), ComparableFloatRef(&expected));
let log_ref = (&x).log_base(&base);
assert_eq!(ComparableFloatRef(&log_ref), ComparableFloatRef(&log));
let mut x_alt = x.clone();
x_alt.log_base_assign(&base);
assert_eq!(ComparableFloatRef(&x_alt), ComparableFloatRef(&log));
};
float_rational_rounding_mode_triple_gen_var_12()
.test_properties_with_limit(20, |(x, base, _rm)| f(x, base, false));
float_rational_rounding_mode_triple_gen_var_13()
.test_properties_with_limit(20, |(x, base, _rm)| f(x, base, true));
}
#[test]
fn log_base_rational_base_fail() {
assert_panic!(Float::from(8).log_base_rational_base_prec_round(
&Rational::from(3u32),
0,
Nearest
));
assert_panic!(Float::from(8).log_base_rational_base_prec_round(&Rational::ONE, 10, Nearest));
assert_panic!(Float::from(8).log_base_rational_base_prec_round(
&Rational::from_unsigneds(1u32, 2),
10,
Nearest
));
assert_panic!(Float::from(2).log_base_rational_base_prec_round(
&Rational::from(3u32),
10,
Exact
));
}
#[test]
#[allow(clippy::type_repetition_in_bounds)]
fn test_primitive_float_log_base_rational_base() {
fn test<T: PrimitiveFloat>(x: T, base: &Rational, out: T)
where
Float: From<T> + PartialOrd<T>,
for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
{
assert_eq!(
NiceFloat(primitive_float_log_base_rational_base(x, base)),
NiceFloat(out)
);
}
test::<f32>(f32::NAN, &Rational::from(10), f32::NAN);
test::<f32>(f32::INFINITY, &Rational::from(10), f32::INFINITY);
test::<f32>(f32::NEGATIVE_INFINITY, &Rational::from(10), f32::NAN);
test::<f32>(0.0, &Rational::from(10), f32::NEGATIVE_INFINITY);
test::<f32>(-0.0, &Rational::from(10), f32::NEGATIVE_INFINITY);
test::<f32>(1.0, &Rational::from(10), 0.0);
test::<f32>(8.0, &Rational::from(4), 1.5); test::<f32>(9.0, &Rational::from(3), 2.0); test::<f32>(1000.0, &Rational::from(10), 3.0); test::<f32>(2.25, &Rational::from_unsigneds(3u8, 2), 2.0); test::<f32>(2.0, &Rational::from_unsigneds(3u8, 2), 1.7095113); test::<f32>(50.0, &Rational::from(10), 1.6989699602127075); test::<f32>(-1.0, &Rational::from(10), f32::NAN);
test::<f64>(f64::NAN, &Rational::from(10), f64::NAN);
test::<f64>(f64::INFINITY, &Rational::from(10), f64::INFINITY);
test::<f64>(f64::NEGATIVE_INFINITY, &Rational::from(10), f64::NAN);
test::<f64>(0.0, &Rational::from(10), f64::NEGATIVE_INFINITY);
test::<f64>(-0.0, &Rational::from(10), f64::NEGATIVE_INFINITY);
test::<f64>(1.0, &Rational::from(10), 0.0);
test::<f64>(8.0, &Rational::from(4), 1.5); test::<f64>(9.0, &Rational::from(3), 2.0); test::<f64>(1000.0, &Rational::from(10), 3.0); test::<f64>(2.25, &Rational::from_unsigneds(3u8, 2), 2.0); test::<f64>(2.0, &Rational::from_unsigneds(3u8, 2), 1.7095112913514547); test::<f64>(50.0, &Rational::from(10), 1.6989700043360187); test::<f64>(-1.0, &Rational::from(10), f64::NAN);
}
#[allow(clippy::type_repetition_in_bounds)]
fn primitive_float_log_base_rational_base_properties_helper<T: PrimitiveFloat>()
where
Float: From<T> + PartialOrd<T>,
for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
{
float_rational_rounding_mode_triple_gen_var_12().test_properties(|(_, base, _)| {
for x in [T::ZERO, T::ONE, T::TWO, T::NEGATIVE_ONE] {
let y = primitive_float_log_base_rational_base(x, &base);
if let Ok(b) = u64::try_from(&base) {
assert_eq!(NiceFloat(y), NiceFloat(primitive_float_log_base(x, b)));
}
}
});
}
#[test]
fn primitive_float_log_base_rational_base_properties() {
apply_fn_to_primitive_floats!(primitive_float_log_base_rational_base_properties_helper);
}