/*
  This file is part of the BJP package.

  BJP package is free software: you can redistribute it and/or modify it
  under the terms of the GNU Lesser General Public License as published by the
  Free Software Foundation, either version 3 of the License, or (at your
  option) any later version.

  BJP package is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
  License for more details.

  You should have received a copy of the GNU Lesser General Public License
  along with BJP package.  If not, see <http://www.gnu.org/licenses/>.
 */
package util.math;

import java.util.Random;

import machineLearning.Datasets.FastDataset;
import machineLearning.Graphs.GraphsSimple.UGraphSimple;
import machineLearning.independenceBased.BayesianTest;
import machineLearning.independenceBased.IndependenceTest;
import machineLearning.independenceBased.TestsAccuracy;
import machineLearning.independenceBased.VertexSeparation;
import rep.UndirectedGraph;

/**
 * @author fschluter
 *
 */
public class UndirectedGraphsQualityMeasures {

	/**
	 * @param trueGraph
	 * @param graph
	 * @return
	 */
	public static int hammingDistance(UndirectedGraph trueGraph, UndirectedGraph graph) {
		int hammingDistance = 0, n = trueGraph.getNumberOfNodes();
		for (int i = 0; i < n-1; i++) {
			for (int j = i+1; j < n; j++) {
				if (!((trueGraph.adjMatrix[i][j] && graph.adjMatrix[i][j]) || (!trueGraph.adjMatrix[i][j] && !graph.adjMatrix[i][j]))) {
					hammingDistance++;
				}
			}
		}
		return hammingDistance;
	}

	/**
	 * @param trueGraph
	 * @param graph
	 * @return
	 */
	public static int falsePositives(UndirectedGraph trueGraph, UndirectedGraph graph) {
		int falsePositives = 0, n = trueGraph.getNumberOfNodes();
		for (int i = 0; i < n; i++) {
			for (int j = i+1; j < n; j++) {
				if (i != j) {
					if (!trueGraph.adjMatrix[i][j] && graph.adjMatrix[i][j]) {
						falsePositives++;
					}
				}
			}
		}
		return falsePositives;
	}

	/**
	 * @param trueGraph
	 * @param graph
	 * @return
	 */
	public static int falseNegatives(UndirectedGraph trueGraph, UndirectedGraph graph) {
		int falseNegatives = 0, n = trueGraph.getNumberOfNodes();
		for (int i = 0; i < n; i++) {
			for (int j = i+1; j < n; j++) {
				if (i != j) {
					if (trueGraph.adjMatrix[i][j] && !graph.adjMatrix[i][j]) {
						falseNegatives++;
					}
				}
			}
		}
		return falseNegatives;
	}

	/**
	 * FMeasure is: 2pr/p+r , where p is precision and r is recall
	 * 
	 * p and q are equally weighted. As for the separate values, F-measure
	 * ranges from 0 to 1, with 1 representing p and r of 1.
	 * 
	 * @param structure
	 * @param trueStructure
	 * @return
	 */
	public static double fMeasure(UndirectedGraph trueGraph, UndirectedGraph graph) {
		double p = precision(trueGraph, graph);
		double r = recall(trueGraph, graph);
		if (p + r == 0)
			return 0;
		else
			return (2 * p * r) / (p + r);

	}

	/**
	 * Precision is: true interactions found / total interactions found
	 * 
	 * It measures how much of the learned structure comprises correctly
	 * identified interactions
	 * 
	 * @param graph
	 * @param trueGraph
	 * @return
	 */
	public static double precision(UndirectedGraph trueGraph, UndirectedGraph graph) {
		int n = trueGraph.getNumberOfNodes();
		double totalInteractionsFound = 0;
		double trueInteractionsFound = 0;
		for (int i = 0; i < n - 1; i++) {
			for (int j = i + 1; j < n; j++) {
				if (graph.existEdge(i, j))
					totalInteractionsFound++;
				if (graph.existEdge(i, j) && trueGraph.existEdge(i, j))
					trueInteractionsFound++;
			}
		}
		if (totalInteractionsFound == 0)
			return 0;
		else {
			return trueInteractionsFound / totalInteractionsFound;
		}
	}

	/**
	 * Recall is: true interactions found / total true interactions present
	 * 
	 * it measures how many of the interactions present in the problem have been
	 * found.
	 * 
	 * @param structure
	 * @param trueStructure
	 * @return
	 */
	public static double recall(UndirectedGraph trueGraph, UndirectedGraph graph) {
		int n = trueGraph.getNumberOfNodes();
		double trueInteractionsFound = 0;
		double totalTrueInteractionsFound = 0;
		for (int i = 0; i < n - 1; i++) {
			for (int j = i + 1; j < n; j++) {
				if (graph.existEdge(i, j) && trueGraph.existEdge(i, j))
					trueInteractionsFound++;
				if (trueGraph.existEdge(i, j))
					totalTrueInteractionsFound++;
			}
		}
		if (totalTrueInteractionsFound == 0)
			return 0;
		else {
			return trueInteractionsFound / totalTrueInteractionsFound;
		}
	}

	public static double accuracy(UndirectedGraph outputGraph,
			String pathToTrainSet, int D, int numTests, long seed) {
		FastDataset fastDataset = new FastDataset(pathToTrainSet);
		fastDataset.readAllWSchema(pathToTrainSet, 1, D, ',');			
		fastDataset.setBinarySchema();
		
		UGraphSimple u = new UGraphSimple(outputGraph.getNumberOfNodes(),outputGraph.adjMatrix);
		
		VertexSeparation vertexSeparationTest = new VertexSeparation(u);
		
		IndependenceTest trainIT = new BayesianTest(fastDataset, true);
		trainIT.setThreshold(0.5);
		int n= outputGraph.getNumberOfNodes();
		TestsAccuracy testsAccuracy = new TestsAccuracy(vertexSeparationTest, trainIT, n - 2, numTests, new Random(seed));
		double accuracy = testsAccuracy.accuracy();
		return accuracy;
		
	}

}
