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 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);
    }
    double dot(Vector3D other) {
        double ret = 0.;
        for(int i = 0; i<4; ++i) 
            ret += vec_[i] * other.vec_[i];
        return ret;
    }
    private double tempsize() {
        return Math.sqrt(temp_.x() + temp_.y() + temp_.z());
    }
    double dist(Vector3D other) {
        subtract(other,temp2_);
        temp2_.times(temp2_,temp_);
        return tempsize();
    }
    double magn() {
        times(this,temp_);
        return tempsize();
    }

    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();
    }
}