// 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::basic::floats::PrimitiveFloat;
use malachite_base::num::basic::unsigneds::PrimitiveUnsigned;
use malachite_base::num::conversion::mantissa_and_exponent::{
from_sci_mantissa_and_exponent_round, sci_mantissa_and_exponent_round,
};
use malachite_base::num::conversion::traits::SciMantissaAndExponent;
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_12, primitive_float_signed_pair_gen_var_1,
primitive_float_signed_pair_gen_var_2, primitive_float_unsigned_pair_gen_var_1,
primitive_float_unsigned_pair_gen_var_2,
primitive_float_unsigned_rounding_mode_triple_gen_var_1,
primitive_float_unsigned_rounding_mode_triple_gen_var_2, unsigned_gen_var_1,
unsigned_rounding_mode_pair_gen_var_1,
};
use std::cmp::Ordering::{self, *};
use std::panic::catch_unwind;
#[test]
pub fn test_sci_mantissa_and_exponent() {
fn test_unsigned, U: PrimitiveFloat>(
x: T,
mantissa: U,
exponent: u64,
) {
assert_eq!(NiceFloat(x.sci_mantissa()), NiceFloat(mantissa));
assert_eq!(SciMantissaAndExponent::::sci_exponent(x), exponent);
let (actual_mantissa, actual_exponent) = x.sci_mantissa_and_exponent();
assert_eq!(NiceFloat(actual_mantissa), NiceFloat(mantissa));
assert_eq!(actual_exponent, exponent);
}
test_unsigned::(1, 1.0, 0);
test_unsigned::(2, 1.0, 1);
test_unsigned::(3, 1.5, 1);
test_unsigned::(100, 1.5625, 6);
test_unsigned::(65536, 1.0, 16);
test_unsigned::(u16::MAX, 1.9999695, 15);
test_unsigned::(u32::MAX, 1.0, 32);
test_unsigned::(u64::MAX, 1.0, 64);
test_unsigned::(u16::MAX, 1.999969482421875, 15);
test_unsigned::(u32::MAX, 1.9999999995343387, 31);
test_unsigned::(u64::MAX, 1.0, 64);
fn test_primitive_float(x: T, mantissa: T, exponent: i64) {
assert_eq!(NiceFloat(x.sci_mantissa()), NiceFloat(mantissa));
assert_eq!(x.sci_exponent(), exponent);
let (actual_mantissa, actual_exponent) = x.sci_mantissa_and_exponent();
assert_eq!(NiceFloat(actual_mantissa), NiceFloat(mantissa));
assert_eq!(actual_exponent, exponent);
}
test_primitive_float::(1.0, 1.0, 0);
test_primitive_float::(core::f32::consts::PI, core::f32::consts::FRAC_PI_2, 1);
test_primitive_float::(0.1, 1.6, -4);
test_primitive_float::(10.0, 1.25, 3);
test_primitive_float::(f32::MIN_POSITIVE_SUBNORMAL, 1.0, -149);
test_primitive_float::(f32::MAX_SUBNORMAL, 1.9999998, -127);
test_primitive_float::(f32::MIN_POSITIVE_NORMAL, 1.0, -126);
test_primitive_float::(f32::MAX_FINITE, 1.9999999, 127);
test_primitive_float::(1.0, 1.0, 0);
test_primitive_float::(core::f64::consts::PI, core::f64::consts::FRAC_PI_2, 1);
test_primitive_float::(0.1, 1.6, -4);
test_primitive_float::(10.0, 1.25, 3);
test_primitive_float::(f64::MIN_POSITIVE_SUBNORMAL, 1.0, -1074);
test_primitive_float::(f64::MAX_SUBNORMAL, 1.9999999999999996, -1023);
test_primitive_float::(f64::MIN_POSITIVE_NORMAL, 1.0, -1022);
test_primitive_float::(f64::MAX_FINITE, 1.9999999999999998, 1023);
}
fn sci_mantissa_and_exponent_fail_helper_unsigned<
T: PrimitiveUnsigned + SciMantissaAndExponent,
U: PrimitiveFloat,
>() {
assert_panic!(SciMantissaAndExponent::::sci_mantissa_and_exponent(
T::ZERO
));
}
fn sci_mantissa_and_exponent_fail_helper_primitive_float() {
assert_panic!(T::NAN.sci_mantissa_and_exponent());
assert_panic!(T::INFINITY.sci_mantissa_and_exponent());
assert_panic!(T::NEGATIVE_INFINITY.sci_mantissa_and_exponent());
assert_panic!(T::ZERO.sci_mantissa_and_exponent());
assert_panic!(T::NEGATIVE_ZERO.sci_mantissa_and_exponent());
}
#[test]
pub fn sci_mantissa_and_exponent_fail() {
apply_fn_to_unsigneds_and_primitive_floats!(sci_mantissa_and_exponent_fail_helper_unsigned);
apply_fn_to_primitive_floats!(sci_mantissa_and_exponent_fail_helper_primitive_float);
}
#[test]
pub fn test_sci_mantissa_and_exponent_round() {
fn test, U: PrimitiveFloat>(
x: T,
rm: RoundingMode,
out: Option<(U, u64, Ordering)>,
) {
assert_eq!(
sci_mantissa_and_exponent_round::(x, rm).map(|(m, e, o)| (NiceFloat(m), e, o)),
out.map(|(m, e, o)| (NiceFloat(m), e, o))
);
}
test::(1, Floor, Some((1.0, 0, Equal)));
test::(1, Down, Some((1.0, 0, Equal)));
test::(1, Ceiling, Some((1.0, 0, Equal)));
test::(1, Up, Some((1.0, 0, Equal)));
test::(1, Nearest, Some((1.0, 0, Equal)));
test::(1, Exact, Some((1.0, 0, Equal)));
test::(2, Floor, Some((1.0, 1, Equal)));
test::(3, Floor, Some((1.5, 1, Equal)));
test::(100, Floor, Some((1.5625, 6, Equal)));
test::(65536, Floor, Some((1.0, 16, Equal)));
test::(u16::MAX, Floor, Some((1.9999695, 15, Equal)));
test::(u16::MAX, Down, Some((1.9999695, 15, Equal)));
test::(u16::MAX, Ceiling, Some((1.9999695, 15, Equal)));
test::(u16::MAX, Up, Some((1.9999695, 15, Equal)));
test::(u16::MAX, Nearest, Some((1.9999695, 15, Equal)));
test::(u16::MAX, Exact, Some((1.9999695, 15, Equal)));
test::(u32::MAX, Floor, Some((1.9999999, 31, Less)));
test::(u32::MAX, Down, Some((1.9999999, 31, Less)));
test::(u32::MAX, Ceiling, Some((1.0, 32, Greater)));
test::(u32::MAX, Up, Some((1.0, 32, Greater)));
test::(u32::MAX, Nearest, Some((1.0, 32, Greater)));
test::(u32::MAX, Exact, None);
test::(u64::MAX, Floor, Some((1.9999999, 63, Less)));
test::(u64::MAX, Down, Some((1.9999999, 63, Less)));
test::(u64::MAX, Ceiling, Some((1.0, 64, Greater)));
test::(u64::MAX, Up, Some((1.0, 64, Greater)));
test::(u64::MAX, Nearest, Some((1.0, 64, Greater)));
test::(u64::MAX, Exact, None);
test::(u16::MAX, Floor, Some((1.999969482421875, 15, Equal)));
test::(u16::MAX, Down, Some((1.999969482421875, 15, Equal)));
test::(u16::MAX, Ceiling, Some((1.999969482421875, 15, Equal)));
test::(u16::MAX, Up, Some((1.999969482421875, 15, Equal)));
test::(u16::MAX, Nearest, Some((1.999969482421875, 15, Equal)));
test::(u16::MAX, Exact, Some((1.999969482421875, 15, Equal)));
test::(u32::MAX, Floor, Some((1.9999999995343387, 31, Equal)));
test::(u32::MAX, Down, Some((1.9999999995343387, 31, Equal)));
test::(u32::MAX, Ceiling, Some((1.9999999995343387, 31, Equal)));
test::(u32::MAX, Up, Some((1.9999999995343387, 31, Equal)));
test::(u32::MAX, Nearest, Some((1.9999999995343387, 31, Equal)));
test::(u32::MAX, Exact, Some((1.9999999995343387, 31, Equal)));
test::(u64::MAX, Floor, Some((1.9999999999999998, 63, Less)));
test::(u64::MAX, Down, Some((1.9999999999999998, 63, Less)));
test::(u64::MAX, Ceiling, Some((1.0, 64, Greater)));
test::(u64::MAX, Up, Some((1.0, 64, Greater)));
test::(u64::MAX, Nearest, Some((1.0, 64, Greater)));
test::(u64::MAX, Exact, None);
}
fn sci_mantissa_and_exponent_round_fail_helper() {
assert_panic!(sci_mantissa_and_exponent_round::(T::ZERO, Floor));
}
#[test]
pub fn sci_mantissa_and_exponent_round_fail() {
apply_fn_to_unsigneds_and_primitive_floats!(sci_mantissa_and_exponent_round_fail_helper);
}
#[test]
pub fn test_from_sci_mantissa_and_exponent() {
fn test_unsigned, U: PrimitiveFloat>(
mantissa: U,
exponent: u64,
x: Option,
) {
assert_eq!(T::from_sci_mantissa_and_exponent(mantissa, exponent), x);
}
test_unsigned::(1.0, 0, Some(1));
test_unsigned::(1.0, 1, Some(2));
test_unsigned::(1.5, 0, Some(2));
test_unsigned::(1.5, 1, Some(3));
test_unsigned::(1.5626, 6, Some(100));
test_unsigned::(1.0, 16, Some(65536));
test_unsigned::(1.0, 100, None);
test_unsigned::(1.0, 32, None);
fn test_primitive_float(mantissa: T, exponent: i64, x: Option) {
assert_eq!(
T::from_sci_mantissa_and_exponent(mantissa, exponent).map(NiceFloat),
x.map(NiceFloat)
);
}
test_primitive_float::(1.0, 0, Some(1.0));
test_primitive_float::(std::f32::consts::FRAC_PI_2, 1, Some(core::f32::consts::PI));
test_primitive_float::(1.6, -4, Some(0.1));
test_primitive_float::(1.25, 3, Some(10.0));
test_primitive_float::(1.0, -149, Some(f32::MIN_POSITIVE_SUBNORMAL));
test_primitive_float::(1.9999998, -127, Some(f32::MAX_SUBNORMAL));
test_primitive_float::(1.0, -126, Some(f32::MIN_POSITIVE_NORMAL));
test_primitive_float::(1.9999999, 127, Some(f32::MAX_FINITE));
test_primitive_float::(2.0, 1, None);
test_primitive_float::(1.1, -2000, None);
test_primitive_float::(1.1, 2000, None);
test_primitive_float::(1.999, -149, None); // precision too high
test_primitive_float::(1.0, 0, Some(1.0));
test_primitive_float::(std::f64::consts::FRAC_PI_2, 1, Some(core::f64::consts::PI));
test_primitive_float::(1.6, -4, Some(0.1));
test_primitive_float::(1.25, 3, Some(10.0));
test_primitive_float::(1.0, -1074, Some(f64::MIN_POSITIVE_SUBNORMAL));
test_primitive_float::(1.9999999999999996, -1023, Some(f64::MAX_SUBNORMAL));
test_primitive_float::(1.0, -1022, Some(f64::MIN_POSITIVE_NORMAL));
test_primitive_float::(1.9999999999999998, 1023, Some(f64::MAX_FINITE));
test_primitive_float::(2.0, 1, None);
test_primitive_float::(1.1, -2000, None);
test_primitive_float::(1.1, 2000, None);
test_primitive_float::(1.999, -1074, None); // precision too high
}
#[test]
pub fn test_from_sci_mantissa_and_exponent_round() {
fn test(
mantissa: U,
exponent: u64,
rm: RoundingMode,
xo: Option<(T, Ordering)>,
) {
assert_eq!(
from_sci_mantissa_and_exponent_round::(mantissa, exponent, rm),
xo
);
}
test::(1.0, 0, Floor, Some((1, Equal)));
test::(1.0, 0, Down, Some((1, Equal)));
test::(1.0, 0, Ceiling, Some((1, Equal)));
test::(1.0, 0, Up, Some((1, Equal)));
test::(1.0, 0, Nearest, Some((1, Equal)));
test::(1.0, 0, Exact, Some((1, Equal)));
test::(1.25, 0, Floor, Some((1, Less)));
test::(1.25, 0, Down, Some((1, Less)));
test::(1.25, 0, Ceiling, Some((2, Greater)));
test::(1.25, 0, Up, Some((2, Greater)));
test::(1.25, 0, Nearest, Some((1, Less)));
test::(1.25, 0, Exact, None);
test::(1.5, 0, Floor, Some((1, Less)));
test::(1.5, 0, Down, Some((1, Less)));
test::(1.5, 0, Ceiling, Some((2, Greater)));
test::(1.5, 0, Up, Some((2, Greater)));
test::(1.5, 0, Nearest, Some((2, Greater)));
test::(1.5, 0, Exact, None);
test::(1.75, 0, Floor, Some((1, Less)));
test::(1.75, 0, Down, Some((1, Less)));
test::(1.75, 0, Ceiling, Some((2, Greater)));
test::(1.75, 0, Up, Some((2, Greater)));
test::(1.75, 0, Nearest, Some((2, Greater)));
test::(1.75, 0, Exact, None);
test::(1.5, 1, Floor, Some((3, Equal)));
test::(1.5, 1, Down, Some((3, Equal)));
test::(1.5, 1, Ceiling, Some((3, Equal)));
test::(1.5, 1, Up, Some((3, Equal)));
test::(1.5, 1, Nearest, Some((3, Equal)));
test::(1.5, 1, Exact, Some((3, Equal)));
test::(1.0, 100, Floor, None);
test::(1.0, 100, Down, None);
test::(1.0, 100, Ceiling, None);
test::(1.0, 100, Up, None);
test::(1.0, 100, Nearest, None);
test::(1.0, 100, Exact, None);
}
fn from_sci_mantissa_and_exponent_round_fail_helper() {
assert_panic!(from_sci_mantissa_and_exponent_round::(
U::ZERO,
0,
Floor
));
}
#[test]
pub fn from_sci_mantissa_and_exponent_round_fail() {
apply_fn_to_unsigneds_and_primitive_floats!(from_sci_mantissa_and_exponent_round_fail_helper);
}
fn sci_mantissa_and_exponent_properties_helper_unsigned<
T: PrimitiveUnsigned + SciMantissaAndExponent,
U: PrimitiveFloat,
>() {
unsigned_gen_var_1::().test_properties(|x| {
let (mantissa, exponent) = SciMantissaAndExponent::::sci_mantissa_and_exponent(x);
assert_eq!(NiceFloat(x.sci_mantissa()), NiceFloat(mantissa));
assert_eq!(SciMantissaAndExponent::::sci_exponent(x), exponent);
assert_eq!(
sci_mantissa_and_exponent_round(x, Nearest).map(|(m, e, _)| (NiceFloat(m), e)),
Some((NiceFloat(mantissa), exponent))
);
assert!(exponent <= T::WIDTH);
assert!(mantissa >= U::ONE);
assert!(mantissa < U::TWO);
});
}
fn sci_mantissa_and_exponent_properties_helper_primitive_float() {
primitive_float_gen_var_12::().test_properties(|x| {
let (mantissa, exponent) = x.sci_mantissa_and_exponent();
assert_eq!(NiceFloat(x.sci_mantissa()), NiceFloat(mantissa));
assert_eq!(x.sci_exponent(), exponent);
assert_eq!(
NiceFloat(T::from_sci_mantissa_and_exponent(mantissa, exponent).unwrap()),
NiceFloat(x.abs())
);
assert!(exponent >= T::MIN_EXPONENT);
assert!(exponent <= T::MAX_EXPONENT);
assert!(mantissa >= T::ONE);
assert!(mantissa < T::TWO);
let precision = x.precision();
assert_eq!(mantissa.precision(), precision);
assert!(precision <= T::max_precision_for_sci_exponent(exponent));
});
}
#[test]
fn sci_mantissa_and_exponent_properties() {
apply_fn_to_unsigneds_and_primitive_floats!(
sci_mantissa_and_exponent_properties_helper_unsigned
);
apply_fn_to_primitive_floats!(sci_mantissa_and_exponent_properties_helper_primitive_float);
}
fn sci_mantissa_and_exponent_properties_round_helper() {
unsigned_rounding_mode_pair_gen_var_1::().test_properties(|(x, rm)| {
if let Some((mantissa, exponent, o)) = sci_mantissa_and_exponent_round::(x, rm) {
assert!(mantissa >= U::ONE);
assert!(mantissa < U::TWO);
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!(
sci_mantissa_and_exponent_round(x, rm),
Some((mantissa, exponent, Equal))
);
}
} else {
assert!(sci_mantissa_and_exponent_round::(x, Exact).is_none(),);
}
}
});
unsigned_gen_var_1::().test_properties(|n| {
let (floor_mantissa, floor_exponent, o_floor) =
sci_mantissa_and_exponent_round::(n, Floor).unwrap();
assert_ne!(o_floor, Greater);
assert_eq!(
sci_mantissa_and_exponent_round::(n, Down).unwrap(),
(floor_mantissa, floor_exponent, o_floor)
);
let (ceiling_mantissa, ceiling_exponent, o_ceiling) =
sci_mantissa_and_exponent_round::(n, Ceiling).unwrap();
assert_ne!(o_ceiling, Less);
assert_eq!(
sci_mantissa_and_exponent_round::(n, Up).unwrap(),
(ceiling_mantissa, ceiling_exponent, o_ceiling)
);
let (nearest_mantissa, nearest_exponent, o_nearest) =
sci_mantissa_and_exponent_round::(n, Nearest).unwrap();
if let Some((mantissa, exponent, o)) = sci_mantissa_and_exponent_round::(n, Exact) {
assert_eq!(o, Equal);
assert_eq!(floor_mantissa, mantissa);
assert_eq!(ceiling_mantissa, mantissa);
assert_eq!(nearest_mantissa, mantissa);
assert_eq!(floor_exponent, exponent);
assert_eq!(ceiling_exponent, exponent);
assert_eq!(nearest_exponent, exponent);
} else {
assert_ne!(
(floor_mantissa, floor_exponent),
(ceiling_mantissa, ceiling_exponent)
);
assert!(
(nearest_mantissa, nearest_exponent, o_nearest)
== (floor_mantissa, floor_exponent, Less)
|| (nearest_mantissa, nearest_exponent, o_nearest)
== (ceiling_mantissa, ceiling_exponent, Greater)
);
if ceiling_mantissa == U::ONE {
assert_eq!(floor_mantissa, U::TWO.next_lower());
assert_eq!(floor_exponent, ceiling_exponent - 1);
} else {
assert_eq!(floor_mantissa, ceiling_mantissa.next_lower());
assert_eq!(floor_exponent, ceiling_exponent);
}
}
});
}
#[test]
fn sci_mantissa_and_exponent_round_properties() {
apply_fn_to_unsigneds_and_primitive_floats!(sci_mantissa_and_exponent_properties_round_helper);
}
fn from_sci_mantissa_and_exponent_properties_helper_unsigned<
T: PrimitiveUnsigned + SciMantissaAndExponent,
U: PrimitiveFloat,
>() {
primitive_float_unsigned_pair_gen_var_1::().test_properties(|(m, e)| {
T::from_sci_mantissa_and_exponent(m, e);
});
primitive_float_unsigned_pair_gen_var_2::().test_properties(|(m, e)| {
assert!(m >= U::ONE && m < U::TWO);
let on = T::from_sci_mantissa_and_exponent(m, e);
assert_eq!(
from_sci_mantissa_and_exponent_round(m, e, Nearest).map(|p| p.0),
on
);
});
}
fn from_sci_mantissa_and_exponent_properties_helper_primitive_float() {
primitive_float_signed_pair_gen_var_1().test_properties(|(mantissa, exponent)| {
T::from_sci_mantissa_and_exponent(mantissa, exponent);
});
primitive_float_signed_pair_gen_var_2::().test_properties(|(mantissa, exponent)| {
let f = T::from_sci_mantissa_and_exponent(mantissa, exponent).unwrap();
assert!(!f.is_nan());
assert_eq!(f.sign(), Greater);
assert_eq!(f.sci_mantissa_and_exponent(), (mantissa, exponent));
});
}
#[test]
fn from_sci_mantissa_and_exponent_properties() {
apply_fn_to_unsigneds_and_primitive_floats!(
from_sci_mantissa_and_exponent_properties_helper_unsigned
);
apply_fn_to_primitive_floats!(from_sci_mantissa_and_exponent_properties_helper_primitive_float);
}
fn from_sci_mantissa_and_exponent_properties_round_helper<
T: PrimitiveUnsigned,
U: PrimitiveFloat,
>() {
primitive_float_unsigned_rounding_mode_triple_gen_var_1::().test_properties(
|(m, e, rm)| {
let on = from_sci_mantissa_and_exponent_round::(m, e, rm);
if let Some((x, o)) = on {
assert!(m >= U::ONE && m < U::TWO);
if o == Equal {
for rm in exhaustive_rounding_modes() {
assert_eq!(
from_sci_mantissa_and_exponent_round::(m, e, rm),
Some((x, Equal))
);
}
} else {
assert!(from_sci_mantissa_and_exponent_round::(m, e, Exact).is_none());
}
}
},
);
primitive_float_unsigned_rounding_mode_triple_gen_var_2::().test_properties(|(m, e, rm)| {
assert!(m >= U::ONE && m < U::TWO);
from_sci_mantissa_and_exponent_round::(m, e, rm);
});
primitive_float_unsigned_pair_gen_var_2::().test_properties(|(m, e)| {
if let Some(ceiling_n) = from_sci_mantissa_and_exponent_round::(m, e, Ceiling) {
assert_eq!(
from_sci_mantissa_and_exponent_round::(m, e, Up).unwrap(),
ceiling_n
);
let floor_n = from_sci_mantissa_and_exponent_round::(m, e, Floor).unwrap();
assert_eq!(
from_sci_mantissa_and_exponent_round::(m, e, Down).unwrap(),
floor_n
);
let nearest_n = from_sci_mantissa_and_exponent_round::(m, e, Nearest).unwrap();
if let Some(n) = from_sci_mantissa_and_exponent_round::(m, e, Exact) {
assert_eq!(floor_n, n);
assert_eq!(ceiling_n, n);
assert_eq!(nearest_n, n);
} else {
assert!(nearest_n == floor_n || nearest_n == ceiling_n);
if floor_n.0 != T::MAX {
assert_eq!(ceiling_n.0, floor_n.0 + T::ONE);
}
}
}
});
}
#[test]
fn from_sci_mantissa_and_exponent_round_properties() {
apply_fn_to_unsigneds_and_primitive_floats!(
from_sci_mantissa_and_exponent_properties_round_helper
);
}