// 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::arithmetic::log_base_power_of_2::ceiling_log_base_power_of_2_naive;
use malachite_base::num::arithmetic::traits::DivisibleBy;
use malachite_base::num::basic::floats::PrimitiveFloat;
use malachite_base::num::basic::unsigneds::PrimitiveUnsigned;
use malachite_base::num::conversion::traits::ExactFrom;
use malachite_base::test_util::generators::{
primitive_float_gen_var_18, primitive_float_unsigned_pair_gen_var_3, unsigned_gen_var_1,
unsigned_gen_var_11, unsigned_pair_gen_var_21,
};
use std::panic::catch_unwind;
fn floor_log_base_power_of_2_helper_unsigned() {
let test = |n: T, pow, out| {
assert_eq!(n.floor_log_base_power_of_2(pow), out);
};
test(T::ONE, 1, 0);
test(T::ONE, 5, 0);
test(T::TWO, 1, 1);
test(T::TWO, 2, 0);
test(T::exact_from(3), 1, 1);
test(T::exact_from(3), 2, 0);
test(T::exact_from(4), 1, 2);
test(T::exact_from(4), 2, 1);
test(T::exact_from(4), 3, 0);
test(T::exact_from(5), 1, 2);
test(T::exact_from(5), 2, 1);
test(T::exact_from(5), 3, 0);
test(T::exact_from(100), 1, 6);
test(T::exact_from(100), 2, 3);
test(T::exact_from(100), 3, 2);
test(T::exact_from(100), 4, 1);
test(T::exact_from(100), 7, 0);
test(T::exact_from(128), 1, 7);
test(T::exact_from(128), 2, 3);
test(T::exact_from(128), 3, 2);
test(T::exact_from(128), 4, 1);
test(T::exact_from(128), 7, 1);
test(T::MAX, 1, T::WIDTH - 1);
}
fn floor_log_base_power_of_2_helper_primitive_float() {
let test = |n: T, pow, out| {
assert_eq!(n.floor_log_base_power_of_2(pow), out);
};
test(T::ONE, 1, 0);
test(T::ONE, 5, 0);
test(T::TWO, 1, 1);
test(T::TWO, 2, 0);
test(T::from(3.0f32), 1, 1);
test(T::from(3.0f32), 2, 0);
test(T::from(4.0f32), 1, 2);
test(T::from(4.0f32), 2, 1);
test(T::from(4.0f32), 3, 0);
test(T::from(5.0f32), 1, 2);
test(T::from(5.0f32), 2, 1);
test(T::from(5.0f32), 3, 0);
test(T::from(100.0f32), 1, 6);
test(T::from(100.0f32), 2, 3);
test(T::from(100.0f32), 3, 2);
test(T::from(100.0f32), 4, 1);
test(T::from(100.0f32), 7, 0);
test(T::from(128.0f32), 1, 7);
test(T::from(128.0f32), 2, 3);
test(T::from(128.0f32), 3, 2);
test(T::from(128.0f32), 4, 1);
test(T::from(128.0f32), 7, 1);
test(T::from(0.1f32), 1, -4);
test(T::from(0.1f32), 2, -2);
test(T::from(0.1f32), 3, -2);
test(T::from(0.1f32), 4, -1);
test(T::MIN_POSITIVE_SUBNORMAL, 1, T::MIN_EXPONENT);
test(T::MAX_FINITE, 1, T::MAX_EXPONENT);
}
#[test]
fn test_floor_log_base_power_of_2() {
apply_fn_to_unsigneds!(floor_log_base_power_of_2_helper_unsigned);
apply_fn_to_primitive_floats!(floor_log_base_power_of_2_helper_primitive_float);
}
fn floor_log_base_power_of_2_fail_helper_unsigned() {
assert_panic!(T::ZERO.floor_log_base_power_of_2(2));
assert_panic!(T::TWO.floor_log_base_power_of_2(0));
}
fn floor_log_base_power_of_2_fail_helper_primitive_float() {
assert_panic!(T::ZERO.floor_log_base_power_of_2(2));
assert_panic!(T::NEGATIVE_ONE.floor_log_base_power_of_2(2));
assert_panic!(T::NEGATIVE_INFINITY.floor_log_base_power_of_2(2));
assert_panic!(T::INFINITY.floor_log_base_power_of_2(2));
assert_panic!(T::NAN.floor_log_base_power_of_2(2));
assert_panic!(T::TWO.floor_log_base_power_of_2(0));
}
#[test]
fn floor_log_base_power_of_2_fail() {
apply_fn_to_unsigneds!(floor_log_base_power_of_2_fail_helper_unsigned);
apply_fn_to_primitive_floats!(floor_log_base_power_of_2_fail_helper_primitive_float);
}
fn ceiling_log_base_power_of_2_helper_unsigned() {
let test = |n: T, pow, out| {
assert_eq!(n.ceiling_log_base_power_of_2(pow), out);
};
test(T::ONE, 1, 0);
test(T::ONE, 5, 0);
test(T::TWO, 1, 1);
test(T::TWO, 2, 1);
test(T::exact_from(3), 1, 2);
test(T::exact_from(3), 2, 1);
test(T::exact_from(4), 1, 2);
test(T::exact_from(4), 2, 1);
test(T::exact_from(4), 3, 1);
test(T::exact_from(5), 1, 3);
test(T::exact_from(5), 2, 2);
test(T::exact_from(5), 3, 1);
test(T::exact_from(100), 1, 7);
test(T::exact_from(100), 2, 4);
test(T::exact_from(100), 3, 3);
test(T::exact_from(100), 4, 2);
test(T::exact_from(100), 7, 1);
test(T::exact_from(128), 1, 7);
test(T::exact_from(128), 2, 4);
test(T::exact_from(128), 3, 3);
test(T::exact_from(128), 4, 2);
test(T::exact_from(128), 7, 1);
test(T::MAX, 1, T::WIDTH);
}
fn ceiling_log_base_power_of_2_helper_primitive_float() {
let test = |n: T, pow, out| {
assert_eq!(n.ceiling_log_base_power_of_2(pow), out);
};
test(T::ONE, 1, 0);
test(T::ONE, 5, 0);
test(T::TWO, 1, 1);
test(T::TWO, 2, 1);
test(T::from(3.0f32), 1, 2);
test(T::from(3.0f32), 2, 1);
test(T::from(4.0f32), 1, 2);
test(T::from(4.0f32), 2, 1);
test(T::from(4.0f32), 3, 1);
test(T::from(5.0f32), 1, 3);
test(T::from(5.0f32), 2, 2);
test(T::from(5.0f32), 3, 1);
test(T::from(100.0f32), 1, 7);
test(T::from(100.0f32), 2, 4);
test(T::from(100.0f32), 3, 3);
test(T::from(100.0f32), 4, 2);
test(T::from(100.0f32), 7, 1);
test(T::from(128.0f32), 1, 7);
test(T::from(128.0f32), 2, 4);
test(T::from(128.0f32), 3, 3);
test(T::from(128.0f32), 4, 2);
test(T::from(128.0f32), 7, 1);
test(T::from(0.1f32), 1, -3);
test(T::from(0.1f32), 2, -1);
test(T::from(0.1f32), 3, -1);
test(T::from(0.1f32), 4, 0);
test(T::MIN_POSITIVE_SUBNORMAL, 1, T::MIN_EXPONENT);
test(T::MAX_FINITE, 1, T::MAX_EXPONENT + 1);
}
#[test]
fn test_ceiling_log_base_power_of_2() {
apply_fn_to_unsigneds!(ceiling_log_base_power_of_2_helper_unsigned);
apply_fn_to_primitive_floats!(ceiling_log_base_power_of_2_helper_primitive_float);
}
fn ceiling_log_base_power_of_2_fail_helper_unsigned() {
assert_panic!(T::ZERO.ceiling_log_base_power_of_2(2));
assert_panic!(T::TWO.ceiling_log_base_power_of_2(0));
}
fn ceiling_log_base_power_of_2_fail_helper_primitive_float() {
assert_panic!(T::ZERO.ceiling_log_base_power_of_2(2));
assert_panic!(T::NEGATIVE_ONE.ceiling_log_base_power_of_2(2));
assert_panic!(T::NEGATIVE_INFINITY.ceiling_log_base_power_of_2(2));
assert_panic!(T::INFINITY.ceiling_log_base_power_of_2(2));
assert_panic!(T::NAN.ceiling_log_base_power_of_2(2));
assert_panic!(T::TWO.ceiling_log_base_power_of_2(0));
}
#[test]
fn ceiling_log_base_power_of_2_fail() {
apply_fn_to_unsigneds!(ceiling_log_base_power_of_2_fail_helper_unsigned);
apply_fn_to_primitive_floats!(ceiling_log_base_power_of_2_fail_helper_primitive_float);
}
fn checked_log_base_power_of_2_helper_unsigned() {
let test = |n: T, pow, out| {
assert_eq!(n.checked_log_base_power_of_2(pow), out);
};
test(T::ONE, 1, Some(0));
test(T::ONE, 5, Some(0));
test(T::TWO, 1, Some(1));
test(T::TWO, 2, None);
test(T::exact_from(3), 1, None);
test(T::exact_from(3), 2, None);
test(T::exact_from(4), 1, Some(2));
test(T::exact_from(4), 2, Some(1));
test(T::exact_from(4), 3, None);
test(T::exact_from(5), 1, None);
test(T::exact_from(5), 2, None);
test(T::exact_from(5), 3, None);
test(T::exact_from(100), 1, None);
test(T::exact_from(100), 2, None);
test(T::exact_from(100), 3, None);
test(T::exact_from(100), 4, None);
test(T::exact_from(100), 7, None);
test(T::exact_from(128), 1, Some(7));
test(T::exact_from(128), 2, None);
test(T::exact_from(128), 3, None);
test(T::exact_from(128), 4, None);
test(T::exact_from(128), 7, Some(1));
test(T::MAX, 1, None);
}
fn checked_log_base_power_of_2_helper_primitive_float() {
let test = |n: T, pow, out| {
assert_eq!(n.checked_log_base_power_of_2(pow), out);
};
test(T::ONE, 1, Some(0));
test(T::ONE, 5, Some(0));
test(T::TWO, 1, Some(1));
test(T::TWO, 2, None);
test(T::from(3.0f32), 1, None);
test(T::from(3.0f32), 2, None);
test(T::from(4.0f32), 1, Some(2));
test(T::from(4.0f32), 2, Some(1));
test(T::from(4.0f32), 3, None);
test(T::from(5.0f32), 1, None);
test(T::from(5.0f32), 2, None);
test(T::from(5.0f32), 3, None);
test(T::from(100.0f32), 1, None);
test(T::from(100.0f32), 2, None);
test(T::from(100.0f32), 3, None);
test(T::from(100.0f32), 4, None);
test(T::from(100.0f32), 7, None);
test(T::from(128.0f32), 1, Some(7));
test(T::from(128.0f32), 2, None);
test(T::from(128.0f32), 3, None);
test(T::from(128.0f32), 4, None);
test(T::from(128.0f32), 7, Some(1));
test(T::from(0.1f32), 1, None);
test(T::from(0.1f32), 2, None);
test(T::from(0.1f32), 3, None);
test(T::from(0.1f32), 4, None);
test(T::MIN_POSITIVE_SUBNORMAL, 1, Some(T::MIN_EXPONENT));
test(T::MAX_FINITE, 1, None);
}
#[test]
fn test_checked_log_base_power_of_2() {
apply_fn_to_unsigneds!(checked_log_base_power_of_2_helper_unsigned);
apply_fn_to_primitive_floats!(checked_log_base_power_of_2_helper_primitive_float);
}
fn checked_log_base_power_of_2_fail_helper_unsigned() {
assert_panic!(T::ZERO.checked_log_base_power_of_2(2));
assert_panic!(T::TWO.checked_log_base_power_of_2(0));
}
fn checked_log_base_power_of_2_fail_helper_primitive_float() {
assert_panic!(T::ZERO.checked_log_base_power_of_2(2));
assert_panic!(T::NEGATIVE_ONE.checked_log_base_power_of_2(2));
assert_panic!(T::NEGATIVE_INFINITY.checked_log_base_power_of_2(2));
assert_panic!(T::INFINITY.checked_log_base_power_of_2(2));
assert_panic!(T::NAN.checked_log_base_power_of_2(2));
assert_panic!(T::TWO.checked_log_base_power_of_2(0));
}
#[test]
fn checked_log_base_power_of_2_fail() {
apply_fn_to_unsigneds!(checked_log_base_power_of_2_fail_helper_unsigned);
apply_fn_to_primitive_floats!(checked_log_base_power_of_2_fail_helper_primitive_float);
}
fn floor_log_base_power_of_2_properties_helper_unsigned() {
unsigned_pair_gen_var_21::().test_properties(|(n, pow)| {
let floor_log = n.floor_log_base_power_of_2(pow);
assert!(floor_log < T::WIDTH);
assert_eq!(floor_log == 0, n.significant_bits() - 1 < pow);
if pow < T::WIDTH {
assert_eq!(n.floor_log_base(T::power_of_2(pow)), floor_log);
}
let product = floor_log * pow;
if product < T::WIDTH {
assert!(T::power_of_2(product) <= n);
}
let product_2 = product + pow;
if product_2 < T::WIDTH {
assert!(T::power_of_2(product_2) > n);
}
let ceiling_log = n.ceiling_log_base_power_of_2(pow);
if n.is_power_of_2() && (n.significant_bits() - 1).divisible_by(pow) {
assert_eq!(ceiling_log, floor_log);
} else {
assert_eq!(ceiling_log, floor_log + 1);
}
});
unsigned_gen_var_1::().test_properties(|n| {
assert_eq!(n.floor_log_base_power_of_2(1), n.floor_log_base_2());
assert_eq!(n.floor_log_base_power_of_2(T::WIDTH), 0);
});
unsigned_gen_var_11().test_properties(|pow| {
assert_eq!(T::ONE.floor_log_base_power_of_2(pow), 0);
if pow < T::WIDTH {
assert_eq!(T::power_of_2(pow).floor_log_base_power_of_2(pow), 1);
}
});
}
fn floor_log_base_power_of_2_properties_helper_primitive_float() {
primitive_float_unsigned_pair_gen_var_3::().test_properties(|(n, pow)| {
let floor_log = n.floor_log_base_power_of_2(pow);
assert!(floor_log <= T::MAX_EXPONENT);
assert!(floor_log >= T::MIN_EXPONENT);
let i_pow = i64::exact_from(pow);
if i_pow >= T::MIN_EXPONENT && i_pow <= T::MAX_EXPONENT {
assert_eq!(floor_log == 0, n >= T::ONE && n < T::power_of_2(i_pow));
}
let product = floor_log * i_pow;
if product >= T::MIN_EXPONENT && product <= T::MAX_EXPONENT {
assert!(T::power_of_2(product) <= n);
}
let product_2 = product + i_pow;
if product_2 >= T::MIN_EXPONENT && product_2 <= T::MAX_EXPONENT {
assert!(T::power_of_2(product_2) > n);
}
let ceiling_log = n.ceiling_log_base_power_of_2(pow);
if n.is_power_of_2() && n.floor_log_base_2().divisible_by(i_pow) {
assert_eq!(ceiling_log, floor_log);
} else {
assert_eq!(ceiling_log, floor_log + 1);
}
});
primitive_float_gen_var_18::().test_properties(|n| {
assert_eq!(n.floor_log_base_power_of_2(1), n.floor_log_base_2());
});
unsigned_gen_var_11().test_properties(|pow| {
assert_eq!(T::ONE.floor_log_base_power_of_2(pow), 0);
});
}
#[test]
fn floor_log_base_power_of_2_properties_unsigned() {
apply_fn_to_unsigneds!(floor_log_base_power_of_2_properties_helper_unsigned);
apply_fn_to_primitive_floats!(floor_log_base_power_of_2_properties_helper_primitive_float);
}
fn ceiling_log_base_power_of_2_properties_helper_unsigned() {
unsigned_pair_gen_var_21::().test_properties(|(n, pow)| {
let ceiling_log = n.ceiling_log_base_power_of_2(pow);
assert!(ceiling_log <= T::WIDTH);
assert_eq!(ceiling_log, ceiling_log_base_power_of_2_naive(n, pow));
assert_eq!(ceiling_log == 0, n == T::ONE);
if pow < T::WIDTH {
assert_eq!(n.ceiling_log_base(T::power_of_2(pow)), ceiling_log);
}
let product = ceiling_log * pow;
if product < T::WIDTH {
assert!(T::power_of_2(product) >= n);
}
if product != 0 {
assert!(T::power_of_2(product - pow) < n);
}
let floor_log = n.floor_log_base_power_of_2(pow);
if n.is_power_of_2() && (n.significant_bits() - 1).divisible_by(pow) {
assert_eq!(floor_log, ceiling_log);
} else {
assert_eq!(floor_log, ceiling_log - 1);
}
});
unsigned_gen_var_1::().test_properties(|n| {
assert_eq!(n.ceiling_log_base_power_of_2(1), n.ceiling_log_base_2());
assert_eq!(
n.ceiling_log_base_power_of_2(T::WIDTH),
u64::from(n != T::ONE)
);
});
unsigned_gen_var_11().test_properties(|pow| {
assert_eq!(T::ONE.ceiling_log_base_power_of_2(pow), 0);
if pow < T::WIDTH {
assert_eq!(T::power_of_2(pow).ceiling_log_base_power_of_2(pow), 1);
}
});
}
fn ceiling_log_base_power_of_2_properties_helper_primitive_float() {
primitive_float_unsigned_pair_gen_var_3::().test_properties(|(n, pow)| {
let ceiling_log = n.ceiling_log_base_power_of_2(pow);
assert!(ceiling_log <= T::MAX_EXPONENT + 1);
assert!(ceiling_log >= T::MIN_EXPONENT);
let i_pow = i64::exact_from(pow);
if i_pow >= T::MIN_EXPONENT && i_pow <= T::MAX_EXPONENT {
assert_eq!(
ceiling_log == 0,
n > T::ONE / T::power_of_2(i_pow) && n <= T::ONE
);
}
let product = ceiling_log * i_pow;
if product >= T::MIN_EXPONENT && product <= T::MAX_EXPONENT {
assert!(T::power_of_2(product) >= n);
}
let product_2 = product - i_pow;
if product_2 >= T::MIN_EXPONENT && product_2 <= T::MAX_EXPONENT {
assert!(T::power_of_2(product_2) < n);
}
let floor_log = n.floor_log_base_power_of_2(pow);
if n.is_power_of_2() && n.floor_log_base_2().divisible_by(i_pow) {
assert_eq!(floor_log, ceiling_log);
} else {
assert_eq!(floor_log, ceiling_log - 1);
}
});
primitive_float_gen_var_18::().test_properties(|n| {
assert_eq!(n.ceiling_log_base_power_of_2(1), n.ceiling_log_base_2());
});
unsigned_gen_var_11().test_properties(|pow| {
assert_eq!(T::ONE.ceiling_log_base_power_of_2(pow), 0);
});
}
#[test]
fn ceiling_log_base_power_of_2_properties() {
apply_fn_to_unsigneds!(ceiling_log_base_power_of_2_properties_helper_unsigned);
apply_fn_to_primitive_floats!(ceiling_log_base_power_of_2_properties_helper_primitive_float);
}
fn checked_log_base_power_of_2_properties_helper_unsigned() {
unsigned_pair_gen_var_21::().test_properties(|(n, pow)| {
let checked_log = n.checked_log_base_power_of_2(pow);
assert_eq!(
checked_log.is_some(),
n.is_power_of_2() && (n.significant_bits() - 1).divisible_by(pow)
);
if pow < T::WIDTH {
assert_eq!(n.checked_log_base(T::power_of_2(pow)), checked_log);
}
if let Some(log) = checked_log {
assert_eq!(T::power_of_2(log * pow), n);
assert!(log <= T::WIDTH);
assert_eq!(log == 0, n == T::ONE);
assert_eq!(n.floor_log_base_power_of_2(pow), log);
assert_eq!(n.ceiling_log_base_power_of_2(pow), log);
}
});
unsigned_gen_var_1::().test_properties(|n| {
assert_eq!(n.checked_log_base_power_of_2(1), n.checked_log_base_2());
assert_eq!(
n.checked_log_base_power_of_2(T::WIDTH),
if n == T::ONE { Some(0) } else { None }
);
});
unsigned_gen_var_11().test_properties(|pow| {
assert_eq!(T::ONE.checked_log_base_power_of_2(pow), Some(0));
if pow < T::WIDTH {
assert_eq!(T::power_of_2(pow).checked_log_base_power_of_2(pow), Some(1));
}
});
}
fn checked_log_base_power_of_2_properties_helper_primitive_float() {
primitive_float_unsigned_pair_gen_var_3::().test_properties(|(n, pow)| {
let checked_log = n.checked_log_base_power_of_2(pow);
let i_pow = i64::exact_from(pow);
assert_eq!(
checked_log.is_some(),
n.is_power_of_2() && n.checked_log_base_2().unwrap().divisible_by(i_pow)
);
if let Some(log) = checked_log {
assert_eq!(T::power_of_2(log * i_pow), n);
assert!(log <= T::MAX_EXPONENT);
assert!(log >= T::MIN_EXPONENT);
assert_eq!(log == 0, n == T::ONE);
assert_eq!(n.floor_log_base_power_of_2(pow), log);
assert_eq!(n.ceiling_log_base_power_of_2(pow), log);
}
});
primitive_float_gen_var_18::().test_properties(|n| {
assert_eq!(n.checked_log_base_power_of_2(1), n.checked_log_base_2());
});
unsigned_gen_var_11().test_properties(|pow| {
assert_eq!(T::ONE.checked_log_base_power_of_2(pow), Some(0));
let i_pow = i64::exact_from(pow);
if i_pow >= T::MIN_EXPONENT && i_pow <= T::MAX_EXPONENT {
assert_eq!(
T::power_of_2(i_pow).checked_log_base_power_of_2(pow),
Some(1)
);
}
});
}
#[test]
fn checked_log_base_power_of_2_properties() {
apply_fn_to_unsigneds!(checked_log_base_power_of_2_properties_helper_unsigned);
apply_fn_to_primitive_floats!(checked_log_base_power_of_2_properties_helper_primitive_float);
}