/**
   * Retrieves the neuron at location {@code (i, j)} in the map. The neuron at position {@code (0,
   * 0)} is located at the upper-left corner of the map.
   *
   * @param i Row index.
   * @param j Column index.
   * @return the neuron at {@code (i, j)}.
   * @throws OutOfRangeException if {@code i} or {@code j} is out of range.
   * @see #getNeuron(int,int,HorizontalDirection,VerticalDirection)
   */
  public Neuron getNeuron(int i, int j) {
    if (i < 0 || i >= numberOfRows) {
      throw new OutOfRangeException(i, 0, numberOfRows - 1);
    }
    if (j < 0 || j >= numberOfColumns) {
      throw new OutOfRangeException(j, 0, numberOfColumns - 1);
    }

    return network.getNeuron(identifiers[i][j]);
  }
  /** Creates the neighbour relationships between neurons. */
  private void createLinks() {
    // "linkEnd" will store the identifiers of the "neighbours".
    final List<Long> linkEnd = new ArrayList<Long>();
    final int iLast = numberOfRows - 1;
    final int jLast = numberOfColumns - 1;
    for (int i = 0; i < numberOfRows; i++) {
      for (int j = 0; j < numberOfColumns; j++) {
        linkEnd.clear();

        switch (neighbourhood) {
          case MOORE:
            // Add links to "diagonal" neighbours.
            if (i > 0) {
              if (j > 0) {
                linkEnd.add(identifiers[i - 1][j - 1]);
              }
              if (j < jLast) {
                linkEnd.add(identifiers[i - 1][j + 1]);
              }
            }
            if (i < iLast) {
              if (j > 0) {
                linkEnd.add(identifiers[i + 1][j - 1]);
              }
              if (j < jLast) {
                linkEnd.add(identifiers[i + 1][j + 1]);
              }
            }
            if (wrapRows) {
              if (i == 0) {
                if (j > 0) {
                  linkEnd.add(identifiers[iLast][j - 1]);
                }
                if (j < jLast) {
                  linkEnd.add(identifiers[iLast][j + 1]);
                }
              } else if (i == iLast) {
                if (j > 0) {
                  linkEnd.add(identifiers[0][j - 1]);
                }
                if (j < jLast) {
                  linkEnd.add(identifiers[0][j + 1]);
                }
              }
            }
            if (wrapColumns) {
              if (j == 0) {
                if (i > 0) {
                  linkEnd.add(identifiers[i - 1][jLast]);
                }
                if (i < iLast) {
                  linkEnd.add(identifiers[i + 1][jLast]);
                }
              } else if (j == jLast) {
                if (i > 0) {
                  linkEnd.add(identifiers[i - 1][0]);
                }
                if (i < iLast) {
                  linkEnd.add(identifiers[i + 1][0]);
                }
              }
            }
            if (wrapRows && wrapColumns) {
              if (i == 0 && j == 0) {
                linkEnd.add(identifiers[iLast][jLast]);
              } else if (i == 0 && j == jLast) {
                linkEnd.add(identifiers[iLast][0]);
              } else if (i == iLast && j == 0) {
                linkEnd.add(identifiers[0][jLast]);
              } else if (i == iLast && j == jLast) {
                linkEnd.add(identifiers[0][0]);
              }
            }

            // Case falls through since the "Moore" neighbourhood
            // also contains the neurons that belong to the "Von
            // Neumann" neighbourhood.

            // fallthru (CheckStyle)
          case VON_NEUMANN:
            // Links to preceding and following "row".
            if (i > 0) {
              linkEnd.add(identifiers[i - 1][j]);
            }
            if (i < iLast) {
              linkEnd.add(identifiers[i + 1][j]);
            }
            if (wrapRows) {
              if (i == 0) {
                linkEnd.add(identifiers[iLast][j]);
              } else if (i == iLast) {
                linkEnd.add(identifiers[0][j]);
              }
            }

            // Links to preceding and following "column".
            if (j > 0) {
              linkEnd.add(identifiers[i][j - 1]);
            }
            if (j < jLast) {
              linkEnd.add(identifiers[i][j + 1]);
            }
            if (wrapColumns) {
              if (j == 0) {
                linkEnd.add(identifiers[i][jLast]);
              } else if (j == jLast) {
                linkEnd.add(identifiers[i][0]);
              }
            }
            break;

          default:
            throw new MathInternalError(); // Cannot happen.
        }

        final Neuron aNeuron = network.getNeuron(identifiers[i][j]);
        for (long b : linkEnd) {
          final Neuron bNeuron = network.getNeuron(b);
          // Link to all neighbours.
          // The reverse links will be added as the loop proceeds.
          network.addLink(aNeuron, bNeuron);
        }
      }
    }
  }