"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/** A four dimensional vector */
var Vector4 = /** @class */ (function () {
    function Vector4(x, y, z, w) {
        this.x = x;
        this.y = y;
        this.z = z;
        this.w = w;
    }
    //#region create
    /**
     * Factory acting as constructor for Vector4
     * @param x
     * @param y
     * @param z
     * @param w
     */
    Vector4.create = function (x, y, z, w) {
        if (x === void 0) { x = 0; }
        if (y === void 0) { y = 0; }
        if (z === void 0) { z = 0; }
        if (w === void 0) { w = 0; }
        return new Vector4(x, y, z, w);
    };
    //#endregion create
    //#region Copy/Clone
    /**
     * Clone function for Vector4
     * @param input
     */
    Vector4.clone = function (input) {
        return { x: input.x, y: input.y, z: input.z, w: input.w };
    };
    /**
     * Copy Vector4 vector
     * @param input
     */
    Vector4.copy = function (from, to) {
        to.x = from.x;
        to.y = from.y;
        to.z = from.z;
        to.w = from.w;
    };
    //#endregion Copy/Clone
    //#region IsEqual (compare)
    /**
     * Compare two Vector4's on equality (with optional deviation)
     * @param first
     * @param second
     * @param deviation
     */
    Vector4.isEqual = function (first, second, deviation) {
        if (deviation === void 0) { deviation = 0; }
        var diff = Vector4.sub(first, second);
        return (Math.abs(diff.x) <= deviation &&
            Math.abs(diff.y) <= deviation &&
            Math.abs(diff.z) <= deviation &&
            Math.abs(diff.w) <= deviation);
    };
    //#endregion IsEqual (compare)
    //#region Absolute vector
    /**
     * Creates a new Vector4 with absolute values of components
     * @param input
     */
    Vector4.absoluteComponents = function (input) {
        return {
            x: Math.abs(input.x),
            y: Math.abs(input.y),
            z: Math.abs(input.z),
            w: Math.abs(input.w),
        };
    };
    //#endregion Absolute vector
    //#region Normalize vector
    /**
     * Returns normalized Vector4
     * @param vector
     */
    Vector4.normalize = function (vector, length) {
        if (length === void 0) { length = 1.0; }
        var magnitude = Vector4.magnitude(vector);
        if (magnitude === 0) {
            return undefined;
        }
        return Vector4.scalarMultiply(length / magnitude, vector);
    };
    /**
     * Normalizes Vector4 vector
     * @param vector
     */
    Vector4.normalizeInPlace = function (vector, length) {
        if (length === void 0) { length = 1.0; }
        var magnitude = Vector4.magnitude(vector);
        if (magnitude === 0) {
            return;
        }
        Vector4.scalarMultiplyInPlace(length / magnitude, vector);
    };
    //#endregion Normalize vector
    //#region Simple Algebra (addition + substraction)
    /**
     * Returns sum of given Vector4s
     * @param vectors
     */
    Vector4.add = function () {
        var vectors = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            vectors[_i] = arguments[_i];
        }
        var result = Vector4.create();
        var len = vectors.length;
        for (var idx = 0; idx < len; idx++) {
            var vector = vectors[idx];
            result.x += vector.x;
            result.y += vector.y;
            result.z += vector.z;
            result.w += vector.w;
        }
        return result;
    };
    /**
     * Sums up given Vector4s and saves it into the first
     * @param vectors
     */
    Vector4.addOnFirst = function () {
        var vectors = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            vectors[_i] = arguments[_i];
        }
        var len = vectors.length;
        if (len > 0) {
            var result = vectors[0];
            var vector = void 0;
            for (var idx = 1; idx < len; idx++) {
                vector = vectors[idx];
                result.x += vector.x;
                result.y += vector.y;
                result.z += vector.z;
                result.w += vector.w;
            }
            return result;
        }
        return undefined;
    };
    /**
     * Subtracts second from first Vector4
     * @param first
     * @param second
     */
    Vector4.sub = function (first, second) {
        return {
            x: first.x - second.x,
            y: first.y - second.y,
            z: first.z - second.z,
            w: first.w - second.w,
        };
    };
    /**
     * Subtracts given Vector4s from first and saves it into the first
     * @param vectors
     */
    Vector4.subOnFirst = function () {
        var vectors = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            vectors[_i] = arguments[_i];
        }
        var len = vectors.length;
        if (len > 0) {
            var result = vectors[0];
            var vector = void 0;
            for (var idx = 1; idx < len; idx++) {
                vector = vectors[idx];
                result.x -= vector.x;
                result.y -= vector.y;
                result.z -= vector.z;
                result.w -= vector.w;
            }
            return result;
        }
        return undefined;
    };
    //#endregion Simple Algebra
    //#region Products (inner/dot + cross3 + matrix4)
    /**
     * Returns inner product of first and second Vector4
     * @param first
     * @param second
     */
    Vector4.innerProduct = function (first, second) {
        return (first.x * second.x +
            first.y * second.y +
            first.z * second.z +
            first.w * second.w);
    };
    /**
     * Returns product of vector and matrix as new vec4
     * @param vector vec4
     * @param matrix Matrix4
     */
    //prettier-ignore
    Vector4.multiplyWithMatrix4 = function (f, matrix) {
        var me = matrix.elements;
        var result = Vector4.create(0, 0, 0, 1);
        // tslint:disable: one-variable-per-declaration
        var a11 = me[0][0], a12 = me[0][1], a13 = me[0][2], a14 = me[0][3];
        var a21 = me[1][0], a22 = me[1][1], a23 = me[1][2], a24 = me[1][3];
        var a31 = me[2][0], a32 = me[2][1], a33 = me[2][2], a34 = me[2][3];
        var a41 = me[3][0], a42 = me[3][1], a43 = me[3][2], a44 = me[3][3];
        result.x = a11 * f.x + a12 * f.y + a13 * f.z + a14 * f.w;
        result.y = a21 * f.x + a22 * f.y + a23 * f.z + a24 * f.w;
        result.z = a31 * f.x + a32 * f.y + a33 * f.z + a34 * f.w;
        result.w = a41 * f.x + a42 * f.y + a43 * f.z + a44 * f.w;
        return result;
    };
    //#endregion Products (inner + cross + matrix)
    //#region Scalar (addition + multiplication)
    /**
     * Returns Vector4 with offset
     * @param offset
     * @param vector
     */
    Vector4.scalarAdd = function (offset, vector) {
        return {
            x: offset + vector.x,
            y: offset + vector.y,
            z: offset + vector.z,
            w: offset + vector.w,
        };
    };
    /**
     * Adds offset on Vector4 vector
     * @param offset
     * @param vector
     */
    Vector4.scalarAddInPlace = function (offset, vector) {
        vector.x += offset;
        vector.y += offset;
        vector.z += offset;
        vector.w += offset;
    };
    /**
     * Returns scaled Vector4
     * @param scalar
     * @param vector
     */
    Vector4.scalarMultiply = function (scalar, vector) {
        return {
            x: scalar * vector.x,
            y: scalar * vector.y,
            z: scalar * vector.z,
            w: scalar * vector.w,
        };
    };
    /**
     * Scales Vector4 vector
     * @param scalar
     * @param vector
     */
    Vector4.scalarMultiplyInPlace = function (scalar, vector) {
        vector.x *= scalar;
        vector.y *= scalar;
        vector.z *= scalar;
        vector.w *= scalar;
    };
    //#endregion Scalar
    //#region Magnitude (length)
    /**
     * Returns magnitude (length) of Vector4
     * @param vector
     */
    Vector4.magnitude = function (vector) {
        return Math.sqrt(vector.x * vector.x +
            vector.y * vector.y +
            vector.z * vector.z +
            vector.w * vector.w);
    };
    /**
     * Returns squared magnitude of Vector4
     * @param vector
     */
    Vector4.magnitudeSquared = function (vector) {
        return (vector.x * vector.x +
            vector.y * vector.y +
            vector.z * vector.z +
            vector.w * vector.w);
    };
    //#endregion Magnitude (length)
    //#region Distance
    /**
     * Returns distance between first and second Vector4
     * @param first
     * @param second
     */
    Vector4.distance = function (first, second) {
        var x = first.x - second.x;
        var y = first.y - second.y;
        var z = first.z - second.z;
        var w = first.w - second.w;
        return Math.sqrt(x * x + y * y + z * z + w * w);
    };
    //#endregion Distance
    //#region Min/Max
    /**
     * Returns first parameter with the maximum entries from other Vector4 parameters
     * @param input
     * @oaram vectors
     */
    Vector4.maximizeInPlace = function (input) {
        var vectors = [];
        for (var _i = 1; _i < arguments.length; _i++) {
            vectors[_i - 1] = arguments[_i];
        }
        for (var i = 0, il = vectors.length; i < il; ++i) {
            var v = vectors[i];
            input.x = Math.max(input.x, v.x);
            input.y = Math.max(input.y, v.y);
            input.z = Math.max(input.z, v.z);
            input.w = Math.max(input.w, v.w);
        }
    };
    /**
     * Returns first parameter with the minimum entries from other Vector4 parameters
     * @param input
     * @oaram vectors
     */
    Vector4.minimizeInPlace = function (input) {
        var vectors = [];
        for (var _i = 1; _i < arguments.length; _i++) {
            vectors[_i - 1] = arguments[_i];
        }
        for (var i = 0, il = vectors.length; i < il; ++i) {
            var v = vectors[i];
            input.x = Math.min(input.x, v.x);
            input.y = Math.min(input.y, v.y);
            input.z = Math.min(input.z, v.z);
            input.w = Math.min(input.w, v.w);
        }
    };
    //#endregion Min/Max
    //#region Reverse (Negation)
    /**
     * Returns a Vector4 with negated components
     * @param vector
     */
    Vector4.reverse = function (vector) {
        return {
            x: -vector.x,
            y: -vector.y,
            z: -vector.z,
            w: -vector.w,
        };
    };
    /**
     * Negates components of Vector4 vector
     * @param vector
     */
    Vector4.reverseInPlace = function (vector) {
        vector.x = -vector.x;
        vector.y = -vector.y;
        vector.z = -vector.z;
        vector.w = -vector.w;
        return vector;
    };
    //#endregion Reverse (Negation)
    //#region Convenience (Stringify + HasInvalidEntries)
    /** Convenience function to stringify Vector4 */
    Vector4.stringify = function (v, precision) {
        if (precision === void 0) { precision = 3; }
        return ("{x: " +
            v.x.toFixed(precision) +
            ", y: " +
            v.y.toFixed(precision) +
            ", z: " +
            v.z.toFixed(precision) +
            ", w: " +
            v.w.toFixed(precision) +
            "}");
    };
    /** Convenience function to check if Vector4 has invalid entries (no Not-a-Number, on infinite values) */
    Vector4.hasInvalidEntries = function (v) {
        var product = v.x * v.y * v.z * v.w;
        return isNaN(product) || !isFinite(product);
    };
    return Vector4;
}());
exports.Vector4 = Vector4;
