package math.matrix;

import math.number.NumberComplex;
import thisandthat.WObject;

/**
 * Complex vector v<sub>i</sub>&isin;&#x2102;
 * @author Daniel Weschke
 */
public class VectorComplex extends WObject{
	/**
	 * a<sub>i0</sub> = real &real;,
	 * a<sub>i1</sub> = imaginary &image;
	 */
	private double[][] vector;
	
	public VectorComplex(){
	}
	
	/**
	 * Create zero vector
	 * @param n rows
	 */
	public VectorComplex(int n){
		vector = new double[n][2];
		int i;
		for(i=0; i<n; i++){
			vector[i][0] = 0;
			vector[i][1] = 0;
		}
	}
	
	/**
	 * Create vector with given x
	 * @param x
	 */
	public VectorComplex(double x){
		vector = new double[][]{{x,0}};
	}
	
	/**
	 * Create vector with given x
	 * @param x
	 */
	public VectorComplex(NumberComplex x){
		vector = new double[][]{{x.ℜ(),x.ℑ()}};
	}
	
	/**
	 * Create vector with given x and y value
	 * @param x
	 * @param y
	 */
	public VectorComplex(NumberComplex x, NumberComplex y){
		vector = new double[][]{{x.ℜ(),x.ℑ()}, {y.ℜ(),y.ℑ()}};
	}
	
	/**
	 * Create vector with given x, y and z value
	 * @param x
	 * @param y
	 * @param z
	 */
	public VectorComplex(NumberComplex x, NumberComplex y, NumberComplex z){
		vector = new double[][]{{x.ℜ(),x.ℑ()}, {y.ℜ(),y.ℑ()}, {z.ℜ(),z.ℑ()}};
	}
	
	/**
	 * Create vector with given array
	 * @param v array
	 */
	public VectorComplex(double[] v){
		int i;
		int n = v.length;
		vector = new double[n][2];
		for(i=0; i<n; i++)
			vector[i][0] = v[i];
	}
	
	/**
	 * Create vector with given arrays
	 * @param real array
	 * @param imag array
	 */
	public VectorComplex(double[] real, double[] imag){
		int i;
		int n = real.length;
		vector = new double[n][2];
		for(i=0; i<n; i++){
			vector[i][0] = real[i];
			vector[i][1] = imag[i];
		}
	}
	
	/**
	 * Create vector with given double array
	 * @param v double array
	 */
	public VectorComplex(double[][] v){
		int i;
		int n = v.length;
		vector = new double[n][2];
		for(i=0; i<n; i++){
			vector[i][0] = v[i][0];
			vector[i][1] = v[i][1];	
		}
	}
	
	/**
	 * Copy vector
	 * @param v vector
	 */
	public VectorComplex(VectorComplex v){
		this(v.vector);
	}
	
	/**
	 * @return vector array
	 */
	public double[][] get(){
		return vector;
	}
	
	/**
	 * @param i index
	 * @return value
	 * @see #getRe(int)
	 * @see #getIm(int)
	 */
	public NumberComplex get(int i){
		return new NumberComplex(vector[i][0],vector[i][1]);
	}
	
	/**
	 * get real value at index
	 * @param i index
	 * @return value
	 * @see #get(int)
	 * @see #getIm(int)
	 */
	public double getRe(int i){
		return vector[i][0];
	}
	
	/**
	 * get real value at index
	 * @param i index
	 * @return value
	 * @see #get(int)
	 * @see #getRe(int)
	 */
	public double getIm(int i){
		return vector[i][0];
	}
	
	/**
	 * set value at index
	 * @param i index
	 * @param re real value
	 * @param im imaginary value
	 */
	public void set(int i, double re, double im){
		vector[i][0] = re;
		vector[i][1] = im;
	}
	
	/**
	 * set value at index
	 * @param i index
	 * @param a value
	 */
	public void set(int i, NumberComplex a){
		set(i, a.ℜ(), a.ℑ());
	}
	
	/**
	 * get x (first element)
	 * @return x value
	 */
	public NumberComplex x(){
		return n()>0 ? new NumberComplex(vector[0][0],vector[0][1]) : null;
	}
	
	/**
	 * get y (second element)
	 * @return y value
	 */
	public NumberComplex y(){
		return n()>1 ? new NumberComplex(vector[1][0],vector[1][1]) : null;
	}
	
	/**
	 * get z (third element)
	 * @return z value
	 */
	public NumberComplex z(){
		return n()>2 ? new NumberComplex(vector[2][0],vector[2][1]) : null;
	}
	
	/**
	 * Create vector with zeros
	 * @param n size
	 * @return zero vector
	 */
	public static VectorComplex zeros(int n){
		return new VectorComplex(n);
	}
	
	/**
	 * Create vector with ones
	 * @param n size
	 * @return one vector
	 */
	public static VectorComplex ones(int n){
		return fill(n, 1);
	}
	
	/**
	 * Create vector with given complex number
	 * @param n size
	 * @param s real number
	 * @return complex vector filled with real numbers
	 */
	public static VectorComplex fill(int n, double s){
		return fill(n, s, 0);
	}
	
	/**
	 * Create vector with given real and imaginary part
	 * @param n size
	 * @param r real
	 * @param i imaginary
	 * @return vector filled with complex numbers
	 */
	public static VectorComplex fill(int n, double r, double i){
		VectorComplex a = new VectorComplex(n);
		int j;
		for(j=0; j<n; j++){
			a.vector[j][0] = r;
			a.vector[j][0] = i;
		}
		return a;
	}
	
	/**
	 * Create vector with given complex number
	 * @param n size
	 * @param z complex number
	 * @return vector filled with complex numbers
	 */
	public static VectorComplex fill(int n, NumberComplex z){
		return fill(n, z.ℜ(), z.ℑ());
	}
	
	/**
	 * Create vector and fill it from a start value to an end value
	 * @param start
	 * @param end
	 * @return filled vector [start,start+1,...,end]
	 */
	public static VectorComplex fill(NumberComplex start, NumberComplex end){
		double deltaRe = end.ℜ()-start.ℜ();
		double deltaIm = end.ℑ()-start.ℑ();
		NumberComplex increment = new NumberComplex(
				deltaRe>1 ? 1 : deltaRe<-1 ? -1 : 0,
				deltaIm>1 ? 1 : deltaIm<-1 ? -1 : 0);
		return fill(start, increment, end);
	}
	
	/**
	 * Create vector and fill it from a start value and increments it to an end value
	 * @param start
	 * @param increment
	 * @param end
	 * @return filled vector [start,start+increment,start+2*increment,...,end]
	 */
	public static VectorComplex fill(NumberComplex start, NumberComplex increment, NumberComplex end){
		double deltaRe = end.ℜ()-start.ℜ();
		double deltaIm = end.ℑ()-start.ℑ();
		int nRe = increment.ℜ()!=0?(int)(deltaRe/increment.ℜ())+1:0;
		int nIm = increment.ℑ()!=0?(int)(deltaIm/increment.ℑ())+1:0;
		int n = nRe > nIm ? nRe : nIm;
		if(n==0||(deltaRe>=0&&increment.ℜ()<0&&deltaIm>=0&&increment.ℑ()<0)||
				(deltaRe<0&&increment.ℜ()>=0&&deltaIm<0&&increment.ℑ()>=0)||
				(increment.ℜ()==0&&increment.ℑ()==0)) return new VectorComplex(start);
		VectorComplex a = new VectorComplex(n);
		int i;
		for(i=0; i<n; i++){
			a.vector[i][0] = start.ℜ() + i*increment.ℜ();
			a.vector[i][1] = start.ℑ() + i*increment.ℑ();
		}
		return a;
	}
	
	/**
	 * Create zero vector and fill it with given values at given indices
	 * @param n
	 * @param indeces
	 * @param a value vector
	 * @return filled vector
	 */
	public static VectorComplex fill(int n, int[] indeces, VectorComplex a){
		VectorComplex c = new VectorComplex(n);
		for(int i=0; i<indeces.length; i++){
			c.vector[indeces[i]-1][0] = a.vector[i][0];
			c.vector[indeces[i]-1][1] = a.vector[i][1];
		}
		return c;
	}
	
	/**
	 * Fill vector with given values at given indices
	 * @param indices
	 * @param a value vector
	 */
	public void fill(int[] indices, VectorComplex a){
		for(int i=0; i<indices.length; i++){
			vector[indices[i]][0] = a.vector[i][0];
			vector[indices[i]][1] = a.vector[i][1];
		}
	}
	
	/**
	 * @return dimension
	 */
	public int n(){
		return vector.length;
	}
	
	/**
	 * @return length or magnitude or norm 
	 */
	public double norm(){
		int i;
		double x=0;
		for(i=0; i<vector.length; i++)
			x += vector[i][0]*vector[i][0] + vector[i][1]*vector[i][1];
		return Math.sqrt(x);
	}
	
	/**
	 * add a Vector.
	 * @param a vector to add
	 * @return added Vector
	 */
	public VectorComplex plus(VectorComplex a){
		int i;
		VectorComplex c = new VectorComplex(n());
		for(i=0; i<n(); i++){
			c.vector[i][0] = vector[i][0] + a.vector[i][0];
			c.vector[i][1] = vector[i][1] + a.vector[i][1];
		}
		return c;
	}
	
	/**
	 * add a Vector.
	 * @param s as vector to add
	 * @return added Vector
	 */
	public VectorComplex plus(NumberComplex s){
		return plus(VectorComplex.fill(n(), s));
	}
	
	/**
	 * subtract a Vector.
	 * @param a vector to subtract
	 * @return subtracted Vector
	 */
	public VectorComplex minus(VectorComplex a){
		return plus(a.timesE(VectorComplex.fill(n(),new NumberComplex(-1,-1))));
	}
	
	/**
	 * Subtract a Vector.
	 * @param s as vector to subtract
	 * @return subtracted Vector
	 */
	public VectorComplex minus(NumberComplex s){
		return plus(VectorComplex.fill(n(), s.times(new NumberComplex(-1,-1))));
	}
    
    /**
     * Multiplicate the vector element wise.
     * @param a vector
     * @return element-wise multiplicated vector
     */
    public VectorComplex timesE(VectorComplex a){
    	int i;
    	VectorComplex c = new VectorComplex(n());
    	for(i=0; i<n(); i++)
    		c.set(i, get(i).times(a.get(i)));
    	return c;
    }
    
    /**
     * Divide the vector element wise
     * @param a vector
     * @return element-wise divided vector
     */
    public VectorComplex overE(VectorComplex a){
    	int i;
    	VectorComplex c = new VectorComplex(n());
    	for(i=0; i<n(); i++)
    		c.set(i, get(i).over(a.get(i)));
    	return c;
    }
	
	/**
	 * revolve vector 90°. max 2 dimension vector.
	 * @return 90° revolved vector
	 */
	public VectorComplex orthogonal(){
		//TODO: ???
		return new VectorComplex(get(1),get(0));
	}
	
    /**
     * Creates a sub vector only with chosen rows
     * @param indices rows
     * @return the sub vector
     */
    public VectorComplex subVector(int[] indices){
    	int n = indices.length;
    	VectorComplex red = new VectorComplex(n);
		for(int i=0; i<n; i++){
			red.vector[i][0] = vector[indices[i]-1][0];
			red.vector[i][1] = vector[indices[i]-1][1];
		}
		return new VectorComplex(red);
    }
	
	/**
	 * Find value in vector
	 * @return index
	 */
	public int find(NumberComplex a){
		int i;
		for(i=0; i<n(); i++)
			if(a == get(i)) return i;
		return -1;
	}
	
	public boolean isZero(){
		int i;
		boolean result = true;
		for(i=0; i<n(); i++){
			if(vector[i][0] > 0){
				result = false;
				break;
			}
			if(vector[i][1] > 0){
				result = false;
				break;
			}
		}
		return result;
	}
	
    @Override
	public String toString(){
    	int i;
    	String output = "";
    	if(vector!=null){
            for(i=0; i<n(); i++) {
            	vector[i] = (vector[i][0] <= 0.0 && vector[i][0]>-1E-10) ? null : vector[i];
                output += String.format("% 10.4g % 10.4gi \n", vector[i][0], vector[i][1]);
            }
    	}
        return output;
    }
    
    /**
     * Transpose (horizontal)
     * @return string of vector in transposed form
     */
	public String toStringT(){
    	int i;
    	String output = "";
    	if(vector!=null){
            for(i=0; i<n(); i++) {
            	vector[i] = (vector[i][0] <= 0.0 && vector[i][0]>-1E-10) ? null : vector[i];
                output += String.format("% 10.4g % 10.4gi ", vector[i][0], vector[i][1]);
            }
            output += "\n";
    	}
        return output;
    }
	
	/**
	 * Transpose (horizontal)
	 */
	public void toConsoleT(){
		System.out.println(toStringT());
	}

    /**
     * test client
     * @param args
     */
    public static void main(String[] args) { 
		VectorComplex a = new VectorComplex(new NumberComplex(5), new NumberComplex(1), new NumberComplex(1));
		System.out.println(a.toString());
		VectorComplex.fill(new NumberComplex(2.), new NumberComplex(2.5)).println();
		VectorComplex.fill(new NumberComplex(2.), new NumberComplex(8)).println();
		VectorComplex.fill(new NumberComplex(8.), new NumberComplex(2)).println();
		VectorComplex.fill(new NumberComplex(2.), new NumberComplex(2), new NumberComplex(8)).println();
		VectorComplex.fill(new NumberComplex(8.), new NumberComplex(-2), new NumberComplex(2)).toConsoleT();
		System.out.println("a isNull? : " + a.isZero());
		System.out.println("a-a isNull? : " + a.minus(a).isZero());
   }
}
