package junto.util;

import gnu.trove.map.hash.TIntDoubleHashMap;
import gnu.trove.iterator.TIntDoubleIterator;
import java.io.*;
import java.util.*;

public class RyanFeatureVector implements Comparable, Serializable {

    public int index;
    public double value;
    public RyanFeatureVector next;
    
    public RyanFeatureVector(int i, double v, RyanFeatureVector n) {
	index = i;
	value = v;
	next = n;
    }
    
    public RyanFeatureVector add(String feat, double val, RyanAlphabet dataAlphabet) {
    	int num = dataAlphabet.lookupIndex(feat);
    	if(num >= 0)
    		return new RyanFeatureVector(num,val,this);
    	return this;
    }

    
    public void add(int i1, double v1) {
	
	RyanFeatureVector new_node = new RyanFeatureVector(this.index, this.value, this.next);
	
	this.index = i1;
	this.value = v1;
	this.next = new_node;
	
    }



    public static RyanFeatureVector cat(RyanFeatureVector fv1, RyanFeatureVector fv2) {
	RyanFeatureVector result = new RyanFeatureVector(-1,-1.0,null);
	for(RyanFeatureVector curr = fv1; curr.next != null; curr = curr.next) {
	    if(curr.index < 0)
		continue;
	    result = new RyanFeatureVector(curr.index,curr.value,result);
	}
	for(RyanFeatureVector curr = fv2; curr.next != null; curr = curr.next) {
	    if(curr.index < 0)
		continue;
	    result = new RyanFeatureVector(curr.index,curr.value,result);
	}
	return result;
	
    }
    
    // fv1 - fv2
    public static RyanFeatureVector getDistVector(RyanFeatureVector fv1, RyanFeatureVector fv2) {
    	RyanFeatureVector result = new RyanFeatureVector(-1, -1.0, null);
    	for (RyanFeatureVector curr = fv1; curr.next != null; curr = curr.next) {
			if (curr.index < 0)
				continue;
			result = new RyanFeatureVector(curr.index, curr.value, result);
		}
		for (RyanFeatureVector curr = fv2; curr.next != null; curr = curr.next) {
			if (curr.index < 0)
				continue;
			result = new RyanFeatureVector(curr.index, -curr.value, result);
		}
		return result;
    }
    
    public static RyanFeatureVector getAddedVector(RyanFeatureVector fv1, RyanFeatureVector fv2, double rate) {
    	
    	TIntDoubleHashMap hm = new TIntDoubleHashMap();
    	for (RyanFeatureVector curr = fv1; curr.next != null; curr = curr.next) {
    		if (curr.index >= 0) {
    			hm.put(curr.index, (hm.containsKey(curr.index) ? hm.get(curr.index) : 0) + curr.value);
    		}
    	}

    	for (RyanFeatureVector curr = fv2; curr.next != null; curr = curr.next) {
    		if (curr.index >= 0) {
    			hm.put(curr.index, (hm.containsKey(curr.index) ? hm.get(curr.index) : 0) + rate * curr.value);
    		}
    	}

    	RyanFeatureVector result = new RyanFeatureVector(-1, -1, null);
    	TIntDoubleIterator hmIter = hm.iterator();
    	while (hmIter.hasNext()) {
    		hmIter.advance();
    		result = new RyanFeatureVector(hmIter.key(), hmIter.value(), result);
    	}
		return result;
    }
	
    public static double dotProduct(RyanFeatureVector fv1, RyanFeatureVector fv2) {
	double result = 0.0;
	TIntDoubleHashMap hm1 = new TIntDoubleHashMap();
	TIntDoubleHashMap hm2 = new TIntDoubleHashMap();

	for(RyanFeatureVector curr = fv1; curr.next != null; curr = curr.next) {
	    if(curr.index < 0)
		continue;
	    hm1.put(curr.index,hm1.get(curr.index)+curr.value);
	}
	for(RyanFeatureVector curr = fv2; curr.next != null; curr = curr.next) {
	    if(curr.index < 0)
		continue;
	    hm2.put(curr.index,hm2.get(curr.index)+curr.value);
	}

	int[] keys = hm1.keys();

	for(int i = 0; i < keys.length; i++) {
	    double v1 = hm1.get(keys[i]);
	    double v2 = hm2.get(keys[i]);
	    result += v1*v2;
	}
		
	return result;
		
    }

    public static double oneNorm(RyanFeatureVector fv1) {
	double sum = 0.0;
	for(RyanFeatureVector curr = fv1; curr.next != null; curr = curr.next) {
	    if(curr.index < 0)
		continue;
	    sum += curr.value;
	}
	return sum;
    }
	
    public static int size(RyanFeatureVector fv1) {
	int sum = 0;
	for(RyanFeatureVector curr = fv1; curr.next != null; curr = curr.next) {
	    if(curr.index < 0)
		continue;
	    sum++;
	}
	return sum;
    }
	
    public static double twoNorm(RyanFeatureVector fv1) {
	TIntDoubleHashMap hm = new TIntDoubleHashMap();
	double sum = 0.0;
	for(RyanFeatureVector curr = fv1; curr.next != null; curr = curr.next) {
	    if(curr.index < 0)
		continue;
	    hm.put(curr.index,hm.get(curr.index)+curr.value);
	}
	int[] keys = hm.keys();

	for(int i = 0; i < keys.length; i++)
	    sum += Math.pow(hm.get(keys[i]),2.0);
		
	return Math.sqrt(sum);
    }

    public static RyanFeatureVector twoNormalize(RyanFeatureVector fv1) {
	return normalize(fv1,twoNorm(fv1));
    }
	

    public static RyanFeatureVector oneNormalize(RyanFeatureVector fv1) {
	return normalize(fv1,oneNorm(fv1));
    }
	
    public static RyanFeatureVector normalize(RyanFeatureVector fv1, double norm) {
	RyanFeatureVector result = new RyanFeatureVector(-1,-1.0,null);
	for(RyanFeatureVector curr = fv1; curr.next != null; curr = curr.next) {
	    if(curr.index < 0)
		continue;
	    result = new RyanFeatureVector(curr.index,curr.value/norm,result);
	}
	return result;
    }

    public String toString() {
	if (next == null)
	   return "" + index + ":" + value;
	return index + ":" + value + " " + next.toString();
    }


    public void sort() {
	ArrayList features = new ArrayList();

	for(RyanFeatureVector curr = this; curr != null; curr = curr.next)
	    if(curr.index >= 0)
		features.add(curr);

	Object[] feats = features.toArray();

	Arrays.sort(feats);

	RyanFeatureVector fv = new RyanFeatureVector(-1,-1.0,null);
	for(int i = feats.length-1; i >= 0; i--) {
	    RyanFeatureVector tmp = (RyanFeatureVector)feats[i];
	    fv = new RyanFeatureVector(tmp.index,tmp.value,fv);
	}
	
	this.index = fv.index;
	this.value = fv.value;
	this.next = fv.next;

    }

    public int compareTo(Object o) {
	RyanFeatureVector fv = (RyanFeatureVector)o;
	if(index < fv.index)
	    return -1;
	if(index > fv.index)
	    return 1;
	return 0;
    }


    public double dotProdoct(double[] weights) {
    	double score = 0.0;
	for(RyanFeatureVector curr = this; curr != null; curr = curr.next) {
	    if (curr.index >= 0)
		score += weights[curr.index]*curr.value;
	}
	return score;
    }
    

}
