package cs437.som.network;

import cs437.som.Dimension;
import cs437.som.SOMError;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * A simple self-organizing map, using a hexagonal grid for the neurons.
 *
 * An simple example usage:
 *
 * <code>
 * <pre>
 * private static final int iterations = 500;
 *
 * public static void main(String[] args) {
 *     TrainableSelfOrganizingMap som = new BasicHexGridSOM(7, 2, iterations);
 *     Random r = new SecureRandom();
 *
 *     for (int i = 0; i < iterations; i++) {
 *         double[] in = {r.nextDouble() * 10, r.nextDouble() * 10};
 *         som.trainWith(in);
 *     }
 *
 *     // At this point, the SOM would be queried for it's BMU.
 * }
 * </pre>
 * </code>
 */
public class BasicHexGridSOM extends NetworkBase {

    /**
     * Create a new BasicHexGridSOM.
     * 
     * @param gridSize The neuron grid dimensions.
     * @param inputVectorSize The input vector size.
     * @param expectedIterations The expected number of training iterations.
     */
    public BasicHexGridSOM(Dimension gridSize, int inputVectorSize,
                           int expectedIterations) {
        super(gridSize, inputVectorSize, expectedIterations);
    }

    @Override
    protected double neuronDistance(int neuron0, int neuron1) {
        int row0 = neuron0 / gridSize.x;
        int col0 = neuron0 % gridSize.x;
        int row1 = neuron1 / gridSize.x;
        int col1 = neuron1 % gridSize.x;

        int dx = row1 - row0;
        int dy = col1 - col0;

        int distance;
        if (sign(dx) == sign(dy)) {
            distance = Math.abs(dx + dy);
        } else {
            distance = Math.max(Math.abs(dx), Math.abs(dy));
        }

        return distance;
    }

    private int sign(int n) {
        return (n >= 0) ? 1 : -1;
    }

    @Override
    public String toString() {
        return "BasicHexGridSOM{time=" + time +
                ", weightMatrix=" +
                (weightMatrix == null ? null : Arrays.deepToString(weightMatrix)) +
                ", neuronCount=" + neuronCount +
                ", inputSize=" + inputVectorSize + '}';
    }

    @Override
    public void write(OutputStreamWriter destination) throws IOException {
        destination.write(String.format("Map type: BasicHexGridSOM%n"));
        super.write(destination);
        destination.flush();
    }

    /**
     * Read a BasicHexGridSOM from an input stream.
     *
     * @param input The stream to read from.  This stream should be passed in
     * as soon as it is known to represent a BasicHexGridSOM.
     * @return A BasicHexGridSOM as represented by the contents of
     * {@code input}.
     * @throws IOException if something fails while reading the stream.
     */
    public static BasicHexGridSOM read(BufferedReader input) throws IOException {
        Pattern dimensionRegEx = Pattern.compile(
                "(?:grid)?\\s*dimensions\\s*:\\s*(\\d+)\\s*,\\s*(\\d+)",
                Pattern.CASE_INSENSITIVE);
        Pattern inputVectorSizeRegEx = Pattern.compile(
            "(?:input)?\\s*length\\s*:\\s*(\\d+)",
            Pattern.CASE_INSENSITIVE);
        Pattern iterationsRegEx = Pattern.compile("iterations\\s*:\\s*(\\d+)",
                Pattern.CASE_INSENSITIVE);
        Pattern weightRegEx = Pattern.compile("weights\\s*(?::)",
                Pattern.CASE_INSENSITIVE);

        Dimension dimension = null;
        int inputVectorSize = 0;
        int iterations = 0;

        String line = input.readLine();
        Matcher match = weightRegEx.matcher(line);
        while (! match.matches() && input.ready()) {
            Matcher dimMatch = dimensionRegEx.matcher(line);
            if (dimMatch.matches()) {
                dimension = new Dimension(Integer.parseInt(dimMatch.group(1)),
                        Integer.parseInt(dimMatch.group(2)));
            }

            Matcher inputMatch = inputVectorSizeRegEx.matcher(line);
            if (inputMatch.matches()) {
                inputVectorSize = Integer.parseInt(inputMatch.group(1));
            }

            Matcher iterationsMatch = iterationsRegEx.matcher(line);
            if (iterationsMatch.matches()) {
                iterations = Integer.parseInt(iterationsMatch.group(1));
            }
            line = input.readLine();
            match = weightRegEx.matcher(line);
        }

        if (dimension == null || inputVectorSize < 1) {
            throw new SOMError("A valid dimension and input vector size must" +
                    " appear in a map's configuration%nand they must appear " +
                    "before the weight matrix.");
        }

        BasicHexGridSOM bhgsom =
                new BasicHexGridSOM(dimension, inputVectorSize, iterations);
        bhgsom.weightMatrix =
                readWeightMatrix(input, dimension.area, inputVectorSize);
        return bhgsom;
    }
}

@@ -2962,1818 +2962,120 @@
     
-Pattern dimensionRegEx = Pattern.compile(%0A                %22(?:grid)?%5C%5Cs*dimensions%5C%5Cs*:%5C%5Cs*(%5C%5Cd+)%5C%5Cs*,%5C%5Cs*(%5C%5Cd+)%22,%0A                Pattern.CASE_INSENSITIVE);%0A        Pattern inputVectorSizeRegEx = Pattern.compile(%0A            %22(?:input)?%5C%5Cs*length%5C%5Cs*:%5C%5Cs*(%5C%5Cd+)%22,%0A            Pattern.CASE_INSENSITIVE);%0A        Pattern iterationsRegEx = Pattern.compile(%22iterations%5C%5Cs*:%5C%5Cs*(%5C%5Cd+)%22,%0A                Pattern.CASE_INSENSITIVE);%0A        Pattern weightRegEx = Pattern.compile(%22weights%5C%5Cs*(?::)%22,%0A                Pattern.CASE_INSENSITIVE);%0A%0A        Dimension dimension = null;%0A        int inputVectorSize = 0;%0A        int iterations = 0;%0A%0A        String line = input.readLine();%0A        Matcher match = weightRegEx.matcher(line);%0A        while (! match.matches() && input.ready()) %7B%0A            Matcher dimMatch = dimensionRegEx.matcher(line);%0A            if (dimMatch.matches()) %7B%0A                dimension = new Dimension(Integer.parseInt(dimMatch.group(1)),%0A                        Integer.parseInt(dimMatch.group(2)));%0A            %7D%0A%0A            Matcher inputMatch = inputVectorSizeRegEx.matcher(line);%0A            if (inputMatch.matches()) %7B%0A                inputVectorSize = Intege
+SOMFileReader sfr = new SOMFileReader();%0A                sf
 r.parse
-Int
 (input
-Match.group(1));%0A            %7D%0A%0A            Matcher iterationsMatch = iterationsRegEx.matcher(line);%0A            if (iterationsMatch.matches()) %7B%0A                iterations = Integer.parseInt(iterationsMatch.group(1));%0A            %7D%0A            line = input.readLine();%0A            match = weightRegEx.matcher(line);%0A        %7D%0A%0A        if (dimension == null %7C%7C inputVectorSize %3C 1) %7B%0A            throw new SOMError(%22A valid dimension and input vector size must%22 +%0A                    %22 appear in a map's configuration%25nand they must appear %22 +%0A                    %22before the weight matrix.%22);%0A        %7D%0A%0A       
+);%0A%0A        BasicHexGridSOM bhgsom = new
  Bas
@@ -3086,25 +3086,17 @@
 xGridSOM
- bhgsom =
+(
 %0A       
@@ -3104,36 +3104,20 @@
         
-new BasicHexGridSOM(
+sfr.
 dimensio
@@ -3119,16 +3119,20 @@
 ension, 
+sfr.
 inputVec
@@ -3140,16 +3140,20 @@
 orSize, 
+sfr.
 iteratio
@@ -3186,32 +3186,16 @@
 Matrix =
-%0A               
  readWei
@@ -3208,15 +3208,36 @@
 rix(
+%0A                
 input, 
+sfr.
 dime
@@ -3248,16 +3248,20 @@
 n.area, 
+sfr.
 inputVec

Change BasicHexGridSOM to use SOMFileReader for creation from a file.