/*license*\ XBN-Java: http://xbnjava.aliteralmind.com Copyright (C) 2014, Jeff Epstein (aliteralmind __DASH__ github __AT__ yahoo __DOT__ com) This software is dual-licensed under the: - Lesser General Public License (LGPL) version 3.0 or, at your option, any later version; - Apache Software License (ASL) version 2.0. Either license may be applied at your discretion. More information may be found at - http://en.wikipedia.org/wiki/Multi-licensing. The text of both licenses is available in the root directory of this project, under the names "LICENSE_lgpl-3.0.txt" and "LICENSE_asl-2.0.txt". The latest copies may be downloaded at: - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt \*license*/ package com.github.xbn.util.matrix; import com.github.xbn.lang.CrashIfObject; import com.github.xbn.number.IndexInRange; /** * <p>For traversing the elements of an <i>external</i> rectangular double-array * in one of eight directions: up, down, left, right, up-left, up-right, * down-left, down-right.</p> * * {@.codelet.and.out com.github.xbn.examples.util.matrix.BoundedMatrixXmpl%eliminateCommentBlocksAndPackageDecl()} * @see MatrixElement * @see <a href="https://aliteralmind.wordpress.com/2014/11/30/matrix/">Blog post: Building a thermonuclear bomb to kill a moderately annoying fly (Or: How I went a leeettle overboard with problem eleven on Project Euler)</a> * @since 0.1.5 * @author Copyright (C) 2014, Jeff Epstein ({@code aliteralmind __DASH__ github __AT__ yahoo __DOT__ com}), dual-licensed under the LGPL (version 3.0 or later) or the ASL (version 2.0). See source code for details. <a href="http://xbnjava.aliteralmind.com">{@code http://xbnjava.aliteralmind.com}</a>, <a href="https://github.com/aliteralmind/xbnjava">{@code https://github.com/aliteralmind/xbnjava}</a> */ public class BoundedMatrix { private final MatrixElement[][] coords; private final int elemsInRow; /** * <p>Create a new square grid.</p> * * <p>Equal to * <br/>     {@link #BoundedMatrix(int, int) this}{@code (row_andColCount, row_andColCount)}</p> */ public BoundedMatrix(int row_andColCount) { this(row_andColCount, row_andColCount); } /** * <p>Create a new grid.</p> * @param row_count The number of sub-arrays in the overall array. May * not be less than zero. Get with {@link #getRowCount() getRowCount}{@code ()}. * @param col_count The number of elements within each row. May * not be less than zero. Get with {@link #getElementsInRowCount() getElementsInRowCount}{@code ()}. * @see #BoundedMatrix(int) BoundedMatrix(i) * @see #BoundedMatrix(com.github.xbn.util.matrix.BoundedMatrix) BoundedMatrix(bm) */ public BoundedMatrix(int row_count, int col_count) { coords = getArrayFromWidthHeight(row_count, col_count); this.elemsInRow = col_count; } /** * Create a new instance as a duplicate of another. * @param to_copy May not be <code>null</code> * @see #getObjectCopy() * @see #BoundedMatrix(int, int) */ public BoundedMatrix(BoundedMatrix to_copy) { try { coords = new MatrixElement[to_copy.getRowCount()][to_copy.getElementsInRowCount()]; } catch(NullPointerException npx) { throw new NullPointerException("to_copy"); } for(int i = 0; i < coords.length; i++) { for(int j = 0; j < coords[0].length; j++) { coords[i][j] = to_copy.coords[i][j].getObjectCopy(); } } elemsInRow = to_copy.getElementsInRowCount(); } /* * <p>Create a new instance from a provided element double-array.</p> * * @param coords It is <i>assumed</i> that this is non-null, and valid--meaning <code>BoundedMatrix.{@link #crashIfBadCoordsArray(com.github.xbn.util.matrix.MatrixElement[][])}(coords)</code> would not result in a crash. * @see #BoundedMatrix(int, int) protected BoundedMatrix(MatrixElement[][] coords) { this.coords = coords; elemsInRow = coords[0].length; } */ /** * <p>The number of sub-arrays in the overall array.</p> * * @return <code>row_count</code> as provided to the * {@linkplain #BoundedMatrix(int, int) constructor}. */ public int getRowCount() { return coords.length; } /** * <p>The number of elements in each row (each sub-array).</p> * * @return <code>col_count</code> as provided to the * {@linkplain #BoundedMatrix(int, int) constructor}. */ public int getElementsInRowCount() { return elemsInRow; } /** * The total number of elements in the matrix. * @return <code>({@link #getElementsInRowCount() getElementsInRowCount}() * {@link #getRowCount() getRowCount}())</code> */ public int getElementCount() { return (getElementsInRowCount() * getRowCount()); } /** * Get the element at a specific location in the grid. * @return {@link #get(int, int, java.lang.String, java.lang.String) get}{@code (row_idx, col_idx, "col_idx", "row_idx")} */ public MatrixElement get(int row_idx, int col_idx) { return get(row_idx, col_idx, "col_idx", "row_idx"); } /** * Get the element at a specific location in the grid. * @param col_idx The index corresponding to * <code>col_count</code>, as provided to the * {@linkplain #BoundedMatrix(int, int) constructor}. Must be valid given * {@link #getElementsInRowCount() getElementsInRowCount}{@code ()}. * @param row_idx The index corresponding to <code>row_count</code>, * as provided to the constructor. Must be valid given {@link #getRowCount() getRowCount}{@code ()}. * @return A <code>MatrixElement</code> with the provided indexes. * @exception ArrayIndexOutOfBoundsException If either index is invalid. * @see #get(int, int) */ public MatrixElement get(int row_idx, int col_idx, String ri_name, String ci_name) { try { return coords[row_idx][col_idx]; } catch(ArrayIndexOutOfBoundsException abx) { throw new ArrayIndexOutOfBoundsException(ri_name + "=" + col_idx + ", " + ci_name + "=" + row_idx + ", getElementsInRowCount()=" + getElementsInRowCount() + ", getRowCount()=" + getRowCount()); } } /** * Are there at least X-number of neighbors between the element and the * edge?. * @param element May not be <code>null</code> * @return <code>{@link #isNeighborCountAtLeast(int, int, com.github.xbn.util.matrix.MatrixDirection, int) isNeighborCountAtLeast}(element.getColumnIndex(), element.getRowIndex(), direction, expected)</code> */ public boolean isNeighborCountAtLeast(MatrixElement element, MatrixDirection direction, int expected) { try { return isNeighborCountAtLeast(element.getColumnIndex(), element.getRowIndex(), direction, expected); } catch(NullPointerException npx) { throw new NullPointerException("element"); } } /** * Are there at least X-number of neighbors between the element and the * edge?. * @return <code>({@link #getNeighborCount(int, int, com.github.xbn.util.matrix.MatrixDirection)}(row_idx, col_idx, direction) >= expected)</code> * @see #isNeighborCountAtLeast(com.github.xbn.util.matrix.MatrixElement, com.github.xbn.util.matrix.MatrixDirection, int) */ public boolean isNeighborCountAtLeast(int row_idx, int col_idx, MatrixDirection direction, int expected) { return (getNeighborCount(row_idx, col_idx, direction) >= expected); } /** * <p>How many neighbors are there between an element and the * edge?.</p> * @param element May not be <code>null</code>. * @return <code>{@link #getNeighborCount(int, int, com.github.xbn.util.matrix.MatrixDirection)}(element.{@link MatrixElement#getColumnIndex() getColumnIndex}(), element.{@link MatrixElement#getRowIndex() getRowIndex}(), direction)</code> */ public int getNeighborCount(MatrixElement element, MatrixDirection direction) { try { return getNeighborCount(element.getRowIndex(), element.getColumnIndex(), direction); } catch(NullPointerException npx) { throw new NullPointerException("element"); } } /** * <p>How many neighbors are there between an element and the grid's * edge?.</p> * @param direction May not be <code>null</code>. * @return <code>getVHNeighborCount(hvDirection)</code> * <br/>Where <code>hvDirection</code> is equal to * <br>     <code>{@link BoundedMatrix#getShortestVHForDiagonal(int, int, com.github.xbn.util.matrix.MatrixDirection)}(row_idx, col_idx, direction)</code> * @see #getNeighborCount(com.github.xbn.util.matrix.MatrixElement, com.github.xbn.util.matrix.MatrixDirection) getNeighborCount(gc,gd) * @see #isNeighborCountAtLeast(int, int, com.github.xbn.util.matrix.MatrixDirection, int) isNeighborCountAtLeast(i,i,gd,i) */ public int getNeighborCount(int row_idx, int col_idx, MatrixDirection direction) { MatrixElement element = get(row_idx, col_idx); VertHorizDirection hvDirection = null; try { hvDirection = getShortestVHForDiagonal(row_idx, col_idx, direction); } catch(NullPointerException npx) { throw new NullPointerException("direction"); } return getVHNeighborCount(row_idx, col_idx, hvDirection); } /** * How many neighbors are there between an element and the grid's edge, * in a <i>horizontal or vertical</i> direction only?. * @param row_idx The index of the sub-array within the overall * array. This <i>should</i> be a valid index given * {@link #getRowCount() getRowCount}{@code ()}. * @param col_idx The index of an element within the sub-array. * This <i>should</i> be a valid index given {@link #getRowCount() getRowCount}{@code ()}. * @param hv_direction May not be <code>null</code>. * @return The number of neighbor-elements between the element and the edge, * not including itself. * @see #getNeighborCount(int, int, com.github.xbn.util.matrix.MatrixDirection) */ public int getVHNeighborCount(int row_idx, int col_idx, VertHorizDirection hv_direction) { int count = -1; switch(hv_direction) { case UP: count = row_idx; break; case DOWN: count = (getRowCount() - (row_idx + 1)); break; case LEFT: count = col_idx; break; case RIGHT: count = (getElementsInRowCount() - (col_idx + 1)); break; default: throw new IllegalArgumentException("Unexpected value for hv_direction: " + hv_direction); } //System.out.println("hv_direction=" + hv_direction + ", row_idx=" + row_idx + ", col_idx=" + col_idx + ", count=" + count + ", getRowCount()=" + getRowCount() + ", getElementsInRowCount()=" + getElementsInRowCount() + ""); return count; } /** * Get the distance and direction between two elements. * @param start_vertIdx Vertical index of the start element. Must * be valid given {@link #getRowCount() getRowCount}{@code ()}. * @param start_horizIdx Horizontal index of the start element. Must * be valid given {@link #getElementsInRowCount() getElementsInRowCount}{@code ()}. * @param end_vertIdx Vertical index of the end element. Must * be valid given {@link #getRowCount() getRowCount}{@code ()}. * @param end_horizIdx Horizontal index of the end element. Must * be valid given {@link #getElementsInRowCount() getElementsInRowCount}{@code ()}. * @return <code>start.getDistance(end)</code> * <br/>Where {@code start} is * <br/>     <code>{@link #get(int, int) get}(start_vertIdx, start_horizIdx)</code> * <br/>and {@code end} is * <br/>     <code>get(end_vertIdx, end_horizIdx)</code> */ public DistanceDirection getNeighborDistDir(int start_vertIdx, int start_horizIdx, int end_vertIdx, int end_horizIdx) { MatrixElement start = get(start_vertIdx, start_horizIdx, "start_vertIdx", "start_horizIdx"); MatrixElement end = get(end_horizIdx, end_vertIdx, "end_vertIdx", "end_horizIdx"); return DistanceDirection.newForStartEnd(start, end); } /** * Get an element that is a neighbor of another. * @return <blockquote><pre>{@link #getNeighbor(int, int, com.github.xbn.util.matrix.MatrixDirection, int, com.github.xbn.util.matrix.EdgeExceeded) getNeighbor}(element.{@link MatrixElement#getRowIndex() getRowIndex}(), element.{@link MatrixElement#getColumnIndex() getColumnIndex}(), * direction, doors_down, crash_or_wrap)</pre></blockquote> */ public MatrixElement getNeighbor(MatrixElement element, MatrixDirection direction, int doors_down, EdgeExceeded crash_or_wrap) { return getNeighbor(element.getRowIndex(), element.getColumnIndex(), direction, doors_down, crash_or_wrap); } /** * Get an element that is a neighbor of another. * @param col_idx The }, index within a sub-array. Must be valid * given {@link #getElementsInRowCount() getElementsInRowCount}{@code ()}. * @param row_idx The index of the sub-array within the overall array. * Must be valid given {@link #getRowCount() getRowCount}{@code ()}. * @param direction May not be <code>null</code>. * @param doors_down The adjacency of the neighbor. Zero is itself, * one is directly next door in the provided <code>direction</code>, * negative one is directly next door in the opposite <code>direction</code>. * For particularly high or low values, multiple wraps will be made. * @param crash_or_wrap May not be <code>null</code>. * @return The grid element that is <code>doors_down</code> } elements * away from {@link #get(int, int) get}{@code (row_idx, col_idx)}, in * the requested <code>direction</code>. If wrapping and the neighbor is * outside of the grid boundaries, then it wraps around to the other * side, <a href="https://www.youtube.com/watch?v=i3Pr8yC8_F4&t=29s">a * la Asteroids</a>. * @exception IllegalArgumentException If * <code>crash_or_wrap.{@link EdgeExceeded#CRASH CRASH}</code> and the * edge is exceeded. * @see #getNeighbor(com.github.xbn.util.matrix.MatrixElement, com.github.xbn.util.matrix.MatrixDirection, int, com.github.xbn.util.matrix.EdgeExceeded) */ public MatrixElement getNeighbor(int row_idx, int col_idx, MatrixDirection direction, int doors_down, EdgeExceeded crash_or_wrap) { int row = row_idx; int col = col_idx; try { row += direction.getVertIncrement() * doors_down; if(row < 0) { throwVertExceedsIAXIfCrash(crash_or_wrap, col_idx, row_idx, direction, doors_down, row); //Wrap while(row < 0) { row += getRowCount(); } } else if(row >= getRowCount()) { throwVertExceedsIAXIfCrash(crash_or_wrap, col_idx, row_idx, direction, doors_down, row); //Wrap while(row >= getRowCount()) { row -= getRowCount(); } } } catch(NullPointerException npx) { CrashIfObject.nullOrReturnCause(direction, "direction", null, npx); } col += direction.getHorizIncrement() * doors_down; if(col < 0) { throwHorizExceedsIAXIfCrash(crash_or_wrap, col_idx, row_idx, direction, doors_down, col); //Wrap while(col < 0) { col += getElementsInRowCount(); } } else if(col >= getElementsInRowCount()) { throwHorizExceedsIAXIfCrash(crash_or_wrap, col_idx, row_idx, direction, doors_down, col); //Wrap while(col >= getRowCount()) { col -= getElementsInRowCount(); } } return get(row, col); } private final void throwHorizExceedsIAXIfCrash( EdgeExceeded crash_or_wrap, int col_idx, int row_idx, MatrixDirection direction, int doors_down, int resulting_idx) { try { if(crash_or_wrap.doCrash()) { throw new IllegalArgumentException( "Horizontal index exceeds edge. col_idx=" + col_idx + ", direction=" + direction + ", doors_down=" + doors_down + ", resulting horizontal index after move: " + resulting_idx + ", getElementsInRowCount()=" + getElementsInRowCount() + " (row_idx=" + row_idx + ")."); } } catch(NullPointerException npx) { CrashIfObject.nullOrReturnCause(crash_or_wrap, "crash_or_wrap", null, npx); } } private final void throwVertExceedsIAXIfCrash( EdgeExceeded crash_or_wrap, int col_idx, int row_idx, MatrixDirection direction, int doors_down, int resulting_idx) { try { if(crash_or_wrap.doCrash()) { throw new IllegalArgumentException( "Vertical index exceeds edge. row_idx=" + row_idx + ", direction=" + direction + ", doors_down=" + doors_down + ", resulting vertical index after move: " + resulting_idx + ", getRowCount()=" + getRowCount() + " (col_idx=" + col_idx + ")."); } } catch(NullPointerException npx) { CrashIfObject.nullOrReturnCause(crash_or_wrap, "crash_or_wrap", null, npx); } } /** * If <i><code>this</code></i> direction happens to be diagonal (for * example, {@link MatrixDirection#UP_LEFT}), then get the vertical or * horizontal direction (for example, either {@link MatrixDirection#UP} * or {@link MatrixDirection#LEFT}) that represents the <i>shortest</i> * distance from a element to an edge. * * @param direction May not be <code>null</code>. * @return If <code>direction</code> is already * {@linkplain MatrixDirection#isVertical() vertical} or * {@linkplain MatrixDirection#isHorizontal() horizontal}, its * {@linkplain MatrixDirection#getVertPortion() vertical} or * {@linkplain MatrixDirection#getHorizPortion() horizontal <i>portion</i>} * is returned. If {@linkplain MatrixDirection#isDiagonal() diagonal}, * then the portion whose * {@linkplain #getVHNeighborCount(int, int, com.github.xbn.util.matrix.VertHorizDirection) distance to the edge} * is longest is returned. (In the case of a tie, vertical is returned). * @see #getLongestVHForDiagonal(int, int, com.github.xbn.util.matrix.MatrixDirection) getLongestVHForDiagonal * @see #getNeighborCount(int, int, com.github.xbn.util.matrix.MatrixDirection) BoundedMatrix#getNeighborCount */ public VertHorizDirection getShortestVHForDiagonal(int row_idx, int col_idx, MatrixDirection direction) { return getVHPortion(ShortLong.SHORT, row_idx, col_idx, direction); } /** * If <i><code>this</code></i> direction happens to be diagonal (for * example, {@link MatrixDirection#UP_LEFT}), then get the vertical or * horizontal direction (for example, either {@link MatrixDirection#UP} * or {@link MatrixDirection#LEFT}) that represents the <i>longest</i> * distance from a element to an edge. * * @param row_idx The element of the sub-array within the overall * array. This <i>should</i> be a valid index given * {@link #getRowCount() getRowCount}{@code ()}. * @param col_idx The index of an element within the sub-array. * This <i>should</i> be a valid index given {@link #getRowCount() getRowCount}{@code ()}. * @param direction May not be <code>null</code>. * @return If <code>direction</code> is already * {@linkplain MatrixDirection#isVertical() vertical} or * {@linkplain MatrixDirection#isHorizontal() horizontal}, its * {@linkplain MatrixDirection#getVertPortion() vertical} or * {@linkplain MatrixDirection#getHorizPortion() horizontal <i>portion</i>} * is returned. If {@linkplain MatrixDirection#isDiagonal() diagonal}, * then the portion whose * {@linkplain #getVHNeighborCount(int, int, com.github.xbn.util.matrix.VertHorizDirection) distance to the edge} * is shortest is returned. (In the case of a tie, vertical is returned). * @see #getShortestVHForDiagonal(int, int, com.github.xbn.util.matrix.MatrixDirection) getShortestVHForDiagonal * @see MatrixDirection#isDiagonal() * @see #getNeighborCount(int, int, com.github.xbn.util.matrix.MatrixDirection) BoundedMatrix#getNeighborCount */ public VertHorizDirection getLongestVHForDiagonal(int row_idx, int col_idx, MatrixDirection direction) { return getVHPortion(ShortLong.LONG, row_idx, col_idx, direction); } private static enum ShortLong {SHORT, LONG}; private VertHorizDirection getVHPortion(ShortLong short_long, int row_idx, int col_idx, MatrixDirection direction) { try { if(direction.isVertical()) { return direction.getVertPortion(); } else if(direction.isHorizontal()) { return direction.getHorizPortion(); } } catch(NullPointerException npx) { throw CrashIfObject.nullOrReturnCause(direction, "direction", null, npx); } //Diagonal VertHorizDirection vert = direction.getVertPortion(); VertHorizDirection horiz = direction.getHorizPortion(); int v = getVHNeighborCount(row_idx, col_idx, vert); int h = getVHNeighborCount(row_idx, col_idx, horiz); if(v == h) { return vert; } //Different return ((short_long == ShortLong.SHORT) ? (v < h) ? vert : horiz : (v < h) ? horiz : vert ); } /** * Get the directly-adjacent element. * @return <blockquote><pre>{@link #moveNextDoor(int, int, com.github.xbn.util.matrix.MatrixDirection, com.github.xbn.util.matrix.EdgeExceeded) moveNextDoor}(element.{@link MatrixElement#getRowIndex() getRowIndex}(), element.{@link MatrixElement#getColumnIndex() getColumnIndex}(), direction, * crash_or_wrap)</pre></blockquote> */ public MatrixElement moveNextDoor(MatrixElement element, MatrixDirection direction, EdgeExceeded crash_or_wrap) { try { return moveNextDoor(element.getRowIndex(), element.getColumnIndex(), direction, crash_or_wrap); } catch(NullPointerException npx) { throw new NullPointerException("element"); } } /** * Get a directly-adjacent element. * @param direction May not be <code>null</code>. * @return <code>{@link #getNeighbor(int, int, com.github.xbn.util.matrix.MatrixDirection, int, com.github.xbn.util.matrix.EdgeExceeded) getNeighbor}(coord.{@link MatrixElement#getRowIndex() getRowIndex}{@code ()}, coord.{@link MatrixElement#getColumnIndex() getColumnIndex}{@code ()}, * <br/>     {@link MatrixDirection}.{@link MatrixDirection#UP UP}, 1, crash_or_wrap)</code> * @see #moveNextDoor(com.github.xbn.util.matrix.MatrixElement, com.github.xbn.util.matrix.MatrixDirection, com.github.xbn.util.matrix.EdgeExceeded) moveNextDoor(me,md,ee) * @see #moveUp(com.github.xbn.util.matrix.MatrixElement, com.github.xbn.util.matrix.EdgeExceeded) moveUp(gc, ee) * @see #moveDown(com.github.xbn.util.matrix.MatrixElement, com.github.xbn.util.matrix.EdgeExceeded) moveDown(gc, ee) * @see #moveLeft(com.github.xbn.util.matrix.MatrixElement, com.github.xbn.util.matrix.EdgeExceeded) moveLeft(gc, ee) * @see #moveRight(com.github.xbn.util.matrix.MatrixElement, com.github.xbn.util.matrix.EdgeExceeded) moveRight(gc, ee) * @see #moveUpLeft(com.github.xbn.util.matrix.MatrixElement, com.github.xbn.util.matrix.EdgeExceeded) moveUpLeft(gc, ee) * @see #moveUpRight(com.github.xbn.util.matrix.MatrixElement, com.github.xbn.util.matrix.EdgeExceeded) moveUpRight(gc, ee) * @see #moveDownLeft(com.github.xbn.util.matrix.MatrixElement, com.github.xbn.util.matrix.EdgeExceeded) moveDownLeft(gc, ee) * @see #moveDownRight(com.github.xbn.util.matrix.MatrixElement, com.github.xbn.util.matrix.EdgeExceeded) moveDownRight(gc, ee) */ public MatrixElement moveNextDoor(int row_idx, int col_idx, MatrixDirection direction, EdgeExceeded crash_or_wrap) { return getNeighbor(row_idx, col_idx, direction, 1, crash_or_wrap); } /** * Get a directly-adjacent element, up. * @param element May not be <code>null</code>. * @return <code>{@link #moveNextDoor(com.github.xbn.util.matrix.MatrixElement, com.github.xbn.util.matrix.MatrixDirection, com.github.xbn.util.matrix.EdgeExceeded) moveNextDoor}(element, {@link MatrixDirection}.{@link MatrixDirection#UP UP}, crash_or_wrap)</code> */ public MatrixElement moveUp(MatrixElement element, EdgeExceeded crash_or_wrap) { return moveNextDoor(element, MatrixDirection.UP, crash_or_wrap); } /** * Get a directly-adjacent element, down. * @param element May not be <code>null</code>. * @return <code>{@link #moveNextDoor(com.github.xbn.util.matrix.MatrixElement, com.github.xbn.util.matrix.MatrixDirection, com.github.xbn.util.matrix.EdgeExceeded) moveNextDoor}(element, {@link MatrixDirection}.{@link MatrixDirection#DOWN DOWN}, crash_or_wrap)</code> */ public MatrixElement moveDown(MatrixElement element, EdgeExceeded crash_or_wrap) { return moveNextDoor(element, MatrixDirection.DOWN, crash_or_wrap); } /** * Get a directly-adjacent element, left. * @param element May not be <code>null</code>. * @return <code>{@link #moveNextDoor(com.github.xbn.util.matrix.MatrixElement, com.github.xbn.util.matrix.MatrixDirection, com.github.xbn.util.matrix.EdgeExceeded) moveNextDoor}(element, {@link MatrixDirection}.{@link MatrixDirection#LEFT LEFT}, crash_or_wrap)</code> */ public MatrixElement moveLeft(MatrixElement element, EdgeExceeded crash_or_wrap) { return moveNextDoor(element, MatrixDirection.LEFT, crash_or_wrap); } /** * Get a directly-adjacent element, right. * @param element May not be <code>null</code>. * @return <code>{@link #moveNextDoor(com.github.xbn.util.matrix.MatrixElement, com.github.xbn.util.matrix.MatrixDirection, com.github.xbn.util.matrix.EdgeExceeded) moveNextDoor}(element, {@link MatrixDirection}.{@link MatrixDirection#RIGHT RIGHT}, crash_or_wrap)</code> */ public MatrixElement moveRight(MatrixElement element, EdgeExceeded crash_or_wrap) { return moveNextDoor(element, MatrixDirection.RIGHT, crash_or_wrap); } /** * Get a directly-adjacent element, up-left. * @param element May not be <code>null</code>. * @return <code>{@link #moveNextDoor(com.github.xbn.util.matrix.MatrixElement, com.github.xbn.util.matrix.MatrixDirection, com.github.xbn.util.matrix.EdgeExceeded) moveNextDoor}(element, {@link MatrixDirection}.{@link MatrixDirection#UP_LEFT UP_LEFT}, crash_or_wrap)</code> */ public MatrixElement moveUpLeft(MatrixElement element, EdgeExceeded crash_or_wrap) { return moveNextDoor(element, MatrixDirection.UP_LEFT, crash_or_wrap); } /** * Get a directly-adjacent element, up-right. * @param element May not be <code>null</code>. * @return <code>{@link #moveNextDoor(com.github.xbn.util.matrix.MatrixElement, com.github.xbn.util.matrix.MatrixDirection, com.github.xbn.util.matrix.EdgeExceeded) moveNextDoor}(element, {@link MatrixDirection}.{@link MatrixDirection#UP_RIGHT UP_RIGHT}, crash_or_wrap)</code> */ public MatrixElement moveUpRight(MatrixElement element, EdgeExceeded crash_or_wrap) { return moveNextDoor(element, MatrixDirection.UP_RIGHT, crash_or_wrap); } /** * Get a directly-adjacent element, down-left. * @param element May not be <code>null</code>. * @return <code>{@link #moveNextDoor(com.github.xbn.util.matrix.MatrixElement, com.github.xbn.util.matrix.MatrixDirection, com.github.xbn.util.matrix.EdgeExceeded) moveNextDoor}(element, {@link MatrixDirection}.{@link MatrixDirection#DOWN_LEFT DOWN_LEFT}, crash_or_wrap)</code> */ public MatrixElement moveDownLeft(MatrixElement element, EdgeExceeded crash_or_wrap) { return moveNextDoor(element, MatrixDirection.DOWN_LEFT, crash_or_wrap); } /** * Get a directly-adjacent element, down-right. * @param element May not be <code>null</code>. * @return <code>{@link #moveNextDoor(com.github.xbn.util.matrix.MatrixElement, com.github.xbn.util.matrix.MatrixDirection, com.github.xbn.util.matrix.EdgeExceeded) moveNextDoor}(element, {@link MatrixDirection}.{@link MatrixDirection#DOWN_RIGHT DOWN_RIGHT}, crash_or_wrap)</code> */ public MatrixElement moveDownRight(MatrixElement element, EdgeExceeded crash_or_wrap) { return moveNextDoor(element, MatrixDirection.DOWN_RIGHT, crash_or_wrap); } /** * <p>Get the range of indexes in a single row, that have the required * number of neighbors. This is useful for narrowing down which elements * to analyze.</p> * * {@.codelet.and.out com.github.xbn.examples.util.matrix.GetRowItemIdxRangeForNeighborCountXmpl%eliminateCommentBlocksAndPackageDecl()} * * @param row_idx <i>Should</i> be between zero and * <code>({@link #getElementsInRowCount() getElementsInRowCount}() - 1)</code>, inclusive. * @param direction May not be <code>null</code>. * @param neighbor_count The number of required neighbors between each * element and the edge, going in the requsted {@code direction}. If * negative, then * <code>direction.{@link MatrixDirection#getOpposite() getOpposite}()</code> * is used. * @return The range of indexes in the row that have the required number of * neighbors. If none, this returns <code>null</code>. * @see #getColItemIdxRangeForNeighborCount(int, com.github.xbn.util.matrix.MatrixDirection, int) */ public IndexInRange getRowItemIdxRangeForNeighborCount(int row_idx, MatrixDirection direction, int neighbor_count) { if(row_idx < 0 || (getElementsInRowCount() - 1) < row_idx) { throw new IllegalArgumentException("row_idx (" + row_idx + ") must be between 0 and (getElementsInRowCount() - 1) (" + (getElementsInRowCount() - 1) + ", inclusive."); } if(neighbor_count < 0) { neighbor_count = Math.abs(neighbor_count); direction = direction.getOpposite(); } VertHorizDirection horizDir = direction.getHorizPortion(); VertHorizDirection vertDir = direction.getVertPortion(); if(direction.hasUp() && row_idx < neighbor_count) { return null; } if(direction.hasDown() && (getRowCount() - neighbor_count) <= row_idx) { return null; } if((direction.hasUp() || direction.hasDown()) && getRowCount() <= neighbor_count) { return null; } if(direction.isUp() || direction.isDown()) { //Every element in the row has the required number of elements //above or below it return new IndexInRange(0, getElementsInRowCount()); } if(direction.hasHorizontal() && neighbor_count >= getElementsInRowCount()) { return null; } //Diagonal //Vertically, every element in the row has the required neighbors. //Horizontally, some may not. int leftIdx = (!direction.hasLeft() ? 0 : neighbor_count); int rightIdxExcl = (direction.hasRight() ? getElementsInRowCount() - neighbor_count : getElementsInRowCount()); return new IndexInRange(leftIdx, rightIdxExcl); } /** * <p>Get the range of indexes in a single column, that have the * required number of neighbors. This is useful for narrowing down which * elements to analyze.</p> * @param col_idx <i>Should</i> be between zero and * <code>({@link #getRowCount() getRowCount}() - 1)</code>, inclusive. * @param direction May not be <code>null</code>. * @param neighbor_count The number of required neighbors between each * element and the edge (going in the requsted {@code direction}. If * negative, then * <code>direction.{@link MatrixDirection#getOpposite() getOpposite}()</code> * is used. * @return The range of indexes in the column that have the required * number of neighbors. If none, this returns <code>null</code>. * @see #getRowItemIdxRangeForNeighborCount(int, com.github.xbn.util.matrix.MatrixDirection, int) */ public IndexInRange getColItemIdxRangeForNeighborCount(int col_idx, MatrixDirection direction, int neighbor_count) { if(col_idx < 0 || (getRowCount() - 1) < col_idx) { throw new IllegalArgumentException("col_idx (" + col_idx + ") must be between 0 and (getRowCount() - 1) (" + (getRowCount() - 1) + ", inclusive."); } if(neighbor_count < 0) { neighbor_count = Math.abs(neighbor_count); direction = direction.getOpposite(); } VertHorizDirection horizDir = direction.getHorizPortion(); VertHorizDirection vertDir = direction.getVertPortion(); if(direction.hasLeft() && col_idx < neighbor_count) { return null; } if(direction.hasRight() && (getElementsInRowCount() - neighbor_count) <= col_idx) { return null; } if((direction.hasLeft() || direction.hasRight()) && getElementsInRowCount() <= neighbor_count) { return null; } if(direction.isLeft() || direction.isRight()) { //Every element in the col has the required number of elements //above or below it return new IndexInRange(0, getRowCount()); } if(direction.hasVertical() && neighbor_count >= getRowCount()) { return null; } //Diagonal //Horizontally, every element in the col has the required neighbors. //Vertically, some may not. int topIdx = (!direction.hasUp() ? 0 : neighbor_count); int bottomIdxExcl = (direction.hasDown() ? getRowCount() - neighbor_count : getRowCount()); return new IndexInRange(topIdx, bottomIdxExcl); } public String toString() { return "rows=" + getRowCount() + ", elements-in-row=" + getElementsInRowCount(); } /** * <p>Get a duplicate of this object.</p> * @return <code>(new {@link #BoundedMatrix(BoundedMatrix) BoundedMatrix}(this))</code> */ public BoundedMatrix getObjectCopy() { return (new BoundedMatrix(this)); } /** * Is a element valid for this grid?. * @param grid May not be <code>null</code>. * @param col_idx Horizontal index. * @param row_idx Vertical index * @return <code>true</code> If the indexes are valid given the grid's * {@linkplain #getElementsInRowCount() width} and {@linkplain #getRowCount() row count}. */ public static final boolean isValidElement(BoundedMatrix grid, int row_idx, int col_idx) { try { return (0 <= col_idx && col_idx < grid.getElementsInRowCount() && 0 <= row_idx && row_idx < grid.getRowCount()); } catch(NullPointerException npx) { throw new NullPointerException("grid"); } } public static final void crashIfBadCoordsArray(MatrixElement[][] coords) { int lenFirst = -1; try { lenFirst = coords[0].length; } catch(NullPointerException npx) { CrashIfObject.nnull(coords, "coords", null); CrashIfObject.nullOrReturnCause(coords[0], "coords[0]", null, npx); } for(int i = 0; i < coords.length; i++) { try { if(coords[i].length != lenFirst) { throw new IllegalArgumentException("coords[" + i + "].length (" + coords[i].length + ") is different than coords[0].length (" + coords[0].length + ")"); } } catch(NullPointerException npx) { throw CrashIfObject.nullOrReturnCause(coords[i], "coords[" + i + "]", null, npx); } for(int j = 0; j < lenFirst; j++) { MatrixElement coord = coords[i][j]; try { if(coord.getColumnIndex() != i || coord.getRowIndex() != j) { throw new IllegalArgumentException("coords[" + i + "][" + j + "] has unexpected elements: " + coord); } } catch(NullPointerException npx) { throw CrashIfObject.nullOrReturnCause(coords[i][j], "coords[" + i + "][" + j + "]", null, npx); } } } } /** * Get a new double array of elements with a specific width and * height. * @param col_count May not be less than zero. * @param row_count May not be less than zero. * @return A * <br/>     <code>new MatrixElement[row_count][col_count]</code> * <br/>Where each element is a <code>MatrixElement</code>, having * {@linkplain MatrixElement#getColumnIndex() horizontal} and * {@linkplain MatrixElement#getRowIndex() vertical} indexes * equivalent to its location in the array (vertical is the sub-array, * horizontal is the }, within that array). */ public MatrixElement[][] getArrayFromWidthHeight(int row_count, int col_count) { MatrixElement[][] coords = null; try { coords = new MatrixElement[row_count][col_count]; } catch(ArrayIndexOutOfBoundsException ibx) { throw new ArrayIndexOutOfBoundsException("col_count=" + col_count + ", row_count=" + row_count); } for(int h = 0; h < row_count; h++) { for(int w = 0; w < col_count; w++) { coords[h][w] = new MatrixElement(h, w); } } return coords; } }