public class Vector2D extends CoordinatePair {
    
    public Vector2D() { super(); }
    
    public Vector2D(double X, double Y) { super(X, Y); }

    public Vector2D(Vector2D template) { super((CoordinatePair) template); }
    
    public boolean isNull() { return x == 0.0 && y == 0.0; }	

    public void add(Vector2D v) { x += v.x; y += v.y; }

    public static Vector2D add(Vector2D a, Vector2D b) {
	return new Vector2D(a.x + b.x, a.y + b.y);
    }

    public void subtract(Vector2D v) { x -= v.x; y-= v.y; }

    public static Vector2D difference(Vector2D a, Vector2D b) {
	Vector2D value = new Vector2D();
	value.x = a.x - b.x;
	value.y = a.y - b.y;
	return value;
    }

    public void scale(double value) { x *= value; y *= value; }
    
    public void rotate(double alpha) {
	double cosA = Math.cos(alpha);
	double sinA = Math.sin(alpha);
	
	double newx = x * cosA - y * sinA;
	y           = x * sinA + y * cosA;
	x = newx;
    }
    
    public double dot(Vector2D v) { return x*v.x + y*v.y; }

    public double norm() { return x*x + y*y; }

    public double length() { return Math.sqrt(norm()); }

    // Null vector should throw approriate exception
    public double angle() {
	double alpha = 0.0;
	
	if(x != 0.0) alpha = Math.atan(y/x);
	else alpha = (y == 0.0) ? 0.0 : Math.PI/2.0; 
	
	// Get the proper branching
	if(x < 0.0) {
	    if(alpha < 0.0) alpha += Math.PI;
	    else alpha += Math.PI;
	}
	return alpha;
    }
	
    public void normalize() throws IllegalStateException { 
	double l=norm(); 
	if(l == 0.0) throw new IllegalStateException("Null Vector");
	x /= l; y /= l; 
    }

    public void invert() { x *= -1; y *= -1; }	
    
    public static Vector2D sum(Vector2D[] v) {
	Vector2D value = new Vector2D();

	for(int i=0; i<v.length; i++) {
	    value.x += v[i].x;
	    value.y += v[i].y;
	}

	return value;
    }

    public static Vector2D average(Vector2D a, Vector2D b) {
	Vector2D value = add(a, b);
	value.x /= 2; value.y /= 2;
	return value;
    }

    public static Vector2D average(Vector2D[] v) {
	Vector2D value = sum(v);
	value.x /= v.length; value.y /= v.length;
	return value;
    }	

    public PolarVector2D polar() {
	PolarVector2D p = new PolarVector2D();
	p.x = length();
	p.y = angle();	
	return p;
    }


    public void math(char op, Vector2D v) throws IllegalArgumentException {
	switch(op) {
	case '+': x += v.x; y += v.y; break;
	case '-': x -= v.x; y -= v.y; break;
	default: throw new IllegalArgumentException("Illegal Operation");
	}
    }


    public static Vector2D math(Vector2D a, char op, Vector2D b) throws IllegalArgumentException {
	Vector2D result = new Vector2D();

	switch(op) {
	case '+': result.x = a.x + b.x; result.y = a.y + b.y; break;
	case '-': result.x = a.x - b.x; result.y = a.y - b.y; break;
	default: throw new IllegalArgumentException("Illegal Operation");
	}

	return result;
    }

    public void math(char op, double b) throws IllegalArgumentException {
	switch(op) {
	case '*': x *= b; y *= b; break;
	case '/': x /= b; y /= b; break;
	case '^': x = Math.pow(x, b); y = Math.pow(y, b); break;	    
	default: throw new IllegalArgumentException("Illegal Operation");
	}
    }


    public static Vector2D math(Vector2D a, char op, double b) throws IllegalArgumentException {
	Vector2D result = new Vector2D();

	switch(op) {
	case '*': result.x = a.x * b; result.y = a.y * b; break;
	case '/': result.x = a.x / b; result.y = a.y / b; break;
	case '^': result.x = Math.pow(a.x, b); result.y = Math.pow(a.y, b); break;	    
	default: throw new IllegalArgumentException("Illegal Operation");
	}

	return result;
    }
    
    public void zero() { x = 0.0; y = 0.0; }

    public String toString() { return "(" + x + "," + y + ")"; }		
}
