package net.woodyfolsom.msproj.ann2;

import static org.junit.Assert.assertTrue;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

public class XORFilterTest {
	private static final String FILENAME = "xorPerceptron.net";

	@AfterClass
	public static void deleteNewNet() {
		File file = new File(FILENAME);
		if (file.exists()) {
			file.delete();
		}
	}

	@BeforeClass
	public static void deleteSavedNet() {
		File file = new File(FILENAME);
		if (file.exists()) {
			file.delete();
		}
	}

	@Test
	public void testLearn() throws IOException {
		NeuralNetFilter nnLearner = new XORFilter(0.05,0.0);
		
		// create training set (logical XOR function)
		int size = 1;
		double[][] trainingInput = new double[4 * size][];
		double[][] trainingOutput = new double[4 * size][];
		for (int i = 0; i < size; i++) {
			trainingInput[i * 4 + 0] = new double[] { 0, 0 };
			trainingInput[i * 4 + 1] = new double[] { 0, 1 };
		    trainingInput[i * 4 + 2] = new double[] { 1, 0 };
			trainingInput[i * 4 + 3] = new double[] { 1, 1 };
			trainingOutput[i * 4 + 0] = new double[] { 0 };
			trainingOutput[i * 4 + 1] = new double[] { 1 };
			trainingOutput[i * 4 + 2] = new double[] { 1 };
			trainingOutput[i * 4 + 3] = new double[] { 0 };
		}

		// create training data
		List<NNDataPair> trainingSet = new ArrayList<NNDataPair>();
		String[] inputNames = new String[] {"x","y"};
		String[] outputNames = new String[] {"XOR"};
		for (int i = 0; i < 4*size; i++) {
			trainingSet.add(new NNDataPair(new NNData(inputNames,trainingInput[i]),new NNData(outputNames,trainingOutput[i])));
		}
		
		nnLearner.setMaxTrainingEpochs(20000);
		nnLearner.learn(trainingSet);
		System.out.println("Learned network after " + nnLearner.getActualTrainingEpochs() + " training epochs.");
		
		double[][] validationSet = new double[4][2];

		validationSet[0] = new double[] { 0, 0 };
		validationSet[1] = new double[] { 0, 1 };
		validationSet[2] = new double[] { 1, 0 };
		validationSet[3] = new double[] { 1, 1 };

		System.out.println("Output from eval set (learned network):");
		testNetwork(nnLearner, validationSet, inputNames, outputNames);
	}
	
	@Test
	public void testLearnSaveLoad() throws IOException {
		NeuralNetFilter nnLearner = new XORFilter(0.5,0.0);
		
		// create training set (logical XOR function)
		int size = 2;
		double[][] trainingInput = new double[4 * size][];
		double[][] trainingOutput = new double[4 * size][];
		for (int i = 0; i < size; i++) {
			trainingInput[i * 4 + 0] = new double[] { 0, 0 };
			trainingInput[i * 4 + 1] = new double[] { 0, 1 };
			trainingInput[i * 4 + 2] = new double[] { 1, 0 };
			trainingInput[i * 4 + 3] = new double[] { 1, 1 };
			trainingOutput[i * 4 + 0] = new double[] { 0 };
			trainingOutput[i * 4 + 1] = new double[] { 1 };
			trainingOutput[i * 4 + 2] = new double[] { 1 };
			trainingOutput[i * 4 + 3] = new double[] { 0 };
		}

		// create training data
		List<NNDataPair> trainingSet = new ArrayList<NNDataPair>();
		String[] inputNames = new String[] {"x","y"};
		String[] outputNames = new String[] {"XOR"};
		for (int i = 0; i < 4*size; i++) {
			trainingSet.add(new NNDataPair(new NNData(inputNames,trainingInput[i]),new NNData(outputNames,trainingOutput[i])));
		}
		
		nnLearner.setMaxTrainingEpochs(1);
		nnLearner.learn(trainingSet);
		System.out.println("Learned network after " + nnLearner.getActualTrainingEpochs() + " training epochs.");
		
		double[][] validationSet = new double[4][2];

		validationSet[0] = new double[] { 0, 0 };
		validationSet[1] = new double[] { 0, 1 };
		validationSet[2] = new double[] { 1, 0 };
		validationSet[3] = new double[] { 1, 1 };

		System.out.println("Output from eval set (learned network, pre-serialization):");
		testNetwork(nnLearner, validationSet, inputNames, outputNames);

		FileOutputStream fos = new FileOutputStream(FILENAME);
		assertTrue(nnLearner.save(fos));
		fos.close();
		
		FileInputStream fis = new FileInputStream(FILENAME);
		assertTrue(nnLearner.load(fis));
		fis.close();
		
		System.out.println("Output from eval set (learned network, post-serialization):");
		testNetwork(nnLearner, validationSet, inputNames, outputNames);
	}

	private void testNetwork(NeuralNetFilter nnLearner, double[][] validationSet, String[] inputNames, String[] outputNames) {
		for (int valIndex = 0; valIndex < validationSet.length; valIndex++) {
			NNDataPair dp = new NNDataPair(new NNData(inputNames,validationSet[valIndex]), new NNData(outputNames,validationSet[valIndex]));
			System.out.println(dp + " => " + nnLearner.compute(dp));
		}
	}
}