/** Represents a vector in three-dimensional space. */
public class Vector {
	/** The zero vector, with zero in all components. */
	public static final Vector ZERO = new Vector(0.0, 0.0, 0.0);

	private double x;
	private double y;
	private double z;

	private Vector(double x, double y, double z) {
		this.x = x;
		this.y = y;
		this.z = z;
	}

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

	/** Returns the x-component of this vector. */
	public double getX() { return x; }
	/** Returns the y-component of this vector. */
	public double getY() { return y; }
	/** Returns the z-component of this vector. */
	public double getZ() { return z; }

	/** Returns the square of the length of this vector. */
	public double getLengthSquared() {
		return x * x + y * y + z * z;
	}

	/** Returns the length of this vector. */
	public double getLength() {
		return Math.sqrt(getLengthSquared());
	}

	/** Returns a vector in the same direction as this one, but
	 * whose length is one. */
	public Vector normalize() {
		double len2 = getLengthSquared();
		if(len2 == 0.0 || len2 == 1.0) return this;
		return this.scale(1.0 / Math.sqrt(len2));
	}

	/** Returns the dot-product of this vector and the parameter
	 * vector. */
	public double dot(Vector other) {
		return this.x * other.x + this.y * other.y + this.z * other.z;
	}

	/** Returns the dot-product of this vector and the vector
	 * from the origin to the parameter point. */
	public double dotPoint(Point other) {
		return this.x * other.getX() + this.y * other.getY() + this.z * other.getZ();
	}

	/** Returns the cross-product of this vector and the parameter
	 * vector. */
	public Vector cross(Vector other) {
		return Vector.create(
				this.y * other.z - this.z * other.y,
				this.z * other.x - this.x * other.z,
				this.x * other.y - this.y * other.x);
	}

	/** Returns the sum of this vector and the parameter vector. */
	public Vector add(Vector other) {
		return Vector.create(this.x + other.x, this.y + other.y, this.z + other.z);
	}

	/** Returns the difference of this vector and the parameter vector. */
	public Vector subtract(Vector other) {
		return Vector.create(this.x - other.x, this.y - other.y, this.z - other.z);
	}

	/** Returns the vector resulting from projecting this vector
	 * onto the given vector's direction. */
	public Vector projectOnto(Vector other) {
		double x = this.dot(other);
		return other.scale(x);
	}

	/** Returns a copy of this vector scaled by the given
	 * vector. */
	public Vector scale(double scalar) {
		if(scalar == 1.0) return this;
		return Vector.create(scalar * this.x, scalar * this.y, scalar * this.z);
	}

	/** Creates a vector with the given components. */
	public static Vector create(double x, double y, double z) {
		return new Vector(x, y, z);
	}
}
