mod feistel;
pub use feistel::{FeistelNetwork, Permutor};
#[derive(Debug, Clone)]
pub struct Permutation {
permutor: Permutor,
}
impl Permutation {
pub fn new(len: u128, seed: u64) -> Self {
assert!(len > 0, "Permutation length must be greater than 0");
Self {
permutor: Permutor::new_with_u64_key(len, seed),
}
}
pub fn new_with_key(len: u128, key: [u8; 32]) -> Self {
assert!(len > 0, "Permutation length must be greater than 0");
Self {
permutor: Permutor::new_with_slice_key(len, key),
}
}
#[inline]
pub fn len(&self) -> u128 {
self.permutor.max
}
#[inline]
pub fn is_empty(&self) -> bool {
false
}
#[inline]
pub fn get(&self, i: u128) -> Option<u128> {
if i >= self.permutor.max {
None
} else {
Some(self.permutor.forward(i))
}
}
#[inline]
pub unsafe fn get_unchecked(&self, i: u128) -> u128 {
self.permutor.forward(i)
}
#[inline]
pub fn index_of(&self, value: u128) -> Option<u128> {
if value >= self.permutor.max {
None
} else {
Some(self.permutor.backward(value))
}
}
pub fn iter(&self) -> PermutationIter {
PermutationIter {
permutor: self.permutor.clone(),
}
}
}
impl IntoIterator for Permutation {
type Item = u128;
type IntoIter = PermutationIter;
fn into_iter(self) -> Self::IntoIter {
PermutationIter {
permutor: self.permutor,
}
}
}
impl<'a> IntoIterator for &'a Permutation {
type Item = u128;
type IntoIter = PermutationIter;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
#[derive(Debug, Clone)]
pub struct PermutationIter {
permutor: Permutor,
}
impl Iterator for PermutationIter {
type Item = u128;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.permutor.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = (self.permutor.max - self.permutor.values_returned) as usize;
(remaining, Some(remaining))
}
}
impl ExactSizeIterator for PermutationIter {}
pub fn sample_indices(n: u128, k: usize, seed: u64) -> Vec<u128> {
assert!(n > 0, "n must be greater than 0");
assert!(k as u128 <= n, "k must be <= n");
let perm = Permutation::new(n, seed);
perm.iter().take(k).collect()
}
pub fn sample<T: Clone>(slice: &[T], k: usize, seed: u64) -> Vec<T> {
assert!(!slice.is_empty(), "slice must not be empty");
assert!(k <= slice.len(), "k must be <= slice length");
let indices = sample_indices(slice.len() as u128, k, seed);
indices.iter().map(|&i| slice[i as usize].clone()).collect()
}
pub fn shuffle<T: Clone>(slice: &[T], seed: u64) -> Vec<T> {
assert!(!slice.is_empty(), "slice must not be empty");
let perm = Permutation::new(slice.len() as u128, seed);
(0..slice.len())
.map(|i| {
let perm_idx = perm.get(i as u128).unwrap() as usize;
slice[perm_idx].clone()
})
.collect()
}
pub fn shuffle_in_place<T: Clone>(slice: &mut [T], seed: u64) {
if slice.is_empty() {
return;
}
let shuffled = shuffle(slice, seed);
slice.clone_from_slice(&shuffled);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_permutation_basic() {
let perm = Permutation::new(100, 42);
assert_eq!(perm.len(), 100);
let value = perm.get(50).unwrap();
assert!(value < 100);
let index = perm.index_of(value).unwrap();
assert_eq!(index, 50);
}
#[test]
fn test_permutation_iterator() {
let perm = Permutation::new(10, 42);
let values: Vec<u128> = perm.iter().collect();
assert_eq!(values.len(), 10);
let mut sorted = values.clone();
sorted.sort();
sorted.dedup();
assert_eq!(sorted.len(), 10);
assert!(sorted.iter().all(|&v| v < 10));
}
#[test]
fn test_sample_indices() {
let indices = sample_indices(100, 10, 42);
assert_eq!(indices.len(), 10);
let mut sorted = indices.clone();
sorted.sort();
sorted.dedup();
assert_eq!(sorted.len(), 10);
assert!(sorted.iter().all(|&i| i < 100));
}
#[test]
fn test_sample() {
let data: Vec<i32> = (0..100).collect();
let sampled = sample(&data, 10, 42);
assert_eq!(sampled.len(), 10);
}
#[test]
fn test_shuffle() {
let data: Vec<i32> = (0..10).collect();
let shuffled = shuffle(&data, 42);
assert_eq!(shuffled.len(), 10);
let mut sorted = shuffled.clone();
sorted.sort();
assert_eq!(sorted, data);
}
#[test]
fn test_shuffle_in_place() {
let mut data: Vec<i32> = (0..10).collect();
let original = data.clone();
shuffle_in_place(&mut data, 42);
let mut sorted = data.clone();
sorted.sort();
assert_eq!(sorted, original);
}
#[test]
fn test_deterministic() {
let perm1 = Permutation::new(100, 42);
let perm2 = Permutation::new(100, 42);
for i in 0..100 {
assert_eq!(perm1.get(i), perm2.get(i));
}
}
#[test]
fn test_different_seeds() {
let perm1 = Permutation::new(100, 42);
let perm2 = Permutation::new(100, 43);
let same_count = (0..100)
.filter(|&i| perm1.get(i) == perm2.get(i))
.count();
assert!(same_count < 10);
}
#[test]
#[should_panic(expected = "Permutation length must be greater than 0")]
fn test_zero_length_panics() {
Permutation::new(0, 42);
}
#[test]
fn test_out_of_bounds() {
let perm = Permutation::new(10, 42);
assert!(perm.get(10).is_none());
assert!(perm.get(100).is_none());
assert!(perm.index_of(10).is_none());
}
#[test]
fn test_chi_square_uniformity() {
use std::collections::HashMap;
let n = 4usize; let num_shuffles = 100_000u64;
let factorial_n = (1..=n).product::<usize>();
let mut lcg_state: u64 = 12345; let mut next_seed = || {
lcg_state = lcg_state.wrapping_mul(6364136223846793005).wrapping_add(1);
lcg_state
};
let mut counts: HashMap<Vec<u128>, u64> = HashMap::new();
for _ in 0..num_shuffles {
let seed = next_seed();
let perm = Permutation::new(n as u128, seed);
let values: Vec<u128> = perm.iter().collect();
*counts.entry(values).or_insert(0) += 1;
}
let expected = num_shuffles as f64 / factorial_n as f64;
assert_eq!(
counts.len(),
factorial_n,
"Not all permutations were observed"
);
let min_acceptable = (expected * 0.85) as u64;
let max_acceptable = (expected * 1.15) as u64;
for (perm, count) in &counts {
assert!(
*count >= min_acceptable && *count <= max_acceptable,
"Permutation {:?} has count {} which is outside acceptable range [{}, {}]",
perm,
count,
min_acceptable,
max_acceptable
);
}
let chi_square: f64 = counts
.values()
.map(|&observed| {
let diff = observed as f64 - expected;
diff * diff / expected
})
.sum();
println!("Chi-square statistic: {:.2}", chi_square);
println!(
"Expected ~{} for uniform distribution (df={})",
factorial_n - 1,
factorial_n - 1
);
}
}