use malachite_base::num::conversion::string::options::ToSciOptions;
use malachite_base::num::conversion::traits::{ExactFrom, ToSci};
use malachite_base::rounding_modes::RoundingMode::*;
use malachite_float::Float;
use malachite_float::float::conversion::string::to_sci::{to_sci_string, to_sci_valid};
use malachite_float::test_util::common::parse_hex_string;
use malachite_float::test_util::generators::{
float_to_sci_options_pair_gen_var_1, float_to_sci_options_pair_gen_var_2,
};
use malachite_q::Rational;
use std::panic::catch_unwind;
fn insert_point(s: &str) -> String {
s.find(['e', 'E']).map_or_else(
|| {
if s.contains('.') {
s.to_string()
} else {
format!("{s}.0")
}
},
|i| {
if s[..i].contains('.') {
s.to_string()
} else {
format!("{}.0{}", &s[..i], &s[i..])
}
},
)
}
#[test]
fn test_to_sci_string() {
fn test(s: &str, s_hex: &str, configure: &dyn Fn(&mut ToSciOptions), out: &str) {
let x = parse_hex_string(s_hex);
assert_eq!(x.to_string(), s);
let mut options = ToSciOptions::default();
configure(&mut options);
assert_eq!(to_sci_string(&x, options), out, "{s} {options:?}");
}
let default = &|_: &mut ToSciOptions| {};
test("NaN", "NaN", default, "NaN");
test("Infinity", "Infinity", default, "Infinity");
test("-Infinity", "-Infinity", default, "-Infinity");
test("0.0", "0x0.0", default, "0.0");
test("-0.0", "-0x0.0", default, "-0.0");
test(
"0.0",
"0x0.0",
&|o| {
o.set_precision(3);
o.set_include_trailing_zeros(true);
},
"0.00",
);
test(
"-0.0",
"-0x0.0",
&|o| {
o.set_scale(2);
o.set_include_trailing_zeros(true);
},
"-0.00",
);
test("1.5", "0x1.8#2", default, "1.5");
test("-1.5", "-0x1.8#2", default, "-1.5");
test("1.0", "0x1.0#1", default, "1.0");
test("255.0", "0xff.0#8", default, "255.0");
test("1000000.0", "0xf4240.0#20", default, "1000000.0");
test("1234.5", "0x4d2.8#12", default, "1234.5");
test("0.50", "0x0.8#1", default, "0.5");
test("7.6e-6", "0x0.00008#1", default, "7.62939453125e-6");
test(
"0.0012340000000000001",
"0x0.0050df15a4acf314#53",
default,
"0.001234",
);
test("1234.5", "0x4d2.8#12", &|o| o.set_precision(2), "1.2e3");
test("1234.5", "0x4d2.8#12", &|o| o.set_precision(4), "1234.0");
test("1.5", "0x1.8#2", &|o| o.set_precision(1), "2.0");
test(
"1.5",
"0x1.8#2",
&|o| {
o.set_precision(1);
o.set_rounding_mode(Down);
},
"1.0",
);
test(
"1.5",
"0x1.8#2",
&|o| {
o.set_precision(5);
o.set_include_trailing_zeros(true);
},
"1.5000",
);
test("9.50", "0x9.8#5", &|o| o.set_precision(1), "1.0e1");
test("1.5", "0x1.8#2", &|o| o.set_scale(0), "2.0");
test(
"1.5",
"0x1.8#2",
&|o| {
o.set_scale(0);
o.set_rounding_mode(Down);
},
"1.0",
);
test(
"1.5",
"0x1.8#2",
&|o| {
o.set_scale(3);
o.set_include_trailing_zeros(true);
},
"1.500",
);
test(
"-1.5",
"-0x1.8#2",
&|o| {
o.set_scale(0);
o.set_rounding_mode(Floor);
},
"-2.0",
);
test(
"-1.5",
"-0x1.8#2",
&|o| {
o.set_scale(0);
o.set_rounding_mode(Ceiling);
},
"-1.0",
);
test("0.25", "0x0.4#1", &|o| o.set_scale(0), "0.0");
test(
"0.25",
"0x0.4#1",
&|o| {
o.set_scale(0);
o.set_rounding_mode(Up);
},
"1.0",
);
test("0.50", "0x0.8#1", &|o| o.set_scale(0), "0.0");
test("0.75", "0x0.c#2", &|o| o.set_scale(0), "1.0");
test(
"-4.9276e-6",
"-0x0.000052ac#13",
&|o| o.set_scale(2),
"-0.0",
);
test("1.5", "0x1.8#2", &|o| o.set_size_complete(), "1.5");
test(
"0.00098",
"0x0.004#1",
&|o| o.set_size_complete(),
"0.0009765625",
);
test("255.0", "0xff.0#8", &|o| o.set_size_complete(), "255.0");
test("5.0", "0x5.0#3", &|o| o.set_base(2), "101.0");
test("255.0", "0xff.0#8", &|o| o.set_base(16), "ff.0");
test(
"255.0",
"0xff.0#8",
&|o| {
o.set_base(16);
o.set_uppercase();
},
"FF.0",
);
test("0.50", "0x0.8#1", &|o| o.set_base(16), "0.8");
test(
"1000000.0",
"0xf4240.0#20",
&|o| {
o.set_base(16);
o.set_precision(2);
},
"f.4e+4",
);
test("255.0", "0xff.0#8", &|o| o.set_base(20), "cf.0");
test("1.5", "0x1.8#2", &|o| o.set_base(20), "1.a");
test(
"1.5",
"0x1.8#2",
&|o| {
o.set_base(20);
o.set_uppercase();
},
"1.A",
);
test("14.0", "0xe.0#4", &|o| o.set_base(20), "e.0");
test(
"5600.0",
"0x1.5eE+3#8",
&|o| {
o.set_base(20);
o.set_precision(1);
},
"e.0e+2",
);
test(
"5600.0",
"0x1.5eE+3#8",
&|o| {
o.set_base(20);
o.set_precision(1);
o.set_e_uppercase();
},
"e.0E+2",
);
test(
"3.0",
"0x3.0#2",
&|o| {
o.set_base(21);
o.set_size_complete();
},
"3.0",
);
test("1.5", "0x1.8#2", &|o| o.set_base(21), "1.aaaaaaaaaaaaaab");
test(
"1.5",
"0x1.8#2",
&|o| {
o.set_base(21);
o.set_precision(3);
},
"1.ab",
);
test("255.0", "0xff.0#8", &|o| o.set_base(32), "7v.0");
test("0.50", "0x0.8#1", &|o| o.set_base(32), "0.g");
test(
"0.00098",
"0x0.004#1",
&|o| {
o.set_base(32);
o.set_size_complete();
},
"0.01",
);
test(
"1.5",
"0x1.8#2",
&|o| {
o.set_base(32);
o.set_precision(2);
o.set_rounding_mode(Exact);
},
"1.g",
);
test("7.6e-6", "0x0.00008#1", &|o| o.set_base(32), "0.0008");
test(
"7.6e-6",
"0x0.00008#1",
&|o| {
o.set_base(32);
o.set_neg_exp_threshold(-3);
},
"8.0e-4",
);
test("255.0", "0xff.0#8", &|o| o.set_base(36), "73.0");
test("35.0", "0x23.0#6", &|o| o.set_base(36), "z.0");
test(
"35.0",
"0x23.0#6",
&|o| {
o.set_base(36);
o.set_uppercase();
},
"Z.0",
);
test("0.50", "0x0.8#1", &|o| o.set_base(36), "0.i");
test(
"0.50",
"0x0.8#1",
&|o| {
o.set_base(36);
o.set_size_complete();
},
"0.i",
);
test(
"7.6e-6",
"0x0.00008#1",
&|o| o.set_e_uppercase(),
"7.62939453125E-6",
);
test(
"8.8290e30",
"0x6.f70E+25#13",
&|o| o.set_precision(3),
"8.83e30",
);
test(
"8.8290e30",
"0x6.f70E+25#13",
&|o| {
o.set_precision(3);
o.set_force_exponent_plus_sign(true);
},
"8.83e+30",
);
test(
"7.6e-6",
"0x0.00008#1",
&|o| o.set_neg_exp_threshold(-10),
"0.00000762939453125",
);
test(
"1.5",
"0x1.8#2",
&|o| {
o.set_precision(2);
o.set_rounding_mode(Exact);
},
"1.5",
);
test(
"0.50",
"0x0.8#1",
&|o| {
o.set_scale(1);
o.set_rounding_mode(Exact);
},
"0.5",
);
}
#[test]
fn test_to_sci_string_panics() {
assert_panic!({
let mut options = ToSciOptions::default();
options.set_precision(1);
options.set_rounding_mode(Exact);
to_sci_string(&Float::from(1.5), options)
});
assert_panic!({
let mut options = ToSciOptions::default();
options.set_scale(0);
options.set_rounding_mode(Exact);
to_sci_string(&Float::from(0.5), options)
});
assert_panic!({
let mut options = ToSciOptions::default();
options.set_base(3);
options.set_size_complete();
to_sci_string(&Float::from(0.5), options)
});
}
#[test]
fn test_to_sci_valid() {
fn test(s: &str, s_hex: &str, configure: &dyn Fn(&mut ToSciOptions), out: bool) {
let x = parse_hex_string(s_hex);
assert_eq!(x.to_string(), s);
let mut options = ToSciOptions::default();
configure(&mut options);
assert_eq!(to_sci_valid(&x, options), out, "{s} {options:?}");
}
let default = &|_: &mut ToSciOptions| {};
test("NaN", "NaN", default, true);
test("Infinity", "Infinity", default, true);
test("0.0", "0x0.0", &|o| o.set_size_complete(), true);
test("1.5", "0x1.8#2", default, true);
test("1.5", "0x1.8#2", &|o| o.set_precision(1), true);
test(
"1.5",
"0x1.8#2",
&|o| {
o.set_precision(1);
o.set_rounding_mode(Exact);
},
false,
);
test(
"1.5",
"0x1.8#2",
&|o| {
o.set_precision(2);
o.set_rounding_mode(Exact);
},
true,
);
test(
"0.50",
"0x0.8#1",
&|o| {
o.set_scale(0);
o.set_rounding_mode(Exact);
},
false,
);
test(
"0.50",
"0x0.8#1",
&|o| {
o.set_scale(1);
o.set_rounding_mode(Exact);
},
true,
);
test("0.50", "0x0.8#1", &|o| o.set_size_complete(), true);
test(
"0.50",
"0x0.8#1",
&|o| {
o.set_base(3);
o.set_size_complete();
},
false,
);
test(
"3.0",
"0x3.0#2",
&|o| {
o.set_base(3);
o.set_size_complete();
},
true,
);
test(
"0.50",
"0x0.8#1",
&|o| {
o.set_base(32);
o.set_size_complete();
},
true,
);
test(
"0.50",
"0x0.8#1",
&|o| {
o.set_base(21);
o.set_size_complete();
},
false,
);
test(
"3.0",
"0x3.0#2",
&|o| {
o.set_base(21);
o.set_size_complete();
},
true,
);
test(
"1.5",
"0x1.8#2",
&|o| {
o.set_base(21);
o.set_precision(5);
o.set_rounding_mode(Exact);
},
false,
);
test(
"1.5",
"0x1.8#2",
&|o| {
o.set_base(32);
o.set_precision(2);
o.set_rounding_mode(Exact);
},
true,
);
}
#[test]
fn to_sci_string_properties() {
fn check(x: &Float, options: ToSciOptions) {
let s = to_sci_string(x, options);
assert!(s.is_ascii());
assert!(to_sci_valid(x, options));
if x.is_nan() {
assert_eq!(s, "NaN");
} else {
assert_eq!(s.starts_with('-'), x.is_sign_negative());
if x.is_infinite() {
assert_eq!(s.trim_start_matches('-'), "Infinity");
} else {
assert!(s.contains('.'), "{s:?}");
}
}
if let Some(exponent) = x.get_exponent()
&& exponent.unsigned_abs() <= 10_000
&& options.get_base() <= 14
{
let q = Rational::exact_from(x);
assert!(q.fmt_sci_valid(options));
assert_eq!(
s,
insert_point(&q.to_sci_with_options(options).to_string()),
"{x} {options:?}"
);
}
}
float_to_sci_options_pair_gen_var_1().test_properties(|(x, options)| check(&x, options));
float_to_sci_options_pair_gen_var_2().test_properties(|(x, options)| check(&x, options));
}