// A Lab Test 2 Answer
// CS 176, Summer 2001


#include <cassert>


static int fls(const int mtx[25][25], int x, int y, int m, int n);


struct square {
  int upper_left_x, upper_left_y, size;
  };


// First, implement the original algorithm to find a largest square.

square find_largest_square(const int mtx[25][25], int m, int n) {

  // Given the m-by-n matrix mtx, find and regurn a square of uniform value in
  // mtx that is at least as large as any other square of uniform value in mtx.

  square s;

  s.upper_left_x = 0;
  s.upper_left_y = 0;
  s.size = 0;

  // Sanity checks.  Return the empty square if they fail.

     if ((m < 0) || (m > 25)) return s;
     if ((n < 0) || (n > 25)) return s;
  
  // Go through mtx finding the largest square at each element and remembering
  // the largest square found overall.

     for (int x = 0; x < m; x++)
       for (int y = 0; y < n; y++) {
	 const int size = fls(mtx, x, y, m, n);
	 if (size > s.size) {
	   s.upper_left_x = x;
	   s.upper_left_y = y;
	   s.size = size;
	   }
	 }

  // That's it; return the largest square founnd.

     return s;
  }


static int fls(const int mtx[25][25], int x, int y, int m, int n) {

  // Given the n-by-m matrix mtx, return the size of the largest square having
  // its upper-left element at mtx[x][y].

  // Sanity checks; die if any of them fail.

     assert((0 <= m) && (m <= 25));
     assert((0 <= n) && (n <= 25));
     assert((0 <= x) && (x < m));
     assert((0 <= y) && (y < n));

  int s = 1;

  while ((x + s < m) && (y + s < n)) {
    for (int i = 0; i < s; i++) {
      if (mtx[x + s - 1][y + i] != mtx[x + s][y + i])
	return s;
      if (mtx[x + i][y + s - 1] != mtx[x + i][y + s])
	return s;
      }
    if (mtx[x + s - 1][y + s - 1] != mtx[x + s][y + s])
      return s;
    s++;
    }

  return s;
  }


// Now for the optimizations.  The first optimization avoids finding the
// largest square at mtx[x][y] when mtx[x][y] is too close to the edge of the
// matrix.  Here "too close" means either (n - s <= x < n) or (m - s <= y < m)
// where s is the size of the largest square found so far.  In either case, the
// square at mtx[x][y] can't be larger than s, so there's no reason to to
// consider the square further. (This description assumes the elements of mtx
// are scanned left-to-right and top-to-bottom; other scan orders would require
// different bounds on x and y, but the principle is the same in all cases.)
//
// The changes required to find_largest_square() to implement this optimization
// are pretty simple: make sure x and y don't get too close to the edge of mtx.
// This means just changing the upper bounds of the two for loops.  Note that
// the bounds passed in the call to fls() don't change; it would be incorrect
// to have
//
//   const int size = fls(mtx, x, y, m - s.size, n - s.size);
//
// The bounds on x and y are for the upper-left element of the square; fls()
// needs to consider the whole square and so needs to get as close to the
// matrix edge as it needs.


square find_largest_square_one(const int mtx[25][25], int m, int n) {

  // Given the m-by-n matrix mtx, find and regurn a square of uniform value in
  // mtx that is at least as large as any other square of uniform value in mtx.

  square s;

  s.upper_left_x = 0;
  s.upper_left_y = 0;
  s.size = 0;

  // Sanity checks.  Return the empty square if they fail.

     if ((m < 0) || (m > 25)) return s;
     if ((n < 0) || (n > 25)) return s;
  
  // Go through mtx finding the largest square at each element and remembering
  // the largest square found overall.

     for (int x = 0; x < n - s.size; x++)
       for (int y = 0; y < m - s.size; y++) {
	 const int size = fls(mtx, x, y, m, n);
	 if (size > s.size) {
	   s.upper_left_x = x;
	   s.upper_left_y = y;
	   s.size = size;
	   }
	 }

  // That's it; return the largest square founnd.

     return s;
  }


// fls() needs no changes for the first optimization.

// The second optimization involves the way fls() finds the largest square.
// Rather than start size at 1, start it from one less than the size of the
// square at the element up and to the left of the current element.  Because
// the starting size gets passed in as a prameter, the changes needed to lfs()
// are simple: add a parameter and delete the local declaration of s.


static int fls_two(const int mtx[25][25], int s, int x, int y, int m, int n) {

  // Given the n-by-m matrix mtx, return the size of the largest square having
  // its upper-left element at mtx[x][y].  Start counting square size from s.

  // Sanity checks; die if any of them fail.

     assert((0 <= m) && (m <= 25));
     assert((0 <= n) && (n <= 25));
     assert((0 <= x) && (x < n));
     assert((0 <= y) && (y < m));
     assert(1 <= s);

  while ((x + s < n) && (y + s < m)) {
    for (int i = 0; i < s; i++) {
      if (mtx[x + s - 1][y + i] != mtx[x + s][y + i])
	return s;
      if (mtx[x + i][y + s - 1] != mtx[x + i][y + s])
	return s;
      }
    if (mtx[x + s - 1][y + s - 1] != mtx[x + s][y + s])
      return s;
    s++;
    }

  return s;
  }


// The changes needed in find_largest_square() are a bit more complicated
// because it needs to remember the size of the largest squares at each
// element.  The easiest way to remember the sizes is to store them in a matrix
// so that the sizes[x][y] holds size of the largest square at mtx[x][y].
//
// Some care is necessary when using this scheme because the initial size to
// use when computing the size of the largest square at mtx[x][y] is sizes[x -
// 1][y - 1] (that is, one less than the size of the square up and to the left
// of this square).  The means the initial size for the square at mtx[0][0] is
// sizes[-1][-1], which doesn't exist.
//
// Fortunately, this problem is easy to deal with by shifting sizes down and to
// the right one element; that is sizes[x + 1][y + 1] holds the size of the
// largest square at mtx[x][y].  Notice this shift moves the problem to the
// other end of the matrix; now size[m][n] holds the size of the square at
// mtx[m - 1][n - 1].  However, this problem is easy to solve: make sure the
// size matrix is (m + 1)-by-(n + 1).
//
// There is one more problem to deal with before we can write the code.  What
// is the initial size of the square at mtx[0][0]?  The problem is, there's no
// square up and to the right of mtx[0][0] (every element along the top and
// left-edge of mtx has this problem).  Fortunately, this problem is also easy
// to deal with: just pretend there are squares that have size 1.  Initializing
// sizes to have 1s along the top and left-edge elements does the trick.


square find_largest_square_two(const int mtx[25][25], int m, int n) {

  // Given the m-by-n matrix mtx, find and regurn a square of uniform value in
  // mtx that is at least as large as any other square of uniform value in mtx.

  square s;

  s.upper_left_x = 0;
  s.upper_left_y = 0;
  s.size = 0;

  // Sanity checks.  Return the empty square if they fail.

     if ((m < 0) || (m > 25)) return s;
     if ((n < 0) || (n > 25)) return s;
  
  // Go through mtx finding the largest square at each element and remembering
  // the largest square found overall.

     int square_sizes[25 + 1][25 + 1];

     for (int i = 0; i < 26; i++) {
       square_sizes[0][i] = 1;
       square_sizes[i][0] = 1;
       }

     for (int x = 0; x < m; x++)
       for (int y = 0; y < n; y++) {
	 square_sizes[x + 1][y + 1] = 
	   fls_two(mtx, square_sizes[x][y], x, y, m, n);
	 if (square_sizes[x + 1][y + 1] > s.size) {
	   s.upper_left_x = x;
	   s.upper_left_y = y;
	   s.size = square_sizes[x + 1][y + 1];
	   }
	 }

  // That's it; return the largest square founnd.

     return s;
  }


syntax highlighted by Code2HTML, v. 0.9