TuringSim
C++ framework to simulate abstract computing models
tape.h
1 #pragma once
2 
3 #include <utils/messageException.h>
4 #include <memory/memoryStructure.h>
5 
6 #include <vector>
7 
11 namespace TuringSim::Memory::Tape {
12  template <typename T> class TapeObserver;
13  template <typename T> class TapeModifier;
14 
28  template<typename T>
29  class Tape : public MemoryStructure<T, Tape<T>, TapeObserver<T>, TapeModifier<T>> {
35  friend class TapeObserver<T>;
36 
46  friend class TapeModifier<T>;
47 
48  public:
53  static constexpr size_t MinAllocatedTapeSize = 10;
54 
60  enum class Movement {
61  LEFT,
62  RIGHT,
63  NONE,
64  HALT,
65  };
66 
76  constexpr Tape();
77 
86  constexpr Tape(const T& blank);
87 
91  constexpr Tape(const Tape<T>& other);
92 
96  constexpr Tape(Tape<T>&& other);
97 
102  constexpr Tape<T>& operator=(const Tape<T>& other);
103 
108  constexpr Tape<T>& operator=(Tape<T>&& other);
109 
115  ~Tape() = default;
116 
125  constexpr void left();
126 
135  constexpr void right();
136 
143  constexpr void move(const Movement& movement);
144 
154  [[nodiscard]] constexpr const T& read() const;
155 
161  constexpr void write(const T& symbol);
162 
167  constexpr void erase();
168 
176  template<typename CharT = char, typename Trait = std::char_traits<CharT>>
177  friend std::basic_ostream<CharT, Trait>& operator<<(std::basic_ostream<CharT, Trait>& os, const Movement& movement) {
178  switch (movement) {
180  os << "LEFT"; break;
182  os << "RIGHT"; break;
184  os << "NONE"; break;
186  os << "HALT"; break;
187  }
188  return os;
189  }
190 
191  private:
192  constexpr void allocateLeft();
193  constexpr void allocateRight();
194  constexpr void allocate();
195  constexpr void allocateLeft(size_t);
196  constexpr void allocateRight(size_t);
197  constexpr void allocate(size_t);
198  void setSizeFactor(size_t);
199  constexpr void shrink();
200  constexpr void shrink(std::vector<T>&, size_t);
201 
202  T blank;
203  std::vector<T> leftTape = std::vector<T>(MinAllocatedTapeSize, blank);
204  std::vector<T> rightTape = std::vector<T>(MinAllocatedTapeSize, blank);
205  std::vector<T>* currentTape;
206  size_t position = 0;
207  bool onRight = true;
208 
209  size_t sizeFactor = 2;
210  };
211 
212 
213  template <typename T>
214  constexpr Tape<T>::Tape() :
215  blank(T()),
216  currentTape(&rightTape)
217  {}
218 
219  template <typename T>
220  constexpr Tape<T>::Tape(const T& blank) :
221  blank(blank),
222  currentTape(&rightTape)
223  {}
224 
225  template <typename T>
226  constexpr Tape<T>::Tape(const Tape<T>& other) :
227  blank(other.blank),
228  leftTape(other.leftTape),
229  rightTape(other.rightTape),
230  currentTape(other.currentTape),
231  position(other.position),
232  onRight(other.onRight),
233  sizeFactor(other.sizeFactor)
234  {}
235 
236  template <typename T>
237  constexpr Tape<T>::Tape(Tape<T>&& other) :
238  blank(std::move(other.blank)),
239  leftTape(std::move(other.leftTape)),
240  rightTape(std::move(other.rightTape)),
241  currentTape(other.onRight ? &leftTape : &rightTape),
242  position(std::move(other.position)),
243  onRight(std::move(other.onRight)),
244  sizeFactor(std::move(other.sizeFactor))
245  {}
246 
247  template <typename T>
248  constexpr Tape<T>& Tape<T>::operator=(const Tape<T>& other)
249  {
250  if(this != &other) {
251  blank = other.blank;
252  leftTape = other.leftTape;
253  rightTape = other.rightTape;
254  currentTape = other.currentTape;
255  position = other.position;
256  onRight = other.onRight;
257  sizeFactor = other.sizeFactor;
258  }
259  return *this;
260  }
261 
262  template <typename T>
263  constexpr Tape<T>& Tape<T>::operator=(Tape<T>&& other)
264  {
265  if(this != &other) {
266  blank = std::move(other.blank);
267  leftTape = std::move(other.leftTape);
268  rightTape = std::move(other.rightTape);
269  currentTape = other.onRight ? &leftTape : &rightTape;
270  position = std::move(other.position);
271  onRight = std::move(other.onRight);
272  sizeFactor = std::move(other.sizeFactor);
273  }
274  return *this;
275  }
276 
277 
278  template <typename T>
279  constexpr void Tape<T>::left()
280  {
281  if(onRight) {
282  if(position) {
283  position--;
284  }
285  else {
286  onRight = false;
287  currentTape = &leftTape;
288  }
289  }
290  else {
291  position++;
292  if(position >= leftTape.size()) {
293  allocateLeft();
294  }
295  }
296  }
297 
298  template <typename T>
299  constexpr void Tape<T>::allocateLeft()
300  {
301  leftTape.resize(leftTape.size()*sizeFactor, blank);
302  }
303 
304  template <typename T>
305  constexpr void Tape<T>::allocateLeft(size_t e)
306  {
307  leftTape.resize(leftTape.size() + e, blank);
308  }
309 
310  template <typename T>
311  constexpr void Tape<T>::right()
312  {
313  if(onRight) {
314  position++;
315  if(position >= rightTape.size()) {
316  allocateRight();
317  }
318  }
319  else {
320  if(position) {
321  position--;
322  }
323  else {
324  onRight = true;
325  currentTape = &rightTape;
326  }
327  }
328  }
329 
330  template <typename T>
331  constexpr void Tape<T>::allocateRight()
332  {
333  rightTape.resize(rightTape.size()*sizeFactor, blank);
334  }
335 
336  template <typename T>
337  constexpr void Tape<T>::allocateRight(size_t e)
338  {
339  rightTape.resize(rightTape.size() + e, blank);
340  }
341 
342  template <typename T>
343  constexpr void Tape<T>::move(const Movement& m)
344  {
345  switch(m)
346  {
347  case Movement::LEFT:
348  left();
349  break;
350  case Movement::RIGHT:
351  right();
352  break;
353  case Movement::HALT:
354  case Movement::NONE:
355  break;
356  }
357  }
358 
359  template <typename T>
360  constexpr void Tape<T>::allocate()
361  {
362  allocateRight();
363  allocateLeft();
364  }
365 
366  template <typename T>
367  constexpr void Tape<T>::allocate(size_t e)
368  {
369  allocateRight(e);
370  allocateLeft(e);
371  }
372 
373  template <typename T>
374  constexpr const T& Tape<T>::read() const
375  {
376  return (*currentTape)[position];
377  }
378 
379  template <typename T>
380  constexpr void Tape<T>::write(const T& e)
381  {
382  (*currentTape)[position] = e;
383  }
384 
385  template <typename T>
386  constexpr void Tape<T>::erase()
387  {
388  (*currentTape)[position] = blank;
389  }
390 
391  template <typename T>
392  void Tape<T>::setSizeFactor(size_t e)
393  {
394  if(e < 2) {
395  throw InvalidFactorException("Size factor is too small in Tape<T>::setSizeFactor(size_t). Must be > 1.", e);
396  }
397  sizeFactor = e;
398  }
399 
400  template <typename T>
401  constexpr void Tape<T>::shrink()
402  {
403  shrink(leftTape, onRight ? 0 : position);
404  shrink(rightTape, onRight ? position : 0);
405  }
406 
407  template <typename T>
408  constexpr void Tape<T>::shrink(std::vector<T>& tape, size_t inf)
409  {
410  size_t idxOfLastNonBlank = tape.size()-1;
411 
412  for(; idxOfLastNonBlank > MinAllocatedTapeSize-1 && tape[idxOfLastNonBlank] == blank; --idxOfLastNonBlank){
413  }
414 
415  tape.resize(std::max(idxOfLastNonBlank, inf) + 1);
416  }
417 
425  template <typename T>
426  class TapeObserver : public MemoryObserver<Tape<T>, TapeObserver<T>>
427  {
428  public:
437  constexpr TapeObserver(const Tape<T>& tape) : MemoryObserver<Tape<T>, TapeObserver<T>>(&tape) {}
438 
446  [[nodiscard]] constexpr size_t getRightSize() const { return this->memory->rightTape.size(); }
447 
455  [[nodiscard]] constexpr size_t getLeftSize() const { return this->memory->leftTape.size(); }
456 
464  [[nodiscard]] constexpr const std::vector<T>& getRightTape() const { return this->memory->rightTape; }
465 
473  [[nodiscard]] constexpr const std::vector<T>& getLeftTape() const { return this->memory->leftTape; }
474 
478  [[nodiscard]] constexpr const std::vector<T>* getCurrentTape() const { return this->memory->currentTape; }
479 
488  [[nodiscard]] constexpr size_t getSizeFactor() const { return this->memory->sizeFactor; }
489 
495  [[nodiscard]] constexpr const T& getBlank() const { return this->memory->blank; }
496 
502  [[nodiscard]] constexpr bool isOnRight() const { return this->memory->onRight; }
503 
512  [[nodiscard]] constexpr size_t getAbsolutePosition() const { return this->memory->position; }
513 
519  [[nodiscard]] constexpr long long int getPosition() const { return this->memory->onRight ?
520  static_cast<long long int>(this->memory->position) :
521  -static_cast<long long int>(this->memory->position)-1; }
522 
529  [[nodiscard]] constexpr const T& operator[](long long int position) const {
530  if (position >= 0) {
531  typename std::vector<T>::size_type i = static_cast<typename std::vector<T>::size_type>(position);
532  if (i < this->memory->rightTape.size()) {
533  return this->memory->rightTape[i];
534  }
535  return this->memory->blank;
536  } else {
537  typename std::vector<T>::size_type i = static_cast<typename std::vector<T>::size_type>(-position - 1);
538  if (i < this->memory->leftTape.size()) {
539  return this->memory->leftTape[i];
540  }
541  return this->memory->blank;
542  }
543  }
544  };
545 
557  template <typename T>
558  class TapeModifier : public MemoryModifier<Tape<T>, TapeModifier<T>>
559  {
560  public:
569  constexpr TapeModifier(Tape<T>& tape) : MemoryModifier<Tape<T>, TapeModifier<T>>(&tape) {}
576  constexpr void allocateRight() const { this->memory->allocateRight(); }
583  constexpr void allocateLeft() const { this->memory->allocateLeft(); }
591  constexpr void allocate() const { this->memory->allocate(); }
599  constexpr void allocateRight(size_t additionalSize) const { this->memory->allocateRight(additionalSize); }
607  constexpr void allocateLeft(size_t additionalSize) const { this->memory->allocateLeft(additionalSize); }
616  constexpr void allocate(size_t additionalSize) const { this->memory->allocate(additionalSize); }
624  constexpr void shrink() const { this->memory->shrink(); }
632  constexpr void setSizeFactor(size_t newSizeFactor) const { this->memory->setSizeFactor(newSizeFactor); }
633 
637  constexpr void setPosition(long long int position) {
638  if(position >= 0) {
639  size_t position_u = static_cast<size_t>(position);
640  this->memory->position = position_u;
641  this->memory->onRight = true;
642  size_t rSize = this->memory->rightTape.size();
643  if(position_u >= rSize) {
644  this->memory->allocateRight(position_u - rSize + 1);
645  }
646  this->memory->currentTape = &this->memory->rightTape;
647  }
648  else {
649  size_t position_u = static_cast<size_t>(- position - 1);
650  this->memory->position = position_u;
651  this->memory->onRight = false;
652  size_t lSize = this->memory->leftTape.size();
653  if(position_u >= lSize) {
654  this->memory->allocateLeft(position_u - lSize + 1);
655  }
656  this->memory->currentTape = &this->memory->leftTape;
657  }
658  }
659 
666  constexpr T& operator[](long long int position) {
667  if(position >= 0) {
668  typename std::vector<T>::size_type i = static_cast<typename std::vector<T>::size_type>(position);
669  size_t rSize = this->memory->rightTape.size();
670  if(i >= rSize) {
671  this->memory->allocateRight(i - rSize + 1);
672  }
673  return this->memory->rightTape[i];
674  }
675  else {
676  typename std::vector<T>::size_type i = static_cast<typename std::vector<T>::size_type>(- position - 1);
677  size_t lSize = this->memory->leftTape.size();
678  if(i >= lSize) {
679  this->memory->allocateRight(i - lSize + 1);
680  }
681  return this->memory->leftTape[i];
682  }
683  }
684  };
685 }
TuringSim::Memory::Tape::TapeModifier::TapeModifier
constexpr TapeModifier(Tape< T > &tape)
Construct a modifier of the tape in parameter.
Definition: tape.h:569
TuringSim::Memory::Tape::Tape::move
constexpr void move(const Movement &movement)
Move the I/O head according to the provided Movement.
Definition: tape.h:343
TuringSim::Memory::Tape::TapeModifier
Modifier for the Tape class.
Definition: tape.h:559
TuringSim::Memory::Tape::TapeModifier::shrink
constexpr void shrink() const
Deallocate unused tape.
Definition: tape.h:624
TuringSim::Memory::Tape::TapeModifier::setPosition
constexpr void setPosition(long long int position)
Change the current position of the I/O head.
Definition: tape.h:637
TuringSim::Memory::Tape::TapeObserver::getLeftTape
constexpr const std::vector< T > & getLeftTape() const
Get the left part of the tape.
Definition: tape.h:473
TuringSim::Memory::Tape::TapeModifier::allocateRight
constexpr void allocateRight() const
Allocate some space on the right half-tape.
Definition: tape.h:576
TuringSim::Memory::Tape::TapeObserver::getLeftSize
constexpr size_t getLeftSize() const
Get the size of the left part of the tape.
Definition: tape.h:455
TuringSim::Memory::Tape::Tape::right
constexpr void right()
Move the I/O head to the right.
Definition: tape.h:311
TuringSim::Memory::Tape::TapeObserver::TapeObserver
constexpr TapeObserver(const Tape< T > &tape)
Construct an observer to the tape in parameter.
Definition: tape.h:437
TuringSim::Memory::Tape::Tape::MinAllocatedTapeSize
static constexpr size_t MinAllocatedTapeSize
The size allocated when constructing a tape.
Definition: tape.h:53
TuringSim::Memory::Tape::Tape::~Tape
~Tape()=default
Destruct the tape.
TuringSim::Memory::Tape::Tape::left
constexpr void left()
Move the I/O head to the left.
Definition: tape.h:279
TuringSim::Memory::Tape::TapeModifier::allocateLeft
constexpr void allocateLeft(size_t additionalSize) const
Allocate some space on the left half-tape.
Definition: tape.h:607
TuringSim::Memory::Tape::TapeObserver
Observer for the Tape class.
Definition: tape.h:427
TuringSim::Memory::Tape::TapeModifier::allocateLeft
constexpr void allocateLeft() const
Allocate some space on the left half-tape.
Definition: tape.h:583
TuringSim::Memory::Tape::TapeObserver::getPosition
constexpr long long int getPosition() const
Return the current position.
Definition: tape.h:519
TuringSim::Memory::InvalidFactorException
Exception thrown when a size factor is set to an illegal value.
Definition: memoryStructure.h:22
TuringSim::Memory::Tape::Tape::Movement::LEFT
@ LEFT
TuringSim::Memory::Tape::TapeModifier::allocate
constexpr void allocate(size_t additionalSize) const
Allocate some space on the tape.
Definition: tape.h:616
TuringSim::Memory::Tape::TapeObserver::getAbsolutePosition
constexpr size_t getAbsolutePosition() const
Return the current absolute position.
Definition: tape.h:512
TuringSim::Memory::Tape::Tape::operator<<
friend std::basic_ostream< CharT, Trait > & operator<<(std::basic_ostream< CharT, Trait > &os, const Movement &movement)
Debug printer for tape movement.
Definition: tape.h:177
TuringSim::Memory::Tape::Tape
Class to represent a tape memory.
Definition: tape.h:29
TuringSim::Memory::Tape::Tape::Movement
Movement
Enum to encode possible movements.
Definition: tape.h:60
TuringSim::Memory::Tape::TapeModifier::allocate
constexpr void allocate() const
Allocate some space on the tape.
Definition: tape.h:591
TuringSim::Memory::Tape::TapeObserver::getRightTape
constexpr const std::vector< T > & getRightTape() const
Get the right part of the tape.
Definition: tape.h:464
TuringSim::Memory::Tape::TapeObserver::getCurrentTape
constexpr const std::vector< T > * getCurrentTape() const
Get the current tape. May be the right of the left tape.
Definition: tape.h:478
TuringSim::Memory::Tape
Namespace for Tape, the observer and the modifier.
TuringSim::Memory::MemoryStructure
Base class for all memory structures.
Definition: memoryStructure.h:117
TuringSim::Memory::Tape::Tape::Movement::NONE
@ NONE
TuringSim::Memory::Tape::TapeObserver::getSizeFactor
constexpr size_t getSizeFactor() const
Get the resizing factor of the tape.
Definition: tape.h:488
TuringSim::Memory::Tape::TapeModifier::setSizeFactor
constexpr void setSizeFactor(size_t newSizeFactor) const
Set the resizing factor.
Definition: tape.h:632
TuringSim::Memory::Tape::Tape::Tape
constexpr Tape()
Create a new tape.
Definition: tape.h:214
TuringSim::Memory::Tape::TapeObserver::isOnRight
constexpr bool isOnRight() const
Check if the I/O head is on the right half-tape.
Definition: tape.h:502
TuringSim::Memory::Tape::TapeModifier::allocateRight
constexpr void allocateRight(size_t additionalSize) const
Allocate some space on the right half-tape.
Definition: tape.h:599
TuringSim::Memory::Tape::TapeObserver::getBlank
constexpr const T & getBlank() const
Get the blank symbol.
Definition: tape.h:495
TuringSim::Memory::Tape::Tape::write
constexpr void write(const T &symbol)
Write a value under the I/O head.
Definition: tape.h:380
TuringSim::Memory::Tape::Tape::erase
constexpr void erase()
Write the blank value under the I/O head.
Definition: tape.h:386
TuringSim::Memory::Tape::TapeObserver::getRightSize
constexpr size_t getRightSize() const
Get the size of the right part of the tape.
Definition: tape.h:446
TuringSim::Memory::MemoryObserver
Base class for all memory observers.
Definition: memoryStructure.h:187
TuringSim::Memory::Tape::Tape::Movement::HALT
@ HALT
TuringSim::Memory::MemoryModifier
Base class for all memory modifiers.
Definition: memoryStructure.h:307
TuringSim::Memory::Tape::Tape::operator=
constexpr Tape< T > & operator=(const Tape< T > &other)
Copy a tape.
Definition: tape.h:248
TuringSim::Memory::Tape::Tape::Movement::RIGHT
@ RIGHT
TuringSim::Memory::Tape::TapeModifier::operator[]
constexpr T & operator[](long long int position)
Get a non-const reference to a cell on the tape.
Definition: tape.h:666
TuringSim::Memory::Tape::TapeObserver::operator[]
constexpr const T & operator[](long long int position) const
Return a const reference to a cell of the tape.
Definition: tape.h:529
TuringSim::Memory::Tape::Tape::read
constexpr const T & read() const
Read the value under the I/O head.
Definition: tape.h:374