/**
 *                    BioJava development code
 *
 * This code may be freely distributed and modified under the
 * terms of the GNU Lesser General Public Licence.  This should
 * be distributed with the code.  If you do not have a copy,
 * see:
 *
 *      http://www.gnu.org/copyleft/lesser.html
 *
 * Copyright for this code is held jointly by the individual
 * authors.  These should be listed in @author doc comments.
 *
 * For more information on the BioJava project and its aims,
 * or to join the biojava-l mailing list, visit the home page
 * at:
 *
 *      http://www.biojava.org/
 *
 * Created on 5 Mar 2013
 * Created by Andreas Prlic
 *
 * @since 3.0.6
 */
package org.biojava.nbio.structure.math;

import java.io.Serializable;


/**
 *
 *  A sparse vector, implemented using a symbol table.
 *
 *  Derived from http://introcs.cs.princeton.edu/java/44st/SparseVector.java.html
 *
 *  For additional documentation, see <a href="http://introcs.cs.princeton.edu/44st">Section 4.4</a> of
 *  <i>Introduction to Programming in Java: An Interdisciplinary Approach</i> by Robert Sedgewick and Kevin Wayne.
 */

public class SparseVector implements Serializable{
	/**
	 *
	 */
	private static final long serialVersionUID = 1174668523213431927L;

	private final int N;             // length

	private SymbolTable<Integer, Double> symbolTable;  // the vector, represented by index-value pairs


	/** Constructor. initialize the all 0s vector of length N
	 *
	 * @param N
	 */
	public SparseVector(int N) {
		this.N  = N;
		this.symbolTable = new SymbolTable<Integer, Double>();
	}

	/** Setter method (should it be renamed to set?)
	*
	* @param i set symbolTable[i]
	* @param value
	*/
	public void put(int i, double value) {
		if (i < 0 || i >= N) throw new IllegalArgumentException("Illegal index " + i + " should be > 0 and < " + N);
		if (value == 0.0) symbolTable.delete(i);
		else              symbolTable.put(i, value);
	}

	/** get a value
	 *
	 * @param i
	 * @return  return symbolTable[i]
	 */
	public double get(int i) {
		if (i < 0 || i >= N) throw new IllegalArgumentException("Illegal index " + i + " should be > 0 and < " + N);
		if (symbolTable.contains(i)) return symbolTable.get(i);
		else                return 0.0;
	}

	// return the number of nonzero entries
	public int nnz() {
		return symbolTable.size();
	}

	// return the size of the vector
	public int size() {
		return N;
	}

	/** Calculates the dot product of this vector a with b
	 *
	 * @param b
	 * @return
	 */
	public double dot(SparseVector b) {
		SparseVector a = this;
		if (a.N != b.N) throw new IllegalArgumentException("Vector lengths disagree. " + a.N + " != " + b.N);
		double sum = 0.0;

		// iterate over the vector with the fewest nonzeros
		if (a.symbolTable.size() <= b.symbolTable.size()) {
			for (int i : a.symbolTable)
				if (b.symbolTable.contains(i)) sum += a.get(i) * b.get(i);
		}
		else  {
			for (int i : b.symbolTable)
				if (a.symbolTable.contains(i)) sum += a.get(i) * b.get(i);
		}
		return sum;
	}

	/** Calculates the 2-norm
	 *
	 * @return
	 */
	public double norm() {
		SparseVector a = this;
		return Math.sqrt(a.dot(a));
	}

	/** Calculates  alpha * a
	 *
	 * @param alpha
	 * @return
	 */
	public SparseVector scale(double alpha) {
		SparseVector a = this;
		SparseVector c = new SparseVector(N);
		for (int i : a.symbolTable) c.put(i, alpha * a.get(i));
		return c;
	}

	/** Calcualtes return a + b
	 *
	 * @param b
	 * @return
	 */
	public SparseVector plus(SparseVector b) {
		SparseVector a = this;
		if (a.N != b.N) throw new IllegalArgumentException("Vector lengths disagree : " + a.N + " != " + b.N);
		SparseVector c = new SparseVector(N);
		for (int i : a.symbolTable) c.put(i, a.get(i));                // c = a
		for (int i : b.symbolTable) c.put(i, b.get(i) + c.get(i));     // c = c + b
		return c;
	}

	@Override
	public String toString() {
		StringBuilder s = new StringBuilder();
		for (int i : symbolTable) {
			s.append("(");
			s.append(i);
			s.append(", ");
			s.append(symbolTable.get(i));
			s.append(") ");
		}
		return s.toString();
	}


}

