/*
 * F84DistanceMatrix.java
 *
 * Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard
 *
 * This file is part of BEAST.
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership and licensing.
 *
 * BEAST 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 2
 * of the License, or (at your option) any later version.
 *
 *  BEAST 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 BEAST; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301  USA
 */

package dr.evolution.distance;

import dr.evolution.alignment.PatternList;

/**
 * compute HKY corrected distance matrix
 *
 * @version $Id: F84DistanceMatrix.java,v 1.3 2005/05/24 20:25:56 rambaut Exp $
 *
 * @author Andrew Rambaut
 */
public class F84DistanceMatrix extends DistanceMatrix
{
	//
	// Public stuff
	//
	
	/** constructor */
	public F84DistanceMatrix()
	{
		super();
	}

	/** constructor taking a pattern source */
	public F84DistanceMatrix(PatternList patterns)
	{
		super(patterns);
	}

	/**
	 * set the pattern source
	 */
	public void setPatterns(PatternList patterns) {
		super.setPatterns(patterns);
		
		double[] freqs = patterns.getStateFrequencies();
		stateCount = patterns.getStateCount();
		if (stateCount != 4) {
			throw new IllegalArgumentException("F84DistanceMatrix must have nucleotide patterns");
		}

		double freqA = freqs[0];
		double freqC = freqs[1];
		double freqG = freqs[2];
		double freqT = freqs[3];
		
		double freqR = freqA + freqG;
		double freqY = freqC + freqT;
		
		constA =  ((freqA * freqG) / freqR) + ((freqC * freqT) / freqY);
		constB =  (freqA * freqG) + (freqC * freqT);
		constC =  (freqR * freqY);
	}
		
	/**
	 * Calculate a pairwise distance
	 */
	protected double calculatePairwiseDistance(int taxon1, int taxon2) {
		int state1, state2;
		
		int n = patterns.getPatternCount();
		double weight, distance;
		double sumTs = 0.0;
		double sumTv = 0.0;
		double sumWeight = 0.0;
		
		int[] pattern;
		
		for (int i = 0; i < n; i++) {
			pattern = patterns.getPattern(i);
			
			state1 = pattern[taxon1];
			state2 = pattern[taxon2];
			
			weight = patterns.getPatternWeight(i);
			if (!dataType.isAmbiguousState(state1) && !dataType.isAmbiguousState(state2) && state1 != state2) {

				if ((state1 == 0 && state2 == 2) || (state1 == 2 && state2 == 0)) { 
					// it's a transition
					sumTs += weight;
				} else {
					// it's a transversion
					sumTv += weight;
				}
			}
			sumWeight += weight;
		}
		
		double P = sumTs / sumWeight;
		double Q = sumTv / sumWeight;
		
		double tmp1 = Math.log(1.0 - (P / (2.0 * constA)) - 
								(((constA - constB) * Q) / (2.0 * constA * constC)));
								
		double tmp2 = Math.log(1.0 - (Q / (2.0 * constC)));
		
		distance = -(2.0 * constA * tmp1) +
					(2.0 * (constA - constB - constC) * tmp2);
		
		if (distance < MAX_DISTANCE) {
			return distance;
		} else {
			return MAX_DISTANCE;
		}
	}
	
	//
	// Private stuff
	//
	
	private int stateCount;
	
	//used in correction formula
	private double constA, constB, constC;
	
}
