import static java.util.Arrays.copyOfRange;
import static java.util.Arrays.stream;
final class RectangleCounter {
int countRectangles(final String[] rawGrid) {
final int nRows = rawGrid.length;
if (nRows == 0) return 0;
final int nCols = rawGrid[0].length();
if (nCols == 0) return 0;
return new Grid(nRows, nCols, rawGrid).countRectangles();
}
private static final class Grid {
private enum Tile {
CORNER,
HORIZONTAL_WALL,
VERTICAL_WALL,
SPACE;
private static Tile fromChar(final char rawGridTile) {
switch (rawGridTile) {
case '+': return CORNER;
case '-': return HORIZONTAL_WALL;
case '|': return VERTICAL_WALL;
case ' ': return SPACE;
default: throw new IllegalStateException("Grid tile " + rawGridTile + " not recognized.");
}
}
private boolean isHorizontalConnector() {
return this == CORNER || this == HORIZONTAL_WALL;
}
private boolean isVerticalConnector() {
return this == CORNER || this == VERTICAL_WALL;
}
}
private int nRows, nCols;
private final Tile[][] tiles;
private Grid(final int nRows, final int nCols, final String[] rawGrid) {
this.nRows = nRows;
this.nCols = nCols;
this.tiles = new Tile[nRows][nCols];
for (int nRow = 0; nRow < nRows; nRow++) {
for (int nCol = 0; nCol < nCols; nCol++) {
tiles[nRow][nCol] = Tile.fromChar(rawGrid[nRow].charAt(nCol));
}
}
}
private int countRectangles() {
int result = 0;
for (int topRow = 0; topRow < nRows - 1; topRow++) {
for (int leftCol = 0; leftCol < nCols - 1; leftCol++) {
// Only check rectangles that lie below/to the right of our current coordinate:
for (int bottomRow = topRow + 1; bottomRow < nRows; bottomRow++) {
for (int rightCol = leftCol + 1; rightCol < nCols; rightCol++) {
if (formsRectangle(topRow, bottomRow, leftCol, rightCol)) {
result++;
}
}
}
}
}
return result;
}
private boolean formsRectangle(
final int topRow,
final int bottomRow,
final int leftCol,
final int rightCol) {
return tiles[topRow][leftCol].equals(Tile.CORNER)
&& tiles[topRow][rightCol].equals(Tile.CORNER)
&& tiles[bottomRow][leftCol].equals(Tile.CORNER)
&& tiles[bottomRow][rightCol].equals(Tile.CORNER)
&& isHorizontalLineSegment(topRow, leftCol, rightCol)
&& isHorizontalLineSegment(bottomRow, leftCol, rightCol)
&& isVerticalLineSegment(leftCol, topRow, bottomRow)
&& isVerticalLineSegment(rightCol, topRow, bottomRow);
}
private boolean isHorizontalLineSegment(final int row, final int leftCol, final int rightCol) {
return stream(copyOfRange(getRow(row), leftCol, rightCol))
.allMatch(Tile::isHorizontalConnector);
}
private boolean isVerticalLineSegment(final int col, final int topRow, final int bottomRow) {
return stream(copyOfRange(getCol(col), topRow, bottomRow))
.allMatch(Tile::isVerticalConnector);
}
private Tile[] getRow(final int number) {
return tiles[number];
}
private Tile[] getCol(final int number) {
final Tile[] result = new Tile[nRows];
for (int nRow = 0; nRow < nRows; nRow++) {
result[nRow] = tiles[nRow][number];
}
return result;
}
}
}