use core::cmp::Ordering::{self, *};
use malachite_base::num::arithmetic::traits::LogBase;
use malachite_base::num::basic::floats::PrimitiveFloat;
use malachite_base::num::basic::traits::{
Infinity, NaN, NegativeInfinity, NegativeZero, 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::{
primitive_float_pair_gen, unsigned_rounding_mode_pair_gen_var_3,
};
use malachite_float::arithmetic::log_base_float_base::primitive_float_log_base_float_base;
use malachite_float::arithmetic::log_base_rational_base::primitive_float_log_base_rational_base;
use malachite_float::test_util::arithmetic::log_base_float_base::{
rug_log_base_float_base, rug_log_base_float_base_prec, rug_log_base_float_base_prec_round,
rug_log_base_float_base_round,
};
use malachite_float::test_util::common::rug_round_try_from_rounding_mode;
use malachite_float::test_util::generators::{
float_float_rounding_mode_triple_gen_var_35, float_float_rounding_mode_triple_gen_var_36,
float_float_unsigned_rounding_mode_quadruple_gen_var_11,
float_float_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: &Float, prec: u64, rm: RoundingMode, extreme: bool) -> (Float, Ordering) {
let (log, o) = x.clone().log_base_float_base_prec_round(base, prec, rm);
assert!(log.is_valid());
let (log_alt, o_alt) = x.log_base_float_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_float_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.is_finite() && *base > 1u32 {
let (alt, o_alt) =
x.log_base_rational_base_prec_round_ref(&Rational::exact_from(base), prec, rm);
assert_eq!(ComparableFloatRef(&alt), ComparableFloatRef(&log));
assert_eq!(o_alt, o);
}
let e = log.get_exponent();
let clamp_or_special = log == 0u32
|| log.is_infinite()
|| log.is_nan()
|| e == Some(Float::MIN_EXPONENT)
|| e == Some(Float::MAX_EXPONENT);
if !extreme
&& !clamp_or_special
&& x.significant_bits() <= 128
&& base.significant_bits() <= 128
&& let Ok(rug_rm) = rug_round_try_from_rounding_mode(rm)
{
let (rug_log, rug_o) = rug_log_base_float_base_prec_round(
&rug::Float::exact_from(x),
&rug::Float::exact_from(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_float_base_prec_round() {
let test = |x: f64, base: f64, prec: u64, rm: RoundingMode, out: &str, o_out| {
let x = Float::exact_from(x);
let base = Float::exact_from(base);
let (log, o) = check(&x, &base, prec, rm, false);
assert_eq!(log.to_string(), out);
assert_eq!(o, o_out);
};
test(9.0, 3.0, 10, Exact, "2.0", Equal); test(8.0, 2.0, 10, Exact, "3.0", Equal); test(8.0, 4.0, 10, Exact, "1.5", Equal); test(2.0, 4.0, 10, Exact, "0.5", Equal); test(4.0, 0.5, 10, Exact, "-2.0", Equal); test(0.25, 0.5, 10, Exact, "2.0", Equal); test(8.0, 0.5, 10, Exact, "-3.0", Equal); test(0.25, 2.0, 10, Exact, "-2.0", Equal); test(2.0, 3.0, 20, Floor, "0.630929", Less); test(2.0, 3.0, 20, Ceiling, "0.63093", Greater);
test(3.0, 2.0, 20, Nearest, "1.584963", Greater); test(1.0, 3.0, 10, Exact, "0.0", Equal); test(1.0, 0.5, 10, Exact, "-0.0", Equal); }
#[test]
fn test_log_base_float_base_specials() {
let p = |x: Float, base: Float| x.log_base_float_base_prec(&base, 10).0;
let cf = |x: Float| ComparableFloat(x);
let two = Float::from(2);
let half = Float::from(0.5);
assert!(p(Float::NAN, two.clone()).is_nan());
assert!(p(Float::from(5), Float::NAN).is_nan());
assert!(p(Float::from(-3), two.clone()).is_nan());
assert!(p(Float::NEGATIVE_INFINITY, two.clone()).is_nan());
assert!(p(Float::from(5), Float::from(-2)).is_nan());
assert!(p(Float::from(5), Float::NEGATIVE_INFINITY).is_nan());
assert_eq!(cf(p(Float::from(5), Float::INFINITY)), cf(Float::ZERO));
assert_eq!(
cf(p(half.clone(), Float::INFINITY)),
cf(Float::NEGATIVE_ZERO)
);
assert!(p(Float::INFINITY, Float::INFINITY).is_nan());
assert!(p(Float::ZERO, Float::INFINITY).is_nan());
assert_eq!(cf(p(Float::from(5), Float::ZERO)), cf(Float::NEGATIVE_ZERO));
assert_eq!(cf(p(half.clone(), Float::ZERO)), cf(Float::ZERO));
assert_eq!(
cf(p(Float::from(5), -Float::ZERO)),
cf(Float::NEGATIVE_ZERO)
);
assert!(p(Float::INFINITY, Float::ZERO).is_nan());
assert_eq!(p(Float::from(5), Float::ONE), Float::INFINITY);
assert_eq!(p(half.clone(), Float::ONE), Float::NEGATIVE_INFINITY);
assert_eq!(p(Float::INFINITY, Float::ONE), Float::INFINITY);
assert_eq!(p(Float::ZERO, Float::ONE), Float::NEGATIVE_INFINITY);
assert!(p(Float::ONE, Float::ONE).is_nan());
assert_eq!(p(Float::INFINITY, two.clone()), Float::INFINITY);
assert_eq!(p(Float::ZERO, two.clone()), Float::NEGATIVE_INFINITY);
assert_eq!(p(Float::INFINITY, half.clone()), Float::NEGATIVE_INFINITY);
assert_eq!(p(Float::ZERO, half.clone()), Float::INFINITY);
assert_eq!(cf(p(Float::ONE, two.clone())), cf(Float::ZERO));
assert_eq!(cf(p(Float::ONE, half.clone())), cf(Float::NEGATIVE_ZERO));
let _ = two;
}
#[test]
fn test_log_base_float_base_prec() {
let test = |x: f64, base: f64, prec: u64, out: &str, o_out| {
let x = Float::exact_from(x);
let base = Float::exact_from(base);
let (log, o) = x.clone().log_base_float_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_float_base_prec_ref(&base, prec);
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_float_base_prec_assign(&base, prec);
assert_eq!(ComparableFloatRef(&x_alt), ComparableFloatRef(&log));
assert_eq!(o_alt, o);
let (rug_log, rug_o) = rug_log_base_float_base_prec(
&rug::Float::exact_from(&x),
&rug::Float::exact_from(&base),
prec,
);
assert_eq!(
ComparableFloatRef(&Float::from(&rug_log)),
ComparableFloatRef(&log),
);
assert_eq!(rug_o, o);
};
test(9.0, 3.0, 10, "2.0", Equal);
test(8.0, 4.0, 10, "1.5", Equal);
test(4.0, 0.5, 10, "-2.0", Equal); test(2.0, 3.0, 20, "0.63093", Greater); test(1.0, 3.0, 10, "0.0", Equal);
}
#[test]
fn test_log_base_float_base_round() {
let test = |x: f64, base: f64, rm: RoundingMode, out: &str, o_out| {
let x = Float::exact_from(x);
let base = Float::exact_from(base);
let (log, o) = x.clone().log_base_float_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_float_base_round_ref(&base, rm);
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_float_base_round(
&rug::Float::exact_from(&x),
&rug::Float::exact_from(&base),
rug_rm,
);
assert_eq!(
ComparableFloatRef(&Float::from(&rug_log)),
ComparableFloatRef(&log),
);
assert_eq!(rug_o, o);
}
};
test(81.0, 3.0, Floor, "4.0", Equal);
test(81.0, 3.0, Ceiling, "4.0", Equal);
test(81.0, 3.0, Nearest, "4.0", Equal);
test(81.0, 3.0, Exact, "4.0", Equal);
test(0.25, 0.5, Exact, "2.0", Equal); test(1.0, 3.0, Exact, "0.0", Equal);
test(-3.0, 3.0, Nearest, "NaN", Equal); }
#[test]
fn test_log_base_float_base() {
let test = |x: f64, base: f64, out: &str| {
let x = Float::exact_from(x);
let base = Float::exact_from(base);
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));
assert_eq!(
ComparableFloatRef(&Float::from(&rug_log_base_float_base(
&rug::Float::exact_from(&x),
&rug::Float::exact_from(&base)
))),
ComparableFloatRef(&log),
);
};
test(81.0, 3.0, "4.0"); test(2.0, 4.0, "0.5"); test(0.25, 0.5, "2.0"); }
fn log_base_float_base_prec_round_properties_helper(
x: &Float,
base: &Float,
prec: u64,
rm: RoundingMode,
extreme: bool,
) {
let (log, o) = check(x, base, prec, rm, extreme);
if log.is_normal()
&& x.is_finite()
&& *x > 0u32
&& base.is_finite()
&& *base > 0u32
&& *base != 1u32
{
let x_big = *x > 1u32;
let base_big = *base > 1u32;
if *x != 1u32 && o != Equal {
assert_eq!(log > 0u32, x_big == base_big);
}
}
if o == Equal {
for rm in exhaustive_rounding_modes() {
let (alt, o_alt) = x.log_base_float_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_float_base_prec_round_ref(base, prec, Exact));
}
}
#[test]
fn log_base_float_base_prec_round_properties() {
float_float_unsigned_rounding_mode_quadruple_gen_var_11().test_properties_with_limit(
20,
|(x, base, prec, rm)| {
log_base_float_base_prec_round_properties_helper(&x, &base, prec, rm, false);
},
);
float_float_unsigned_rounding_mode_quadruple_gen_var_12().test_properties_with_limit(
20,
|(x, base, prec, rm)| {
log_base_float_base_prec_round_properties_helper(&x, &base, prec, rm, true);
},
);
unsigned_rounding_mode_pair_gen_var_3().test_properties(|(prec, rm)| {
let f = |x: Float, base: Float| x.log_base_float_base_prec_round(&base, prec, rm);
assert!(f(Float::NAN, Float::from(2)).0.is_nan());
assert!(f(Float::from(2), Float::NAN).0.is_nan());
assert!(f(Float::from(-2), Float::from(2)).0.is_nan());
assert!(f(Float::from(2), Float::from(-2)).0.is_nan());
assert_eq!(f(Float::INFINITY, Float::from(2)), (Float::INFINITY, Equal));
assert_eq!(
f(Float::ZERO, Float::from(2)),
(Float::NEGATIVE_INFINITY, Equal)
);
assert!(f(Float::ONE, Float::ONE).0.is_nan());
assert_eq!(
ComparableFloat(f(Float::ONE, Float::from(2)).0),
ComparableFloat(Float::ZERO)
);
});
}
#[test]
fn log_base_float_base_prec_properties() {
let f = |x: Float, base: Float, prec: u64, extreme: bool| {
let (log, o) = x.clone().log_base_float_base_prec(&base, prec);
assert!(log.is_valid());
let (log_ref, o_ref) = x.log_base_float_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_float_unsigned_rounding_mode_quadruple_gen_var_11()
.test_properties_with_limit(20, |(x, base, prec, _rm)| f(x, base, prec, false));
float_float_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_float_base_round_properties() {
let f = |x: Float, base: Float, rm: RoundingMode, extreme: bool| {
let (log, o) = x.clone().log_base_float_base_round(&base, rm);
assert!(log.is_valid());
let (log_ref, o_ref) = x.log_base_float_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_float_rounding_mode_triple_gen_var_35()
.test_properties_with_limit(20, |(x, base, rm)| f(x, base, rm, false));
float_float_rounding_mode_triple_gen_var_36()
.test_properties_with_limit(20, |(x, base, rm)| f(x, base, rm, true));
}
#[test]
fn log_base_float_base_properties() {
let f = |x: Float, base: Float, 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));
};
float_float_rounding_mode_triple_gen_var_35()
.test_properties_with_limit(20, |(x, base, _rm)| f(x, base, false));
float_float_rounding_mode_triple_gen_var_36()
.test_properties_with_limit(20, |(x, base, _rm)| f(x, base, true));
}
#[test]
fn log_base_float_base_fail() {
assert_panic!(Float::from(8).log_base_float_base_prec_round(&Float::from(3), 0, Nearest));
assert_panic!(Float::from(2).log_base_float_base_prec_round(&Float::from(3), 10, Exact));
}
#[test]
#[allow(clippy::type_repetition_in_bounds)]
fn test_primitive_float_log_base_float_base() {
fn test<T: PrimitiveFloat>(x: T, base: T, out: T)
where
Float: From<T> + PartialOrd<T>,
for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
{
assert_eq!(
NiceFloat(primitive_float_log_base_float_base(x, base)),
NiceFloat(out)
);
}
test::<f32>(f32::NAN, 10.0, f32::NAN);
test::<f32>(8.0, f32::NAN, f32::NAN);
test::<f32>(-1.0, 10.0, f32::NAN); test::<f32>(8.0, -2.0, f32::NAN); test::<f32>(8.0, 4.0, 1.5); test::<f32>(4.0, 0.5, -2.0); test::<f32>(1000.0, 10.0, 3.0); test::<f32>(50.0, 10.0, 1.6989699602127075); test::<f32>(2.0, 1.5, 1.7095113); test::<f32>(0.0, 10.0, f32::NEGATIVE_INFINITY); test::<f32>(0.0, 0.5, f32::INFINITY); test::<f32>(f32::INFINITY, 10.0, f32::INFINITY); test::<f32>(f32::INFINITY, 0.5, f32::NEGATIVE_INFINITY); test::<f32>(1.0, 10.0, 0.0); test::<f32>(8.0, f32::INFINITY, 0.0); test::<f32>(8.0, 1.0, f32::INFINITY); test::<f32>(0.5, 1.0, f32::NEGATIVE_INFINITY); test::<f32>(1.0, 1.0, f32::NAN);
test::<f64>(f64::NAN, 10.0, f64::NAN);
test::<f64>(8.0, f64::NAN, f64::NAN);
test::<f64>(-1.0, 10.0, f64::NAN); test::<f64>(8.0, -2.0, f64::NAN); test::<f64>(8.0, 4.0, 1.5); test::<f64>(4.0, 0.5, -2.0); test::<f64>(1000.0, 10.0, 3.0); test::<f64>(50.0, 10.0, 1.6989700043360187); test::<f64>(2.0, 1.5, 1.7095112913514547); test::<f64>(0.0, 10.0, f64::NEGATIVE_INFINITY); test::<f64>(0.0, 0.5, f64::INFINITY); test::<f64>(f64::INFINITY, 10.0, f64::INFINITY); test::<f64>(f64::INFINITY, 0.5, f64::NEGATIVE_INFINITY); test::<f64>(1.0, 10.0, 0.0); test::<f64>(8.0, f64::INFINITY, 0.0); test::<f64>(8.0, 1.0, f64::INFINITY); test::<f64>(0.5, 1.0, f64::NEGATIVE_INFINITY); test::<f64>(1.0, 1.0, f64::NAN); }
#[allow(clippy::type_repetition_in_bounds)]
fn primitive_float_log_base_float_base_properties_helper<T: PrimitiveFloat>()
where
Float: From<T> + PartialOrd<T>,
for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
Rational: ExactFrom<T>,
{
primitive_float_pair_gen::<T>().test_properties(|(x, base)| {
let y = primitive_float_log_base_float_base(x, base);
if base.is_finite() && base > T::ONE {
assert_eq!(
NiceFloat(y),
NiceFloat(primitive_float_log_base_rational_base(
x,
&Rational::exact_from(base)
))
);
}
});
}
#[test]
fn primitive_float_log_base_float_base_properties() {
apply_fn_to_primitive_floats!(primitive_float_log_base_float_base_properties_helper);
}