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}