const BANKS_PER_WIDTH: usize = 8;
const FEEDBACK_BANKS: [u64; 61 * BANKS_PER_WIDTH] = include!("metashift_banks.inc");
pub fn feedback_for_width_and_bank(width: u32, bank: usize) -> u64 {
assert!(
(4..=64).contains(&width),
"LFSR width must be 4..64, got {width}"
);
let base = (width as usize - 4) * BANKS_PER_WIDTH;
FEEDBACK_BANKS[base + (bank % BANKS_PER_WIDTH)]
}
pub fn feedback_for_width(width: u32) -> u64 {
feedback_for_width_and_bank(width, 0)
}
pub fn width_for_period(period: u64) -> u32 {
assert!(period > 0, "period must be positive");
let bits = 64 - period.leading_zeros();
bits.max(4) }
pub fn feedback_for_size(size: u64) -> u64 {
feedback_for_width_and_bank(width_for_period(size), 0)
}
#[inline]
pub fn lfsr_step(register: u64, feedback: u64) -> u64 {
let lsb = register & 1;
let shifted = register >> 1;
shifted ^ (lsb.wrapping_neg() & feedback)
}
#[inline]
pub fn shuffle_bounded(input: u64, feedback: u64, size: u64, min: u64) -> u64 {
if size == 0 {
return min;
}
let mut register = (input % size) + 1;
let width = width_for_period(size);
let budget = if width >= 63 { u64::MAX } else { 1u64 << width };
let mut steps = 0u64;
loop {
register = lfsr_step(register, feedback);
if register == 0 {
return min;
}
if register <= size {
break;
}
steps += 1;
if steps >= budget {
return min;
}
}
(register - 1) + min
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_bounded_shuffle_is_a_permutation() {
let size = 1000u64;
let feedback = feedback_for_size(size);
let mut seen = vec![false; size as usize];
for input in 0..size {
let out = shuffle_bounded(input, feedback, size, 0);
assert!(out < size, "{out} out of range for size {size}");
assert!(!seen[out as usize], "{out} visited twice");
seen[out as usize] = true;
}
assert!(seen.iter().all(|b| *b), "not every value was visited");
}
#[test]
fn an_empty_range_answers_its_floor() {
for min in [0u64, 7, u64::MAX] {
for input in [0u64, 1, 42, u64::MAX] {
assert_eq!(shuffle_bounded(input, 0, 0, min), min);
}
}
}
#[test]
fn a_single_value_range_is_that_value() {
let feedback = feedback_for_size(1);
for input in [0u64, 1, 42, u64::MAX] {
assert_eq!(shuffle_bounded(input, feedback, 1, 5), 5);
}
}
#[test]
fn a_collapsed_sequence_answers_the_floor() {
for size in [1u64, 2, 3, 16, 1000] {
for input in [0u64, 1, 42, u64::MAX] {
let out = shuffle_bounded(input, 0, size, 9);
assert!(
(9..9 + size).contains(&out),
"size={size} input={input} left the range with {out}"
);
}
}
}
#[test]
fn a_valid_polynomial_never_exhausts_the_budget() {
for size in [1u64, 2, 5, 16, 17, 100, 255, 256, 1000] {
let width = width_for_period(size);
for bank in 0..8 {
let feedback = feedback_for_width_and_bank(width, bank);
let mut seen = vec![false; size as usize];
for input in 0..size {
let out = shuffle_bounded(input, feedback, size, 0);
assert!(out < size, "size={size} bank={bank} left range: {out}");
assert!(
!seen[out as usize],
"size={size} bank={bank} repeated {out}"
);
seen[out as usize] = true;
}
assert!(
seen.iter().all(|b| *b),
"size={size} bank={bank}: the budget truncated a valid permutation"
);
}
}
}
#[test]
fn a_feedback_that_cannot_land_terminates() {
for size in [1u64, 2, 3, 7, 64, 1000] {
for feedback in [
u64::MAX,
1u64 << 63,
(1u64 << 63) | 1,
0xFFFF_0000_FFFF_0000,
] {
for input in [0u64, 1, 42] {
let out = shuffle_bounded(input, feedback, size, 11);
assert!(
(11..11 + size).contains(&out),
"size={size} feedback={feedback:#x} input={input} gave {out}"
);
}
}
}
}
#[test]
fn arbitrary_constants_stay_in_range() {
for feedback in [0u64, 1, 2, 7, 0xB400, u64::MAX] {
for size in [0u64, 1, 2, 5, 64] {
for input in [0u64, 1, 9, u64::MAX] {
let out = shuffle_bounded(input, feedback, size, 3);
let upper = 3 + size.max(1);
assert!(
(3..upper).contains(&out),
"feedback={feedback} size={size} input={input} gave {out}"
);
}
}
}
}
#[test]
fn lfsr_step_leaves_zero_alone() {
assert_eq!(lfsr_step(0, 0xB400), 0);
}
}