From 9983748ee6e2141546bfa81cb94b49492999adca Mon Sep 17 00:00:00 2001 From: nichkara Date: Sat, 20 Sep 2025 13:38:35 +0200 Subject: [PATCH] Init --- .gitignore | 1 + Cargo.toml | 6 + src/lib.rs | 435 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 442 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..8dc6cb4 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "tm_interpreter" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..07e6812 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,435 @@ +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.") + } + } + } +}