use std::collections::{HashMap};
use std::fmt::{Debug};
use std::hash::{Hash};
#[allow(clippy::implicit_hasher)]
pub fn moves<V: Debug + Clone + Hash + Eq>(
mut dest_to_src: HashMap<V, V>,
temp: &V,
) -> impl Iterator<Item=(V, V)> {
let dests: Vec<V> = dest_to_src.iter().map(|(dest, src)| {
assert_ne!(src, temp);
dest.clone()
}).collect();
let mut moves: Vec<(V, V)> = Vec::new(); let mut chain: Vec<V> = Vec::new(); for mut current in dests {
while let Some(src) = dest_to_src.remove(¤t) {
if src == current {
break;
}
chain.push(current);
current = src;
}
let cycle = chain.iter().find(|&dest: &&V| dest == ¤t).cloned();
if cycle.is_some() {
current = temp.clone();
}
while let Some(dest) = chain.pop() {
moves.push((dest.clone(), current));
current = dest;
}
if let Some(src) = cycle {
moves.push((temp.clone(), src));
}
assert_eq!(chain.len(), 0);
}
moves.into_iter().rev()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn all_small() {
const N: usize = 5;
const N1: usize = N + 1;
let mut input = [0; N1];
for src in 0..N1 {
input[src] = src + 10;
}
for test in 0..(N1.pow(N as u32)) {
let mut dest_to_src = HashMap::new();
let mut i = test;
for dest in 0..N {
let src = i % N1;
i /= N1;
if src < N {
dest_to_src.insert(dest, src);
}
}
let mut expected = input;
for (&dest, &src) in &dest_to_src {
expected[dest] = input[src]
}
let pairs: Vec<_> = moves(dest_to_src, &N).collect();
let mut observed = input;
for &(dest, src) in &pairs {
observed[dest] = observed[src];
}
if expected[..N] != observed[..N] {
println!("Test: {:#?}", test);
println!("Input: {:#?}", &input[..N]);
println!("Expected: {:#?}", &expected[..N]);
println!("Attempt:");
for (dest, src) in pairs {
println!(" Move {:#?} <- {:#?}", dest, src);
}
println!("Observed: {:#?}", &observed[..N]);
panic!("Attempt does not work");
}
}
}
}