/* Written by Paul Yang from UC Berkeley, at SURE Program at Clemson Univ.
   5-28-2002 
   Based on Intersil's application note from November 2001, AN9850.2
   titled "Complementary Code Keying Made Simple"
*/

import java.util.*;
import java.io.IOException;
import java.lang.Math;
import java.util.LinkedList;
 
public class cck {
    
    public cck () {
	
    }

    public static void main(String [] args) {
	cck ck1 = new cck();
	System.out.println("welcome to cck test");
	/*
	ck1.initialize();
	ck1.makeCurrentPhases(1,0,1,1,0,1,0,1);
	ck1.currentVectorGen();
	ck1.makeCurrentPhases(0,0,0,0,0,0,0,0);
	ck1.currentVectorGen();
	ck1.makeCurrentPhases(1,1,1,1,1,1,1,1);
	ck1.currentVectorGen();
	*/
	ck1.bigVectorGen();
	System.out.println("\ncck test exit");
    }

    /* generate the phi value associated with these two digits. 
       used for pairs (d1, d0) (d3, d2) (d5, d4) and (d7, d6) 
    */

    public float smallPhiGen(int diplus1, int di) {
	try {
	    if (diplus1 > 1 || di > 1) 
		throw new IOException();
	    return phaseArray[diplus1][di];
	}
	catch(Exception e) {
	    System.out.println("smallPhiGen");
	    return 0;
	}
    }
    
    /* initialize the phaseArray.  smallPhiGen will do lookups here.
       values are actually multiplied by pi 
    */
    public void phaseArrayInit() {
	phaseArray = new float[2][2];
	phaseArray[0][0] = 0;
	phaseArray[0][1] = 1;
	phaseArray[1][0] = (float) 0.5;
	phaseArray[1][1] = (float) -0.5;
    }

    /* get the 8 exponential phases for the vector whose elements are 
       passed into the function 
    */
    public void makeCurrentPhases(int d7, int d6, int d5, int d4, int d3, int d2, int d1, int d0) {
	phi4 = smallPhiGen(d7, d6);
	phi3 = smallPhiGen(d5, d4);
	phi2 = smallPhiGen(d3, d2);
	phi1 = smallPhiGen(d1, d0);
	expPhase = new float[8];
	expPhase[0] = phi1 + phi2 + phi3 + phi4;
	expPhase[1] = phi1 + phi3 + phi4;
	expPhase[2] = phi1 + phi2 + phi4;
	expPhase[3] = phi1 + phi4;
	expPhase[4] = phi1 + phi2 + phi3;
	expPhase[5] = phi1 + phi3;
	expPhase[6] = phi1 + phi2;
	expPhase[7] = phi1;
	
	for (int i=0; i < expPhase.length; i++ ) {
	    if (expPhase[i] > 2) expPhase[i] -= 2;
	    else if (expPhase[i] < -2) expPhase[i] += 2;
	    if (debug) System.out.println("expPhase["+i+"] = "+ expPhase[i]);
	}
    }

    /* generate the 16 length current vector and print it out or store it. 
       [ Re{v0}, Im{v0}, Re{v1}, Im{v1}, ... , Re{v7}, Im{v7} ]
    */
    public void currentVectorGen() {
	String err=" ";
	try{
	    int i;
	    err="cv[i*2] or cv[(i*2)+1]";
	    cv = new int[16];
	    for ( i=0; i < expPhase.length; i++) {
		cv[i*2] = cosLookup(expPhase[i]);
		cv[(i*2)+1] = sinLookup(expPhase[i]);
		if (debug) {
		    System.out.println("cv["+(i*2)+"]"+" = cosLookup("+expPhase[i]+") = "+ cosLookup(expPhase[i]));
		    System.out.println("cv["+((i*2)+1)+"]" + " = sinLookup("+expPhase[i]+") = "+sinLookup(expPhase[i]));
		}
	    }
	    
	    
	    err = "cv";
	    cv[6] = -1 * cv[6];
	    cv[7] = -1 * cv[7];
	    cv[12] = -1 * cv[12];
	    cv[13] = -1 * cv[13];
	    err="printVector";
	    //printVector();
	}
	catch(Exception e) {
	    System.out.println("error in cvg at "+err + " " +e);
	}
    }
    
    public void printVector() {
	System.out.print("[ ");
	for (int i=0; i<cv.length; i++) {
	    System.out.print(cv[i] + ", ");
	}
	System.out.println(" ]");
    }

    /* instead of calculating the value of cos(0), cos(pi/2), cos(pi), ... ,
       we will store these values in an array to increase speed 
    */
    public int cosLookup(float phase) {
	float phase0 =  2*phase;
	int phase1 = (int) phase0;
	if (debug) System.out.println("sin phase = " + phase1);
	if (phase >= 0) 
	    return cosArray[phase1];
	else 
	    return cosArray[-1*phase1];
    }

    public int sinLookup(float phase) {
	float phase0 =  2*phase;
	int phase1 = (int) phase0;
	if (debug) System.out.println("sin phase = " + phase1);
	if (phase >= 0) 
	    return sinArray[phase1];
	else 
	    return -1 * sinArray[ -1*phase1];
    }

    public void initSinCos() {
	sinArray = new int[5];
	cosArray = new int[5];
	sinArray[0] = 0;
	sinArray[1] = 1;
	sinArray[2] = 0;
	sinArray[3] = -1;
	sinArray[4] = 0;
	cosArray[0] = 1;
	cosArray[1] = 0;
	cosArray[2] = -1;
	cosArray[3] = 0;
	cosArray[4] = 1;
    }
	
    public void initialize() {
	initSinCos();
	phaseArrayInit();
    }

    /*
    int getNthBinaryDigit(byte value, int n) {
	byte temp = value << ((byte) (8-n-1));
	temp = temp >> ((byte) 7);
	return (int) temp;
    }
    */

    int getNthBinaryDigit(int value, int n) {
	//if(debug2) System.out.println("before shift: " + value);
	int temp = value << (32-n-1);
	temp = (temp >> 31) & 1;
	//if(debug2) System.out.println("after shift: " + temp);
	return temp;
    }

    /* generates an array of 256 vectors representing the real and imag. parts 
       of the 256 possible code words 
    */
    public void bigVectorGen() {
	bigV = new int[256][16];
	digs = new int[256][8];
	for (int val=0; val<256; val++) {
	    if (debug2) System.out.println("val = " + val);
	    for(int j=0; j<digs[val].length; j++) {
		digs[val][j]=getNthBinaryDigit(val, j);
	    }	
	    cv = new int[16];
	    initialize();
	    if (debug2) {
		System.out.println("shifted 8 bit num = " + digs[val][7]+ digs[val][6]+ digs[val][5]+ digs[val][4]+ 
				   digs[val][3]+ digs[val][2]+ digs[val][1]+ digs[val][0]);
	    }
	    makeCurrentPhases(digs[val][7], digs[val][6], digs[val][5], digs[val][4], 
			      digs[val][3], digs[val][2], digs[val][1], digs[val][0]);
	    currentVectorGen();
	    //if (debug2) printVector();
	    for(int k=0; k<cv.length; k++) {
		bigV[val][k]=cv[k];
	    }
	}		
	printBigV();	
	//the 256 codewords are now in the bigVector

	//now to calculate the distances
	distFound = new boolean[256][256];
	//Paul: initialze the distFound array!
	for(int a=0; a<256; a++) {
	    for (int b=0; b<256; b++) {
		distFound[a][b]=false;
	    }
	}
	dist = new float[256][256];
	for( int currentV=0; currentV<dist.length; currentV++) {
	    for(int compareV=0; compareV<dist.length; compareV++) {
		if (distFound[currentV][compareV] || distFound[compareV][currentV] ) {
		    if (debug3) System.out.println("distFound[currentV][compareV] = "
						   +compareV+" "+currentV +" == true");
		    //do nothing?;
		}
		else {
		    float tempDistSum=0;
		    for(int m=0; m < bigV[currentV].length; m++) {
			tempDistSum += (bigV[currentV][m] - bigV[compareV][m]) * 
			    (bigV[currentV][m] - bigV[compareV][m]);
			if (debug3) System.out.println("tempDistSum = " + tempDistSum);
		    }
		    double tempDistSum0 = Math.sqrt((double) tempDistSum);
		    tempDistSum=(float) tempDistSum0;
		    dist[currentV][compareV]=tempDistSum;
		    dist[compareV][currentV]=tempDistSum;
		    distFound[currentV][compareV]=true;
		    distFound[compareV][currentV]=true;
		}
	    }
	}
	printDist();
	
	shortestD = new float[256];
	for (int curV=0; curV<256; curV++) {
	    float tempShortest = 100;
	    for(int compareV=0; compareV<256; compareV++) {
		if (curV == compareV) {
		    //do nothing?
		}
		else if (dist[curV][compareV] < tempShortest) {
		    tempShortest = dist[curV][compareV];
		}
	    }
	    shortestD[curV] = tempShortest;
	}
	
	printShortest();
		
	
    }
    
    public void printShortest() {
	for (int a1=0; a1<shortestD.length; a1++) {
	    System.out.println("shortestD[" + a1 + "] = " + shortestD[a1]);
	}
    }

    public void printDist() {
	System.out.println("Distance array:");
	for(int val=0; val<256; val++) {
	    LinkedList tempList = new LinkedList();

	    System.out.print("\n\nvector #" + val + " ");
	    //prints out all the 255 distances of the current vector to the others.
	
	    System.out.print("\n\nvector #" + val + " ( ");
	    for (int i=0; i<dist[val].length; i++) {
		System.out.print(dist[val][i] + ", ");
	    }
	    System.out.println(" )\n");

	    for(int i=0; i<dist[val].length; i++) {			    
		Floater tempVal = new Floater(dist[val][i], 1);
		boolean hasIt = false;
		int indx = 0;
		for(int ia=0; ia<tempList.size(); ia++) {
		    if (((Floater) tempList.get(ia)).val == tempVal.val) {
			hasIt = true;
			indx = ia;
		    }
		}
		if (!hasIt) {
		    //if (debug4) System.out.println("Floater val = "+tempVal.val + " added");
		    tempList.add(tempVal);
		}
		else {
		    //int index = tempList.indexOf(tempVal);
		    Floater tFloater = (Floater) tempList.remove(indx);
		    tFloater.occs += 1;
		    tempList.add(tFloater);
		}
	    }
	    for(int i=0; i<tempList.size(); i++) {
		Floater tf = (Floater) tempList.get(i);
		System.out.println("value = "+tf.val + " occurences = "+tf.occs);
	    }	    
	}
    }

    public static class Floater {
	public Floater(float val, int occs) {
	    //if (debug4) System.out.println("Floater created");
	    this.val = val;
	    this.occs = occs;
	}
	public boolean equals(Floater f) {	    
	    boolean retVal = (f.val == val);
	    if (debug4) {
		if (retVal) {
		    System.out.println("f.val == val? " + f.val + " == "+ val+ "? yes");
		}
		else {
		    System.out.println("no");
		}
	    }
	    return retVal;
	}
	float val;
	int occs;
    }

    public void printBigV() {
	for(int val=0; val<256; val++) {
	    System.out.print("vector #" + val + " [ ");
	    for (int i=0; i<bigV[val].length; i++) {
		System.out.print(bigV[val][i] + ", ");
	    }
	    System.out.println(" ]");
	}
    }

    /* global variables */
    float[][] phaseArray;    
    float[] expPhase;
    int[] sinArray;
    int[] cosArray;
    int[] cv;
    int[][] bigV;
    int[][] digs;
    boolean[][] distFound;
    float[][] dist;
    float[] shortestD;
    //phase values are really shownValue * pi
    float phi4;
    float phi3;
    float phi2;
    float phi1;

    boolean debug = false;
    boolean debug2 = false;
    boolean debug3 = false;
    static boolean debug4 = false;
}
