// 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::integers::PrimitiveInt;
use malachite_base::num::basic::signeds::PrimitiveSigned;
use malachite_base::num::basic::unsigneds::PrimitiveUnsigned;
use malachite_base::num::conversion::traits::WrappingFrom;
use malachite_base::rounding_modes::exhaustive::exhaustive_rounding_modes;
use malachite_base::rounding_modes::RoundingMode::{self, *};
use malachite_base::test_util::generators::{
signed_rounding_mode_pair_gen, signed_unsigned_pair_gen_var_1, signed_unsigned_pair_gen_var_16,
signed_unsigned_pair_gen_var_17, signed_unsigned_pair_gen_var_8,
signed_unsigned_rounding_mode_triple_gen_var_1, unsigned_pair_gen_var_14,
unsigned_pair_gen_var_2, unsigned_pair_gen_var_21, unsigned_rounding_mode_pair_gen,
unsigned_unsigned_rounding_mode_triple_gen_var_3,
};
use std::cmp::Ordering::{self, *};
use std::panic::catch_unwind;
#[test]
fn test_round_to_multiple_of_power_of_2() {
fn test(n: T, pow: u64, rm: RoundingMode, out: T, o: Ordering) {
assert_eq!(n.round_to_multiple_of_power_of_2(pow, rm), (out, o));
let mut n = n;
assert_eq!(n.round_to_multiple_of_power_of_2_assign(pow, rm), o);
assert_eq!(n, out);
}
test::(0, 10, Exact, 0, Equal);
test::(17, 0, Exact, 17, Equal);
test::(10, 2, Floor, 8, Less);
test::(10, 2, Ceiling, 12, Greater);
test::(10, 2, Down, 8, Less);
test::(10, 2, Up, 12, Greater);
test::(10, 2, Nearest, 8, Less);
test::(12, 2, Exact, 12, Equal);
test::(-10, 2, Floor, -12, Less);
test::(-10, 2, Ceiling, -8, Greater);
test::(-10, 2, Down, -8, Greater);
test::(-10, 2, Up, -12, Less);
test::(-10, 2, Nearest, -8, Greater);
test::(-12, 2, Exact, -12, Equal);
test::(0xff, 4, Down, 0xf0, Less);
test::(0xff, 4, Floor, 0xf0, Less);
test::(0xef, 4, Up, 0xf0, Greater);
test::(0xef, 4, Ceiling, 0xf0, Greater);
test::(0xe8, 4, Nearest, 0xe0, Less);
test::(1, 8, Nearest, 0, Less);
test::(0x7f, 4, Down, 0x70, Less);
test::(0x7f, 4, Floor, 0x70, Less);
test::(0x6f, 4, Up, 0x70, Greater);
test::(0x6f, 4, Ceiling, 0x70, Greater);
test::(0x68, 4, Nearest, 0x60, Less);
test::(-0x7f, 4, Down, -0x70, Greater);
test::(-0x7f, 4, Floor, -0x80, Less);
test::(-0x7f, 4, Up, -0x80, Less);
test::(-0x7f, 4, Ceiling, -0x70, Greater);
test::(-0x78, 4, Nearest, -0x80, Less);
}
fn round_to_multiple_of_power_of_2_fail_helper() {
assert_panic!(T::exact_from(10).round_to_multiple_of_power_of_2(4, Exact));
assert_panic!(T::MAX.round_to_multiple_of_power_of_2(4, Up));
assert_panic!(T::MAX.round_to_multiple_of_power_of_2(4, Ceiling));
assert_panic!(T::MAX.round_to_multiple_of_power_of_2(4, Nearest));
assert_panic!(T::ONE.round_to_multiple_of_power_of_2(T::WIDTH, Up));
assert_panic!(T::exact_from(10).round_to_multiple_of_power_of_2_assign(4, Exact));
assert_panic!({
let mut n = T::MAX;
n.round_to_multiple_of_power_of_2_assign(4, Up);
});
assert_panic!({
let mut n = T::MAX;
n.round_to_multiple_of_power_of_2_assign(4, Ceiling);
});
assert_panic!({
let mut n = T::MAX;
n.round_to_multiple_of_power_of_2_assign(4, Nearest);
});
assert_panic!({
let mut n = T::ONE;
n.round_to_multiple_of_power_of_2_assign(T::WIDTH, Up);
});
}
fn round_to_multiple_of_power_of_2_signed_fail_helper() {
assert_panic!((-T::MAX).round_to_multiple_of_power_of_2(T::WIDTH, Up));
assert_panic!((-T::MAX).round_to_multiple_of_power_of_2(T::WIDTH, Floor));
assert_panic!((-T::MAX).round_to_multiple_of_power_of_2_assign(T::WIDTH, Up));
assert_panic!({
(-T::MAX).round_to_multiple_of_power_of_2_assign(T::WIDTH, Floor);
});
}
#[test]
fn round_to_multiple_of_power_of_2_fail() {
apply_fn_to_primitive_ints!(round_to_multiple_of_power_of_2_fail_helper);
apply_fn_to_signeds!(round_to_multiple_of_power_of_2_signed_fail_helper);
}
fn round_to_multiple_of_power_of_2_properties_helper_unsigned() {
unsigned_unsigned_rounding_mode_triple_gen_var_3::().test_properties(|(n, pow, rm)| {
let (rounded, o) = n.round_to_multiple_of_power_of_2(pow, rm);
let mut mut_n = n;
assert_eq!(mut_n.round_to_multiple_of_power_of_2_assign(pow, rm), o);
assert_eq!(mut_n, rounded);
assert!(rounded.divisible_by_power_of_2(pow));
assert_eq!(rounded.cmp(&n), o);
match rm {
Floor | Down => assert_ne!(o, Greater),
Ceiling | Up => assert_ne!(o, Less),
Exact => assert_eq!(o, Equal),
_ => {}
}
match rm {
Floor | Down => {
assert!(rounded <= n);
}
Ceiling | Up => {
assert!(rounded >= n);
}
Exact => assert_eq!(rounded, n),
Nearest => {
if let Some(k) = T::ONE.arithmetic_checked_shl(pow) {
let mut closest = None;
let mut second_closest = None;
if rounded <= n {
if let Some(above) = rounded.checked_add(k) {
closest = Some(n - rounded);
second_closest = Some(above - n);
}
} else if let Some(below) = rounded.checked_sub(k) {
closest = Some(rounded - n);
second_closest = Some(n - below);
}
if let (Some(closest), Some(second_closest)) = (closest, second_closest) {
assert!(closest <= second_closest);
if closest == second_closest {
assert!(!rounded.get_bit(pow));
}
}
}
}
}
if o == Equal {
for rm in exhaustive_rounding_modes() {
assert_eq!(n.round_to_multiple_of_power_of_2(pow, rm), (rounded, Equal));
}
} else {
assert_panic!(n.round_to_multiple_of_power_of_2(pow, Exact));
}
});
unsigned_pair_gen_var_2::().test_properties(|(n, pow)| {
if pow < T::WIDTH {
if let Some(shifted) = n.arithmetic_checked_shl(pow) {
let so = (shifted, Equal);
assert_eq!(shifted.round_to_multiple_of_power_of_2(pow, Down), so);
assert_eq!(shifted.round_to_multiple_of_power_of_2(pow, Up), so);
assert_eq!(shifted.round_to_multiple_of_power_of_2(pow, Floor), so);
assert_eq!(shifted.round_to_multiple_of_power_of_2(pow, Ceiling), so);
assert_eq!(shifted.round_to_multiple_of_power_of_2(pow, Nearest), so);
assert_eq!(shifted.round_to_multiple_of_power_of_2(pow, Exact), so);
}
}
});
unsigned_pair_gen_var_14::().test_properties(|(n, pow)| {
let down = n.round_to_multiple_of_power_of_2(pow, Down);
assert_eq!(down.1, Less);
if let Some(k) = T::ONE.arithmetic_checked_shl(pow) {
if let Some(up) = down.0.checked_add(k) {
let up = (up, Greater);
assert_eq!(n.round_to_multiple_of_power_of_2(pow, Up), up);
assert_eq!(n.round_to_multiple_of_power_of_2(pow, Floor), down);
assert_eq!(n.round_to_multiple_of_power_of_2(pow, Ceiling), up);
let nearest = n.round_to_multiple_of_power_of_2(pow, Nearest);
assert!(nearest == down || nearest == up);
}
}
});
unsigned_pair_gen_var_21::().test_properties(|(n, pow)| {
if let Some(shift) = pow.checked_add(T::WIDTH) {
assert_eq!(
n.round_to_multiple_of_power_of_2(shift, Down),
(T::ZERO, if n == T::ZERO { Equal } else { Less })
);
assert_eq!(
n.round_to_multiple_of_power_of_2(shift, Floor),
(T::ZERO, if n == T::ZERO { Equal } else { Less })
);
if let Some(extra_shift) = shift.checked_add(1) {
assert_eq!(
n.round_to_multiple_of_power_of_2(extra_shift, Nearest),
(T::ZERO, if n == T::ZERO { Equal } else { Less })
);
}
}
});
unsigned_rounding_mode_pair_gen::().test_properties(|(n, rm)| {
assert_eq!(n.round_to_multiple_of_power_of_2(0, rm), (n, Equal));
});
unsigned_rounding_mode_pair_gen().test_properties(|(pow, rm)| {
assert_eq!(
T::ZERO.round_to_multiple_of_power_of_2(pow, rm),
(T::ZERO, Equal)
);
});
}
fn round_to_multiple_of_power_of_2_properties_helper_signed<
U: PrimitiveUnsigned + WrappingFrom,
S: PrimitiveSigned + WrappingFrom,
>() {
signed_unsigned_rounding_mode_triple_gen_var_1::().test_properties(|(n, pow, rm)| {
let (rounded, o) = n.round_to_multiple_of_power_of_2(pow, rm);
let mut mut_n = n;
assert_eq!(mut_n.round_to_multiple_of_power_of_2_assign(pow, rm), o);
assert_eq!(mut_n, rounded);
assert!(rounded.divisible_by_power_of_2(pow));
assert_eq!(rounded.cmp(&n), o);
match (n >= S::ZERO, rm) {
(_, Floor) | (true, Down) | (false, Up) => {
assert_ne!(o, Greater);
}
(_, Ceiling) | (true, Up) | (false, Down) => {
assert_ne!(o, Less);
}
(_, Exact) => assert_eq!(o, Equal),
_ => {}
}
match rm {
Floor => assert!(rounded <= n),
Ceiling => assert!(rounded >= n),
Down => assert!(rounded.le_abs(&n)),
Up => assert!(rounded.ge_abs(&n)),
Exact => assert_eq!(rounded, n),
Nearest => {
if let Some(k) = S::ONE.arithmetic_checked_shl(pow) {
let mut closest = None;
let mut second_closest = None;
if rounded <= n {
if let Some(above) = rounded.checked_add(k) {
closest = Some(n - rounded);
second_closest = Some(above - n);
}
} else if let Some(below) = rounded.checked_sub(k) {
closest = Some(rounded - n);
second_closest = Some(n - below);
}
if let (Some(closest), Some(second_closest)) = (closest, second_closest) {
assert!(closest <= second_closest);
if closest == second_closest {
assert!(!rounded.get_bit(pow));
}
}
}
}
}
if o == Equal {
for rm in exhaustive_rounding_modes() {
assert_eq!(n.round_to_multiple_of_power_of_2(pow, rm), (rounded, Equal));
}
} else {
assert_panic!(n.round_to_multiple_of_power_of_2(pow, Exact));
}
});
signed_unsigned_pair_gen_var_1::().test_properties(|(n, pow)| {
if pow < S::WIDTH {
if let Some(shifted) = n.arithmetic_checked_shl(pow) {
let so = (shifted, Equal);
assert_eq!(shifted.round_to_multiple_of_power_of_2(pow, Down), so);
assert_eq!(shifted.round_to_multiple_of_power_of_2(pow, Up), so);
assert_eq!(shifted.round_to_multiple_of_power_of_2(pow, Floor), so);
assert_eq!(shifted.round_to_multiple_of_power_of_2(pow, Ceiling), so);
assert_eq!(shifted.round_to_multiple_of_power_of_2(pow, Nearest), so);
assert_eq!(shifted.round_to_multiple_of_power_of_2(pow, Exact), so);
}
}
});
signed_unsigned_pair_gen_var_8::().test_properties(|(n, pow)| {
let down = n.round_to_multiple_of_power_of_2(pow, Down);
assert_eq!(down.1, if n >= S::ZERO { Less } else { Greater });
if let Some(k) = S::ONE.arithmetic_checked_shl(pow) {
if let Some(up) = if n >= S::ZERO {
down.0.checked_add(k)
} else {
down.0.checked_sub(k)
} {
let up = (up, if n >= S::ZERO { Greater } else { Less });
assert_eq!(n.round_to_multiple_of_power_of_2(pow, Up), up);
if n >= S::ZERO {
assert_eq!(n.round_to_multiple_of_power_of_2(pow, Floor), down);
assert_eq!(n.round_to_multiple_of_power_of_2(pow, Ceiling), up);
} else {
assert_eq!(n.round_to_multiple_of_power_of_2(pow, Floor), up);
assert_eq!(n.round_to_multiple_of_power_of_2(pow, Ceiling), down);
}
let nearest = n.round_to_multiple_of_power_of_2(pow, Nearest);
assert!(nearest == down || nearest == up);
}
}
});
signed_unsigned_pair_gen_var_16::().test_properties(|(i, pow)| {
if let Some(shift) = pow.checked_add(S::WIDTH - 1) {
assert_eq!(
i.round_to_multiple_of_power_of_2(shift, Down),
(S::ZERO, if i == S::ZERO { Equal } else { Less })
);
assert_eq!(
i.round_to_multiple_of_power_of_2(shift, Floor),
(S::ZERO, if i == S::ZERO { Equal } else { Less })
);
if let Some(extra_shift) = shift.checked_add(1) {
assert_eq!(
i.round_to_multiple_of_power_of_2(extra_shift, Nearest),
(S::ZERO, if i == S::ZERO { Equal } else { Less })
);
}
}
});
signed_unsigned_pair_gen_var_17::().test_properties(|(i, pow)| {
if let Some(shift) = pow.checked_add(S::WIDTH - 1) {
assert_eq!(
i.round_to_multiple_of_power_of_2(shift, Down),
(S::ZERO, if i == S::ZERO { Equal } else { Greater })
);
assert_eq!(
i.round_to_multiple_of_power_of_2(shift, Ceiling),
(S::ZERO, if i == S::ZERO { Equal } else { Greater })
);
if let Some(extra_shift) = shift.checked_add(1) {
assert_eq!(
i.round_to_multiple_of_power_of_2(extra_shift, Nearest),
(S::ZERO, if i == S::ZERO { Equal } else { Greater })
);
}
}
});
signed_rounding_mode_pair_gen::().test_properties(|(n, rm)| {
assert_eq!(n.round_to_multiple_of_power_of_2(0, rm), (n, Equal));
});
unsigned_rounding_mode_pair_gen().test_properties(|(pow, rm)| {
assert_eq!(
S::ZERO.round_to_multiple_of_power_of_2(pow, rm),
(S::ZERO, Equal)
);
});
}
#[test]
fn round_to_multiple_of_power_of_2_properties() {
apply_fn_to_unsigneds!(round_to_multiple_of_power_of_2_properties_helper_unsigned);
apply_fn_to_unsigned_signed_pairs!(round_to_multiple_of_power_of_2_properties_helper_signed);
}