
/**This short class animates the polyhedron exchange display**/

public class Deformer implements Runnable {
    PuzzleCanvas X;
    DeformCanvas Y;
    boolean halt;
    double[][] VAR=new double[10][0];
    int COUNT=0;

    public Deformer() {
	halt=true;
    }

    public Deformer(PuzzleCanvas XX,DeformCanvas YY) {
	this.X=XX;
	this.Y=YY;
	halt=false;
    }

    public void run() {
	COUNT=0;
	Y.STR[0]="count 0";
	
	int[][] list=PuzzleGeometry.orderSides(X.P,X.COUNT);
	int N=list.length;
	double record=1000;
      	double[] CURRENT=randomGuess(N);
	double[] BEST=copy(CURRENT);


	while(halt==false) {
	    PuzzlePiece[] Q=PuzzleGeometry.deformation(X.P,X.COUNT,CURRENT);
	    double qual=PuzzleGeometry.quality(Q,X.COUNT);
			      
	    if(qual<record) {
		sendPuzzle(CURRENT,qual);
		record=qual;
		BEST=copy(CURRENT);
	    }

	    if(record<Math.pow(2,-Y.TOL.val)) {
		sendVariation(CURRENT);
 	        CURRENT=randomGuess(N);
		record=1000;
                BEST=copy(CURRENT);
	    }
		
	    CURRENT=randomGuess(BEST,N,record);
	}
	tuneVariation();
	halt=true;
    }





    public void sendVariation(double[] CURRENT) {
	VAR[COUNT]=copy(CURRENT);
	++COUNT;
	Integer I=Integer.valueOf(COUNT);
	Y.STR[0]="count "+I.toString();
	if(COUNT>=Y.LIMIT.val) halt=true;
	Y.repaint();
    }


    public void tuneVariation() {
	int n=COUNT;
	System.out.println("variation count "+n);

	Matrix m=goodMinor();
	if(m==null) {
	    Y.STR[0]="no var";
	    Y.repaint();
	    Y.READY=false;
	    return;
	}


	for(int i=0;i<n;++i) {
	    double[] W=Matrix.actDual(m,VAR,n,i);
	    W=clean(W);
	    ListHelp.print(W);
   	    Y.VAR[i]=copy(W);
	}
	Y.VCOUNT=n;
	Y.READY=true;
    }

    public Matrix goodMinor() {
	boolean test=false;
	int count=0;
	while(test==false) {
	    Matrix m=tryMinor();
	    double d=m.maxNorm();
	    if(d<1000) return m;
	    ++count;
	    if(count>1000) return null;
	}
	return null;
    }

    

    public Matrix tryMinor() {
	int n=COUNT;
	Vector[] V=new Vector[n];
	int[] rand=getRandom(VAR[0].length,n);
	for(int i=0;i<n;++i) V[i]=makeFrom(VAR[i],rand);
	Matrix m=Matrix.makeColumn(V);
	return m.inverse();
    }

    public static int[] getRandom(int N,int n) {
	int[] a=ListHelp.randomPermutation(N);
	a=ListHelp.trim(a,n);
	return a;
    }

    public static Vector makeFrom(double[] a,int[] k) {
	double[] b=new double[a.length];
	for(int i=0;i<k.length;++i) b[i]=a[k[i]];
	return new Vector(b);
    }

    public double[] clean(double[] a) {
	int n=a.length;
	double[] b=new double[n];
	for(int i=0;i<n;++i) {
	    double s=a[i];
	    double t=Math.floor(s+.5);
	    if(Math.abs(s-t)>.001) return null;
	    b[i]=t;
	}
	return b;
    }
    


    
    public void sendPuzzle(double[] d,double qual) {
  	Y.P=PuzzleGeometry.deformation(X.P,X.COUNT,d);
	Y.COUNT=X.COUNT;
	Integer Q=Integer.valueOf(quality(qual));
	Y.STR[1]="guality "+Q.toString();
	Y.repaint();
    }


    

    /**This is large when the number is near 0*/
    public static int quality(double qual) {
	double q=Math.abs(Math.log(qual));
	q=q/Math.log(2);
	return (int)(q);
    }

    
    public static double[] randomGuess(int N) {
	double[] d=new double[N];
	for(int i=0;i<N;++i) {
	    d[i]=.25-.5*Math.random();
	}
	d=unitize(d);
	return d;
    }
    
    public static double[] randomGuess(double[] a,int N,double r0) {
	double r=r0*Math.random();
	if(r>.1) r=.1;
	double[] d=randomGuess(N);
	double[] b=new double[N];
	for(int i=0;i<N;++i) b[i]=(1-r)*a[i]+r*d[i];
	return b;
    }


    public static double[] unitize(double[] d0) {
	double[] d=copy(d0);
	int N=d0.length;
	double s=0;
	for(int i=0;i<N;++i) s=s+Math.abs(d[i]);
	for(int i=0;i<N;++i) d[i]=N*d[i]/s;
	return d;
    }


    public static double[] copy(double[] a) {
    	double[] b=new double[a.length];
	for(int i=0;i<a.length;++i) b[i]=a[i];
	return b;
    }





    
}




   
