/*
Copyright (C) 2011 Diego Darriba, David Posada
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package es.uvigo.darwin.jmodeltest.tree;
import java.util.Hashtable;
import pal.tree.Tree;
/**
* Caches the distances between every couple of trees in a set. This is
* useful for instance when a tree-distance-based algorithm is computed
* many times, like the Decision Theory Information Criterion.
*
* This cache support both euclidean and RF distances.
*
* @author Diego Darriba
*
* @since 3.0
*/
public class TreeDistancesCache {
/** Constant of the euclidean distances */
public static final int EUCLIDEAN = 1;
/** Constant of the RF distances */
public static final int ROBINSON_FOULDS = 2;
/** Cache hash table coupling each pair of trees with its distance */
private Hashtable<TreePair, Double> distances;
/** Sort of computed distance */
private int distanceType;
/**
* Gets the distance type.
*
* @return the sort of distance, according to the defined constants
*/
public int getDistanceType() {
return distanceType;
}
/**
* Instantiates a new tree distances cache.
*
* @param distanceType sort of distance to compute
*/
public TreeDistancesCache(int distanceType) {
if (distanceType != EUCLIDEAN && distanceType != ROBINSON_FOULDS) {
//TODO: EXCEPTION!!!
}
this.distanceType = distanceType;
distances = new Hashtable<TreePair, Double>();
}
/**
* Gets the distance between two trees
*
* @param t1 the first tree
* @param t2 the second tree
*
* @return the distance
*/
public double getDistance(Tree t1, Tree t2) {
double distance = 0.0;
TreePair tp = new TreePair(t1, t2);
if (distances.containsKey(tp)) {
distance = distances.get(tp);
} else {
switch (distanceType) {
case EUCLIDEAN:
distance = TreeUtilities.getEuclideanTreeDistance(t1, t2);
break;
case ROBINSON_FOULDS:
distance = TreeUtilities.getRobinsonFouldsTreeDistance(t1, t2);
break;
}
distances.put(tp, distance);
}
return distance;
}
/**
* This class represents a pair of trees
*/
private class TreePair {
private Tree t1, t2;
public TreePair(Tree t1, Tree t2) {
this.t1 = t1;
this.t2 = t2;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + getOuterType().hashCode();
result = prime * result + ((t1 == null) ? 0 : t1.hashCode());
result = prime * result + ((t2 == null) ? 0 : t2.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
TreePair other = (TreePair) obj;
if (!getOuterType().equals(other.getOuterType())) {
return false;
}
// assume not null members
boolean checkB, checkA;
checkA = (t1.equals(other.t1) && t2.equals(other.t2));
checkB = (t1.equals(other.t2) && t2.equals(other.t1));
return checkA || checkB;
}
private TreeDistancesCache getOuterType() {
return TreeDistancesCache.this;
}
}
}