import java.io.*;
import java.lang.*;
import choco.ContradictionException;
import choco.Problem;
import choco.integer.IntVar;
import java.util.Vector;
import choco.Constraint;

public class Tree {
    private Node root;
    private NodeVector interiorNodes;
    private NodeVector leaves;
    private int count;

    public Tree (String fname,Dict D) throws FileNotFoundException, IOException {
	interiorNodes = new NodeVector();
	leaves = new NodeVector();
	Node p = Node.readNode(fname,D,this);
	root = p;
    }

    public Tree (){
	interiorNodes = new NodeVector();
	leaves = new NodeVector();
	root = null;
	count = 0;
    }

    public String toString(){return root.toString() + ";";}
    public void addInteriorNode(Node p){interiorNodes.insertInDepthOrder(p);}
    public void addLeafNode(Node p){leaves.insertInSpeciesOrder(p);}
    public NodeVector getLeaves(){return leaves;}
    public NodeVector getInteriorNodes(){return interiorNodes;}
    public int noLeaves(){return leaves.size();}
    public void setRoot(Node N){root = N;}
    public int getCount(){return count;}
    public void incCount(){count++;}

    public void look(){
	NodeEnumeration e1 = interiorNodes.elements();
	while (e1.hasMoreElements()) System.out.println(e1.nextElement().look());
	NodeEnumeration e2 = leaves.elements();
	while (e2.hasMoreElements()) System.out.println(e2.nextElement().look());
    }
	
    public int getMaxDDate(){
	int m = -1;
	NodeEnumeration e = interiorNodes.elements();
	while (e.hasMoreElements()) m = Math.max(m,e.nextElement().getDivergenceDate());
	return m;
    }

    public void toUMatrix(UMatrix um) throws ContradictionException {
	NodeVector V = getLeaves();
	int n = V.size();
	Problem pb = um.getProblem();
	IntVar [][] M = um.getM();
	int x;
	//System.out.println("******************************");
	//look();
	//System.out.println("********************************");
	for (int i=0;i<n-2;i++)
	    for (int j=i+1;j<n-1;j++)
		for (int k=j+1;k<n;k++){
		    Node a = V.elementAt(i);
		    Node b = V.elementAt(j);
		    Node c = V.elementAt(k);
		    Node ab = Node.mrca(a,b);
		    Node ac = Node.mrca(a,c);
		    Node bc = Node.mrca(b,c);
		    if (ab == ac && bc.getDepth() > ab.getDepth()) triple(um,b,c,a);
		    if (ab == bc && ac.getDepth() > ab.getDepth()) triple(um,a,c,b);
		    if (ac == bc && ab.getDepth() > ac.getDepth()) triple(um,a,b,c);
		    if (ab == ac && ac == bc) fan(um,a,b,c);
		}
    }
    //
    // determine the values that the decision variables CANNOT have
    //
    // This whole thing is flawed!
    //


    public void breakUp(UMatrix um) throws ContradictionException {
	//System.out.println("call breakup");
	NodeVector V = interiorNodes;
	int i=0;
	while (i<V.size()){// && V.get(i).getId() != 0){
	    Node v = V.get(i); // an interior node
	    //System.out.println(v.look());
	    if (v.degree() == 2 && v.getId() != 0) { // 2 kids and not the root -> make a triple
		Node p  = v.getParent(); 
		Node c1 = v.getChildren().get(0);
		Node c2 = v.getChildren().get(1);
		Node c3 = Node.getASiblingOf(c1);
		v.setSpecies(c2);
		v.clearChildren();
		System.out.println("tri: " + "(" + c1.getSpecies() + "," + c2.getSpecies() + ")" + c3.getSpecies());
		triple(um,c1,c2,c3);
		i++;
	    }
	    else if (v.degree() > 2){// & v.getId() != 0) { // make a fan		
		Node c1 = v.getChildren().get(0);
		Node c2 = v.getChildren().get(1);
		for (int x=0;x<v.degree()-2;x++)
		    for (int y=x+1;y<v.degree()-1;y++)
			for (int z=y+1;z<v.degree();z++){
			    System.out.println("fan: " + "(" + v.getChildren().get(x).getSpecies() + "," + 
			                                      v.getChildren().get(y).getSpecies() + "," + 
			    		                       v.getChildren().get(z).getSpecies() + ")");
			    fan(um,v.getChildren().get(x),v.getChildren().get(y),v.getChildren().get(z));
			}
		v.clearChildren();
		v.getChildren().addElement(c1);
		v.getChildren().addElement(c2);
	    }
	    else i++;
	}
    }

    private void triple(UMatrix um,Node a,Node b,Node c) throws ContradictionException {
	Problem pb = um.getProblem();
	IntVar [][] M = um.getM();
	int i = a.getSpeciesNo();
	int j = b.getSpeciesNo();
	int k = c.getSpeciesNo();
	//System.out.println("tri: (" + a.getSpecies() + "," + b.getSpecies() + ")" + c.getSpecies());
	um.addTriple(new Triple(a,b,c,1));
	pb.post(pb.and(pb.eq(M[i][k],M[j][k]),pb.and(pb.gt(M[i][j],M[j][k]),pb.gt(M[i][j],M[i][k]))));
    }

    private void relaxedTriple(UMatrix um,Node a,Node b,Node c) throws ContradictionException {
	Problem pb = um.getProblem();
	IntVar [][] M = um.getM();
	int i = a.getSpeciesNo();
	int j = b.getSpeciesNo();
	int k = c.getSpeciesNo();
	Constraint U1 = pb.and(pb.eq(M[i][k],M[j][k]),pb.and(pb.gt(M[i][j],M[j][k]),pb.gt(M[i][j],M[i][k])));
	Constraint U2 = pb.and(pb.eq(M[i][j],M[i][k]),pb.eq(M[i][k],M[j][k]));
	pb.post(pb.or(U1,U2));
    }

     private void fan(UMatrix um,Node a,Node b,Node c) throws ContradictionException{
	Problem pb = um.getProblem();
	IntVar [][] M = um.getM();
	int i = a.getSpeciesNo();
	int j = b.getSpeciesNo();
	int k = c.getSpeciesNo();
	//System.out.println("fan: [" + a.getSpecies() + "," + b.getSpecies() + "," + c.getSpecies() + "]");
	um.addTriple(new Triple(a,b,c,2));
	pb.post(pb.and(pb.eq(M[i][j],M[i][k]),pb.and(pb.eq(M[i][k],M[j][k]),pb.eq(M[i][j],M[j][k]))));
    }

    
    public static void main(String[] args) throws Exception {
	Dict d = new Dict();
	Tree t1 = new Tree("cats1.tre",d);
	//System.out.println(t1);
	Tree t2 = new Tree("cats2.tre",d);
	//System.out.println(t2);
	int n = d.size();
	System.out.println(n);
	for (int i=0;i<n;i++) System.out.println(d.elementAt(i));
	//t1.triples();
	//t2.triples();
	System.out.println("*** *** ***");
    }
}
