mod tm_interpreter { use std::collections::HashMap; use std::hash::Hash; pub enum Shift { Left, Right, NoShift, } // Some kind of Turing machine pub struct TM { alphabet: Vec, b: G, sigma: Vec, q: Vec, q0: S, f: Vec, delta: Vec<((S, G), (S, G, Shift))>, } impl TM { pub fn new( alphabet: Vec, b: G, sigma: Vec, q: Vec, q0: S, f: Vec, delta: Vec<((S, G), (S, G, Shift))>, ) -> Result, String> where S: Eq, S: Hash, S: Clone, { if !alphabet.contains(&b) { return Err(String::from("Blank symbol is undefined in alphabet.")); } else if sigma.contains(&b) { return Err(String::from("Blank symbol is not allowed as input.")); } else if !sigma.iter().all(|x| alphabet.contains(x)) { return Err(String::from("An input symbol is undefined in alphabet.")); } else if !q.contains(&q0) { return Err(String::from("The initial state is undefined.")); } else if !f.iter().all(|x| q.contains(x)) { return Err(String::from("A final state is undefined.")); } else { let turing_machine: TM = TM { alphabet: alphabet, b: b, sigma: sigma, q: q, q0: q0, f: f, delta: delta, }; // Checking transition function // Check parameter integrity for delta in turing_machine.delta.iter() { if !turing_machine.q.contains(&delta.0 .0) { return Err(String::from("Invalid input state.")); } if !turing_machine.alphabet.contains(&delta.0 .1) { return Err(String::from("Invalid input symbol.")); } if !turing_machine.q.contains(&delta.1 .0) { return Err(String::from("Invalid output state.")); } if !turing_machine.alphabet.contains(&delta.1 .1) { return Err(String::from("Invalid output symbol.")); } } // Check amount of input combinations if turing_machine.delta.len() < (turing_machine.alphabet.len() * (turing_machine.q.len() - turing_machine.f.len())) { return Err(String::from("Undefined behavior.")); } else if turing_machine.delta.len() > (turing_machine.alphabet.len() * (turing_machine.q.len() - turing_machine.f.len())) { return Err(String::from("Ambiguous behavior.")); } // Check termination and state usage by calculating all possible transitions // Check unused states (for now only detecting direct dead states) for rule in turing_machine.delta.iter() { let is_unused: bool = turing_machine.delta.iter().all(|x| x.1 .0 != rule.0 .0); if is_unused { return Err(String::from("Unused state.")); } } // Check termination // Create map let mut state_map: HashMap> = HashMap::new(); for state in turing_machine.q.iter() { state_map.insert(state.clone(), vec![]); } // Check all rules for rule in turing_machine.delta.iter() { // Get input and output state let input_state: S = rule.0 .0.clone(); let output_state: S = rule.1 .0.clone(); // Check if input is already known to output // Append, if not if !state_map.get(&output_state).unwrap().contains(&input_state) { state_map .get_mut(&output_state) .unwrap() .push(input_state.clone()); } } // Resolve terminating states let mut terminating_states: Vec = turing_machine.f.clone(); let mut update: bool = true; while update { update = false; for state_index in 0..terminating_states.len() { let dependends: Vec = state_map .get(&terminating_states[state_index]) .unwrap() .to_vec(); for dependend in dependends { if !terminating_states.contains(&dependend) { terminating_states.push(dependend); update = true; } } } } if terminating_states.len() != turing_machine.q.len() { return Err(String::from("Unterminated state.")); } return Ok(turing_machine); } } } } #[cfg(test)] mod tests { use crate::tm_interpreter::*; #[test] fn new_positive() { let alphabet: Vec = vec![0, 1]; let b: u8 = 0; let sigma: Vec = vec![1]; let q: Vec = vec!['A', 'B', 'C', 'H']; let q0: char = 'A'; let f: Vec = vec!['H']; let delta: Vec<((char, u8), (char, u8, Shift))> = vec![ (('A', 0), ('B', 1, Shift::Right)), (('A', 1), ('C', 1, Shift::Left)), (('B', 0), ('A', 1, Shift::Left)), (('B', 1), ('B', 1, Shift::Right)), (('C', 0), ('B', 1, Shift::Left)), (('C', 1), ('H', 1, Shift::Right)), ]; let working_machine = TM::::new(alphabet, b, sigma, q, q0, f, delta); match working_machine { Ok(_) => assert_eq!(true, true), Err(e) => { println!("{}", e); panic!("") } } } #[test] fn new_undefined_blank() { let alphabet: Vec = vec![0, 1]; let b: u8 = 2; let sigma: Vec = vec![1]; let q: Vec = vec!['A', 'B', 'C', 'H']; let q0: char = 'A'; let f: Vec = vec!['H']; let delta: Vec<((char, u8), (char, u8, Shift))> = vec![ (('A', 0), ('B', 1, Shift::Right)), (('A', 1), ('C', 1, Shift::Left)), (('B', 0), ('A', 1, Shift::Left)), (('B', 1), ('B', 1, Shift::Right)), (('C', 0), ('B', 1, Shift::Left)), (('C', 1), ('H', 1, Shift::Right)), ]; let working_machine = TM::::new(alphabet, b, sigma, q, q0, f, delta); match working_machine { Ok(_) => panic!(""), Err(e) => { assert_eq!(e.as_str(), "Blank symbol is undefined in alphabet.") } } } #[test] fn new_blank_in_sigma() { let alphabet: Vec = vec![0, 1]; let b: u8 = 1; let sigma: Vec = vec![1]; let q: Vec = vec!['A', 'B', 'C', 'H']; let q0: char = 'A'; let f: Vec = vec!['H']; let delta: Vec<((char, u8), (char, u8, Shift))> = vec![ (('A', 0), ('B', 1, Shift::Right)), (('A', 1), ('C', 1, Shift::Left)), (('B', 0), ('A', 1, Shift::Left)), (('B', 1), ('B', 1, Shift::Right)), (('C', 0), ('B', 1, Shift::Left)), (('C', 1), ('H', 1, Shift::Right)), ]; let working_machine = TM::::new(alphabet, b, sigma, q, q0, f, delta); match working_machine { Ok(_) => panic!(""), Err(e) => { assert_eq!(e.as_str(), "Blank symbol is not allowed as input.") } } } #[test] fn new_undefined_sigma_symbol() { let alphabet: Vec = vec![0, 1]; let b: u8 = 0; let sigma: Vec = vec![1, 2]; let q: Vec = vec!['A', 'B', 'C', 'H']; let q0: char = 'A'; let f: Vec = vec!['H']; let delta: Vec<((char, u8), (char, u8, Shift))> = vec![ (('A', 0), ('B', 1, Shift::Right)), (('A', 1), ('C', 1, Shift::Left)), (('B', 0), ('A', 1, Shift::Left)), (('B', 1), ('B', 1, Shift::Right)), (('C', 0), ('B', 1, Shift::Left)), (('C', 1), ('H', 1, Shift::Right)), ]; let working_machine = TM::::new(alphabet, b, sigma, q, q0, f, delta); match working_machine { Ok(_) => panic!(""), Err(e) => { assert_eq!(e.as_str(), "An input symbol is undefined in alphabet.") } } } #[test] fn new_undefined_initial_state() { let alphabet: Vec = vec![0, 1]; let b: u8 = 0; let sigma: Vec = vec![1]; let q: Vec = vec!['A', 'B', 'C', 'H']; let q0: char = 'D'; let f: Vec = vec!['H']; let delta: Vec<((char, u8), (char, u8, Shift))> = vec![ (('A', 0), ('B', 1, Shift::Right)), (('A', 1), ('C', 1, Shift::Left)), (('B', 0), ('A', 1, Shift::Left)), (('B', 1), ('B', 1, Shift::Right)), (('C', 0), ('B', 1, Shift::Left)), (('C', 1), ('H', 1, Shift::Right)), ]; let working_machine = TM::::new(alphabet, b, sigma, q, q0, f, delta); match working_machine { Ok(_) => panic!(""), Err(e) => { assert_eq!(e.as_str(), "The initial state is undefined.") } } } #[test] fn new_undefined_final_state() { let alphabet: Vec = vec![0, 1]; let b: u8 = 0; let sigma: Vec = vec![1]; let q: Vec = vec!['A', 'B', 'C', 'H']; let q0: char = 'A'; let f: Vec = vec!['Q']; let delta: Vec<((char, u8), (char, u8, Shift))> = vec![ (('A', 0), ('B', 1, Shift::Right)), (('A', 1), ('C', 1, Shift::Left)), (('B', 0), ('A', 1, Shift::Left)), (('B', 1), ('B', 1, Shift::Right)), (('C', 0), ('B', 1, Shift::Left)), (('C', 1), ('H', 1, Shift::Right)), ]; let working_machine = TM::::new(alphabet, b, sigma, q, q0, f, delta); match working_machine { Ok(_) => panic!(""), Err(e) => { assert_eq!(e.as_str(), "A final state is undefined.") } } } #[test] fn new_invalid_inputt_state() { let alphabet: Vec = vec![0, 1]; let b: u8 = 0; let sigma: Vec = vec![1]; let q: Vec = vec!['A', 'B', 'C', 'H']; let q0: char = 'A'; let f: Vec = vec!['H']; let delta: Vec<((char, u8), (char, u8, Shift))> = vec![ (('D', 0), ('B', 1, Shift::Right)), (('A', 1), ('C', 1, Shift::Left)), (('B', 0), ('A', 1, Shift::Left)), (('B', 1), ('B', 1, Shift::Right)), (('C', 0), ('B', 1, Shift::Left)), (('C', 1), ('H', 1, Shift::Right)), ]; let working_machine = TM::::new(alphabet, b, sigma, q, q0, f, delta); match working_machine { Ok(_) => panic!(""), Err(e) => { assert_eq!(e.as_str(), "Invalid input state.") } } } #[test] fn new_invalid_input_symbol() { let alphabet: Vec = vec![0, 1]; let b: u8 = 0; let sigma: Vec = vec![1]; let q: Vec = vec!['A', 'B', 'C', 'H']; let q0: char = 'A'; let f: Vec = vec!['H']; let delta: Vec<((char, u8), (char, u8, Shift))> = vec![ (('A', 4), ('B', 1, Shift::Right)), (('A', 1), ('C', 1, Shift::Left)), (('B', 0), ('A', 1, Shift::Left)), (('B', 1), ('B', 1, Shift::Right)), (('C', 0), ('B', 1, Shift::Left)), (('C', 1), ('H', 1, Shift::Right)), ]; let working_machine = TM::::new(alphabet, b, sigma, q, q0, f, delta); match working_machine { Ok(_) => panic!(""), Err(e) => { assert_eq!(e.as_str(), "Invalid input symbol.") } } } #[test] fn new_undefined_behavior() { let alphabet: Vec = vec![0, 1]; let b: u8 = 0; let sigma: Vec = vec![1]; let q: Vec = vec!['A', 'B', 'C', 'H', 'I']; let q0: char = 'A'; let f: Vec = vec!['H']; let delta: Vec<((char, u8), (char, u8, Shift))> = vec![ (('A', 0), ('B', 1, Shift::Right)), (('A', 1), ('C', 1, Shift::Left)), (('B', 0), ('A', 1, Shift::Left)), (('B', 1), ('B', 1, Shift::Right)), (('C', 0), ('B', 1, Shift::Left)), (('C', 1), ('H', 1, Shift::Right)), ]; let working_machine = TM::::new(alphabet, b, sigma, q, q0, f, delta); match working_machine { Ok(_) => panic!(""), Err(e) => { assert_eq!(e.as_str(), "Undefined behavior.") } } } #[test] fn new_ambigous_behavior() { let alphabet: Vec = vec![0, 1]; let b: u8 = 0; let sigma: Vec = vec![1]; let q: Vec = vec!['A', 'B', 'C', 'H']; let q0: char = 'A'; let f: Vec = vec!['H']; let delta: Vec<((char, u8), (char, u8, Shift))> = vec![ (('A', 0), ('B', 1, Shift::Right)), (('A', 1), ('C', 1, Shift::Left)), (('B', 0), ('A', 1, Shift::Left)), (('B', 0), ('A', 1, Shift::Right)), (('B', 1), ('B', 1, Shift::Right)), (('C', 0), ('B', 1, Shift::Left)), (('C', 1), ('H', 1, Shift::Right)), ]; let working_machine = TM::::new(alphabet, b, sigma, q, q0, f, delta); match working_machine { Ok(_) => panic!(""), Err(e) => { assert_eq!(e.as_str(), "Ambiguous behavior.") } } } #[test] fn new_unterminated_state() { let alphabet: Vec = vec![0, 1]; let b: u8 = 0; let sigma: Vec = vec![1]; let q: Vec = vec!['A', 'B', 'C', 'D', 'H']; let q0: char = 'A'; let f: Vec = vec!['H']; let delta: Vec<((char, u8), (char, u8, Shift))> = vec![ (('A', 0), ('B', 1, Shift::Right)), (('A', 1), ('C', 1, Shift::Left)), (('B', 0), ('A', 1, Shift::Left)), (('B', 1), ('B', 1, Shift::Right)), (('C', 0), ('B', 1, Shift::Left)), (('C', 1), ('H', 1, Shift::Right)), (('D', 0), ('D', 1, Shift::Left)), (('D', 1), ('D', 1, Shift::Right)), ]; let working_machine = TM::::new(alphabet, b, sigma, q, q0, f, delta); match working_machine { Ok(_) => panic!(""), Err(e) => { assert_eq!(e.as_str(), "Unterminated state.") } } } }