// 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::IsPowerOf2;
use malachite_base::num::basic::floats::PrimitiveFloat;
use malachite_base::num::basic::traits::{Infinity, NaN, NegativeInfinity, NegativeZero, Zero};
use malachite_base::num::conversion::traits::{ExactFrom, SciMantissaAndExponent};
use malachite_base::test_util::generators::primitive_float_gen_var_12;
use malachite_float::test_util::common::{parse_hex_string, to_hex_string};
use malachite_float::test_util::generators::{float_gen, float_gen_var_3};
use malachite_float::{ComparableFloat, ComparableFloatRef, Float};
use malachite_q::Rational;
use std::panic::catch_unwind;
#[allow(clippy::redundant_closure_for_method_calls)]
#[test]
fn test_ulp() {
let test = |s, s_hex, out: Option<&str>, out_hex: Option<&str>| {
let x = parse_hex_string(s_hex);
assert_eq!(x.to_string(), s);
let actual_out = x.ulp();
assert!(actual_out.as_ref().map_or(true, Float::is_valid));
let s = actual_out.as_ref().map(|x| x.to_string());
assert_eq!(s.as_deref(), out);
let s = actual_out.map(|x| to_hex_string(&x));
assert_eq!(s.as_deref(), out_hex);
};
test("NaN", "NaN", None, None);
test("Infinity", "Infinity", None, None);
test("-Infinity", "-Infinity", None, None);
test("0.0", "0x0.0", None, None);
test("-0.0", "-0x0.0", None, None);
test("1.0", "0x1.0#1", Some("1.0"), Some("0x1.0#1"));
test("2.0", "0x2.0#1", Some("2.0"), Some("0x2.0#1"));
test("0.5", "0x0.8#1", Some("0.5"), Some("0x0.8#1"));
test(
"0.33333333333333331",
"0x0.55555555555554#53",
Some("6.0e-17"),
Some("0x4.0E-14#1"),
);
test(
"1.4142135623730951",
"0x1.6a09e667f3bcd#53",
Some("2.0e-16"),
Some("0x1.0E-13#1"),
);
test(
"3.1415926535897931",
"0x3.243f6a8885a30#53",
Some("4.0e-16"),
Some("0x2.0E-13#1"),
);
test("-1.0", "-0x1.0#1", Some("1.0"), Some("0x1.0#1"));
test("-2.0", "-0x2.0#1", Some("2.0"), Some("0x2.0#1"));
test("-0.5", "-0x0.8#1", Some("0.5"), Some("0x0.8#1"));
test(
"-0.33333333333333331",
"-0x0.55555555555554#53",
Some("6.0e-17"),
Some("0x4.0E-14#1"),
);
test(
"-1.4142135623730951",
"-0x1.6a09e667f3bcd#53",
Some("2.0e-16"),
Some("0x1.0E-13#1"),
);
test(
"-3.1415926535897931",
"-0x3.243f6a8885a30#53",
Some("4.0e-16"),
Some("0x2.0E-13#1"),
);
}
#[test]
fn ulp_properties() {
float_gen().test_properties(|x| {
let ulp = x.ulp();
ulp.as_ref().map_or_else(
|| {
assert!(!x.is_normal());
},
|ulp| {
assert!(ulp.is_valid());
assert!(ulp.is_power_of_2());
},
);
assert_eq!((-x).ulp().map(ComparableFloat), ulp.map(ComparableFloat));
});
}
#[test]
fn test_increment() {
let test = |s, s_hex, out: &str, out_hex: &str| {
let mut x = parse_hex_string(s_hex);
assert_eq!(x.to_string(), s);
x.increment();
assert!(x.is_valid());
assert_eq!(x.to_string(), out);
assert_eq!(to_hex_string(&x), out_hex);
};
test("1.0", "0x1.0#1", "2.0", "0x2.0#2");
test("2.0", "0x2.0#1", "4.0", "0x4.0#2");
test("0.5", "0x0.8#1", "1.0", "0x1.0#2");
test(
"1.0",
"0x1.0000000000000000000000000#100",
"1.000000000000000000000000000002",
"0x1.0000000000000000000000002#100",
);
test(
"0.33333333333333331",
"0x0.55555555555554#53",
"0.33333333333333337",
"0x0.55555555555558#53",
);
test(
"1.4142135623730951",
"0x1.6a09e667f3bcd#53",
"1.4142135623730954",
"0x1.6a09e667f3bce#53",
);
test(
"3.1415926535897931",
"0x3.243f6a8885a30#53",
"3.1415926535897936",
"0x3.243f6a8885a32#53",
);
test("-1.0", "-0x1.0#1", "-0.0", "-0x0.0");
test("-2.0", "-0x2.0#1", "-0.0", "-0x0.0");
test("-0.5", "-0x0.8#1", "-0.0", "-0x0.0");
test(
"-1.0",
"-0x1.0000000000000000000000000#100",
"-0.999999999999999999999999999998",
"-0x0.ffffffffffffffffffffffffe#99",
);
test(
"-0.33333333333333331",
"-0x0.55555555555554#53",
"-0.33333333333333326",
"-0x0.55555555555550#53",
);
test(
"-1.4142135623730951",
"-0x1.6a09e667f3bcd#53",
"-1.4142135623730949",
"-0x1.6a09e667f3bcc#53",
);
test(
"-3.1415926535897931",
"-0x3.243f6a8885a30#53",
"-3.1415926535897927",
"-0x3.243f6a8885a2e#53",
);
}
#[test]
fn increment_fail() {
assert_panic!({
let mut x = Float::NAN;
x.increment();
});
assert_panic!({
let mut x = Float::INFINITY;
x.increment();
});
assert_panic!({
let mut x = Float::NEGATIVE_INFINITY;
x.increment();
});
assert_panic!({
let mut x = Float::ZERO;
x.increment();
});
assert_panic!({
let mut x = Float::NEGATIVE_ZERO;
x.increment();
});
}
#[test]
fn increment_properties() {
float_gen_var_3().test_properties(|mut x| {
let old_x = x.clone();
x.increment();
let final_x = x.clone();
assert!(x.is_valid());
assert_eq!(
Rational::exact_from(&old_x) + Rational::exact_from(old_x.ulp().unwrap()),
Rational::exact_from(&x)
);
if x != 0u32 {
x.decrement();
assert_eq!(ComparableFloatRef(&x), ComparableFloatRef(&old_x));
}
let mut x = -old_x;
x.decrement();
assert_eq!(ComparableFloat(x), ComparableFloat(-final_x));
});
primitive_float_gen_var_12::().test_properties(|x| {
let next_x = x.next_higher();
if next_x.is_finite() && next_x != 0.0 && x.sci_exponent() == next_x.sci_exponent() {
let mut big_x = Float::from(x);
big_x.increment();
assert_eq!(ComparableFloat(big_x), ComparableFloat(Float::from(next_x)));
}
});
}
#[test]
fn test_decrement() {
let test = |s, s_hex, out: &str, out_hex: &str| {
let mut x = parse_hex_string(s_hex);
assert_eq!(x.to_string(), s);
x.decrement();
assert!(x.is_valid());
assert_eq!(x.to_string(), out);
assert_eq!(to_hex_string(&x), out_hex);
};
test("1.0", "0x1.0#1", "0.0", "0x0.0");
test("2.0", "0x2.0#1", "0.0", "0x0.0");
test("0.5", "0x0.8#1", "0.0", "0x0.0");
test(
"1.0",
"0x1.0000000000000000000000000#100",
"0.999999999999999999999999999998",
"0x0.ffffffffffffffffffffffffe#99",
);
test(
"0.33333333333333331",
"0x0.55555555555554#53",
"0.33333333333333326",
"0x0.55555555555550#53",
);
test(
"1.4142135623730951",
"0x1.6a09e667f3bcd#53",
"1.4142135623730949",
"0x1.6a09e667f3bcc#53",
);
test(
"3.1415926535897931",
"0x3.243f6a8885a30#53",
"3.1415926535897927",
"0x3.243f6a8885a2e#53",
);
test("-1.0", "-0x1.0#1", "-2.0", "-0x2.0#2");
test("-2.0", "-0x2.0#1", "-4.0", "-0x4.0#2");
test("-0.5", "-0x0.8#1", "-1.0", "-0x1.0#2");
test(
"-1.0",
"-0x1.0000000000000000000000000#100",
"-1.000000000000000000000000000002",
"-0x1.0000000000000000000000002#100",
);
test(
"-0.33333333333333331",
"-0x0.55555555555554#53",
"-0.33333333333333337",
"-0x0.55555555555558#53",
);
test(
"-1.4142135623730951",
"-0x1.6a09e667f3bcd#53",
"-1.4142135623730954",
"-0x1.6a09e667f3bce#53",
);
test(
"-3.1415926535897931",
"-0x3.243f6a8885a30#53",
"-3.1415926535897936",
"-0x3.243f6a8885a32#53",
);
}
#[test]
fn decrement_fail() {
assert_panic!({
let mut x = Float::NAN;
x.decrement();
});
assert_panic!({
let mut x = Float::INFINITY;
x.decrement();
});
assert_panic!({
let mut x = Float::NEGATIVE_INFINITY;
x.decrement();
});
assert_panic!({
let mut x = Float::ZERO;
x.decrement();
});
assert_panic!({
let mut x = Float::NEGATIVE_ZERO;
x.decrement();
});
}
#[test]
fn decrement_properties() {
float_gen_var_3().test_properties(|mut x| {
let old_x = x.clone();
x.decrement();
let final_x = x.clone();
assert!(x.is_valid());
assert_eq!(
Rational::exact_from(&old_x) - Rational::exact_from(old_x.ulp().unwrap()),
Rational::exact_from(&x)
);
if x != 0u32 {
x.increment();
assert_eq!(ComparableFloatRef(&x), ComparableFloatRef(&old_x));
}
let mut x = -old_x;
x.increment();
assert_eq!(ComparableFloat(x), ComparableFloat(-final_x));
});
primitive_float_gen_var_12::().test_properties(|x| {
let next_x = x.next_lower();
if next_x.is_finite() && next_x != 0.0 && x.sci_exponent() == next_x.sci_exponent() {
let mut big_x = Float::from(x);
big_x.decrement();
assert_eq!(ComparableFloat(big_x), ComparableFloat(Float::from(next_x)));
}
});
}