turing_machine_rs/machines/classic.rs
1use std::fmt;
2
3use crate::instruction::Head;
4use crate::program::Program;
5use crate::state::Configuration;
6use crate::{Symbol, TuringMachine, With};
7
8/// [`Classic`] is a common [`TuringMachine`] realization that can be used
9/// freely for program execution.
10#[derive(Clone, Debug, PartialEq, Eq)]
11pub struct Classic<S: Symbol> {
12 default: S,
13 program: Program<S>,
14}
15
16impl<S: Symbol> Classic<S> {
17 /// Constructs a new [`Classic`] Turing machine from the program
18 /// [`Program`] and the default symbol [`Symbol`].
19 ///
20 /// Returns [`Ok(Classic)`] when the default symbol is in the program
21 /// alphabet otherwise [`Err(String)`] with diagnostic information.
22 ///
23 /// # Examples
24 /// Trying to return the new machine with a mismatched default symbol:
25 /// ```rust
26 /// use turing_machine_rs::instruction::State;
27 /// use turing_machine_rs::machines::Classic;
28 /// use turing_machine_rs::program::Program;
29 ///
30 /// let program = Program::new(vec!['0', '1'], State(1));
31 /// let machine = Classic::new(program, '!');
32 ///
33 /// assert!(machine.is_err());
34 /// ```
35 ///
36 /// Successful creation:
37 /// ```rust
38 /// use turing_machine_rs::instruction::State;
39 /// use turing_machine_rs::machines::Classic;
40 /// use turing_machine_rs::program::Program;
41 ///
42 /// let program = Program::new(vec!['0', '1'], State(1));
43 /// let machine = Classic::new(program, '0');
44 ///
45 /// assert!(machine.is_ok());
46 /// ```
47 pub fn new(program: Program<S>, default: S) -> Result<Self, String> {
48 match program.alphabet().contains(&default) {
49 true => Ok(Classic { program, default }),
50 false => Err(format!(
51 "new error: default symbol {} is not in alphabet {:?}",
52 default,
53 program.alphabet()
54 )),
55 }
56 }
57}
58
59impl<S: Symbol> TuringMachine<S> for Classic<S> {
60 /// Executes [`Configuration`] once by mutation.
61 ///
62 /// Returns [`Ok(Configuration)`] when an [`crate::instruction::Instruction`]
63 /// exists for the current [`Configuration`] symbol and state.
64 /// And otherwise returns [`Err(String)`] with diagnostic information.
65 fn execute_once(&self, mut conf: Configuration<S>) -> Result<Configuration<S>, String> {
66 let head = Head::new(conf.state, conf.get_symbol().clone());
67 let inst = match self.program.get(&head)? {
68 Some(inst) => inst,
69 None => {
70 return Err(format!(
71 "uncovered case: have no tail for head ({}) in program",
72 head
73 ))
74 }
75 };
76 conf.state = inst.tail.state;
77 conf.set_symbol(inst.tail.symbol.clone());
78 conf.shift(inst.tail.movement, self.default.clone());
79 Ok(conf)
80 }
81
82 /// Executes [`Configuration`] until predicate is `false` by mutation.
83 ///
84 /// Returns [`Ok(Configuration)`] when an [`crate::instruction::Instruction`]
85 /// exists for the current [`Configuration`] symbol and state.
86 /// And otherwise returns [`Err(String)`] with diagnostic information.
87 fn execute_until(
88 &self,
89 mut conf: Configuration<S>,
90 until: impl Fn(&Configuration<S>) -> bool,
91 ) -> Result<Configuration<S>, String> {
92 while !until(&conf) {
93 let head = Head::new(conf.state, conf.get_symbol().clone());
94 let inst = match self.program.get(&head)? {
95 Some(inst) => inst,
96 None => {
97 return Err(format!(
98 "uncovered case: have no tail for head ({}) in program",
99 head
100 ))
101 }
102 };
103 conf.state = inst.tail.state;
104 conf.set_symbol(inst.tail.symbol.clone());
105 conf.shift(inst.tail.movement, self.default.clone());
106 }
107 Ok(conf)
108 }
109}
110
111impl<S: Symbol> With<Classic<S>> for Classic<S> {
112 type Output = Result<Classic<S>, String>;
113
114 /// Makes superposition with two or more [`Classic`] machines by chain.
115 /// This method accept only [`Classic`] struct and can be used only for
116 /// another [`Classic`] machine.
117 ///
118 /// Returns a new [`Ok(Classic)`] when machines can be concatenated
119 /// and [`Err(String)`] with diagnostic information when machines
120 /// have different alphabets or default symbols.
121 fn with(&self, other: &Classic<S>) -> Self::Output {
122 if self.default != other.default {
123 return Err(format!(
124 "with error: classic machines have different default symbols: {} and {}",
125 self.default, other.default,
126 ));
127 }
128 // `Program::with` implementation guarantees that program can
129 // be concatenated only with the same alphabet
130 let program = self.program.with(&other.program)?;
131 Classic::new(program, self.default.clone())
132 }
133}
134
135impl<S: Symbol> With<Classic<S>> for Result<Classic<S>, String> {
136 type Output = Result<Classic<S>, String>;
137
138 /// Makes superposition with two or more [`Classic`] machines by chain.
139 /// This method accept only [`Classic`] struct and can be used only for
140 /// [`Result<Classic, String>`].
141 ///
142 /// Returns a new [`Ok(Classic)`] when `self` is [`Result::Ok`] and machines
143 /// can be concatenated and [`Err(String)`] when `self` is [`Result::Ok`]
144 /// but machines have different alphabets or default symbols.
145 ///
146 /// And Returns a copy of [`Err(String)`] when `self` is [`Result::Err`].
147 fn with(&self, other: &Classic<S>) -> Self::Output {
148 match self {
149 Ok(machine) => machine.with(other),
150 Err(msg) => Err(msg.clone()),
151 }
152 }
153}
154
155impl<S: Symbol> fmt::Display for Classic<S> {
156 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
157 use std::any::type_name;
158
159 write!(
160 f,
161 "Classic<{}> {{ program: {} }}",
162 type_name::<S>(),
163 self.program
164 )
165 }
166}