SGL
grid.h
1 /*
2  * File: grid.h
3  * ------------
4  * This file exports the <code>Grid</code> class, which offers a
5  * convenient abstraction for representing a two-dimensional array.
6  *
7  * @version 2021/04/03
8  * - removed dependency on other custom collections
9  * - removed hashCode
10  * - added to_string
11  * @version 2019/04/09
12  * - renamed private members with underscore naming scheme for consistency
13  * @version 2018/03/12
14  * - added overloads that accept GridLocation: get, inBounds, locations, set, operator []
15  * @version 2018/03/10
16  * - added methods front, back, clear
17  * @version 2017/11/14
18  * - added iterator version checking support
19  * @version 2017/10/18
20  * - fix compiler warnings
21  * @version 2016/12/09
22  * - bug fix in resize method (credit to Liu Ren)
23  * @version 2016/09/24
24  * - refactored to use collections.h utility functions
25  * - made member variables actually private (oops)
26  * - added size() method
27  * @version 2016/08/10
28  * - added constructor support for std initializer_list usage, such as
29  * {{1, 2, 3}, {4, 5, 6}}
30  * @version 2016/08/04
31  * - fixed operator >> to not throw errors
32  * @version 2015/07/05
33  * - using global hashing functions rather than global variables
34  * @version 2014/11/20
35  * - minor bug fixes in member initializers
36  * @version 2014/11/13
37  * - added comparison operators <, >=, etc.
38  * @version 2014/10/10
39  * - added resize(true) function with ability to retain old contents
40  * - made ==, != operators const as they should be
41  * - added comparison operators ==, !=
42  * 2014/08/16
43  * - added width, height functions; added mapAllColumnMajor
44  * 2014/07/09
45  * - changed checkGridIndexes range checking function into a private member
46  * function to avoid unused-function errors on some newer compilers
47  */
48 
49 
50 #ifndef _grid_h
51 #define _grid_h
52 
53 #include <cstdlib>
54 #include <exception>
55 #include <functional>
56 #include <initializer_list>
57 #include <iostream>
58 #include <stdexcept>
59 #include <string>
60 #include <sstream>
61 #include <vector>
62 #include "gridlocation.h"
63 #include "privatestrlib.h"
64 
65 /*
66  * Class: Grid<ValueType>
67  * ----------------------
68  * This class stores an indexed, two-dimensional array. The following code,
69  * for example, creates an identity matrix of size <code>n</code>, in which
70  * the elements are 1.0 along the main diagonal and 0.0 everywhere else:
71  *
72  *<pre>
73  * Grid&lt;double&gt; createIdentityMatrix(int n) {
74  * Grid&lt;double&gt; matrix(n, n);
75  * for (int i = 0; i &lt; n; i++) {
76  * matrix[i][i] = 1.0;
77  * }
78  * return matrix;
79  * }
80  *</pre>
81  */
82 
83 template <typename ValueType>
84 class Grid {
85 public:
86  /* Forward reference */
87  class GridRow;
88  class GridRowConst;
89 
90  /*
91  * Constructor: Grid
92  * Usage: Grid<ValueType> grid;
93  * Grid<ValueType> grid(nRows, nCols);
94  * ------------------------------------------
95  * Initializes a new grid. The second form of the constructor is
96  * more common and creates a grid with the specified number of rows
97  * and columns. Each element of the grid is initialized to the
98  * default value for the type. The default constructor creates an
99  * empty grid for which the client must call <code>resize</code> to
100  * set the dimensions.
101  * The three-argument constructor also accepts an initial value and
102  * fills every cell of the grid with that value.
103  */
104  Grid() = default;
105  Grid(int _rowCount, int _columnCount);
106  Grid(int _rowCount, int _columnCount, const ValueType& value);
107 
108  /*
109  * This constructor uses an initializer list to set up the grid.
110  * Usage: Grid<int> grid {{1, 2, 3}, {4, 5, 6}};
111  */
112  Grid(std::initializer_list<std::initializer_list<ValueType>> list);
113 
114  /*
115  * Destructor: ~Grid
116  * -----------------
117  * Frees any heap storage associated with this grid.
118  */
119  virtual ~Grid() = default;
120 
121  /*
122  * Method: clear
123  * Usage: grid.clear();
124  * --------------------
125  * Sets every value in the grid to its element type's default value.
126  */
127  void clear();
128 
129  /*
130  * Method: equals
131  * Usage: if (grid.equals(grid2)) ...
132  * ----------------------------------
133  * Returns <code>true</code> if this grid contains exactly the same
134  * values as the given other grid.
135  * Identical in behavior to the == operator.
136  */
137  bool equals(const Grid<ValueType>& grid2) const;
138 
139  /*
140  * Method: fill
141  * Usage: grid.fill(value);
142  * ------------------------
143  * Stores the given value in every cell of this grid.
144  */
145  void fill(const ValueType& value);
146 
147  /*
148  * Method: get
149  * Usage: ValueType value = grid.get(row, col);
150  * --------------------------------------------
151  * Returns the element at the specified <code>row</code>/<code>col</code>
152  * position in this grid. This method signals an error if the
153  * <code>row</code> and <code>col</code> arguments are outside
154  * the grid boundaries.
155  */
156  ValueType get(int row, int col);
157  const ValueType& get(int row, int col) const;
158  ValueType get(const GridLocation& loc);
159  const ValueType& get(const GridLocation& loc) const;
160 
161  /*
162  * Method: inBounds
163  * Usage: if (grid.inBounds(row, col)) ...
164  * ---------------------------------------
165  * Returns <code>true</code> if the specified row and column position
166  * is inside the bounds of the grid.
167  */
168  bool inBounds(int row, int col) const;
169  bool inBounds(const GridLocation& loc) const;
170 
171  /*
172  * Method: isEmpty
173  * Usage: if (grid.isEmpty()) ...
174  * ---------------------------------------
175  * Returns <code>true</code> if the grid has 0 rows and/or 0 columns.
176  */
177  bool isEmpty() const;
178 
179  /*
180  * Method: locations
181  * Usage: for (GridLocation loc : grid.locations()) ...
182  * ----------------------------------------------------
183  * Returns a range of (row,col) locations found in this grid.
184  * This allows a nice abstraction for looping over the 2D grid range
185  * of indexes using a single for loop.
186  * By default the locations are arranged in row-major order,
187  * but if you pass the rowMajor parameter of false, the locations will be
188  * returned in column-major order instead.
189  */
190  GridLocationRange locations(bool rowMajor = true) const;
191 
192  /*
193  * Method: mapAll
194  * Usage: grid.mapAll(fn);
195  * -----------------------
196  * Calls the specified function on each element of the grid. The
197  * elements are processed in <b><i>row-major order,</i></b> in which
198  * all the elements of row 0 are processed, followed by the elements
199  * in row 1, and so on.
200  */
201  void mapAll(std::function<void (const ValueType &)>) const;
202 
203  /*
204  * Method: mapAllColumnMajor
205  * Usage: grid.mapAllColumnMajor(fn);
206  * ----------------------------------
207  * Calls the specified function on each element of the grid. The
208  * elements are processed in <b><i>column-major order,</i></b> in which
209  * all the elements of column 0 are processed, followed by the elements
210  * in column 1, and so on.
211  */
212  void mapAllColumnMajor(std::function<void (const ValueType &)>) const;
213 
214  /*
215  * Method: numCols
216  * Usage: int nCols = grid.numCols();
217  * ----------------------------------
218  * Returns the number of columns in the grid.
219  */
220  int numCols() const;
221 
222  /*
223  * Method: numRows
224  * Usage: int nRows = grid.numRows();
225  * ----------------------------------
226  * Returns the number of rows in the grid.
227  */
228  int numRows() const;
229 
230  /*
231  * Method: resize
232  * Usage: grid.resize(nRows, nCols);
233  * ---------------------------------
234  * Reinitializes the grid to have the specified number of rows
235  * and columns. If the 'retain' parameter is true,
236  * the previous grid contents are retained as much as possible.
237  * If 'retain' is not passed or is false, any previous grid contents
238  * are discarded.
239  */
240  void resize(int _rowCount, int _columnCount, bool retain = false);
241 
242  /*
243  * Method: set
244  * Usage: grid.set(row, col, value);
245  * ---------------------------------
246  * Replaces the element at the specified <code>row</code>/<code>col</code>
247  * location in this grid with a new value. This method signals an error
248  * if the <code>row</code> and <code>col</code> arguments are outside
249  * the grid boundaries.
250  */
251  void set(int row, int col, const ValueType& value);
252  void set(const GridLocation& loc, const ValueType& value);
253 
254  /*
255  * Method: size
256  * Usage: int size = grid.size();
257  * ------------------------------
258  * Returns the total number of elements in the grid, which is equal to the
259  * number of rows times the number of columns.
260  */
261  int size() const;
262 
263  /*
264  * Method: toString
265  * Usage: string str = grid.toString();
266  * ------------------------------------
267  * Converts the grid to a printable string representation.
268  * The string returned is a 1-dimensional representation such as:
269  * "{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}"
270  */
271  string toString() const;
272 
273  /*
274  * Method: toString2D
275  * Usage: string str = grid.toString2D();
276  * --------------------------------------
277  * Converts the grid to a printable string representation.
278  * The string returned is a 2-dimensional representation such as:
279  * "{{1, 2, 3},\n
280  * {4, 5, 6},\n
281  * {7, 8, 9}}"
282  */
283  string toString2D(
284  string rowStart = "{",
285  string rowEnd = "}",
286  string colSeparator = ", ",
287  string rowSeparator = ",\n ") const;
288 
289 
290  /*
291  * Operator: []
292  * Usage: grid[row][col]
293  * ----------------------
294  * Overloads <code>[]</code> to select elements from this grid.
295  * This extension enables the use of traditional array notation to
296  * get or set individual elements. This method signals an error if
297  * the <code>row</code> and <code>col</code> arguments are outside
298  * the grid boundaries.
299  */
300  GridRow operator [](int row);
301  const GridRowConst operator [](int row) const;
302  ValueType& operator [](const GridLocation& loc);
303  const ValueType& operator [](const GridLocation& loc) const;
304 
305  /*
306  * Additional Grid operations
307  * --------------------------
308  * In addition to the methods listed in this interface, the Grid
309  * class supports the following operations:
310  *
311  * - Stream I/O using the << and >> operators
312  * - Deep copying for the copy constructor and assignment operator
313  * - Iteration using the range-based for statement and STL iterators
314  *
315  * The iteration forms process the grid in row-major order.
316  */
317 
318  /*
319  * Operator: ==
320  * Usage: if (grid1 == grid2) ...
321  * ------------------------------
322  * Compares two grids for equality.
323  */
324  bool operator ==(const Grid& grid2) const;
325 
326  /*
327  * Operator: !=
328  * Usage: if (grid1 != grid2) ...
329  * ------------------------------
330  * Compares two grids for inequality.
331  */
332  bool operator !=(const Grid& grid2) const;
333 
334  /*
335  * Operators: <, >, <=, >=
336  * Usage: if (grid1 < grid2) ...
337  * -----------------------------
338  * Relational operators to compare two grids.
339  * The <, >, <=, >= operators require that the ValueType has a < operator
340  * so that the elements can be compared pairwise.
341  */
342  bool operator <(const Grid& grid2) const;
343  bool operator <=(const Grid& grid2) const;
344  bool operator >(const Grid& grid2) const;
345  bool operator >=(const Grid& grid2) const;
346 
347  /* Private section */
348 
349  /**********************************************************************/
350  /* Note: Everything below this point in the file is logically part */
351  /* of the implementation and should not be of interest to clients. */
352  /**********************************************************************/
353 
354  /*
355  * Implementation notes: Grid data structure
356  * -----------------------------------------
357  * The Grid is internally managed as a dynamic array of elements.
358  * The array itself is one-dimensional, the logical separation into
359  * rows and columns is done by arithmetic computation. The layout
360  * is in row-major order, which is to say that the entire first row
361  * is laid out contiguously, followed by the entire second row,
362  * and so on.
363  */
364 
365 private:
366  /* Instance variables */
367  std::vector<ValueType> _elements; // The elements, in row-major order
368  int _rowCount = 0; // The number of rows in the grid
369  int _columnCount = 0; // The number of columns in the grid
370 
371  /* Private method prototypes */
372 
373  /*
374  * Throws an ErrorException if the given row/col are not within the range of
375  * (0,0) through (rowMax-1,colMax-1) inclusive.
376  * This is a consolidated error handler for all various Grid members that
377  * accept index parameters.
378  * The prefix parameter represents a text string to place at the start of
379  * the error message, generally to help indicate which member threw the error.
380  */
381  void checkIndexes(int row, int col,
382  int rowMax, int colMax,
383  string prefix) const;
384  int gridCompare(const Grid& grid2) const;
385 
386  /*
387  * Hidden features
388  * ---------------
389  * The remainder of this file consists of the code required to
390  * support deep copying and iteration. Including these methods
391  * in the public interface would make that interface more
392  * difficult to understand for the average client.
393  */
394 
395 public:
396  using iterator = typename std::vector<ValueType>::iterator;
397  using const_iterator = typename std::vector<ValueType>::const_iterator;
398 
399  iterator begin() {
400  return _elements.begin();
401  }
402  iterator end() {
403  return _elements.end();
404  }
405 
406  const_iterator begin() const {
407  return _elements.begin();
408  }
409  const_iterator end() const {
410  return _elements.end();
411  }
412 
413  /*
414  * Private class: Grid<ValType>::GridRow
415  * -------------------------------------
416  * This section of the code defines a nested class within the Grid template
417  * that makes it possible to use traditional subscripting on Grid values.
418  */
419  class GridRow {
420  public:
421  GridRow() : _gp(nullptr), _row(0) {
422  /* Empty */
423  }
424 
425  ValueType& operator [](int col) {
426  _gp->checkIndexes(_row, col, _gp->_rowCount-1, _gp->_columnCount-1, "operator [][]");
427  return _gp->_elements[(_row * _gp->_columnCount) + col];
428  }
429 
430  ValueType operator [](int col) const {
431  _gp->checkIndexes(_row, col, _gp->_rowCount-1, _gp->_columnCount-1, "operator [][]");
432  return _gp->_elements[(_row * _gp->_columnCount) + col];
433  }
434 
435  int size() const {
436  return _gp->numCols();
437  }
438 
439  private:
440  GridRow(Grid* gridRef, int index) {
441  _gp = gridRef;
442  _row = index;
443  }
444 
445  Grid* _gp;
446  int _row;
447  friend class Grid;
448  };
449  friend class GridRow;
450 
451  class GridRowConst {
452  public:
453  GridRowConst() : _gp(nullptr), _row(0) {
454  /* Empty */
455  }
456 
457  const ValueType operator [](int col) const {
458  _gp->checkIndexes(_row, col, _gp->_rowCount-1, _gp->_columnCount-1, "operator [][]");
459  return _gp->_elements[(_row * _gp->_columnCount) + col];
460  }
461 
462  int size() const {
463  return _gp->numCols();
464  }
465 
466  private:
467  GridRowConst(Grid* const gridRef, int index) : _gp(gridRef), _row(index) {}
468 
469  const Grid* const _gp;
470  const int _row;
471  friend class Grid;
472  };
473  friend class GridRowConst;
474 };
475 
476 template <typename ValueType>
477 Grid<ValueType>::Grid(int numRows, int numCols) {
478  resize(numRows, numCols);
479 }
480 
481 template <typename ValueType>
482 Grid<ValueType>::Grid(int numRows, int numCols, const ValueType& value) {
483  resize(numRows, numCols);
484  fill(value);
485 }
486 
487 template <typename ValueType>
488 Grid<ValueType>::Grid(std::initializer_list<std::initializer_list<ValueType>> list) {
489  // create the grid at the proper size
490  _rowCount = list.size();
491  if (list.begin() != list.end()) {
492  _columnCount = list.begin()->size();
493  }
494  resize(_rowCount, _columnCount);
495 
496  // copy the data from the initializer list into the Grid
497  auto rowItr = list.begin();
498  for (int row = 0; row < _rowCount; row++) {
499  if (static_cast<int>(rowItr->size()) != _columnCount) {
500  throw std::runtime_error("Grid::constructor: initializer list is not rectangular (must have same # cols in each row)");
501  }
502  auto colItr = rowItr->begin();
503  for (int col = 0; col < _columnCount; col++) {
504  set(row, col, *colItr);
505  colItr++;
506  }
507  rowItr++;
508  }
509 }
510 
511 template <typename ValueType>
512 void Grid<ValueType>::clear() {
513  ValueType defaultValue = ValueType();
514  for (int r = 0; r < _rowCount; r++) {
515  for (int c = 0; c < _columnCount; c++) {
516  set(r, c, defaultValue);
517  }
518  }
519 }
520 
521 template <typename ValueType>
522 bool Grid<ValueType>::equals(const Grid<ValueType>& grid2) const {
523  // optimization: if literally same grid, stop
524  if (this == &grid2) {
525  return true;
526  }
527 
528  if (_rowCount != grid2._rowCount || _columnCount != grid2._columnCount) {
529  return false;
530  }
531  for (int row = 0; row < _rowCount; row++) {
532  for (int col = 0; col < _columnCount; col++) {
533  if (get(row, col) != grid2.get(row, col)) {
534  return false;
535  }
536  }
537  }
538  return true;
539 }
540 
541 template <typename ValueType>
542 void Grid<ValueType>::fill(const ValueType& value) {
543  for (int row = 0; row < _rowCount; row++) {
544  for (int col = 0; col < _columnCount; col++) {
545  set(row, col, value);
546  }
547  }
548 
549  /* This counts as a semantic update, so we must update the version. */
550  // _elements.updateVersion();
551 }
552 
553 template <typename ValueType>
554 ValueType Grid<ValueType>::get(int row, int col) {
555  checkIndexes(row, col, _rowCount-1, _columnCount-1, "get");
556  return _elements[(row * _columnCount) + col];
557 }
558 
559 template <typename ValueType>
560 const ValueType& Grid<ValueType>::get(int row, int col) const {
561  checkIndexes(row, col, _rowCount-1, _columnCount-1, "get");
562  return _elements[(row * _columnCount) + col];
563 }
564 
565 template <typename ValueType>
566 ValueType Grid<ValueType>::get(const GridLocation& loc) {
567  return get(loc.row, loc.col);
568 }
569 
570 template <typename ValueType>
571 const ValueType& Grid<ValueType>::get(const GridLocation& loc) const {
572  return get(loc.row, loc.col);
573 }
574 
575 template <typename ValueType>
576 bool Grid<ValueType>::inBounds(int row, int col) const {
577  return row >= 0 && col >= 0 && row < _rowCount && col < _columnCount;
578 }
579 
580 template <typename ValueType>
581 bool Grid<ValueType>::inBounds(const GridLocation& loc) const {
582  return inBounds(loc.row, loc.col);
583 }
584 
585 template <typename ValueType>
586 bool Grid<ValueType>::isEmpty() const {
587  return _rowCount == 0 || _columnCount == 0;
588 }
589 
590 template <typename ValueType>
591 GridLocationRange Grid<ValueType>::locations(bool rowMajor) const {
592  return GridLocationRange(0, 0, numRows() - 1, numCols() - 1, rowMajor);
593 }
594 
595 template <typename ValueType>
596 void Grid<ValueType>::mapAll(std::function<void (const ValueType &)> fn) const {
597  for (int i = 0; i < _rowCount; i++) {
598  for (int j = 0; j < _columnCount; j++) {
599  fn(get(i, j));
600  }
601  }
602 }
603 
604 template <typename ValueType>
605 void Grid<ValueType>::mapAllColumnMajor(std::function<void (const ValueType &)> fn) const {
606  for (int j = 0; j < _columnCount; j++) {
607  for (int i = 0; i < _rowCount; i++) {
608  fn(get(i, j));
609  }
610  }
611 }
612 
613 template <typename ValueType>
614 int Grid<ValueType>::numCols() const {
615  return _columnCount;
616 }
617 
618 template <typename ValueType>
619 int Grid<ValueType>::numRows() const {
620  return _rowCount;
621 }
622 
623 template <typename ValueType>
624 void Grid<ValueType>::resize(int numRows, int numCols, bool retain) {
625  if (numRows < 0 || numCols < 0) {
626  std::ostringstream out;
627  out << "Grid::resize: Attempt to resize grid to invalid size ("
628  << numRows << ", " << numCols << ")";
629  throw std::runtime_error(out.str());
630  }
631 
632  // optimization: don't do the resize if we are already that size
633  if (numRows == this->_rowCount && numCols == this->_columnCount && retain) {
634  /* We need to update the version because semantically we've changed the grid,
635  * but we haven't touched our vector.
636  */
637  // _elements.updateVersion();
638  return;
639  }
640 
641  // save backup of old array/size
642  std::vector<ValueType> oldElements = std::move(_elements);
643  int oldnRows = this->_rowCount;
644  int oldnCols = this->_columnCount;
645 
646  // create new empty array and set new size
647  this->_rowCount = numRows;
648  this->_columnCount = numCols;
649  this->_elements = std::vector<ValueType>(numRows * numCols, ValueType());
650 
651  // possibly retain old contents
652  if (retain) {
653  int minRows = oldnRows < numRows ? oldnRows : numRows;
654  int minCols = oldnCols < numCols ? oldnCols : numCols;
655  for (int row = 0; row < minRows; row++) {
656  for (int col = 0; col < minCols; col++) {
657  this->_elements[(row * numCols) + col] = oldElements[(row * oldnCols) + col];
658  }
659  }
660  }
661 }
662 
663 template <typename ValueType>
664 void Grid<ValueType>::set(int row, int col, const ValueType& value) {
665  checkIndexes(row, col, _rowCount - 1, _columnCount - 1, "set");
666  _elements[(row * _columnCount) + col] = value;
667 }
668 
669 template <typename ValueType>
670 void Grid<ValueType>::set(const GridLocation& loc, const ValueType& value) {
671  set(loc.row, loc.col, value);
672 }
673 
674 template <typename ValueType>
675 int Grid<ValueType>::size() const {
676  return _rowCount * _columnCount;
677 }
678 
679 template <typename ValueType>
680 string Grid<ValueType>::toString() const {
681  std::ostringstream os;
682  os << *this;
683  return os.str();
684 }
685 
686 template <typename ValueType>
687 string Grid<ValueType>::toString2D(
688  string rowStart, string rowEnd,
689  string colSeparator, string rowSeparator) const {
690  std::ostringstream os;
691  os << rowStart;
692  int nr = numRows();
693  int nc = numCols();
694  for (int i = 0; i < nr ; i++) {
695  if (i > 0) {
696  os << rowSeparator;
697  }
698  os << rowStart;
699  for (int j = 0; j < nc; j++) {
700  if (j > 0) {
701  os << colSeparator;
702  }
703  writeGenericValue(os, get(i, j), /* forceQuotes */ true);
704  }
705  os << rowEnd;
706  }
707  os << rowEnd;
708  return os.str();
709 }
710 
711 template <typename ValueType>
712 typename Grid<ValueType>::GridRow Grid<ValueType>::operator [](int row) {
713  return GridRow(this, row);
714 }
715 
716 template <typename ValueType>
717 ValueType& Grid<ValueType>::operator [](const GridLocation& loc) {
718  checkIndexes(loc.row, loc.col, _rowCount-1, _columnCount-1, "operator []");
719  return _elements[(loc.row * _columnCount) + loc.col];
720 }
721 
722 template <typename ValueType>
723 const typename Grid<ValueType>::GridRowConst
724 Grid<ValueType>::operator [](int row) const {
725  return GridRowConst(const_cast<Grid*>(this), row);
726 }
727 
728 template <typename ValueType>
729 const ValueType& Grid<ValueType>::operator [](const GridLocation& loc) const {
730  checkIndexes(loc.row, loc.col, _rowCount-1, _columnCount-1, "operator []");
731  return _elements[(loc.row * _columnCount) + loc.col];
732 }
733 
734 template <typename ValueType>
735 bool Grid<ValueType>::operator ==(const Grid& grid2) const {
736  return equals(grid2);
737 }
738 
739 template <typename ValueType>
740 bool Grid<ValueType>::operator !=(const Grid& grid2) const {
741  return !equals(grid2);
742 }
743 
744 template <typename ValueType>
745 bool Grid<ValueType>::operator <(const Grid& grid2) const {
746  return gridCompare(grid2) < 0;
747 }
748 
749 template <typename ValueType>
750 bool Grid<ValueType>::operator <=(const Grid& grid2) const {
751  return gridCompare(grid2) <= 0;
752 }
753 
754 template <typename ValueType>
755 bool Grid<ValueType>::operator >(const Grid& grid2) const {
756  return gridCompare(grid2) > 0;
757 }
758 
759 template <typename ValueType>
760 bool Grid<ValueType>::operator >=(const Grid& grid2) const {
761  return gridCompare(grid2) >= 0;
762 }
763 
764 template <typename ValueType>
765 void Grid<ValueType>::checkIndexes(int row, int col,
766  int rowMax, int colMax,
767  string prefix) const {
768  const int rowMin = 0;
769  const int colMin = 0;
770  if (row < rowMin || row > rowMax || col < colMin || col > colMax) {
771  std::ostringstream out;
772  out << "Grid::" << prefix << ": (" << row << ", " << col << ")"
773  << " is outside of valid range [";
774  if (rowMin < rowMax && colMin < colMax) {
775  out << "(" << rowMin << ", " << colMin << ")..("
776  << rowMax << ", " << colMax << ")";
777  } else if (rowMin == rowMax && colMin == colMax) {
778  out << "(" << rowMin << ", " << colMin << ")";
779  } // else min > max, no range, empty grid
780  out << "]";
781  throw std::runtime_error(out.str());
782  }
783 }
784 
785 template <typename ValueType>
786 int Grid<ValueType>::gridCompare(const Grid& grid2) const {
787  if (_rowCount != grid2._rowCount) return _rowCount - grid2._rowCount;
788  if (_columnCount != grid2._columnCount) return _columnCount - grid2._columnCount;
789 
790  auto itr1 = begin(),
791  itr2 = grid2.begin(),
792  end1 = end(),
793  end2 = grid2.end();
794  for (;
795  itr1 != end1 && itr2 != end2;
796  ++itr1, ++itr2) {
797  // compare each pair of elements from iterators
798 
799  // TO STUDENT:
800  // If the line below is failing to compile in your program, it probably
801  // means that you are trying to make a nested collection
802  // (e.g. Set<Vector<T>>) for some element type T that does not have a
803  // less-than < operator. That operator is *required* in order to make
804  // a Set or Map of Vectors, so that the set/map knows how to sort the
805  // elements into their ascending order.
806  // You should either add a < operator to your class, or consider a
807  // different nested collection solution. Good luck!
808  if (*itr1 < *itr2) {
809  return -1;
810  } else if (*itr2 < *itr1) {
811  return 1;
812  }
813  }
814 
815  // if we get here, everything from v1 matched v2, so they are either equal,
816  // or one is shorter than the other (fewer elements) and is therefore less
817  if (itr1 == end1 && itr2 == end2) {
818  return 0;
819  } else if (itr1 == end1) {
820  return -1;
821  } else {
822  return 1;
823  }
824 }
825 
826 /*
827  * Implementation notes: << and >>
828  * -------------------------------
829  * The insertion and extraction operators use the template facilities in
830  * strlib.h to read and write generic values in a way that treats strings
831  * specially.
832  */
833 template <typename ValueType>
834 std::ostream& operator <<(std::ostream& os, const Grid<ValueType>& grid) {
835  os << "{";
836  int nRows = grid.numRows();
837  int nCols = grid.numCols();
838  for (int i = 0; i < nRows; i++) {
839  if (i > 0) {
840  os << ", ";
841  }
842  os << "{";
843  for (int j = 0; j < nCols; j++) {
844  if (j > 0) {
845  os << ", ";
846  }
847  writeGenericValue(os, grid.get(i, j), /* forceQuotes */ true);
848  }
849  os << "}";
850  }
851  return os << "}";
852 }
853 
854 template <typename ValueType>
855 std::istream& operator >>(std::istream& is, Grid<ValueType>& grid) {
856  std::vector<std::vector<ValueType>> vec2d;
857  if (!(is >> vec2d)) {
858  is.setstate(std::ios_base::failbit);
859  return is;
860  }
861 
862  int nRows = vec2d.size();
863  int nCols = (nRows == 0) ? 0 : vec2d[0].size();
864  grid.resize(nRows, nCols);
865  for (int i = 0; i < nRows; i++) {
866  for (int j = 0; j < nCols; j++) {
867  grid[i][j] = vec2d[i][j];
868  }
869  }
870 
871  return is;
872 }
873 
874 /*
875  * Function: randomElement
876  * Usage: element = randomElement(grid);
877  * -------------------------------------
878  * Returns a randomly chosen element of the given grid.
879  * Throws an error if the grid is empty.
880  */
881 template <typename T>
882 const T& randomElement(const Grid<T>& grid) {
883  if (grid.isEmpty()) {
884  throw std::runtime_error("randomElement: empty grid was passed");
885  }
886 
887  int randomIndex = rand() % grid.size();
888  int row = randomIndex / grid.numCols();
889  int col = randomIndex % grid.numCols();
890  return grid.get(row, col);
891 }
892 
893 /*
894  * Randomly rearranges the elements of the given grid.
895  */
896 template <typename T>
897 void shuffle(Grid<T>& grid) {
898  int rows = grid.numRows();
899  int cols = grid.numCols();
900  int length = rows * cols;
901  for (int i = 0; i < length; i++) {
902  int j = (rand() % (length - i)) + i;
903  if (i != j) {
904  int r1 = i / cols;
905  int c1 = i % cols;
906  int r2 = j / cols;
907  int c2 = j % cols;
908  T temp = grid[r1][c1];
909  grid[r1][c1] = grid[r2][c2];
910  grid[r2][c2] = temp;
911  }
912  }
913 }
914 
915 #endif // _grid_h