pub const MIN_EXPONENT: u32 = 3;
pub const MAX_EXPONENT: u32 = 10;
#[must_use]
pub const fn exponent_to_count(exponent: u32) -> u32 {
exponent_to_count_bounded(exponent, MIN_EXPONENT, MAX_EXPONENT)
}
#[must_use]
pub fn count_to_exponent(count: u32) -> u32 {
count_to_exponent_bounded(count, MIN_EXPONENT, MAX_EXPONENT)
}
#[must_use]
pub const fn exponent_to_count_bounded(exponent: u32, min_exponent: u32, max_exponent: u32) -> u32 {
1u32 << clamp_exponent(exponent, min_exponent, max_exponent)
}
#[must_use]
pub fn count_to_exponent_bounded(count: u32, min_exponent: u32, max_exponent: u32) -> u32 {
let exponent = count.max(1).ilog2();
clamp_exponent(exponent, min_exponent, max_exponent)
}
const fn clamp_exponent(exponent: u32, min_exponent: u32, max_exponent: u32) -> u32 {
if exponent < min_exponent {
min_exponent
} else if exponent > max_exponent {
max_exponent
} else {
exponent
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trips_every_legal_exponent() {
for exponent in MIN_EXPONENT..=MAX_EXPONENT {
let count = exponent_to_count(exponent);
assert_eq!(
count_to_exponent(count),
exponent,
"exponent={exponent} count={count}"
);
}
}
#[test]
fn exponent_to_count_matches_expected_powers_of_two() {
assert_eq!(exponent_to_count(3), 8);
assert_eq!(exponent_to_count(6), 64);
assert_eq!(exponent_to_count(8), 256);
assert_eq!(exponent_to_count(10), 1024);
}
#[test]
fn exponent_to_count_clamps_out_of_range_exponents() {
assert_eq!(exponent_to_count(0), 8, "clamped up to MIN_EXPONENT");
assert_eq!(exponent_to_count(31), 1024, "clamped down to MAX_EXPONENT");
}
#[test]
fn count_to_exponent_clamps_out_of_range_counts() {
assert_eq!(count_to_exponent(0), MIN_EXPONENT);
assert_eq!(count_to_exponent(1), MIN_EXPONENT);
assert_eq!(count_to_exponent(u32::MAX), MAX_EXPONENT);
}
#[test]
fn count_to_exponent_floors_a_non_power_of_two_count() {
assert_eq!(count_to_exponent(300), 8, "floor(log2(300)) == 8 -> 256");
assert_eq!(
count_to_exponent(511),
8,
"just below 512 still floors to 256"
);
}
#[test]
fn bounded_with_the_modules_own_range_matches_the_fixed_range_functions() {
for exponent in 0..16 {
assert_eq!(
exponent_to_count(exponent),
exponent_to_count_bounded(exponent, MIN_EXPONENT, MAX_EXPONENT),
"exponent={exponent}"
);
}
for count in [0, 1, 8, 100, 300, 1024, u32::MAX] {
assert_eq!(
count_to_exponent(count),
count_to_exponent_bounded(count, MIN_EXPONENT, MAX_EXPONENT),
"count={count}"
);
}
}
#[test]
fn bounded_round_trips_every_legal_exponent_in_a_different_range() {
for exponent in 7..=13 {
let count = exponent_to_count_bounded(exponent, 7, 13);
assert_eq!(
count_to_exponent_bounded(count, 7, 13),
exponent,
"exponent={exponent} count={count}"
);
}
}
#[test]
fn bounded_clamps_out_of_range_exponents_and_counts() {
assert_eq!(exponent_to_count_bounded(0, 7, 13), 128, "clamped up to 7");
assert_eq!(
exponent_to_count_bounded(31, 7, 13),
8192,
"clamped down to 13"
);
assert_eq!(count_to_exponent_bounded(0, 7, 13), 7);
assert_eq!(count_to_exponent_bounded(u32::MAX, 7, 13), 13);
}
}