package mikera.vectorz;

import java.nio.DoubleBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import mikera.arrayz.Arrayz;
import mikera.arrayz.INDArray;
import mikera.arrayz.ISparse;
import mikera.arrayz.impl.AbstractArray;
import mikera.arrayz.impl.SliceArray;
import mikera.indexz.AIndex;
import mikera.indexz.Index;
import mikera.matrixx.AMatrix;
import mikera.matrixx.Matrix;
import mikera.matrixx.Matrixx;
import mikera.matrixx.impl.BroadcastVectorMatrix;
import mikera.matrixx.impl.RowMatrix;
import mikera.randomz.Hash;
import mikera.util.Maths;
import mikera.vectorz.impl.ADenseArrayVector;
import mikera.vectorz.impl.ASizedVector;
import mikera.vectorz.impl.ASparseVector;
import mikera.vectorz.impl.ArraySubVector;
import mikera.vectorz.impl.ImmutableVector;
import mikera.vectorz.impl.IndexedElementVisitor;
import mikera.vectorz.impl.IndexedSubVector;
import mikera.vectorz.impl.JoinedVector;
import mikera.vectorz.impl.ListWrapper;
import mikera.vectorz.impl.SparseIndexedVector;
import mikera.vectorz.impl.Vector0;
import mikera.vectorz.impl.VectorIndexScalar;
import mikera.vectorz.impl.VectorIterator;
import mikera.vectorz.impl.WrappedSubVector;
import mikera.vectorz.ops.Logistic;
import mikera.vectorz.util.Constants;
import mikera.vectorz.util.DoubleArrays;
import mikera.vectorz.util.ErrorMessages;
import mikera.vectorz.util.IntArrays;
import mikera.vectorz.util.VectorzException;

/**
 * Main abstract base class for all types of vector
 * 
 * Contains default implementations for most vector operations which can be
 * overriden to achieve better performance in derived classes.
 * 
 * @author Mike
 *
 */
@SuppressWarnings("serial")
public abstract class AVector extends AbstractArray implements IVector {

	// ================================================
	// Abstract interface

	@Override
	public abstract int length();

	@Override
	public abstract double get(int i);
	
	@Override
	public abstract void set(int i, double value);
	
	// ================================================
	// Standard implementations

	@Override
	public double get(long i) {
		if ((i<0)||(i>=length())) throw new IndexOutOfBoundsException(ErrorMessages.invalidIndex(this, i));
		return unsafeGet((int)i);
	}
	
	public void set(long i, double value) {
		if ((i<0)||(i>=length())) throw new IndexOutOfBoundsException(ErrorMessages.invalidIndex(this, i));
		unsafeSet((int)i,value);
	}
	
	@Override
	public final double getElement(long i) {
		return get(i);
	}
	
	@Override
	public void set(int[] indexes, double value) {
		if (indexes.length==1) {
			set(indexes[0],value);
		} else {
			throw new UnsupportedOperationException(""+indexes.length+"D set not supported on AVector");
		}
	}
	
	@Override
	public void set(long[] indexes, double value) {
		if (indexes.length==1) {
			set(Tools.toInt(indexes[0]),value);
		} else {
			throw new UnsupportedOperationException(""+indexes.length+"D set not supported on AVector");
		}
	}
	
	/**
	 * Like set, but performs no bounds checking.
	 * 
	 * Results are undefined if the index is out of range
	 * @param i
	 * @param value
	 */
	public void unsafeSet(int i, double value) {
		set(i,value);
	}
	
	/**
	 * Like get, but performs no bounds checking.
	 * 
	 * Results are undefined if the index is out of range
	 * @param i
	 * @param value
	 */	
	public double unsafeGet(int i) {
		return get(i);
	}
	
	@Override
	public final double get(int x, int y) {
		throw new IllegalArgumentException(ErrorMessages.invalidIndex(this, x,y));
	}
	
	@Override
	public final int dimensionality() {
		return 1;
	}
	
	@Override
	public final double get(int... indexes) {
		if (indexes.length!=1) throw new IllegalArgumentException(ErrorMessages.invalidIndex(this, indexes));
		return get(indexes[0]);
	}
	
	@Override
	public final double get(long[] indexes) {
		if (indexes.length!=1) throw new IllegalArgumentException(ErrorMessages.invalidIndex(this, indexes));
		return get(Tools.toInt(indexes[0]));
	}
	
	@Override
	public final double get(AIndex ix) {
		if (ix.length()!=1) throw new IllegalArgumentException(ErrorMessages.invalidIndex(this, ix));
		return get(ix.get(0));
	}
	
	@Override
	public double get() {
		throw new UnsupportedOperationException("Can't do 0-d get on a vector!");
	}
	
	@Override
	public AScalar slice(int position) {
		return VectorIndexScalar.wrap(this,position);
	}
	
	@Override
	public Object sliceValue(int i) {
		return get(i);
	}
	
	@Override
	public AScalar slice(int dimension, int index) {
		checkDimension(dimension);
		return slice(index);	
	}	
	
	@Override
	public int sliceCount() {
		return length();
	}
	
	@Override
	public List getSlices() {
		return new ListWrapper(this);
	}
	
	@Override
	public int[] getShape() {
		return new int[] {length()};
	}
	
	@Override
	public int[] getShapeClone() {
		return new int[] {length()};
	}
	
	@Override
	public final int getShape(int dim) {
		if (dim==0) {
			return length();
		} else {
			throw new IndexOutOfBoundsException(ErrorMessages.invalidDimension(this, dim));
		}
	}

	
	@Override
	public long[] getLongShape() {
		return new long[] {length()};
	}
		
	@Override
	public long elementCount() {
		return length();
	}
	
	@Override
	public long nonZeroCount() {
		int n=length();
		long result=0;
		for (int i=0; i=n) return vs;
			}
		}
		return vs;
	}
	
	@Override
	public AVector subArray(int[] offsets, int[] shape) {
		if (offsets.length!=1) throw new IllegalArgumentException(ErrorMessages.invalidIndex(this, offsets));
		if (shape.length!=1) throw new IllegalArgumentException(ErrorMessages.invalidIndex(this, offsets));
		return subVector(offsets[0],shape[0]);
	}
	
	
	@Override
	public INDArray rotateView(int dimension, int shift) {
		checkDimension(dimension);
		return rotateView(shift);
	}
	
	@Override
	public AVector rotateView(int shift) {
		int n=length();
		if (n==0) return this;
		
		shift = Maths.mod(shift,n);
		if (shift==0) return this;
			
		return subVector(shift,n-shift).join(subVector(0,shift));
	}	
	
	@Override
	public AVector rotateCopy(int shift) {
		return rotateView(shift).copy();
	}	
	
	/**
	 * Obtains a sub-vector view that refers to this vector.
	 * Changes to the sub-vector will be reflected in this vector
	 */
	public AVector subVector(int offset, int length) {
		int len=checkRange(offset,length);

		if (length==0) return Vector0.INSTANCE;
		if (length==len) return this;
		
		return WrappedSubVector.wrap(this,offset,length);
	}

	/**
	 * Returns a new vector that refers to this vector joined to a second vector.
	 * 
	 * Optimises the type of the returned vector to be as efficient as possible.
	 * 
	 * @param second
	 * @return
	 */
	@Override
	public AVector join(INDArray b) {
		if (b.dimensionality()!=1) throw new IllegalArgumentException(ErrorMessages.incompatibleShapes(this, b));
		return join(b.asVector());
	}
	
	public AVector join(AVector second) {
		if (second.length()==0) return this;
		AVector ej=tryEfficientJoin(second);
		if (ej!=null) return ej;
		return JoinedVector.joinVectors(this,second);
	}
	
	/**
	 * Attempts to perform an efficient join with a second vector. An efficient join is guaranteed 
	 * to be better than a simple JoinedVector(left,right) 
	 * 
	 * If possible, returns the joined vector. If not, returns null
	 */
	public AVector tryEfficientJoin(AVector second) {
		return null;
	}
	
	@Override
	public INDArray join(INDArray a, int dimension) {
		checkDimension(dimension);
		if (a instanceof AVector) {
			return join((AVector)a);
		}
		if (a.dimensionality()!=1) throw new IllegalArgumentException(ErrorMessages.incompatibleShapes(this, a));
		return join(a.asVector());
	}
	
	@Override
	public int compareTo(INDArray a) {
		if (a instanceof AVector) {
			return compareTo((AVector)a);
		} else if (a.dimensionality()!=1) {
			throw new IllegalArgumentException(ErrorMessages.incompatibleShapes(this, a));
		} else {
			return compareTo(a.asVector());
		}
	}
	
	/**
	 * Compares this vector to another vector
	 */
	public int compareTo(AVector a) {
		int len=checkSameLength(a);
		
		for (int i=0; i toList() {
		ArrayList al=new ArrayList(length());
		double[] ds=asDoubleArray();
		if (ds==null) ds=toDoubleArray();
		for (double d: getElements()) {
			al.add(d);
		}
		return al;
	}
	
	@Override
	public boolean epsilonEquals(INDArray a, double tolerance) {
		if (a instanceof AVector) return epsilonEquals((AVector)a,tolerance);
		if (a.dimensionality()!=1) throw new IllegalArgumentException(ErrorMessages.incompatibleShapes(this, a));
		int len=length();
		if (len!=a.getShape(0)) throw new IllegalArgumentException(ErrorMessages.incompatibleShapes(this, a));
		for (int i = 0; i < len; i++) {
			if (!Tools.epsilonEquals(unsafeGet(i), a.get(i), tolerance)) return false;
		}		
		return true;
	}
		
	/**
	 * Returns true if this vector is approximately equal to another vector.
	 * A small default tolerance is used.
	 * @param v
	 * @return
	 */
	public boolean epsilonEquals(AVector v) {
		return epsilonEquals(v,Vectorz.TEST_EPSILON);
	}
	
	@Override
	public boolean epsilonEquals(AVector v,double tolerance) {
		if (this == v) return true;
		int len=checkSameLength(v);
		
		for (int i = 0; i < len; i++) {
			if (!Tools.epsilonEquals(unsafeGet(i), v.unsafeGet(i), tolerance)) return false;
		}
		return true;
	}
	
	/**
	 * Computes the hashcode of a vector.
	 * 
	 * Currently defined to be equal to List.hashCode for a equivalent list of Double values, 
	 * this may change in future versions.
	 */
	@Override
	public int hashCode() {
		int hashCode = 1;
		int len=length();
		for (int i = 0; i < len; i++) {
			hashCode = 31 * hashCode + (Hash.hashCode(unsafeGet(i)));
		}
		return hashCode;
	}
		
	@Override
	public double[] toDoubleArray() {
		double[] result=new double[length()];
		getElements(result,0);
		return result;
	}
	
	@Override
	public INDArray[] toSliceArray() {
		int n=sliceCount();
		INDArray[] al=new INDArray[n];
		for (int i=0; imax) {
				unsafeSet(i,max);
			}
		}
	}
	
	public void clampMax(double max) {
		int len=length();
		for (int i = 0; i < len; i++) {
			double v=unsafeGet(i);
			if (v>max) {
				unsafeSet(i,max);
			}
		}
	}
	
	public void clampMin(double min) {
		int len=length();
		for (int i = 0; i < len; i++) {
			double v=unsafeGet(i);
			if (v100) sub(max); // ensure we don't overflow exp calculation
		applyOp(Ops.EXP);
		divide(elementSum());
	}
	
	@Override
	public void square() {
		applyOp(Ops.SQUARE);
	}
	
	@Override
	public AVector squareCopy() {
		AVector r=clone();
		r.square();
		return r;
	}
	
	@Override
	public AVector sqrtCopy() {
		AVector r=clone();
		r.square();
		return r;
	}
	
    /**
     * Computes the tanh of every element in this vector. Mutates this vector.
     */
	public void tanh() {
		int len=length();
		for (int i=0; i cols=m.getColumns();
		for (int i=0; i al=a.getSliceViews();
		INDArray result=Arrayz.newArray(al.get(0).getShape());
		for (int i=0; iresult) {
				result=comp;
			} 
		}		
		return result;
	}
	
	/**
	 * Returns the index of the maximum absolute element of a vector
	 * @return
	 */
	public int maxAbsElementIndex() {
		int len=length();
		if (len==0) throw new IllegalArgumentException("Can't find maxAbsElementIndex of a 0-length vector");
		int result=0;
		double best=Math.abs(unsafeGet(0));
		for (int i=1; ibest) {
				result=i;
				best=comp;
			} 
		}		
		return result;
	}
	
	/**
	 * Returns the maximum element value in a vector. Synonym for elementMax()
	 * @return
	 */
	public final double maxElement() {
		return elementMax();
	}
	
	/**
	 * Returns the index of the maximum element of a vector
	 * @return
	 */
	public int maxElementIndex() {
		int len=length();
		if (len==0) throw new IllegalArgumentException("Can't find maxElementIndex of a 0-length vector");
		int result=0;
		double best=unsafeGet(0);
		for (int i=1; ibest) {
				result=i;
				best=comp;
			} 
		}		
		return result;
	}
	
	/**
	 * Returns the minimum element value in a vector. Synonym for elementMin()
	 */
	public final double minElement() {
		return elementMin();
	}
	
	/**
	 * Returns the index of the minimum element of a vector
	 */
	public int minElementIndex() {
		int len=length();
		if (len==0) throw new IllegalArgumentException("Can't find minElementIndex of a 0-length vector");
		int result=0;
		double best=unsafeGet(0);
		for (int i=1; i0) multiply(1.0/d);
		return d;
	}
	
	/**
	 * Returns a copy of this vector normalised to a Euclidean length of 1.0
	 * 
	 * Has no effect on a zero-length vector (i.e. it will return a zero length vector)
	 */
	@Override
	public AVector normaliseCopy() {
		double d=magnitude();
		if (d>0.0) return multiplyCopy(1.0/d);
		return copy();
	}
	
	/**
	 * Negates all emlements of this vector in place.
	 */
	@Override
	public void negate() {
		multiply(-1.0);
	}
	
	@Override
	public AVector negateCopy() {
		return multiplyCopy(-1.0);
	}
	
	@Override
	public final AVector scaleCopy(double d) {
		return multiplyCopy(d);
	}
	
	@Override
	public AVector shiftCopy(int shift) {
		if (shift==0) return copy();
		int n=length();
		Vector v=Vector.createLength(n);
		if ((shift>=n)||(shift<=-n)) return v;
		if (shift<0) {
			v.add(-shift,this,0,n+shift);
		} else {
			v.add(0,this,shift,n-shift);
		}
		return v;
	}
	
	@Override
	public void pow(double exponent) {
		if (exponent==1.0) return;
		if (exponent==0.0) {
			set(1.0);
			return;
		}
		if (exponent==2.0) {
			square();
			return;
		}
		// default for arbitrary exponent
		int len=length();
		for (int i=0; i0) {
			sb.append(unsafeGet(0));
			for (int i = 1; i < length; i++) {
				sb.append(',');
				sb.append(unsafeGet(i));
			}
		}
		sb.append(']');
		return sb.toString();
	}
	
	/**
	 * Coerces this vector to the standard dense Vector format.
	 * 
	 * May return the same vector is this is already a dense Vector, otherwise returns a Vector
	 * containing a new clone of the vector elements.
	 */
	@Override
	public Vector toVector() {
		return Vector.create(this);
	}
	
	/**
	 * Creates an immutable copy of a vector
	 * @return
	 */
	@Override
	public AVector immutable() {
		if (!isMutable()) return this;
		return ImmutableVector.create(this);
	}
	
	/**
	 * Coerces to a mutable version of a vector. May or may not be a copy,
	 * but guaranteed to be fully mutable
	 * @return
	 */
	@Override
	public AVector mutable() {
		if (this.isFullyMutable()) {
			return this;
		} else {
			return this.clone();
		}
	}
	
	@Override
	public AVector sparse() {
		if (this instanceof ISparse) return this;
		return Vectorz.createSparse(this);
	}
	
	@Override
	public AVector dense() {
		return denseClone();
	}
	
	@Override
	public final Vector denseClone() {
		return Vector.wrap(this.toDoubleArray());
	}
	
	/**
	 * Creates a new mutable vector representing the normalised value of this vector
	 * 
	 * Returns null if the vector cannot be normalised (i.e. is a zero length vector)
	 * @return
	 */
	public AVector toNormal() {
		double dd=elementSquaredSum();
		if (dd==0.0) return null;
		return scaleCopy(1.0/Math.sqrt(dd));
	}
	
	@Override
	public List asElementList() {
		return new ListWrapper(this);
	}
	
	@Override
	public Iterator iterator() {
		return new VectorIterator(this);
	}
	
	@Override
	public final Iterator elementIterator() {
		return iterator();
	}

	/**
	 * Adds source vector to this vector at the specified indexes which should map from source->this
	 * @param source
	 * @param sourceToDest
	 * @param factor
	 */
	public void addMultiple(Vector source, Index sourceToDest, double factor) {
		int len=source.length();
		if (len!=sourceToDest.length()) throw new IllegalArgumentException("Index length must match source length.");
		double[] data=source.getArray();
		for (int i=0; ithis
	 * @param source
	 * @param sourceToDest
	 * @param factor
	 */
	public void addMultiple(AVector source, Index sourceToDest, double factor) {
		int len=source.length();
		if (len!=sourceToDest.length()) throw new IllegalArgumentException("Index length must match source length.");
		for (int i=0; i 

Ads help maintain this website.