package is2.data;





import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

public final class FV extends IFV { 
	
	private FV subfv1;
	private FV subfv2;
	private boolean negateSecondSubFV = false;
	
    private int size;
   
	// content of the nodes NxC
	private int m_index[];
	
	// type of the nodes NxT
	
	public FV() {
		this(10);
	}

	public FV(int initialCapacity) {
		m_index = new int[initialCapacity];
	}


	public FV (FV fv1, FV fv2) {
		subfv1 = fv1;
		subfv2 = fv2;
	}

	public FV (FV fv1, FV fv2, boolean negSecond) {
		this(0);
		subfv1 = fv1;
		subfv2 = fv2;
		negateSecondSubFV = negSecond;
	}

	/**
	 * Read a feature vector
	 * @param index
	 * @param value
	 */
	public FV(DataInputStream dos, int capacity) throws IOException {
		this(capacity);
		size= m_index.length;
		
		for (int i=0; i<size; i++) m_index[i] = dos.readInt();	
	}


	/**
	 * Read a feature vector
	 * @param index
	 * @param value
	 */
	public FV(DataInputStream dos) throws IOException {
		this(dos.readInt());
		size= m_index.length;
		
		for (int i=0; i<size; i++)  m_index[i] = dos.readInt();
		
		
	}

	/**
     * Increases the capacity of this <tt>Graph</tt> instance, if
     * necessary, to ensure  that it can hold at least the number of nodes
     * specified by the minimum capacity argument. 
     *
     * @param   minCapacity   the desired minimum capacity.
     */
    private void ensureCapacity(int minCapacity) {
	
    	
    	if (minCapacity > m_index.length) {
    		
    		int oldIndex[] = m_index;
    	
    		int newCapacity = ( m_index.length * 3)/2 + 1;
    
    		
    		if (newCapacity < minCapacity) newCapacity = minCapacity;

    		m_index = new int[newCapacity];
    		System.arraycopy(oldIndex, 0, m_index, 0, oldIndex.length);
    		
    	}
    }
	
   
    final public int size() {
	  return size;
    }

    final public boolean isEmpty() {
	  return size == 0;
    }

    @Override
	final public void clear() {
    	size = 0;
    }
    
    
    final public int createFeature(int i, double v) {
    	
    	ensureCapacity(size+1); 
    	m_index[size] =i;
    	size++;
    	return size-1;
    }
    
    final public int createFeature(int i) {
    	
    	ensureCapacity(size+1); 
    	m_index[size] =i;
    	size++;
    	return size-1;
    }
   
    
	final public int getIndex(int i) {
		return m_index[i];
	}

	public void setIndex(int p, int i) {
		m_index[p] = i;
	}
    
	
	 /**
     * Trims the capacity of this <tt>Graph</tt> instance to true size.
     *  An application can use this operation to minimize
     * the storage of an <tt>Graph</tt> instance.
     */
    public void trimToSize() {

		if (size < m_index.length) {
		
		
			int oldIndex[] = m_index;
			
			m_index = new int[size];
			System.arraycopy(oldIndex, 0, m_index, 0, size);
			
		}
		
    }

    
	
	
    
    @Override
	final public void add(int i) {
        if (i>=0) {
			ensureCapacity(size+1); 
			m_index[size] =i;
			size++;
		}
    }

    final public void add(int[] i) {
    	
    	for(int k =0;k<i.length;k++) 	add(i[k]);
    	
    }

    final public void put(int i, double f) {
        if (i>=0) createFeature(i,f);
    }
   
	
	// fv1 - fv2
	public FV getDistVector(FV fl2) {
		return new FV(this, fl2, true);
	}

	
	public double getScore(double[] parameters, boolean negate) {
		double score = 0.0;

		if (null != subfv1) {
			score += subfv1.getScore(parameters, negate);

			if (null != subfv2) {
				if (negate) score += subfv2.getScore(parameters, !negateSecondSubFV);
				else score += subfv2.getScore(parameters, negateSecondSubFV);
				
			}
		}

		if (negate) for(int i=0;i<size;i++)	score -= parameters[m_index[i]];
		else for(int i=0;i<size;i++) score += parameters[m_index[i]];
		
			
		return score;
	}
	
	
	final public float getScore(float[] parameters, boolean negate) {
		float score = 0.0F;

		if (null != subfv1) { 
			score += subfv1.getScore(parameters, negate);

			if (null != subfv2) {
				if (negate) score += subfv2.getScore(parameters, !negateSecondSubFV);
				else score += subfv2.getScore(parameters, negateSecondSubFV);
				
			}
		}
		
		// warning changed the the value 
		
		if (negate) for(int i=0;i<size;i++)	score -= parameters[m_index[i]];//*m_value[i];
		else for(int i=0;i<size;i++) score += parameters[m_index[i]];//*m_value[i];
			
		return score;
	}

	final public int getScore(short[] parameters, boolean negate) {
		int score = 0;

		if (null != subfv1) { 
			score += subfv1.getScore(parameters, negate);

			if (null != subfv2) {
				if (negate) score += subfv2.getScore(parameters, !negateSecondSubFV);
				else score += subfv2.getScore(parameters, negateSecondSubFV);
				
			}
		}
		
		// warning changed the value 
		
		if (negate) for(int i=0;i<size;i++)	score -= parameters[m_index[i]];//*m_value[i];
		else for(int i=0;i<size;i++) score += parameters[m_index[i]];//*m_value[i];
			
		return score;
	}

	
	
	public void update(double[] parameters, double[] total, double alpha_k, double upd) {
		update(parameters, total, alpha_k, upd, false);
	}

	public final void update(double[] parameters, double[] total, double alpha_k, double upd, boolean negate) {

		if (null != subfv1) {
			subfv1.update(parameters, total, alpha_k, upd, negate);

			if (null != subfv2) {
				if (negate) subfv2.update(parameters, total, alpha_k, upd, !negateSecondSubFV);
				else subfv2.update(parameters, total, alpha_k, upd, negateSecondSubFV);
			}
		}

		if (negate) {
			for(int i=0;i<size;i++) { 
				parameters[m_index[i]] -= alpha_k;//*getValue(i);
				total[m_index[i]] -= upd*alpha_k;//*getValue(i);
			}
		} else {
			for(int i=0;i<size;i++){ 	
				parameters[m_index[i]] += alpha_k;//*getValue(i);
				total[m_index[i]] += upd*alpha_k;//*getValue(i); 
			}
		}


	}
	
	public final void update(short[] parameters, short[] total, double alpha_k, double upd, boolean negate) {

		if (null != subfv1) {
			subfv1.update(parameters, total, alpha_k, upd, negate);

			if (null != subfv2) {
				if (negate) subfv2.update(parameters, total, alpha_k, upd, !negateSecondSubFV);
				else subfv2.update(parameters, total, alpha_k, upd, negateSecondSubFV);
			}
		}

		if (negate) {
			for(int i=0;i<size;i++) { 
				parameters[m_index[i]] -= alpha_k;//*getValue(i);
				total[m_index[i]] -= upd*alpha_k;//*getValue(i);
			}
		} else {
			for(int i=0;i<size;i++){ 	
				parameters[m_index[i]] += alpha_k;//*getValue(i);
				total[m_index[i]] += upd*alpha_k;//*getValue(i); 
			}
		}


	}
	
	
	public final void update(float[] parameters, float[] total, double alpha_k, double upd, boolean negate) {

		if (null != subfv1) {
			subfv1.update(parameters, total, alpha_k, upd, negate);

			if (null != subfv2 && negate) {
					subfv2.update(parameters, total, alpha_k, upd, !negateSecondSubFV);
				} else {
					subfv2.update(parameters, total, alpha_k, upd, negateSecondSubFV);
				}
			
		}
	
		if (negate) {
			for(int i=0;i<size;i++){ 
				parameters[getIndex(i)] -= alpha_k;
				total[getIndex(i)] -= upd*alpha_k;  
			}
		} else {
			for(int i=0;i<size;i++){ 
				parameters[getIndex(i)] += alpha_k;
				total[getIndex(i)] += upd*alpha_k; //
			}
		}


	}
	
	
	public final void update(float[] parameters, float[] total, double alpha_k, 
			double upd, boolean negate, float[] totalp, Long2IntInterface li) {

		if (null != subfv1) {
			subfv1.update(parameters, total, alpha_k, upd, negate,totalp,li);

			if (null != subfv2 && negate) {
					subfv2.update(parameters, total, alpha_k, upd, !negateSecondSubFV,totalp,li);
				} else {
					subfv2.update(parameters, total, alpha_k, upd, negateSecondSubFV,totalp,li);
				}
		}
	
		if (negate) {
			for(int i=0;i<size;i++){ 
				parameters[getIndex(i)] -= alpha_k;
				total[getIndex(i)] -= upd*alpha_k;
				
				totalp[li.l2i(getIndex(i))] -=upd*alpha_k;
			//	totalp[getIndex(i)] -=upd*alpha_k;
			}
		} else {
			for(int i=0;i<size;i++){ 
				parameters[getIndex(i)] += alpha_k;
				total[getIndex(i)] += upd*alpha_k; //

				totalp[li.l2i(getIndex(i))] +=upd*alpha_k;
			//	totalp[getIndex(i)] +=upd*alpha_k;
			}
		}
	}
	



	private static IntIntHash hm1;
	private static IntIntHash hm2;
	
	public int dotProduct(FV fl2) {

		if (hm1==null) hm1 = new IntIntHash(size(),0.4F);
		else hm1.clear();
		
		addFeaturesToMap(hm1);
		
		if (hm2==null)hm2 = new IntIntHash(fl2.size,0.4F);
		else hm2.clear();
		
		fl2.addFeaturesToMap(hm2);

		int[] keys = hm1.keys();

		int result = 0;
		for(int i = 0; i < keys.length; i++) result += hm1.get(keys[i])*hm2.get(keys[i]);

		return result;

	}

	public double twoNorm(FV fl2) {

		if (hm1==null) hm1 = new IntIntHash(size(),0.4F);
		else hm1.clear();
		
		addFeaturesToMap(hm1);
		
		if (hm2==null)hm2 = new IntIntHash(fl2.size,0.4F);
		else hm2.clear();
		
		fl2.addFeaturesToMap(hm2);

		int[] keys = hm1.keys();

		int result = 0;
		for(int i = 0; i < keys.length; i++) result += hm1.get(keys[i])*hm2.get(keys[i]);

		
		return Math.sqrt((double)result);
		

	}
	
	public void addFeaturesToMap(IntIntHash map) {
		
		if (null != subfv1) {
			subfv1.addFeaturesToMap(map);

			if (null != subfv2) {
			 subfv2.addFeaturesToMap(map, negateSecondSubFV);
				
			}
		}

	
		for(int i=0;i<size;i++) if (!map.adjustValue(getIndex(i), 1)) map.put(getIndex(i), 1);
	
		
	
	}

	
	
	private void addFeaturesToMap(IntIntHash map, boolean negate) {
		
		if (null != subfv1) {
			subfv1.addFeaturesToMap(map, negate);

			if (null != subfv2) {
				if (negate)  subfv2.addFeaturesToMap(map, !negateSecondSubFV);
				else  subfv2.addFeaturesToMap(map, negateSecondSubFV);
				
			}
		}

		if (negate) { 
			for(int i=0;i<size;i++) if (!map  . adjustValue(getIndex(i), -1)) map.put(getIndex(i), -1);
		} else {
			for(int i=0;i<size;i++) if (!map.adjustValue(getIndex(i), 1)) map.put(getIndex(i), 1);
		}
		
	
	}

	
	@Override
	public final String toString() {
		StringBuilder sb = new StringBuilder();
		toString(sb);
		return sb.toString();
	}

	private final void toString(StringBuilder sb) {
		if (null != subfv1) {
			subfv1.toString(sb);

			if (null != subfv2)
				subfv2.toString(sb);
		}
		for(int i=0;i<size;i++)
			sb.append(getIndex(i)).append(' ');
	}

	public void writeKeys(DataOutputStream dos) throws IOException {
		
	//	int keys[] = keys();
	//	dos.writeInt(keys.length);
	//	for(int i=0;i<keys.length;i++) {
	//		dos.writeInt(keys[i]);
	//	}
		
		
		//int keys[] = keys();
		dos.writeInt(size);
		for(int i=0;i<size;i++) {
			dos.writeInt(m_index[i]);
		}
	
	}

	public void readKeys(DataInputStream dos) throws IOException {
		
		int keys = dos.readInt();
		for (int i=0; i<keys; i++) createFeature(dos.readInt(), 1.0);
	
		
	}

	final public static FV cat(FV f1,FV f2) {
		if (f1==null) return f2;
		if (f2==null) return f1;
		return new FV(f1, f2);
	}

	final public static FV cat(FV f1,FV f2, FV f3) {
		return FV.cat(f1, FV.cat(f2, f3));
	}
	final public static FV cat(FV f1,FV f2, FV f3, FV f4) {
		return FV.cat(f1, FV.cat(f2, FV.cat(f3, f4)));
	}

	
	final public static FV read(DataInputStream dis) throws IOException {
		int cap = dis.readInt();
		if (cap == 0) return null; 
		return new FV(dis,cap);

	}

	/* (non-Javadoc)
	 * @see is2.IFV#getScore()
	 */
	@Override
	public double getScore() {
		//System.out.println("not implemented");
		// TODO Auto-generated method stub
		return 0;
	}

	/* (non-Javadoc)
	 * @see is2.IFV#clone()
	 */
	@Override
	public IFV clone() {
		FV f= new FV(this.size);
		for(int i=0;i<this.size;i++) {
			f.m_index[i]=m_index[i];
		}
		f.size=this.size;
		return f;
	}

	

}
