pub fn johnson_trotter<T: Copy + Ord>(items: &[T]) -> Vec<Vec<T>> {
if items.is_empty() {
return vec![vec![]];
}
let mut elements = items.to_vec();
let mut dirs = vec![-1; elements.len()];
let mut result = Vec::new();
result.push(elements.clone());
loop {
let mut mobile_index = None;
let mut mobile_value = None;
for i in 0..elements.len() {
let dir = dirs[i];
let adj = (i as isize + dir as isize) as usize;
if adj < elements.len() && elements[i] > elements[adj] {
if mobile_value.is_none_or(|val| elements[i] > val) {
mobile_index = Some(i);
mobile_value = Some(elements[i]);
}
}
}
let Some(i) = mobile_index else {
break;
};
let dir = dirs[i];
let j = (i as isize + dir as isize) as usize;
elements.swap(i, j);
dirs.swap(i, j);
if let Some(val) = mobile_value {
for d_i in 0..elements.len() {
if elements[d_i] > val {
dirs[d_i] = -dirs[d_i];
}
}
}
result.push(elements.clone());
}
result
}
#[cfg(test)]
mod tests {
use super::johnson_trotter;
#[test]
fn test_jt_basic() {
let data = vec![1, 2, 3];
let perms = johnson_trotter(&data);
let expected = vec![
vec![1, 2, 3],
vec![1, 3, 2],
vec![3, 1, 2],
vec![3, 2, 1],
vec![2, 3, 1],
vec![2, 1, 3],
];
assert_eq!(perms, expected);
}
#[test]
fn test_jt_empty() {
let data: Vec<i32> = vec![];
let perms = johnson_trotter(&data);
assert_eq!(perms, vec![vec![]]);
}
#[test]
fn test_jt_single() {
let data = vec![42];
let perms = johnson_trotter(&data);
assert_eq!(perms, vec![vec![42]]);
}
}