turing_machine_rs/state/configuration.rs
1use std::fmt::{Display, Error, Formatter};
2
3use crate::instruction::{Move, State};
4use crate::state::Tape;
5use crate::Symbol;
6
7/// [`Configuration`] is a struct that represents the state of a Turing machine.
8/// Machines do not implement their state as a part of themselves;
9/// instead, machines mutate configurations according to their program.
10#[derive(Clone, Debug, Eq, PartialEq)]
11pub struct Configuration<S: Symbol> {
12 tape: Tape<S>,
13 index: usize,
14 /// [`Configuration`] [`State`] is used by [`crate::TuringMachine`]
15 /// and cannot be changed by self-methods.
16 pub state: State,
17}
18
19impl<S: Symbol> Configuration<S> {
20 /// Constructs a new [`Configuration`] from the [`Tape`],
21 /// the index [`usize`] and the [`State`].
22 ///
23 /// Returns a new [`Ok(Configuration)`] if the index is within
24 /// the bounds of the [`Tape`], otherwise an [`Err(String)`]
25 /// with diagnostic information.
26 pub fn new(tape: Tape<S>, index: usize, state: State) -> Result<Self, String> {
27 match tape.len() > index {
28 true => Ok(Configuration { tape, index, state }),
29 false => Err(format!(
30 "index out of bounds: the len is {} but the index is {}",
31 tape.len(),
32 index
33 )),
34 }
35 }
36
37 /// Constructs a new [`Configuration`] from the [`Tape`],
38 /// the index `0` and the state `1`.
39 /// This configuration named `normal` or `nrm`.
40 ///
41 /// Returns a new [`Ok(Configuration)`] if the [`Tape`] is not empty
42 /// otherwise an [`Err(String)`] with diagnostic information.
43 pub fn new_nrm(tape: Tape<S>) -> Result<Self, String> {
44 Configuration::new(tape, 0, State(1))
45 }
46
47 /// Constructs a new [`Configuration`] from the [`Tape`],
48 /// the index `tape.len() - 1` and the state: `1`.
49 /// This configuration named `standart` or `std`.
50 ///
51 /// Returns a new [`Ok(Configuration)`] if the [`Tape`] is not empty
52 /// otherwise an [`Err(String)`] with diagnostic information.
53 pub fn new_std(tape: Tape<S>) -> Result<Self, String> {
54 let last = tape.len() - 1;
55 Configuration::new(tape, last, State(1))
56 }
57
58 /// Destructs [`Configuration`] into `(Tape<S>, usize, State)`. May be used
59 /// only with owned values.
60 pub fn destruct(self) -> (Tape<S>, usize, State) {
61 (self.tape, self.index, self.state)
62 }
63
64 /// Returns the [`Tape`] reference of the [`Configuration`].
65 ///
66 /// Zero cost method.
67 pub fn tape(&self) -> &Tape<S> {
68 &self.tape
69 }
70
71 /// Returns the [`Tape`] copy of the [`Configuration`].
72 pub fn into_tape(self) -> Tape<S> {
73 self.tape
74 }
75
76 /// Returns the current symbol reference. This reference is always exists.
77 ///
78 /// # Panics
79 /// [`Configuration`] could panic only if source code is broken - this
80 /// would be a bug. [`Configuration`] controls its inner index so it always
81 /// valid.
82 ///
83 /// So you could open an issue on [GitHub](https://github.com/Helltraitor/turing-machine-rs).
84 pub fn get_symbol(&self) -> &S {
85 self.tape
86 .get(self.index)
87 .expect("get_symbol error: returned value must be Some because of bound checking")
88 }
89
90 /// Returns the current [`Tape`] index. This value is always in tape bounds.
91 pub fn index(&self) -> usize {
92 self.index
93 }
94
95 /// Returns `true` if the [`Tape`] is not empty, otherwise `false`.
96 ///
97 /// Note that Turing [`Tape`] cannot be empty but this method can return
98 /// `false` (because [`Tape`] type is based on the [`Vec`] type).
99 pub fn is_empty(&self) -> bool {
100 self.tape.is_empty()
101 }
102
103 /// Returns the [`Tape`] length.
104 pub fn len(&self) -> usize {
105 self.tape.len()
106 }
107
108 /// Sets [`Symbol`] at index position in the [`Tape`].
109 ///
110 /// # Panics
111 /// [`Configuration`] could panic only if source code is broken - this
112 /// would be a bug. [`Configuration`] controls its inner index so it always
113 /// valid.
114 ///
115 /// So you could open an issue on [GitHub](https://github.com/Helltraitor/turing-machine-rs).
116 pub fn set_symbol(&mut self, symbol: S) {
117 self.tape.set(self.index, symbol);
118 }
119
120 /// Shifts the [`Tape`] to left or right if [`Move`] is [`Move::Left`]
121 /// or [`Move::Right`], otherwise do nothing (when [`Move::None`]).
122 /// If [`Configuration`] reachs the begin or the end of the [`Tape`]
123 /// then [`Tape`] extends by [`Tape::insert`] method, otherwise only
124 /// changes self index.
125 pub fn shift(&mut self, movement: Move, default: S) {
126 match movement {
127 Move::Left if self.index == 0 => self.tape.insert(0, default),
128 Move::Left => self.index -= 1,
129 Move::None => {}
130 Move::Right => {
131 self.index += 1;
132 if self.index == self.tape.len() {
133 self.tape.insert(self.index, default);
134 }
135 }
136 };
137 }
138}
139
140impl<S: Symbol> Display for Configuration<S> {
141 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
142 write!(
143 f,
144 "Configuration {{ Tape: \"{}\", Index: {}, State: {} }}",
145 self.tape, self.index, self.state
146 )
147 }
148}