public class Vector3D {
    private double vec_[] = new double[4];
    static private Vector3D temp_ = new Vector3D(),temp2_ = new Vector3D();
    
    Vector3D(double x, double y, double z, double w) {
        vec_[0] = x;
        vec_[1] = y;
        vec_[2] = z;
        vec_[3] = w;
    }
    Vector3D(double x, double y, double z) {
        this(x,y,z,1.);
    }
    Vector3D() {
        this(.0,0.,.0,1.);
    }
    Vector3D(Vector3D other) {
        copy(other);
    }
    double[] vector() {
        return vec_;
    }
    double get(int i) {
        return vec_[i];
    }
    double set(int i, double v) {
        return vec_[i] = v;
    }
    double x() {
        return vec_[0];
    }
    double y() {
        return vec_[1];
    }
    double z() {
        return vec_[2];
    }
    double w() {
        return vec_[3];
    }
	Vector3D zero() {
		for(int i=0; i<4; ++i)
			vec_[i] = 0.;
		return this;
	}
    Vector3D copy(Vector3D other) {
        for(int i = 0; i<4; ++i) 
            vec_[i] = other.vec_[i];
        return this;
    }
    Vector3D reverse() {
        for(int i = 0; i<4; ++i) 
            vec_[i] = -vec_[i];
        return this;
    }
    Vector3D add(Vector3D other, Vector3D out) {
        for(int i = 0; i<4; ++i) 
            out.vec_[i] = vec_[i] + other.vec_[i];
        return out;
    }
    Vector3D add(Vector3D other) {
        temp_.copy(this);
        return temp_.add(other,this);
    }
    Vector3D subtract(Vector3D other, Vector3D out) {
        for(int i = 0; i<4; ++i) 
            out.vec_[i] = vec_[i] - other.vec_[i];
        return out;
    }
    Vector3D subtract(Vector3D other) {
        temp_.copy(this);
        return temp_.subtract(other,this);
    }
    Vector3D times(Vector3D other, Vector3D out) {
        for(int i = 0; i<4; ++i) 
            out.vec_[i] = vec_[i] * other.vec_[i];
        return out;
    }
    Vector3D lerp(Vector3D other, double t, Vector3D out) {
        // maybe the syntax should be 
        // out.add(this,temp_.subtract(other,this).times(t));
        // dest always the object
        // i guess either is "natural" in some way
        return other.subtract(this,temp_).times(t).add(this,out);
        /*
        System.out.println("lerp "+this+" ("+t+") "+other+":");
        other.subtract(this,temp_);
        System.out.println("- "+temp_);
        temp_.times(t);
        System.out.println("* "+temp_);
        temp_.add(this,out); 
        System.out.println("+ "+out);
        return out;
        */
    }
    Vector3D times(double a,Vector3D out) {
        for(int i = 0; i<4; ++i) 
            out.vec_[i] = vec_[i] * a;
        return out;
    }
    Vector3D times(double a) {
        return times(a,this);
    }
    Vector3D divide(double a) {
        return times(1./a);
    }
	Vector3D divide(double a,Vector3D out) {
		return times(1./a,out);
	}
    double dot(Vector3D other) {
        double ret = 0.;
        for(int i = 0; i<4; ++i) 
            ret += vec_[i] * other.vec_[i];
        return ret;
    }
	double sum() {
		return vec_[0]+vec_[1]+vec_[2]+vec_[3];
	}
    private double tempsize() {
        return Math.sqrt(temp_.sum());
    }
    double dist(Vector3D other) {
        subtract(other,temp2_);
        return Math.sqrt(temp_.dot(temp_));
	}
    double magn() {
        return Math.sqrt(dot(this));
    }
	Vector3D normalize() {
		divide(magn());
		return this;
	}

    public String toString() {
        StringBuffer buf = new StringBuffer(100);
        buf.append("{");
        for(int i = 0; i<4; ++i) {
            buf.append(vec_[i]);
            if(i!=3)
                buf.append(",");
        }
        buf.append("}");
        return buf.toString();
    }
	/* ccw test: CCW, CW, or co-linear, adapted from graphviz from ? */
	enum Wise {CCW, CW, ON};
	static Wise wiseness(Vector3D a, Vector3D b, Vector3D c) {
		double d = (a.y() - b.y())*(c.x() - b.x()) -
			   (c.y() - b.y())*(a.x() - b.x());
		return (d > 1e-8) ? Wise.CCW :((d < -1e-8) ? Wise.CW : Wise.ON);
	}
}