436 lines
16 KiB
Rust
436 lines
16 KiB
Rust
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<G, S> {
|
|
alphabet: Vec<G>,
|
|
b: G,
|
|
sigma: Vec<G>,
|
|
q: Vec<S>,
|
|
q0: S,
|
|
f: Vec<S>,
|
|
delta: Vec<((S, G), (S, G, Shift))>,
|
|
}
|
|
|
|
impl<Gx, Sx> TM<Gx, Sx> {
|
|
pub fn new<G: std::cmp::PartialEq, S: std::cmp::PartialEq>(
|
|
alphabet: Vec<G>,
|
|
b: G,
|
|
sigma: Vec<G>,
|
|
q: Vec<S>,
|
|
q0: S,
|
|
f: Vec<S>,
|
|
delta: Vec<((S, G), (S, G, Shift))>,
|
|
) -> Result<TM<G, S>, 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<G, S> = 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<S, Vec<S>> = 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<S> = turing_machine.f.clone();
|
|
let mut update: bool = true;
|
|
while update {
|
|
update = false;
|
|
|
|
for state_index in 0..terminating_states.len() {
|
|
let dependends: Vec<S> = 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<u8> = vec![0, 1];
|
|
let b: u8 = 0;
|
|
let sigma: Vec<u8> = vec![1];
|
|
let q: Vec<char> = vec!['A', 'B', 'C', 'H'];
|
|
let q0: char = 'A';
|
|
let f: Vec<char> = 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::<char, u8>::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<u8> = vec![0, 1];
|
|
let b: u8 = 2;
|
|
let sigma: Vec<u8> = vec![1];
|
|
let q: Vec<char> = vec!['A', 'B', 'C', 'H'];
|
|
let q0: char = 'A';
|
|
let f: Vec<char> = 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::<char, u8>::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<u8> = vec![0, 1];
|
|
let b: u8 = 1;
|
|
let sigma: Vec<u8> = vec![1];
|
|
let q: Vec<char> = vec!['A', 'B', 'C', 'H'];
|
|
let q0: char = 'A';
|
|
let f: Vec<char> = 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::<char, u8>::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<u8> = vec![0, 1];
|
|
let b: u8 = 0;
|
|
let sigma: Vec<u8> = vec![1, 2];
|
|
let q: Vec<char> = vec!['A', 'B', 'C', 'H'];
|
|
let q0: char = 'A';
|
|
let f: Vec<char> = 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::<char, u8>::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<u8> = vec![0, 1];
|
|
let b: u8 = 0;
|
|
let sigma: Vec<u8> = vec![1];
|
|
let q: Vec<char> = vec!['A', 'B', 'C', 'H'];
|
|
let q0: char = 'D';
|
|
let f: Vec<char> = 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::<char, u8>::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<u8> = vec![0, 1];
|
|
let b: u8 = 0;
|
|
let sigma: Vec<u8> = vec![1];
|
|
let q: Vec<char> = vec!['A', 'B', 'C', 'H'];
|
|
let q0: char = 'A';
|
|
let f: Vec<char> = 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::<char, u8>::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<u8> = vec![0, 1];
|
|
let b: u8 = 0;
|
|
let sigma: Vec<u8> = vec![1];
|
|
let q: Vec<char> = vec!['A', 'B', 'C', 'H'];
|
|
let q0: char = 'A';
|
|
let f: Vec<char> = 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::<char, u8>::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<u8> = vec![0, 1];
|
|
let b: u8 = 0;
|
|
let sigma: Vec<u8> = vec![1];
|
|
let q: Vec<char> = vec!['A', 'B', 'C', 'H'];
|
|
let q0: char = 'A';
|
|
let f: Vec<char> = 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::<char, u8>::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<u8> = vec![0, 1];
|
|
let b: u8 = 0;
|
|
let sigma: Vec<u8> = vec![1];
|
|
let q: Vec<char> = vec!['A', 'B', 'C', 'H', 'I'];
|
|
let q0: char = 'A';
|
|
let f: Vec<char> = 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::<char, u8>::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<u8> = vec![0, 1];
|
|
let b: u8 = 0;
|
|
let sigma: Vec<u8> = vec![1];
|
|
let q: Vec<char> = vec!['A', 'B', 'C', 'H'];
|
|
let q0: char = 'A';
|
|
let f: Vec<char> = 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::<char, u8>::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<u8> = vec![0, 1];
|
|
let b: u8 = 0;
|
|
let sigma: Vec<u8> = vec![1];
|
|
let q: Vec<char> = vec!['A', 'B', 'C', 'D', 'H'];
|
|
let q0: char = 'A';
|
|
let f: Vec<char> = 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::<char, u8>::new(alphabet, b, sigma, q, q0, f, delta);
|
|
match working_machine {
|
|
Ok(_) => panic!(""),
|
|
Err(e) => {
|
|
assert_eq!(e.as_str(), "Unterminated state.")
|
|
}
|
|
}
|
|
}
|
|
}
|