use core::ops::RangeInclusive;
use fixed_decimal::Decimal;
use fixed_decimal::Sign;
use fixed_decimal::SignedRoundingMode as SRM;
use fixed_decimal::UnsignedRoundingMode as URM;
use writeable::Writeable;
#[test]
pub fn test_ecma402_table() {
let cases: [(_, _, _, _, _, _, _); 9] = [
("ceil", SRM::Ceil, -1, 1, 1, 1, 2),
("floor", SRM::Floor, -2, 0, 0, 0, 1),
("expand", SRM::Unsigned(URM::Expand), -2, 1, 1, 1, 2),
("trunc", SRM::Unsigned(URM::Trunc), -1, 0, 0, 0, 1),
("half_ceil", SRM::HalfCeil, -1, 0, 1, 1, 2),
("half_floor", SRM::HalfFloor, -2, 0, 0, 1, 1),
(
"half_expand",
SRM::Unsigned(URM::HalfExpand),
-2,
0,
1,
1,
2,
),
("half_trunc", SRM::Unsigned(URM::HalfTrunc), -1, 0, 0, 1, 1),
("half_even", SRM::Unsigned(URM::HalfEven), -2, 0, 0, 1, 2),
];
for (name, mode, e1, e2, e3, e4, e5) in cases {
let mut fd1: Decimal = "-1.5".parse().unwrap();
let mut fd2: Decimal = "0.4".parse().unwrap();
let mut fd3: Decimal = "0.5".parse().unwrap();
let mut fd4: Decimal = "0.6".parse().unwrap();
let mut fd5: Decimal = "1.5".parse().unwrap();
fd1.round_with_mode(0, mode);
fd2.round_with_mode(0, mode);
fd3.round_with_mode(0, mode);
fd4.round_with_mode(0, mode);
fd5.round_with_mode(0, mode);
assert_eq!(
fd1.write_to_string(),
e1.write_to_string(),
"-1.5 failed for {name}"
);
assert_eq!(
fd2.write_to_string(),
e2.write_to_string(),
"0.4 failed for {name}"
);
assert_eq!(
fd3.write_to_string(),
e3.write_to_string(),
"0.5 failed for {name}"
);
assert_eq!(
fd4.write_to_string(),
e4.write_to_string(),
"0.6 failed for {name}"
);
assert_eq!(
fd5.write_to_string(),
e5.write_to_string(),
"1.5 failed for {name}"
);
}
}
#[test]
pub fn test_within_ranges() {
struct TestCase {
rounding_mode_name: &'static str,
rounding_mode: SRM,
range_n2000: RangeInclusive<i32>,
range_n1000: RangeInclusive<i32>,
range_0: RangeInclusive<i32>,
range_1000: RangeInclusive<i32>,
range_2000: RangeInclusive<i32>,
}
let cases: [TestCase; 9] = [
TestCase {
rounding_mode_name: "ceil",
rounding_mode: SRM::Ceil,
range_n2000: -2999..=-2000,
range_n1000: -1999..=-1000,
range_0: -999..=0,
range_1000: 1..=1000,
range_2000: 1001..=2000,
},
TestCase {
rounding_mode_name: "floor",
rounding_mode: SRM::Floor,
range_n2000: -2000..=-1001,
range_n1000: -1000..=-1,
range_0: 0..=999,
range_1000: 1000..=1999,
range_2000: 2000..=2999,
},
TestCase {
rounding_mode_name: "expand",
rounding_mode: SRM::Unsigned(URM::Expand),
range_n2000: -2000..=-1001,
range_n1000: -1000..=-1,
range_0: 0..=0,
range_1000: 1..=1000,
range_2000: 1001..=2000,
},
TestCase {
rounding_mode_name: "trunc",
rounding_mode: SRM::Unsigned(URM::Trunc),
range_n2000: -2999..=-2000,
range_n1000: -1999..=-1000,
range_0: -999..=999,
range_1000: 1000..=1999,
range_2000: 2000..=2999,
},
TestCase {
rounding_mode_name: "half_ceil",
rounding_mode: SRM::HalfCeil,
range_n2000: -2500..=-1501,
range_n1000: -1500..=-501,
range_0: -500..=449,
range_1000: 500..=1449,
range_2000: 1500..=2449,
},
TestCase {
rounding_mode_name: "half_floor",
rounding_mode: SRM::HalfFloor,
range_n2000: -2449..=-1500,
range_n1000: -1449..=-500,
range_0: -449..=500,
range_1000: 501..=1500,
range_2000: 1501..=2500,
},
TestCase {
rounding_mode_name: "half_expand",
rounding_mode: SRM::Unsigned(URM::HalfExpand),
range_n2000: -2449..=-1500,
range_n1000: -1449..=-500,
range_0: -449..=449,
range_1000: 500..=1449,
range_2000: 1500..=2449,
},
TestCase {
rounding_mode_name: "half_trunc",
rounding_mode: SRM::Unsigned(URM::HalfTrunc),
range_n2000: -2500..=-1501,
range_n1000: -1500..=-501,
range_0: -500..=500,
range_1000: 501..=1500,
range_2000: 1501..=2500,
},
TestCase {
rounding_mode_name: "half_even",
rounding_mode: SRM::Unsigned(URM::HalfEven),
range_n2000: -2500..=-1500,
range_n1000: -1449..=-501,
range_0: -500..=500,
range_1000: 501..=1449,
range_2000: 1500..=2500,
},
];
for TestCase {
rounding_mode_name,
rounding_mode,
range_n2000,
range_n1000,
range_0,
range_1000,
range_2000,
} in cases
{
for n in range_n2000 {
let mut fd = Decimal::from(n);
fd.round_with_mode(3, rounding_mode);
assert_eq!(fd.write_to_string(), "-2000", "{rounding_mode_name}: {n}");
let mut fd = Decimal::from(n - 1000000);
fd.multiply_pow10(-5);
fd.round_with_mode(-2, rounding_mode);
assert_eq!(
fd.write_to_string(),
"-10.02",
"{rounding_mode_name}: {n} ÷ 10^5 ± 10"
);
}
for n in range_n1000 {
let mut fd = Decimal::from(n);
fd.round_with_mode(3, rounding_mode);
assert_eq!(fd.write_to_string(), "-1000", "{rounding_mode_name}: {n}");
let mut fd = Decimal::from(n - 1000000);
fd.multiply_pow10(-5);
fd.round_with_mode(-2, rounding_mode);
assert_eq!(
fd.write_to_string(),
"-10.01",
"{rounding_mode_name}: {n} ÷ 10^5 ± 10"
);
}
for n in range_0 {
let mut fd = Decimal::from(n);
fd.round_with_mode(3, rounding_mode);
fd.set_sign(Sign::None); assert_eq!(fd.write_to_string(), "000", "{rounding_mode_name}: {n}");
let (mut fd, expected) = if n < 0 {
(
{
let mut fd = Decimal::from(n - 1000000);
fd.multiply_pow10(-5);
fd
},
"-10.00",
)
} else {
(
{
let mut fd = Decimal::from(n + 1000000);
fd.multiply_pow10(-5);
fd
},
"10.00",
)
};
fd.round_with_mode(-2, rounding_mode);
assert_eq!(
fd.write_to_string(),
expected,
"{rounding_mode_name}: {n} ÷ 10^5 ± 10"
);
}
for n in range_1000 {
let mut fd = Decimal::from(n);
fd.round_with_mode(3, rounding_mode);
assert_eq!(fd.write_to_string(), "1000", "{rounding_mode_name}: {n}");
let mut fd = Decimal::from(n + 1000000);
fd.multiply_pow10(-5);
fd.round_with_mode(-2, rounding_mode);
assert_eq!(
fd.write_to_string(),
"10.01",
"{rounding_mode_name}: {n} ÷ 10^5 ± 10"
);
}
for n in range_2000 {
let mut fd = Decimal::from(n);
fd.round_with_mode(3, rounding_mode);
assert_eq!(fd.write_to_string(), "2000", "{rounding_mode_name}: {n}");
let mut fd = Decimal::from(n + 1000000);
fd.multiply_pow10(-5);
fd.round_with_mode(-2, rounding_mode);
assert_eq!(
fd.write_to_string(),
"10.02",
"{rounding_mode_name}: {n} ÷ 10^5 ± 10"
);
}
}
}
#[test]
pub fn extra_rounding_mode_cases() {
struct TestCase {
input: &'static str,
position: i16,
all_expected: [&'static str; 9],
}
let cases: [TestCase; 8] = [
TestCase {
input: "505.050",
position: -3,
all_expected: [
"505.050", "505.050", "505.050", "505.050", "505.050", "505.050", "505.050",
"505.050", "505.050",
],
},
TestCase {
input: "505.050",
position: -2,
all_expected: [
"505.05", "505.05", "505.05", "505.05", "505.05", "505.05", "505.05", "505.05",
"505.05",
],
},
TestCase {
input: "505.050",
position: -1,
all_expected: [
"505.1", "505.0", "505.1", "505.0", "505.1", "505.0", "505.1", "505.0", "505.0",
],
},
TestCase {
input: "505.050",
position: 0,
all_expected: [
"506", "505", "506", "505", "505", "505", "505", "505", "505",
],
},
TestCase {
input: "505.050",
position: 1,
all_expected: [
"510", "500", "510", "500", "510", "510", "510", "510", "510",
],
},
TestCase {
input: "505.050",
position: 2,
all_expected: [
"600", "500", "600", "500", "500", "500", "500", "500", "500",
],
},
TestCase {
input: "505.050",
position: 3,
all_expected: [
"1000", "000", "1000", "000", "1000", "1000", "1000", "1000", "1000",
],
},
TestCase {
input: "505.050",
position: 4,
all_expected: [
"10000", "0000", "10000", "0000", "0000", "0000", "0000", "0000", "0000",
],
},
];
let rounding_modes: [(&'static str, SRM); 9] = [
("ceil", SRM::Ceil),
("floor", SRM::Floor),
("expand", SRM::Unsigned(URM::Expand)),
("trunc", SRM::Unsigned(URM::Trunc)),
("half_ceil", SRM::HalfCeil),
("half_floor", SRM::HalfFloor),
("half_expand", SRM::Unsigned(URM::HalfExpand)),
("half_trunc", SRM::Unsigned(URM::HalfTrunc)),
("half_even", SRM::Unsigned(URM::HalfEven)),
];
for TestCase {
input,
position,
all_expected,
} in cases
{
for ((rounding_mode_name, rounding_mode), expected) in
rounding_modes.iter().zip(all_expected.iter())
{
let mut fd: Decimal = input.parse().unwrap();
fd.round_with_mode(position, *rounding_mode);
assert_eq!(
&*fd.write_to_string(),
*expected,
"{input}: {rounding_mode_name} @ {position}"
)
}
}
}
#[test]
pub fn test_ecma402_table_with_increments() {
use fixed_decimal::RoundingIncrement;
#[rustfmt::skip] let cases: [(_, _, [(_, _, _, _, _, _, _); 9]); 3] = [
("two", RoundingIncrement::MultiplesOf2, [
("ceil", SRM::Ceil, "-1.4", "0.4", "0.6", "0.6", "1.6"),
("floor", SRM::Floor, "-1.6", "0.4", "0.4", "0.6", "1.4"),
("expand", SRM::Unsigned(URM::Expand), "-1.6", "0.4", "0.6", "0.6", "1.6"),
("trunc", SRM::Unsigned(URM::Trunc), "-1.4", "0.4", "0.4", "0.6", "1.4"),
("half_ceil", SRM::HalfCeil, "-1.4", "0.4", "0.6", "0.6", "1.6"),
("half_floor", SRM::HalfFloor, "-1.6", "0.4", "0.4", "0.6", "1.4"),
("half_expand", SRM::Unsigned(URM::HalfExpand), "-1.6", "0.4", "0.6", "0.6", "1.6"),
("half_trunc", SRM::Unsigned(URM::HalfTrunc), "-1.4", "0.4", "0.4", "0.6", "1.4"),
("half_even", SRM::Unsigned(URM::HalfEven), "-1.6", "0.4", "0.4", "0.6", "1.6"),
]),
("five", RoundingIncrement::MultiplesOf5, [
("ceil", SRM::Ceil, "-1.5", "0.5", "0.5", "1.0", "1.5"),
("floor", SRM::Floor, "-1.5", "0.0", "0.5", "0.5", "1.5"),
("expand", SRM::Unsigned(URM::Expand), "-1.5", "0.5", "0.5", "1.0", "1.5"),
("trunc", SRM::Unsigned(URM::Trunc), "-1.5", "0.0", "0.5", "0.5", "1.5"),
("half_ceil", SRM::HalfCeil, "-1.5", "0.5", "0.5", "0.5", "1.5"),
("half_floor", SRM::HalfFloor, "-1.5", "0.5", "0.5", "0.5", "1.5"),
("half_expand", SRM::Unsigned(URM::HalfExpand), "-1.5", "0.5", "0.5", "0.5", "1.5"),
("half_trunc", SRM::Unsigned(URM::HalfTrunc), "-1.5", "0.5", "0.5", "0.5", "1.5"),
("half_even", SRM::Unsigned(URM::HalfEven), "-1.5", "0.5", "0.5", "0.5", "1.5"),
]),
("twenty-five", RoundingIncrement::MultiplesOf25, [
("ceil", SRM::Ceil, "-0.0", "2.5", "2.5", "2.5", "2.5"),
("floor", SRM::Floor, "-2.5", "0.0", "0.0", "0.0", "0.0"),
("expand", SRM::Unsigned(URM::Expand), "-2.5", "2.5", "2.5", "2.5", "2.5"),
("trunc", SRM::Unsigned(URM::Trunc), "-0.0", "0.0", "0.0", "0.0", "0.0"),
("half_ceil", SRM::HalfCeil, "-2.5", "0.0", "0.0", "0.0", "2.5"),
("half_floor", SRM::HalfFloor, "-2.5", "0.0", "0.0", "0.0", "2.5"),
("half_expand", SRM::Unsigned(URM::HalfExpand), "-2.5", "0.0", "0.0", "0.0", "2.5"),
("half_trunc", SRM::Unsigned(URM::HalfTrunc), "-2.5", "0.0", "0.0", "0.0", "2.5"),
("half_even", SRM::Unsigned(URM::HalfEven), "-2.5", "0.0", "0.0", "0.0", "2.5"),
]),
];
for (increment_str, increment, cases) in cases {
for (rounding_mode_name, rounding_mode, e1, e2, e3, e4, e5) in cases {
let mut fd1: Decimal = "-1.5".parse().unwrap();
let mut fd2: Decimal = "0.4".parse().unwrap();
let mut fd3: Decimal = "0.5".parse().unwrap();
let mut fd4: Decimal = "0.6".parse().unwrap();
let mut fd5: Decimal = "1.5".parse().unwrap();
fd1.round_with_mode_and_increment(-1, rounding_mode, increment);
fd2.round_with_mode_and_increment(-1, rounding_mode, increment);
fd3.round_with_mode_and_increment(-1, rounding_mode, increment);
fd4.round_with_mode_and_increment(-1, rounding_mode, increment);
fd5.round_with_mode_and_increment(-1, rounding_mode, increment);
assert_eq!(
fd1.write_to_string(),
e1,
"-1.5 failed for {rounding_mode_name} with increments of {increment_str}"
);
assert_eq!(
fd2.write_to_string(),
e2,
"0.4 failed for {rounding_mode_name} with increments of {increment_str}"
);
assert_eq!(
fd3.write_to_string(),
e3,
"0.5 failed for {rounding_mode_name} with increments of {increment_str}"
);
assert_eq!(
fd4.write_to_string(),
e4,
"0.6 failed for {rounding_mode_name} with increments of {increment_str}"
);
assert_eq!(
fd5.write_to_string(),
e5,
"1.5 failed for {rounding_mode_name} with increments of {increment_str}"
);
}
}
}