package dkohl.bayes.statistic;

import java.util.LinkedList;
import java.util.Vector;

import dkohl.bayes.probability.Assignment;
import dkohl.bayes.probability.Probability;

/**
 * A data set, defined as a vector of data points
 * 
 * @author Daniel Kohlsdorf
 */
public class DataSet extends Vector<DataPoint> {

	private static final long serialVersionUID = 1L;

	public LinkedList<LinkedList<Assignment>> getAssignmentMatchesForQuery(
			LinkedList<Assignment> given) {
		LinkedList<LinkedList<Assignment>> assignments = new LinkedList<LinkedList<Assignment>>();
		for (DataPoint point : this) {
			boolean insert = true;
			for (Assignment assignment : given) {
				if (!match(point, assignment)) {
					insert = false;
				}
			}
			if (insert) {
				assignments.add(new LinkedList<Assignment>(point.values()));
			}
		}
		return assignments;
	}

	/**
	 * Is the assignment equal to a data point / observation ?
	 * 
	 * @param point
	 *            the point / observation
	 * @param query
	 *            the assignment
	 * @return
	 */
	private boolean match(String queryName, DataPoint point,
			LinkedList<Assignment> query) {
		boolean queryFound = false;
		for (Assignment assignment : query) {
			if (!match(point, assignment)) {
				return false;
			}
			if (point.containsKey(queryName)) {
				queryFound = true;
			}
		}
		if (!queryFound) {
			return false;
		}
		return true;
	}

	private boolean match(DataPoint point, Assignment query) {
		String name = query.getVariable().getName();
		String value = query.getValue();
		if (point.containsKey(name)) {
			if (point.get(name).getValue().equals(value)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Estimating probability for: P(Query | given_1 .... given_N) = #(Query |
	 * given_1 .... given_N) / #(given_1 .... given_N)
	 * 
	 * @param query
	 * @param given
	 * @return
	 */
	public Probability prob(Assignment query, LinkedList<Assignment> given) {
		int matches = 0;
		int num_query_given = 0;
		for (DataPoint point : this) {
			if (match(query.getVariable().getName(), point, given)) {
				matches += 1;
				if (match(point, query)) {
					num_query_given += 1; // point.getWeight();
				}
			}
		}

		if (matches == 0) {
			return new Probability(0);
		}

		return new Probability(num_query_given / ((double) matches));
	}

}
