package com.flowpowered.math.vector;

import java.io.Serializable;
import java.lang.Override;
import java.util.Random;

import com.flowpowered.math.GenericMath;
import com.flowpowered.math.HashFunctions;
import com.flowpowered.math.TrigMath;

public class Vector2f implements Vectorf, Comparable<Vector2f>, Serializable, Cloneable {
    private static final long serialVersionUID = 1;
    public static final Vector2f ZERO = new Vector2f(0, 0);
    public static final Vector2f UNIT_X = new Vector2f(1, 0);
    public static final Vector2f UNIT_Y = new Vector2f(0, 1);
    public static final Vector2f ONE = new Vector2f(1, 1);
    private final float x;
    private final float y;
    private transient volatile boolean hashed = false;
    private transient volatile int hashCode = 0;

    public Vector2f() {
        this(0, 0);
    }

    public Vector2f(Vector2f v) {
        this(v.x, v.y);
    }

    public Vector2f(Vector3f v) {
        this(v.getX(), v.getY());
    }

    public Vector2f(Vector4f v) {
        this(v.getX(), v.getY());
    }

    public Vector2f(VectorNf v) {
        this(v.get(0), v.get(1));
    }

    public Vector2f(double x, double y) {
        this((float) x, (float) y);
    }

    public Vector2f(float x, float y) {
        this.x = x;
        this.y = y;
    }

    public float getX() {
        return x;
    }

    public float getY() {
        return y;
    }

    public int getFloorX() {
        return GenericMath.floor(x);
    }

    public int getFloorY() {
        return GenericMath.floor(y);
    }

    public Vector2f add(Vector2f v) {
        return add(v.x, v.y);
    }

    public Vector2f add(double x, double y) {
        return add((float) x, (float) y);
    }

    public Vector2f add(float x, float y) {
        return new Vector2f(this.x + x, this.y + y);
    }

    public Vector2f sub(Vector2f v) {
        return sub(v.x, v.y);
    }

    public Vector2f sub(double x, double y) {
        return sub((float) x, (float) y);
    }

    public Vector2f sub(float x, float y) {
        return new Vector2f(this.x - x, this.y - y);
    }

    public Vector2f mul(double a) {
        return mul((float) a);
    }

    @Override
    public Vector2f mul(float a) {
        return mul(a, a);
    }

    public Vector2f mul(Vector2f v) {
        return mul(v.x, v.y);
    }

    public Vector2f mul(double x, double y) {
        return mul((float) x, (float) y);
    }

    public Vector2f mul(float x, float y) {
        return new Vector2f(this.x * x, this.y * y);
    }

    public Vector2f div(double a) {
        return div((float) a);
    }

    @Override
    public Vector2f div(float a) {
        return div(a, a);
    }

    public Vector2f div(Vector2f v) {
        return div(v.x, v.y);
    }

    public Vector2f div(double x, double y) {
        return div((float) x, (float) y);
    }

    public Vector2f div(float x, float y) {
        return new Vector2f(this.x / x, this.y / y);
    }

    public float dot(Vector2f v) {
        return dot(v.x, v.y);
    }

    public float dot(double x, double y) {
        return dot((float) x, (float) y);
    }

    public float dot(float x, float y) {
        return this.x * x + this.y * y;
    }

    public Vector2f project(Vector2f v) {
        return project(v.x, v.y);
    }

    public Vector2f project(double x, double y) {
        return project((float) x, (float) y);
    }

    public Vector2f project(float x, float y) {
        final float lengthSquared = x * x + y * y;
        if (Math.abs(lengthSquared) < GenericMath.FLT_EPSILON) {
            throw new ArithmeticException("Cannot project onto the zero vector");
        }
        final float a = dot(x, y) / lengthSquared;
        return new Vector2f(a * x, a * y);
    }

    public Vector2f pow(double pow) {
        return pow((float) pow);
    }

    @Override
    public Vector2f pow(float power) {
        return new Vector2f(Math.pow(x, power), Math.pow(y, power));
    }

    @Override
    public Vector2f ceil() {
        return new Vector2f(Math.ceil(x), Math.ceil(y));
    }

    @Override
    public Vector2f floor() {
        return new Vector2f(GenericMath.floor(x), GenericMath.floor(y));
    }

    @Override
    public Vector2f round() {
        return new Vector2f(Math.round(x), Math.round(y));
    }

    @Override
    public Vector2f abs() {
        return new Vector2f(Math.abs(x), Math.abs(y));
    }

    @Override
    public Vector2f negate() {
        return new Vector2f(-x, -y);
    }

    public Vector2f min(Vector2f v) {
        return min(v.x, v.y);
    }

    public Vector2f min(double x, double y) {
        return min((float) x, (float) y);
    }

    public Vector2f min(float x, float y) {
        return new Vector2f(Math.min(this.x, x), Math.min(this.y, y));
    }

    public Vector2f max(Vector2f v) {
        return max(v.x, v.y);
    }

    public Vector2f max(double x, double y) {
        return max((float) x, (float) y);
    }

    public Vector2f max(float x, float y) {
        return new Vector2f(Math.max(this.x, x), Math.max(this.y, y));
    }

    public float distanceSquared(Vector2f v) {
        return distanceSquared(v.x, v.y);
    }

    public float distanceSquared(double x, double y) {
        return distanceSquared((float) x, (float) y);
    }

    public float distanceSquared(float x, float y) {
        final float dx = this.x - x;
        final float dy = this.y - y;
        return dx * dx + dy * dy;
    }

    public float distance(Vector2f v) {
        return distance(v.x, v.y);
    }

    public float distance(double x, double y) {
        return distance((float) x, (float) y);
    }

    public float distance(float x, float y) {
        return (float) Math.sqrt(distanceSquared(x, y));
    }

    @Override
    public float lengthSquared() {
        return x * x + y * y;
    }

    @Override
    public float length() {
        return (float) Math.sqrt(lengthSquared());
    }

    @Override
    public Vector2f normalize() {
        final float length = length();
        if (Math.abs(length) < GenericMath.FLT_EPSILON) {
            throw new ArithmeticException("Cannot normalize the zero vector");
        }
        return new Vector2f(x / length, y / length);
    }

    /**
     * Return the axis with the minimal value.
     *
     * @return {@link int} axis with minimal value
     */
    @Override
    public int getMinAxis() {
        return x < y ? 0 : 1;
    }

    /**
     * Return the axis with the maximum value.
     *
     * @return {@link int} axis with maximum value
     */
    @Override
    public int getMaxAxis() {
        return x > y ? 0 : 1;
    }

    public Vector3f toVector3() {
        return toVector3(0);
    }

    public Vector3f toVector3(double z) {
        return toVector3((float) z);
    }

    public Vector3f toVector3(float z) {
        return new Vector3f(this, z);
    }

    public Vector4f toVector4() {
        return toVector4(0, 0);
    }

    public Vector4f toVector4(double z, double w) {
        return toVector4((float) z, (float) w);
    }

    public Vector4f toVector4(float z, float w) {
        return new Vector4f(this, z, w);
    }

    public VectorNf toVectorN() {
        return new VectorNf(this);
    }

    @Override
    public float[] toArray() {
        return new float[]{x, y};
    }

    @Override
    public Vector2i toInt() {
        return new Vector2i(x, y);
    }

    @Override
    public Vector2l toLong() {
        return new Vector2l(x, y);
    }

    @Override
    public Vector2f toFloat() {
        return new Vector2f(x, y);
    }

    @Override
    public Vector2d toDouble() {
        return new Vector2d(x, y);
    }

    @Override
    public int compareTo(Vector2f v) {
        return (int) Math.signum(lengthSquared() - v.lengthSquared());
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof Vector2f)) {
            return false;
        }
        final Vector2f vector2 = (Vector2f) o;
        if (Float.compare(vector2.x, x) != 0) {
            return false;
        }
        if (Float.compare(vector2.y, y) != 0) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        if (!hashed) {
            final int result = (x != +0.0f ? HashFunctions.hash(x) : 0);
            hashCode = 31 * result + (y != +0.0f ? HashFunctions.hash(y) : 0);
            hashed = true;
        }
        return hashCode;
    }

    @Override
    public Vector2f clone() {
        return new Vector2f(this);
    }

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

    public static Vector2f from(float n) {
         return n == 0 ? ZERO : new Vector2f(n, n);
    }

    public static Vector2f from(float x, float y) {
         return x == 0 && y == 0 ? ZERO : new Vector2f(x, y);
    }

    /**
     * Gets the direction vector of a random angle using the random specified.
     *
     * @param random to use
     * @return the random direction vector
     */
    public static Vector2f createRandomDirection(Random random) {
        return createDirectionRad(random.nextFloat() * (float) TrigMath.TWO_PI);
    }

    /**
     * Gets the direction vector of a certain angle in degrees.
     *
     * @param angle in degrees
     * @return the direction vector
     */
    public static Vector2f createDirectionDeg(double angle) {
        return createDirectionDeg((float) angle);
    }

    /**
     * Gets the direction vector of a certain angle in degrees.
     *
     * @param angle in degrees
     * @return the direction vector
     */
    public static Vector2f createDirectionDeg(float angle) {
        return createDirectionRad((float) Math.toRadians(angle));
    }

    /**
     * Gets the direction vector of a certain angle in radians.
     *
     * @param angle in radians
     * @return the direction vector
     */
    public static Vector2f createDirectionRad(double angle) {
        return createDirectionRad((float) angle);
    }

    /**
     * Gets the direction vector of a certain angle in radians.
     *
     * @param angle in radians
     * @return the direction vector
     */
    public static Vector2f createDirectionRad(float angle) {
        return new Vector2f(TrigMath.cos(angle), TrigMath.sin(angle));
    }
}


package com.flowpowered.math.vector;

import java.io.Serializable;
import java.util.Random;

import com.flowpowered.math.GenericMath;
import com.flowpowered.math.HashFunctions;
import com.flowpowered.math.TrigMath;

public class Vector3f implements Vectorf, Comparable<Vector3f>, Serializable, Cloneable {
    private static final long serialVersionUID = 1;
    public static final Vector3f ZERO = new Vector3f(0, 0, 0);
    public static final Vector3f UNIT_X = new Vector3f(1, 0, 0);
    public static final Vector3f UNIT_Y = new Vector3f(0, 1, 0);
    public static final Vector3f UNIT_Z = new Vector3f(0, 0, 1);
    public static final Vector3f ONE = new Vector3f(1, 1, 1);
    public static final Vector3f RIGHT = UNIT_X;
    public static final Vector3f UP = UNIT_Y;
    public static final Vector3f FORWARD = UNIT_Z;
    private final float x;
    private final float y;
    private final float z;
    private transient volatile boolean hashed = false;
    private transient volatile int hashCode = 0;

    public Vector3f() {
        this(0, 0, 0);
    }

    public Vector3f(Vector2f v) {
        this(v, 0);
    }

    public Vector3f(Vector2f v, double z) {
        this(v, (float) z);
    }

    public Vector3f(Vector2f v, float z) {
        this(v.getX(), v.getY(), z);
    }

    public Vector3f(Vector3f v) {
        this(v.x, v.y, v.z);
    }

    public Vector3f(Vector4f v) {
        this(v.getX(), v.getY(), v.getZ());
    }

    public Vector3f(VectorNf v) {
        this(v.get(0), v.get(1), v.size() > 2 ? v.get(2) : 0);
    }

    public Vector3f(double x, double y, double z) {
        this((float) x, (float) y, (float) z);
    }

    public Vector3f(float x, float y, float z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public float getX() {
        return x;
    }

    public float getY() {
        return y;
    }

    public float getZ() {
        return z;
    }

    public int getFloorX() {
        return GenericMath.floor(x);
    }

    public int getFloorY() {
        return GenericMath.floor(y);
    }

    public int getFloorZ() {
        return GenericMath.floor(z);
    }

    public Vector3f add(Vector3f v) {
        return add(v.x, v.y, v.z);
    }

    public Vector3f add(double x, double y, double z) {
        return add((float) x, (float) y, (float) z);
    }

    public Vector3f add(float x, float y, float z) {
        return new Vector3f(this.x + x, this.y + y, this.z + z);
    }

    public Vector3f sub(Vector3f v) {
        return sub(v.x, v.y, v.z);
    }

    public Vector3f sub(double x, double y, double z) {
        return sub((float) x, (float) y, (float) z);
    }

    public Vector3f sub(float x, float y, float z) {
        return new Vector3f(this.x - x, this.y - y, this.z - z);
    }

    public Vector3f mul(double a) {
        return mul((float) a);
    }

    @Override
    public Vector3f mul(float a) {
        return mul(a, a, a);
    }

    public Vector3f mul(Vector3f v) {
        return mul(v.x, v.y, v.z);
    }

    public Vector3f mul(double x, double y, double z) {
        return mul((float) x, (float) y, (float) z);
    }

    public Vector3f mul(float x, float y, float z) {
        return new Vector3f(this.x * x, this.y * y, this.z * z);
    }

    public Vector3f div(double a) {
        return div((float) a);
    }

    @Override
    public Vector3f div(float a) {
        return div(a, a, a);
    }

    public Vector3f div(Vector3f v) {
        return div(v.x, v.y, v.z);
    }

    public Vector3f div(double x, double y, double z) {
        return div((float) x, (float) y, (float) z);
    }

    public Vector3f div(float x, float y, float z) {
        return new Vector3f(this.x / x, this.y / y, this.z / z);
    }

    public float dot(Vector3f v) {
        return dot(v.x, v.y, v.z);
    }

    public float dot(double x, double y, double z) {
        return dot((float) x, (float) y, (float) z);
    }

    public float dot(float x, float y, float z) {
        return this.x * x + this.y * y + this.z * z;
    }

    public Vector3f project(Vector3f v) {
        return project(v.x, v.y, v.z);
    }

    public Vector3f project(double x, double y, double z) {
        return project((float) x, (float) y, (float) z);
    }

    public Vector3f project(float x, float y, float z) {
        final float lengthSquared = x * x + y * y + z * z;
        if (Math.abs(lengthSquared) < GenericMath.FLT_EPSILON) {
            throw new ArithmeticException("Cannot project onto the zero vector");
        }
        final float a = dot(x, y, z) / lengthSquared;
        return new Vector3f(a * x, a * y, a * z);
    }

    public Vector3f cross(Vector3f v) {
        return cross(v.x, v.y, v.z);
    }

    public Vector3f cross(double x, double y, double z) {
        return cross((float) x, (float) y, (float) z);
    }

    public Vector3f cross(float x, float y, float z) {
        return new Vector3f(this.y * z - this.z * y, this.z * x - this.x * z, this.x * y - this.y * x);
    }

    public Vector3f pow(double pow) {
        return pow((float) pow);
    }

    @Override
    public Vector3f pow(float power) {
        return new Vector3f(Math.pow(x, power), Math.pow(y, power), Math.pow(z, power));
    }

    @Override
    public Vector3f ceil() {
        return new Vector3f(Math.ceil(x), Math.ceil(y), Math.ceil(z));
    }

    @Override
    public Vector3f floor() {
        return new Vector3f(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z));
    }

    @Override
    public Vector3f round() {
        return new Vector3f(Math.round(x), Math.round(y), Math.round(z));
    }

    @Override
    public Vector3f abs() {
        return new Vector3f(Math.abs(x), Math.abs(y), Math.abs(z));
    }

    @Override
    public Vector3f negate() {
        return new Vector3f(-x, -y, -z);
    }

    public Vector3f min(Vector3f v) {
        return min(v.x, v.y, v.z);
    }

    public Vector3f min(double x, double y, double z) {
        return min((float) x, (float) y, (float) z);
    }

    public Vector3f min(float x, float y, float z) {
        return new Vector3f(Math.min(this.x, x), Math.min(this.y, y), Math.min(this.z, z));
    }

    public Vector3f max(Vector3f v) {
        return max(v.x, v.y, v.z);
    }

    public Vector3f max(double x, double y, double z) {
        return max((float) x, (float) y, (float) z);
    }

    public Vector3f max(float x, float y, float z) {
        return new Vector3f(Math.max(this.x, x), Math.max(this.y, y), Math.max(this.z, z));
    }

    public float distanceSquared(Vector3f v) {
        return distanceSquared(v.x, v.y, v.z);
    }

    public float distanceSquared(double x, double y, double z) {
        return distanceSquared((float) x, (float) y, (float) z);
    }

    public float distanceSquared(float x, float y, float z) {
        final float dx = this.x - x;
        final float dy = this.y - y;
        final float dz = this.z - z;
        return dx * dx + dy * dy + dz * dz;
    }

    public float distance(Vector3f v) {
        return distance(v.x, v.y, v.z);
    }

    public float distance(double x, double y, double z) {
        return distance((float) x, (float) y, (float) z);
    }

    public float distance(float x, float y, float z) {
        return (float) Math.sqrt(distanceSquared(x, y, z));
    }

    @Override
    public float lengthSquared() {
        return x * x + y * y + z * z;
    }

    @Override
    public float length() {
        return (float) Math.sqrt(lengthSquared());
    }

    @Override
    public Vector3f normalize() {
        final float length = length();
        if (Math.abs(length) < GenericMath.FLT_EPSILON) {
            throw new ArithmeticException("Cannot normalize the zero vector");
        }
        return new Vector3f(x / length, y / length, z / length);
    }

    /**
     * Returns the axis with the minimal value.
     *
     * @return {@link int} axis with minimal value
     */
    @Override
    public int getMinAxis() {
        return x < y ? (x < z ? 0 : 2) : (y < z ? 1 : 2);
    }

    /**
     * Returns the axis with the maximum value.
     *
     * @return {@link int} axis with maximum value
     */
    @Override
    public int getMaxAxis() {
        return x < y ? (y < z ? 2 : 1) : (x < z ? 2 : 0);
    }

    public Vector2f toVector2() {
        return new Vector2f(this);
    }

    public Vector2f toVector2(boolean useZ) {
        return new Vector2f(x, useZ ? z : y);
    }

    public Vector4f toVector4() {
        return toVector4(0);
    }

    public Vector4f toVector4(double w) {
        return toVector4((float) w);
    }

    public Vector4f toVector4(float w) {
        return new Vector4f(this, w);
    }

    public VectorNf toVectorN() {
        return new VectorNf(this);
    }

    @Override
    public float[] toArray() {
        return new float[]{x, y, z};
    }

    @Override
    public Vector3i toInt() {
        return new Vector3i(x, y, z);
    }

    @Override
    public Vector3l toLong() {
        return new Vector3l(x, y, z);
    }

    @Override
    public Vector3f toFloat() {
        return new Vector3f(x, y, z);
    }

    @Override
    public Vector3d toDouble() {
        return new Vector3d(x, y, z);
    }

    @Override
    public int compareTo(Vector3f v) {
        return (int) Math.signum(lengthSquared() - v.lengthSquared());
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof Vector3f)) {
            return false;
        }
        final Vector3f vector3 = (Vector3f) o;
        if (Float.compare(vector3.x, x) != 0) {
            return false;
        }
        if (Float.compare(vector3.y, y) != 0) {
            return false;
        }
        if (Float.compare(vector3.z, z) != 0) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        if (!hashed) {
            int result = (x != +0.0f ? HashFunctions.hash(x) : 0);
            result = 31 * result + (y != +0.0f ? HashFunctions.hash(y) : 0);
            hashCode = 31 * result + (z != +0.0f ? HashFunctions.hash(z) : 0);
            hashed = true;
        }
        return hashCode;
    }

    @Override
    public Vector3f clone() {
        return new Vector3f(this);
    }

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

    public static Vector3f from(float n) {
         return n == 0 ? ZERO : new Vector3f(n, n, n);
    }

    public static Vector3f from(float x, float y, float z) {
         return x == 0 && y == 0 && z == 0 ? ZERO : new Vector3f(x, y, z);
    }

    /**
     * Gets the direction vector of a random pitch and yaw using the random specified.
     *
     * @param random to use
     * @return the random direction vector
     */
    public static Vector3f createRandomDirection(Random random) {
        return createDirectionRad(random.nextFloat() * (float) TrigMath.TWO_PI,
                random.nextFloat() * (float) TrigMath.TWO_PI);
    }

    /**
     * Gets the direction vector of a certain theta and phi in degrees. This uses the standard math spherical coordinate system.
     *
     * @param theta in degrees
     * @param phi in degrees
     * @return the direction vector
     */
    public static Vector3f createDirectionDeg(double theta, double phi) {
        return createDirectionDeg((float) theta, (float) phi);
    }

    /**
     * Gets the direction vector of a certain theta and phi in degrees. This uses the standard math spherical coordinate system.
     *
     * @param theta in degrees
     * @param phi in degrees
     * @return the direction vector
     */
    public static Vector3f createDirectionDeg(float theta, float phi) {
        return createDirectionRad((float) Math.toRadians(theta), (float) Math.toRadians(phi));
    }

    /**
     * Gets the direction vector of a certain theta and phi in radians. This uses the standard math spherical coordinate system.
     *
     * @param theta in radians
     * @param phi in radians
     * @return the direction vector
     */
    public static Vector3f createDirectionRad(double theta, double phi) {
        return createDirectionRad((float) theta, (float) phi);
    }

    /**
     * Gets the direction vector of a certain theta and phi in radians. This uses the standard math spherical coordinate system.
     *
     * @param theta in radians
     * @param phi in radians
     * @return the direction vector
     */
    public static Vector3f createDirectionRad(float theta, float phi) {
        final float f = TrigMath.sin(phi);
        return new Vector3f(f * TrigMath.cos(theta), f * TrigMath.sin(theta), TrigMath.cos(phi));
    }
}


package com.flowpowered.math;

import java.awt.Color;
import java.security.SecureRandom;
import java.util.Random;

import com.flowpowered.math.imaginary.*;
import com.flowpowered.math.vector.*;

/**
 * Class containing generic mathematical functions.
 */
public class GenericMath {
    /**
     * A "close to zero" double epsilon value for use
     */
    public static final double DBL_EPSILON = Double.longBitsToDouble(0x3cb0000000000000L);
    /**
     * A "close to zero" float epsilon value for use
     */
    public static final float FLT_EPSILON = Float.intBitsToFloat(0x34000000);
    private static final ThreadLocal<Random> THREAD_LOCAL_RANDOM = new ThreadLocal<Random>() {
        private final Random random = new SecureRandom();

        @Override
        protected Random initialValue() {
            synchronized (random) {
                return new Random(random.nextLong());
            }
        }
    };

    private GenericMath() {
    }

    /**
     * Gets the difference between two angles This value is always positive (0 - 180)
     *
     * @param angle1 The first angle
     * @param angle2 The second angle
     * @return the positive angle difference
     */
    public static float getDegreeDifference(float angle1, float angle2) {
        return Math.abs(wrapAngleDeg(angle1 - angle2));
    }

    /**
     * Gets the difference between two radians This value is always positive (0 - PI)
     *
     * @param radian1 The first angle
     * @param radian2 The second angle
     * @return the positive radian difference
     */
    public static double getRadianDifference(double radian1, double radian2) {
        return Math.abs(wrapAngleRad(radian1 - radian2));
    }

    /**
     * Wraps the angle between -180 and 180 degrees
     *
     * @param angle to wrap
     * @return -180 < angle <= 180
     */
    public static float wrapAngleDeg(float angle) {
        angle %= 360f;
        if (angle <= -180) {
            return angle + 360;
        } else if (angle > 180) {
            return angle - 360;
        } else {
            return angle;
        }
    }

    /**
     * Wraps the radian between -PI and PI
     *
     * @param angle to wrap
     * @return -PI < radian <= PI
     */
    public static double wrapAngleRad(double angle) {
        angle %= TrigMath.TWO_PI;
        if (angle <= -TrigMath.PI) {
            return angle + TrigMath.TWO_PI;
        }
        if (angle > TrigMath.PI) {
            return angle - TrigMath.TWO_PI;
        }
        return angle;
    }

    /**
     * Wraps the pitch angle between -90 and 90 degrees
     *
     * @param angle to wrap
     * @return -90 < angle < 90
     */
    public static float wrapAnglePitchDeg(float angle) {
        angle = wrapAngleDeg(angle);
        if (angle < -90) {
            return -90;
        }
        if (angle > 90) {
            return 90;
        }
        return angle;
    }

    /**
     * Wraps a byte between 0 and 256
     *
     * @param value to wrap
     * @return 0 < byte < 256
     */
    public static byte wrapByte(int value) {
        value %= 256;
        if (value < 0) {
            value += 256;
        }
        return (byte) value;
    }

    /**
     * Rounds a number to the amount of decimals specified
     *
     * @param input to round
     * @param decimals to round to
     * @return the rounded number
     */
    public static double round(double input, int decimals) {
        final double p = Math.pow(10, decimals);
        return Math.round(input * p) / p;
    }

    /**
     * Calculates the linear interpolation between a and b with the given percent
     *
     * @param a The first know value
     * @param b The second know value
     * @param percent The percent
     * @return the interpolated value
     */
    public static double lerp(double a, double b, double percent) {
        return (1 - percent) * a + percent * b;
    }

    /**
     * Calculates the linear interpolation between a and b with the given percent
     *
     * @param a The first know value
     * @param b The second know value
     * @param percent The percent
     * @return the interpolated value
     */
    public static float lerp(float a, float b, float percent) {
        return (1 - percent) * a + percent * b;
    }

    /**
     * Calculates the linear interpolation between a and b with the given percent
     *
     * @param a The first know value
     * @param b The second know value
     * @param percent The percent
     * @return the interpolated value
     */
    public static int lerp(int a, int b, int percent) {
        return (1 - percent) * a + percent * b;
    }

    /**
     * Calculates the linear interpolation between a and b with the given percent
     *
     * @param a The first know value
     * @param b The second know value
     * @param percent The percent
     * @return the interpolated vector
     */
    public static Vector3f lerp(Vector3f a, Vector3f b, float percent) {
        return a.mul(1 - percent).add(b.mul(percent));
    }

    /**
     * Calculates the linear interpolation between a and b with the given percent
     *
     * @param a The first know value
     * @param b The second know value
     * @param percent The percent
     * @return the interpolated vector
     */
    public static Vector3d lerp(Vector3d a, Vector3d b, double percent) {
        return a.mul(1 - percent).add(b.mul(percent));
    }

    /**
     * Calculates the linear interpolation between a and b with the given percent
     *
     * @param a The first know value
     * @param b The second know value
     * @param percent The percent
     * @return the interpolated vector
     */
    public static Vector2f lerp(Vector2f a, Vector2f b, float percent) {
        return a.mul(1 - percent).add(b.mul(percent));
    }

    /**
     * Calculates the linear interpolation between a and b with the given percent
     *
     * @param a The first know value
     * @param b The second know value
     * @param percent The percent
     * @return the interpolated vector
     */
    public static Vector2d lerp(Vector2d a, Vector2d b, double percent) {
        return a.mul(1 - percent).add(b.mul(percent));
    }

    /**
     * Calculates the value at x using linear interpolation
     *
     * @param x the X coord of the value to interpolate
     * @param x1 the X coord of q0
     * @param x2 the X coord of q1
     * @param q0 the first known value (x1)
     * @param q1 the second known value (x2)
     * @return the interpolated value
     */
    public static double lerp(double x, double x1, double x2, double q0, double q1) {
        return ((x2 - x) / (x2 - x1)) * q0 + ((x - x1) / (x2 - x1)) * q1;
    }

    /**
     * Calculates the linear interpolation between a and b with the given percent
     *
     * @param a The first know value
     * @param b The second know value
     * @param percent The percent
     * @return Color
     */
    public static Color lerp(Color a, Color b, float percent) {
        int red = (int) lerp(a.getRed(), b.getRed(), percent);
        int blue = (int) lerp(a.getBlue(), b.getBlue(), percent);
        int green = (int) lerp(a.getGreen(), b.getGreen(), percent);
        int alpha = (int) lerp(a.getAlpha(), b.getAlpha(), percent);
        return new Color(red, green, blue, alpha);
    }

    /**
     * Interpolates a quaternion between two others using spherical linear interpolation.
     *
     * @param a The first quaternion
     * @param b The second quaternion
     * @param percent The percent for the interpolation, between 0 and 1 inclusively
     * @return The interpolated quaternion
     */
    public static Quaternionf slerp(Quaternionf a, Quaternionf b, float percent) {
        final float inverted;
        float cosineTheta = a.dot(b);
        if (cosineTheta < 0) {
            cosineTheta = -cosineTheta;
            inverted = -1;
        } else {
            inverted = 1;
        }
        if (1 - cosineTheta < GenericMath.FLT_EPSILON) {
            return a.mul(1 - percent).add(b.mul(percent * inverted));
        }
        final float theta = (float) TrigMath.acos(cosineTheta);
        final float sineTheta = TrigMath.sin(theta);
        final float coefficient1 = TrigMath.sin((1 - percent) * theta) / sineTheta;
        final float coefficient2 = TrigMath.sin(percent * theta) / sineTheta * inverted;
        return a.mul(coefficient1).add(b.mul(coefficient2));
    }

    /**
     * Interpolates a quaternion between two others using spherical linear interpolation.
     *
     * @param a The first quaternion
     * @param b The second quaternion
     * @param percent The percent for the interpolation, between 0 and 1 inclusively
     * @return The interpolated quaternion
     */
    public static Quaterniond slerp(Quaterniond a, Quaterniond b, double percent) {
        final double inverted;
        double cosineTheta = a.dot(b);
        if (cosineTheta < 0) {
            cosineTheta = -cosineTheta;
            inverted = -1;
        } else {
            inverted = 1;
        }
        if (1 - cosineTheta < GenericMath.DBL_EPSILON) {
            return a.mul(1 - percent).add(b.mul(percent * inverted));
        }
        final double theta = (double) TrigMath.acos(cosineTheta);
        final double sineTheta = TrigMath.sin(theta);
        final double coefficient1 = TrigMath.sin((1 - percent) * theta) / sineTheta;
        final double coefficient2 = TrigMath.sin(percent * theta) / sineTheta * inverted;
        return a.mul(coefficient1).add(b.mul(coefficient2));
    }

    /**
     * Interpolates a quaternion between two others using linear interpolation.
     *
     * @param a The first quaternion
     * @param b The second quaternion
     * @param percent The percent for the interpolation, between 0 and 1 inclusively
     * @return The interpolated quaternion
     */
    public static Quaternionf lerp(Quaternionf a, Quaternionf b, float percent) {
        return a.mul(1 - percent).add(b.mul(percent));
    }

    /**
     * Interpolates a quaternion between two others using linear interpolation.
     *
     * @param a The first quaternion
     * @param b The second quaternion
     * @param percent The percent for the interpolation, between 0 and 1 inclusively
     * @return The interpolated quaternion
     */
    public static Quaterniond lerp(Quaterniond a, Quaterniond b, double percent) {
        return a.mul(1 - percent).add(b.mul(percent));
    }

    /**
     * Calculates the value at x,y using bilinear interpolation
     *
     * @param x the X coord of the value to interpolate
     * @param y the Y coord of the value to interpolate
     * @param q00 the first known value (x1, y1)
     * @param q01 the second known value (x1, y2)
     * @param q10 the third known value (x2, y1)
     * @param q11 the fourth known value (x2, y2)
     * @param x1 the X coord of q00 and q01
     * @param x2 the X coord of q10 and q11
     * @param y1 the Y coord of q00 and q10
     * @param y2 the Y coord of q01 and q11
     * @return the interpolated value
     */
    public static double biLerp(double x, double y, double q00, double q01,
                                double q10, double q11, double x1, double x2, double y1, double y2) {
        double q0 = lerp(x, x1, x2, q00, q10);
        double q1 = lerp(x, x1, x2, q01, q11);
        return lerp(y, y1, y2, q0, q1);
    }

    /**
     * Calculates the value at x,y,z using trilinear interpolation
     *
     * @param x the X coord of the value to interpolate
     * @param y the Y coord of the value to interpolate
     * @param z the Z coord of the value to interpolate
     * @param q000 the first known value (x1, y1, z1)
     * @param q001 the second known value (x1, y2, z1)
     * @param q010 the third known value (x1, y1, z2)
     * @param q011 the fourth known value (x1, y2, z2)
     * @param q100 the fifth known value (x2, y1, z1)
     * @param q101 the sixth known value (x2, y2, z1)
     * @param q110 the seventh known value (x2, y1, z2)
     * @param q111 the eighth known value (x2, y2, z2)
     * @param x1 the X coord of q000, q001, q010 and q011
     * @param x2 the X coord of q100, q101, q110 and q111
     * @param y1 the Y coord of q000, q010, q100 and q110
     * @param y2 the Y coord of q001, q011, q101 and q111
     * @param z1 the Z coord of q000, q001, q100 and q101
     * @param z2 the Z coord of q010, q011, q110 and q111
     * @return the interpolated value
     */
    public static double triLerp(double x, double y, double z, double q000, double q001,
                                 double q010, double q011, double q100, double q101, double q110, double q111,
                                 double x1, double x2, double y1, double y2, double z1, double z2) {
        double q00 = lerp(x, x1, x2, q000, q100);
        double q01 = lerp(x, x1, x2, q010, q110);
        double q10 = lerp(x, x1, x2, q001, q101);
        double q11 = lerp(x, x1, x2, q011, q111);
        double q0 = lerp(y, y1, y2, q00, q10);
        double q1 = lerp(y, y1, y2, q01, q11);
        return lerp(z, z1, z2, q0, q1);
    }

    /**
     * Blends two colors into one.
     *
     * @param a The first color
     * @param b The second color
     * @return The blended color
     */
    public static Color blend(Color a, Color b) {
        return lerp(a, b, a.getAlpha() / 255f);
    }

    /**
     * Clamps the value between the low and high boundaries
     *
     * @param value The value to clamp
     * @param low The low bound of the clamp
     * @param high The high bound of the clamp
     * @return the clamped value
     */
    public static double clamp(double value, double low, double high) {
        if (value < low) {
            return low;
        }
        if (value > high) {
            return high;
        }
        return value;
    }

    /**
     * Clamps the value between the low and high boundaries
     *
     * @param value The value to clamp
     * @param low The low bound of the clamp
     * @param high The high bound of the clamp
     * @return the clamped value
     */
    public static int clamp(int value, int low, int high) {
        if (value < low) {
            return low;
        }
        if (value > high) {
            return high;
        }
        return value;
    }

    /**
     * Returns a fast estimate of the inverse square root of the value
     *
     * @param a The value
     * @return The estimate of the inverse square root
     */
    public static double inverseSqrt(double a) {
        final double halfA = 0.5d * a;
        a = Double.longBitsToDouble(0x5FE6EB50C7B537AAl - (Double.doubleToRawLongBits(a) >> 1));
        return a * (1.5d - halfA * a * a);
    }

    /**
     * Returns a fast estimate of the square root of the value
     *
     * @param a The value
     * @return The estimate of the square root
     */
    public static double sqrt(double a) {
        return a * inverseSqrt(a);
    }

    /**
     * Rounds 'a' down to the closest integer
     *
     * @param a The value to floor
     * @return The closest integer
     */
    public static int floor(double a) {
        int y = (int) a;
        if (a < y) {
            return y - 1;
        }
        return y;
    }

    /**
     * Rounds 'a' down to the closest integer
     *
     * @param a The value to floor
     * @return The closest integer
     */
    public static int floor(float a) {
        int y = (int) a;
        if (a < y) {
            return y - 1;
        }
        return y;
    }

    /**
     * Rounds 'a' down to the closest long
     *
     * @param a The value to floor
     * @return The closest long
     */
    public static long floorl(double a) {
        long y = (long) a;
        if (a < y) {
            return y - 1;
        }
        return y;
    }

    /**
     * Rounds 'a' down to the closest long
     *
     * @param a The value to floor
     * @return The closest long
     */
    public static long floorl(float a) {
        long y = (long) a;
        if (a < y) {
            return y - 1;
        }
        return y;
    }

    /**
     * Gets the maximum byte value from two values
     *
     * @param value1 The first value
     * @param value2 The second value
     * @return the maximum of value1 and value2
     */
    public static byte max(byte value1, byte value2) {
        return value1 > value2 ? value1 : value2;
    }

    /**
     * Rounds an integer up to the next power of 2.
     *
     * @param a The integer to round
     * @return the lowest power of 2 greater or equal to 'a'
     */
    public static int roundUpPow2(int a) {
        if (a <= 0) {
            return 1;
        } else if (a > 0x40000000) {
            throw new IllegalArgumentException("Rounding " + a + " to the next highest power of two would exceed the int range");
        } else {
            a--;
            a |= a >> 1;
            a |= a >> 2;
            a |= a >> 4;
            a |= a >> 8;
            a |= a >> 16;
            a++;
            return a;
        }
    }

    /**
     * Rounds an integer up to the next power of 2.
     *
     * @param a The long to round
     * @return the lowest power of 2 greater or equal to 'a'
     */
    public static long roundUpPow2(long a) {
        if (a <= 0) {
            return 1;
        } else if (a > 0x4000000000000000L) {
            throw new IllegalArgumentException("Rounding " + a + " to the next highest power of two would exceed the int range");
        } else {
            a--;
            a |= a >> 1;
            a |= a >> 2;
            a |= a >> 4;
            a |= a >> 8;
            a |= a >> 16;
            a |= a >> 32;
            a++;
            return a;
        }
    }

    /**
     * Casts a value to a float. May return null.
     *
     * @param o The object to attempt to cast
     * @return The object as a float
     */
    public static Float castFloat(Object o) {
        if (o == null) {
            return null;
        }
        if (o instanceof Number) {
            return ((Number) o).floatValue();
        }
        try {
            return Float.valueOf(o.toString());
        } catch (NumberFormatException e) {
            return null;
        }
    }

    /**
     * Casts a value to a byte. May return null.
     *
     * @param o The object to attempt to cast
     * @return The object as a byte
     */
    public static Byte castByte(Object o) {
        if (o == null) {
            return null;
        }
        if (o instanceof Number) {
            return ((Number) o).byteValue();
        }
        try {
            return Byte.valueOf(o.toString());
        } catch (NumberFormatException e) {
            return null;
        }
    }

    /**
     * Casts a value to a short. May return null.
     *
     * @param o The object to attempt to cast
     * @return The object as a short
     */
    public static Short castShort(Object o) {
        if (o == null) {
            return null;
        }
        if (o instanceof Number) {
            return ((Number) o).shortValue();
        }
        try {
            return Short.valueOf(o.toString());
        } catch (NumberFormatException e) {
            return null;
        }
    }

    /**
     * Casts a value to an integer. May return null.
     *
     * @param o The object to attempt to cast
     * @return The object as an int
     */
    public static Integer castInt(Object o) {
        if (o == null) {
            return null;
        }
        if (o instanceof Number) {
            return ((Number) o).intValue();
        }
        try {
            return Integer.valueOf(o.toString());
        } catch (NumberFormatException e) {
            return null;
        }
    }

    /**
     * Casts a value to a double. May return null.
     *
     * @param o The object to attempt to cast
     * @return The object as a double
     */
    public static Double castDouble(Object o) {
        if (o == null) {
            return null;
        }
        if (o instanceof Number) {
            return ((Number) o).doubleValue();
        }
        try {
            return Double.valueOf(o.toString());
        } catch (NumberFormatException e) {
            return null;
        }
    }

    /**
     * Casts a value to a long. May return null.
     *
     * @param o The object to attempt to cast
     * @return The object as a long
     */
    public static Long castLong(Object o) {
        if (o == null) {
            return null;
        }
        if (o instanceof Number) {
            return ((Number) o).longValue();
        }
        try {
            return Long.valueOf(o.toString());
        } catch (NumberFormatException e) {
            return null;
        }
    }

    /**
     * Casts a value to a boolean. May return null.
     *
     * @param o The object to attempt to cast
     * @return The object as a boolean
     */
    public static Boolean castBoolean(Object o) {
        if (o == null) {
            return null;
        }
        if (o instanceof Boolean) {
            return (Boolean) o;
        } else if (o instanceof String) {
            try {
                return Boolean.parseBoolean((String) o);
            } catch (IllegalArgumentException e) {
                return null;
            }
        }

        return null;
    }

    /**
     * Calculates the mean of a set of values
     *
     * @param values to calculate the mean of
     * @return the mean of the values
     */
    public static int mean(int... values) {
        int sum = 0;
        for (int v : values) {
            sum += v;
        }
        return sum / values.length;
    }

    /**
     * Calculates the mean of a set of values.
     *
     * @param values to calculate the mean of
     * @return the mean of the values
     */
    public static double mean(double... values) {
        double sum = 0;
        for (double v : values) {
            sum += v;
        }
        return sum / values.length;
    }

    /**
     * Converts an integer to hexadecimal form with at least the minimum of digits specified (by adding leading zeros).
     *
     * @param dec The integer to convert
     * @param minDigits The minimum of digits in the hexadecimal form
     * @return The integer in hexadecimal form
     */
    public static String decToHex(int dec, int minDigits) {
        String ret = Integer.toHexString(dec);
        while (ret.length() < minDigits) {
            ret = '0' + ret;
        }
        return ret;
    }

    /**
     * Returns the modulo of 'a' by 'div' with corrections for negative numbers.
     *
     * @param a The number as an int
     * @param div The div as an int
     * @return The corrected modulo
     */
    public static int mod(int a, int div) {
        final int remainder = a % div;
        return remainder < 0 ? remainder + div : remainder;
    }

    /**
     * Returns the modulo of 'a' by 'div' with corrections for negative numbers.
     *
     * @param a The dividend
     * @param div The divider
     * @return The corrected modulo
     */
    public static float mod(float a, float div) {
        final float remainder = a % div;
        return remainder < 0 ? remainder + div : remainder;
    }

    /**
     * Returns the modulo of 'a' by 'div' with corrections for negative numbers.
     *
     * @param a The dividend
     * @param div The divider
     * @return The corrected modulo
     */
    public static double mod(double a, double div) {
        final double remainder = a % div;
        return remainder < 0 ? remainder + div : remainder;
    }

    /**
     * Gets a thread local Random object that is seeded using SecureRandom. Only one Random is created per thread.
     *
     * @return The random for the thread
     */
    public static Random getRandom() {
        return THREAD_LOCAL_RANDOM.get();
    }

    /**
     * Determines if the given number is a power of two. A number is a power of 2 if it is 1 or greater, and it contains no similar bits of the given number - 1.
     *
     * @return true if num is a power of two
     */
    public static boolean isPowerOfTwo(int num) {
        return num > 0 && ((num & (num - 1)) == 0);
    }

    /**
     * Converts a multiplication into a shift.
     *
     * @param a the multiplicand
     * @return the left shift required to multiply by the multiplicand
     */
    public static int multiplyToShift(int a) {
        if (a < 1) {
            throw new IllegalArgumentException("Multiplicand must be at least 1");
        }
        int shift = 31 - Integer.numberOfLeadingZeros(a);
        if ((1 << shift) != a) {
            throw new IllegalArgumentException("Multiplicand must be a power of 2");
        }
        return shift;
    }

    /**
     * Attempts to normalize a vector. If this fails, the method catches the exception and return a zero vector of the same dimension instead.
     *
     * @param v The vector to attempt to normalize
     * @return The normalized vector, or the zero vector if it couldn't be normalized.
     */
    public static Vector2f normalizeSafe(Vector2f v) {
        try {
            return v.normalize();
        } catch (ArithmeticException ex) {
            return Vector2f.ZERO;
        }
    }

    /**
     * Attempts to normalize a vector. If this fails, the method catches the exception and return a zero vector of the same dimension instead.
     *
     * @param v The vector to attempt to normalize
     * @return The normalized vector, or the zero vector if it couldn't be normalized.
     */
    public static Vector2d normalizeSafe(Vector2d v) {
        try {
            return v.normalize();
        } catch (ArithmeticException ex) {
            return Vector2d.ZERO;
        }
    }

    /**
     * Attempts to normalize a vector. If this fails, the method catches the exception and return a zero vector of the same dimension instead.
     *
     * @param v The vector to attempt to normalize
     * @return The normalized vector, or the zero vector if it couldn't be normalized.
     */
    public static Vector3f normalizeSafe(Vector3f v) {
        try {
            return v.normalize();
        } catch (ArithmeticException ex) {
            return Vector3f.ZERO;
        }
    }

    /**
     * Attempts to normalize a vector. If this fails, the method catches the exception and return a zero vector of the same dimension instead.
     *
     * @param v The vector to attempt to normalize
     * @return The normalized vector, or the zero vector if it couldn't be normalized.
     */
    public static Vector3d normalizeSafe(Vector3d v) {
        try {
            return v.normalize();
        } catch (ArithmeticException ex) {
            return Vector3d.ZERO;
        }
    }

    /**
     * Attempts to normalize a vector. If this fails, the method catches the exception and return a zero vector of the same dimension instead.
     *
     * @param v The vector to attempt to normalize
     * @return The normalized vector, or the zero vector if it couldn't be normalized.
     */
    public static Vector4f normalizeSafe(Vector4f v) {
        try {
            return v.normalize();
        } catch (ArithmeticException ex) {
            return Vector4f.ZERO;
        }
    }

    /**
     * Attempts to normalize a vector. If this fails, the method catches the exception and return a zero vector of the same dimension instead.
     *
     * @param v The vector to attempt to normalize
     * @return The normalized vector, or the zero vector if it couldn't be normalized.
     */
    public static Vector4d normalizeSafe(Vector4d v) {
        try {
            return v.normalize();
        } catch (ArithmeticException ex) {
            return Vector4d.ZERO;
        }
    }

    /**
     * Attempts to normalize a vector. If this fails, the method catches the exception and return a zero vector of the same dimension instead.
     *
     * @param v The vector to attempt to normalize
     * @return The normalized vector, or the zero vector if it couldn't be normalized.
     */
    public static VectorNf normalizeSafe(VectorNf v) {
        try {
            return v.normalize();
        } catch (ArithmeticException ex) {
            return new VectorNf(v.size());
        }
    }

    /**
     * Attempts to normalize a vector. If this fails, the method catches the exception and return a zero vector of the same dimension instead.
     *
     * @param v The vector to attempt to normalize
     * @return The normalized vector, or the zero vector if it couldn't be normalized.
     */
    public static VectorNd normalizeSafe(VectorNd v) {
        try {
            return v.normalize();
        } catch (ArithmeticException ex) {
            return new VectorNd(v.size());
        }
    }
}


package com.flowpowered.math.imaginary;

import java.io.Serializable;

import com.flowpowered.math.GenericMath;
import com.flowpowered.math.HashFunctions;
import com.flowpowered.math.TrigMath;
import com.flowpowered.math.matrix.Matrix3d;
import com.flowpowered.math.vector.Vector3d;

/**
 * Represent a quaternion of the form xi + yj + zk + w. The x, y, z and w components are stored as doubles. This class is immutable.
 */
public class Quaterniond implements Imaginaryd, Comparable<Quaterniond>, Serializable, Cloneable {
    private static final long serialVersionUID = 1;
    /**
     * An immutable identity (0, 0, 0, 0) quaternion.
     */
    public static final Quaterniond ZERO = new Quaterniond(0, 0, 0, 0);
    /**
     * An immutable identity (0, 0, 0, 1) quaternion.
     */
    public static final Quaterniond IDENTITY = new Quaterniond(0, 0, 0, 1);
    private final double x;
    private final double y;
    private final double z;
    private final double w;
    private transient volatile boolean hashed = false;
    private transient volatile int hashCode = 0;

    /**
     * Constructs a new quaternion. The components are set to the identity (0, 0, 0, 1).
     */
    public Quaterniond() {
        this(0, 0, 0, 1);
    }

    /**
     * Constructs a new quaternion from the float components.
     *
     * @param x The x (imaginary) component
     * @param y The y (imaginary) component
     * @param z The z (imaginary) component
     * @param w The w (real) component
     */
    public Quaterniond(float x, float y, float z, float w) {
        this((double) x, (double) y, (double) z, (double) w);
    }

    /**
     * Constructs a new quaternion from the double components.
     *
     * @param x The x (imaginary) component
     * @param y The y (imaginary) component
     * @param z The z (imaginary) component
     * @param w The w (real) component
     */
    public Quaterniond(double x, double y, double z, double w) {
        this.x = x;
        this.y = y;
        this.z = z;
        this.w = w;
    }

    /**
     * Copy constructor.
     *
     * @param q The quaternion to copy
     */
    public Quaterniond(Quaterniond q) {
        this(q.x, q.y, q.z, q.w);
    }

    /**
     * Gets the x (imaginary) component of this quaternion.
     *
     * @return The x (imaginary) component
     */
    public double getX() {
        return x;
    }

    /**
     * Gets the y (imaginary) component of this quaternion.
     *
     * @return The y (imaginary) component
     */
    public double getY() {
        return y;
    }

    /**
     * Gets the z (imaginary) component of this quaternion.
     *
     * @return The z (imaginary) component
     */
    public double getZ() {
        return z;
    }

    /**
     * Gets the w (real) component of this quaternion.
     *
     * @return The w (real) component
     */
    public double getW() {
        return w;
    }

    /**
     * Adds another quaternion to this one.
     *
     * @param q The quaternion to add
     * @return A new quaternion, which is the sum of both
     */
    public Quaterniond add(Quaterniond q) {
        return add(q.x, q.y, q.z, q.w);
    }

    /**
     * Adds the float components of another quaternion to this one.
     *
     * @param x The x (imaginary) component of the quaternion to add
     * @param y The y (imaginary) component of the quaternion to add
     * @param z The z (imaginary) component of the quaternion to add
     * @param w The w (real) component of the quaternion to add
     * @return A new quaternion, which is the sum of both
     */
    public Quaterniond add(float x, float y, float z, float w) {
        return add((double) x, (double) y, (double) z, (double) w);
    }

    /**
     * Adds the double components of another quaternion to this one.
     *
     * @param x The x (imaginary) component of the quaternion to add
     * @param y The y (imaginary) component of the quaternion to add
     * @param z The z (imaginary) component of the quaternion to add
     * @param w The w (real) component of the quaternion to add
     * @return A new quaternion, which is the sum of both
     */
    public Quaterniond add(double x, double y, double z, double w) {
        return new Quaterniond(this.x + x, this.y + y, this.z + z, this.w + w);
    }

    /**
     * Subtracts another quaternion from this one.
     *
     * @param q The quaternion to subtract
     * @return A new quaternion, which is the difference of both
     */
    public Quaterniond sub(Quaterniond q) {
        return sub(q.x, q.y, q.z, q.w);
    }

    /**
     * Subtracts the float components of another quaternion from this one.
     *
     * @param x The x (imaginary) component of the quaternion to subtract
     * @param y The y (imaginary) component of the quaternion to subtract
     * @param z The z (imaginary) component of the quaternion to subtract
     * @param w The w (real) component of the quaternion to subtract
     * @return A new quaternion, which is the difference of both
     */
    public Quaterniond sub(float x, float y, float z, float w) {
        return sub((double) x, (double) y, (double) z, (double) w);
    }

    /**
     * Subtracts the double components of another quaternion from this one.
     *
     * @param x The x (imaginary) component of the quaternion to subtract
     * @param y The y (imaginary) component of the quaternion to subtract
     * @param z The z (imaginary) component of the quaternion to subtract
     * @param w The w (real) component of the quaternion to subtract
     * @return A new quaternion, which is the difference of both
     */
    public Quaterniond sub(double x, double y, double z, double w) {
        return new Quaterniond(this.x - x, this.y - y, this.z - z, this.w - w);
    }

    /**
     * Multiplies the components of this quaternion by a float scalar.
     *
     * @param a The multiplication scalar
     * @return A new quaternion, which has each component multiplied by the scalar
     */
    public Quaterniond mul(float a) {
        return mul((double) a);
    }

    /**
     * Multiplies the components of this quaternion by a double scalar.
     *
     * @param a The multiplication scalar
     * @return A new quaternion, which has each component multiplied by the scalar
     */
    @Override
    public Quaterniond mul(double a) {
        return new Quaterniond(x * a, y * a, z * a, w * a);
    }

    /**
     * Multiplies another quaternion with this one.
     *
     * @param q The quaternion to multiply with
     * @return A new quaternion, which is the product of both
     */
    public Quaterniond mul(Quaterniond q) {
        return mul(q.x, q.y, q.z, q.w);
    }

    /**
     * Multiplies the float components of another quaternion with this one.
     *
     * @param x The x (imaginary) component of the quaternion to multiply with
     * @param y The y (imaginary) component of the quaternion to multiply with
     * @param z The z (imaginary) component of the quaternion to multiply with
     * @param w The w (real) component of the quaternion to multiply with
     * @return A new quaternion, which is the product of both
     */
    public Quaterniond mul(float x, float y, float z, float w) {
        return mul((double) x, (double) y, (double) z, (double) w);
    }

    /**
     * Multiplies the double components of another quaternion with this one.
     *
     * @param x The x (imaginary) component of the quaternion to multiply with
     * @param y The y (imaginary) component of the quaternion to multiply with
     * @param z The z (imaginary) component of the quaternion to multiply with
     * @param w The w (real) component of the quaternion to multiply with
     * @return A new quaternion, which is the product of both
     */
    public Quaterniond mul(double x, double y, double z, double w) {
        return new Quaterniond(
                this.w * x + this.x * w + this.y * z - this.z * y,
                this.w * y + this.y * w + this.z * x - this.x * z,
                this.w * z + this.z * w + this.x * y - this.y * x,
                this.w * w - this.x * x - this.y * y - this.z * z);
    }

    /**
     * Divides the components of this quaternion by a float scalar.
     *
     * @param a The division scalar
     * @return A new quaternion, which has each component divided by the scalar
     */
    public Quaterniond div(float a) {
        return div((double) a);
    }

    /**
     * Divides the components of this quaternion by a double scalar.
     *
     * @param a The division scalar
     * @return A new quaternion, which has each component divided by the scalar
     */
    @Override
    public Quaterniond div(double a) {
        return new Quaterniond(x / a, y / a, z / a, w / a);
    }

    /**
     * Divides this quaternions by another one.
     *
     * @param q The quaternion to divide with
     * @return The quotient of the two quaternions
     */
    public Quaterniond div(Quaterniond q) {
        return div(q.x, q.y, q.z, q.w);
    }

    /**
     * Divides this quaternions by the float components of another one.
     *
     * @param x The x (imaginary) component of the quaternion to divide with
     * @param y The y (imaginary) component of the quaternion to divide with
     * @param z The z (imaginary) component of the quaternion to divide with
     * @param w The w (real) component of the quaternion to divide with
     * @return The quotient of the two quaternions
     */
    public Quaterniond div(float x, float y, float z, float w) {
        return div((double) x, (double) y, (double) z, (double) w);
    }

    /**
     * Divides this quaternions by the double components of another one.
     *
     * @param x The x (imaginary) component of the quaternion to divide with
     * @param y The y (imaginary) component of the quaternion to divide with
     * @param z The z (imaginary) component of the quaternion to divide with
     * @param w The w (real) component of the quaternion to divide with
     * @return The quotient of the two quaternions
     */
    public Quaterniond div(double x, double y, double z, double w) {
        final double d = x * x + y * y + z * z + w * w;
        return new Quaterniond(
                (this.x * w - this.w * x - this.z * y + this.y * z) / d,
                (this.y * w + this.z * x - this.w * y - this.x * z) / d,
                (this.z * w - this.y * x + this.x * y - this.w * z) / d,
                (this.w * w + this.x * x + this.y * y + this.z * z) / d);
    }

    /**
     * Returns the dot product of this quaternion with another one.
     *
     * @param q The quaternion to calculate the dot product with
     * @return The dot product of the two quaternions
     */
    public double dot(Quaterniond q) {
        return dot(q.x, q.y, q.z, q.w);
    }

    /**
     * Returns the dot product of this quaternion with the float components of another one.
     *
     * @param x The x (imaginary) component of the quaternion to calculate the dot product with
     * @param y The y (imaginary) component of the quaternion to calculate the dot product with
     * @param z The z (imaginary) component of the quaternion to calculate the dot product with
     * @param w The w (real) component of the quaternion to calculate the dot product with
     * @return The dot product of the two quaternions
     */
    public double dot(float x, float y, float z, float w) {
        return dot((double) x, (double) y, (double) z, (double) w);
    }

    /**
     * Returns the dot product of this quaternion with the double components of another one.
     *
     * @param x The x (imaginary) component of the quaternion to calculate the dot product with
     * @param y The y (imaginary) component of the quaternion to calculate the dot product with
     * @param z The z (imaginary) component of the quaternion to calculate the dot product with
     * @param w The w (real) component of the quaternion to calculate the dot product with
     * @return The dot product of the two quaternions
     */
    public double dot(double x, double y, double z, double w) {
        return this.x * x + this.y * y + this.z * z + this.w * w;
    }

    /**
     * Rotates a vector by this quaternion.
     *
     * @param v The vector to rotate
     * @return The rotated vector
     */
    public Vector3d rotate(Vector3d v) {
        return rotate(v.getX(), v.getY(), v.getZ());
    }

    /**
     * Rotates the float components of a vector by this quaternion.
     *
     * @param x The x component of the vector
     * @param y The y component of the vector
     * @param z The z component of the vector
     * @return The rotated vector
     */
    public Vector3d rotate(float x, float y, float z) {
        return rotate((double) x, (double) y, (double) z);
    }

    /**
     * Rotates the double components of a vector by this quaternion.
     *
     * @param x The x component of the vector
     * @param y The y component of the vector
     * @param z The z component of the vector
     * @return The rotated vector
     */
    public Vector3d rotate(double x, double y, double z) {
        final double length = length();
        if (Math.abs(length) < GenericMath.DBL_EPSILON) {
            throw new ArithmeticException("Cannot rotate by the zero quaternion");
        }
        final double nx = this.x / length;
        final double ny = this.y / length;
        final double nz = this.z / length;
        final double nw = this.w / length;
        final double px = nw * x + ny * z - nz * y;
        final double py = nw * y + nz * x - nx * z;
        final double pz = nw * z + nx * y - ny * x;
        final double pw = -nx * x - ny * y - nz * z;
        return new Vector3d(
                pw * -nx + px * nw - py * nz + pz * ny,
                pw * -ny + py * nw - pz * nx + px * nz,
                pw * -nz + pz * nw - px * ny + py * nx);
    }

    /**
     * Returns a unit vector representing the direction of this quaternion, which is {@link Vector3d#FORWARD} rotated by this quaternion.
     *
     * @return The vector representing the direction this quaternion is pointing to
     */
    public Vector3d getDirection() {
        return rotate(Vector3d.FORWARD);
    }

    /**
     * Returns the axis of rotation for this quaternion.
     *
     * @return The axis of rotation
     */
    public Vector3d getAxis() {
        final double q = (double) Math.sqrt(1 - w * w);
        return new Vector3d(x / q, y / q, z / q);
    }

    /**
     * Returns the angles in degrees around the x, y and z axes that correspond to the rotation represented by this quaternion.
     *
     * @return The angle in degrees for each axis, stored in a vector, in the corresponding component
     */
    public Vector3d getAxesAnglesDeg() {
        return getAxesAnglesRad().mul(TrigMath.RAD_TO_DEG);
    }

    /**
     * Returns the angles in radians around the x, y and z axes that correspond to the rotation represented by this quaternion.
     *
     * @return The angle in radians for each axis, stored in a vector, in the corresponding component
     */
    public Vector3d getAxesAnglesRad() {
        final double roll;
        final double pitch;
        double yaw;
        final double test = w * x - y * z;
        if (Math.abs(test) < 0.4999) {
            roll = TrigMath.atan2(2 * (w * z + x * y), 1 - 2 * (x * x + z * z));
            pitch = TrigMath.asin(2 * test);
            yaw = TrigMath.atan2(2 * (w * y + z * x), 1 - 2 * (x * x + y * y));
        } else {
            final int sign = (test < 0) ? -1 : 1;
            roll = 0;
            pitch = sign * Math.PI / 2;
            yaw = -sign * 2 * TrigMath.atan2(z, w);
        }
        return new Vector3d(pitch, yaw, roll);
    }

    /**
     * Conjugates the quaternion.
 Conjugation of a quaternion a is an operation returning quaternion a' such that a' * a = a * a' = |a|2 where
     * |a|2 is squared length of a.
     *
     * @return the conjugated quaternion
     */
    @Override
    public Quaterniond conjugate() {
        return new Quaterniond(-x, -y, -z, w);
    }

    /**
     * Inverts the quaternion.
 Inversion of a quaternion a returns quaternion a-1 = a' / |a|2 where a' is {@link #conjugate()
     * conjugation} of a, and |a|2 is squared length of a.
 For any quaternions a, b, c, such that a * b = c equations
     * a-1 * c = b and c * b-1 = a are true.
     *
     * @return the inverted quaternion
     */
    @Override
    public Quaterniond invert() {
        final double lengthSquared = lengthSquared();
        if (Math.abs(lengthSquared) < GenericMath.DBL_EPSILON) {
            throw new ArithmeticException("Cannot invert a quaternion of length zero");
        }
        return conjugate().div(lengthSquared);
    }

    /**
     * Returns the square of the length of this quaternion.
     *
     * @return The square of the length
     */
    @Override
    public double lengthSquared() {
        return x * x + y * y + z * z + w * w;
    }

    /**
     * Returns the length of this quaternion.
     *
     * @return The length
     */
    @Override
    public double length() {
        return (double) Math.sqrt(lengthSquared());
    }

    /**
     * Normalizes this quaternion.
     *
     * @return A new quaternion of unit length
     */
    @Override
    public Quaterniond normalize() {
        final double length = length();
        if (Math.abs(length) < GenericMath.DBL_EPSILON) {
            throw new ArithmeticException("Cannot normalize the zero quaternion");
        }
        return new Quaterniond(x / length, y / length, z / length, w / length);
    }

    /**
     * Converts this quaternion to a complex by extracting the rotation around
     * the axis and returning it as a rotation in the plane perpendicular to the
     * rotation axis.
     *
     * @return The rotation without the axis as a complex
     */
    public Complexd toComplex() {
        final double w2 = w * w;
        return new Complexd(2 * w2 - 1, 2 * w * (double) Math.sqrt(1 - w2));
    }

    @Override
    public Quaternionf toFloat() {
        return new Quaternionf(x, y, z, w);
    }

    @Override
    public Quaterniond toDouble() {
        return new Quaterniond(x, y, z, w);
    }

   @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof Quaterniond)) {
            return false;
        }
        final Quaterniond quaternion = (Quaterniond) o;
        if (Double.compare(quaternion.w, w) != 0) {
            return false;
        }
        if (Double.compare(quaternion.x, x) != 0) {
            return false;
        }
        if (Double.compare(quaternion.y, y) != 0) {
            return false;
        }
        if (Double.compare(quaternion.z, z) != 0) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        if (!hashed) {
            int result = (x != +0.0f ? HashFunctions.hash(x) : 0);
            result = 31 * result + (y != +0.0f ? HashFunctions.hash(y) : 0);
            result = 31 * result + (z != +0.0f ? HashFunctions.hash(z) : 0);
            hashCode = 31 * result + (w != +0.0f ? HashFunctions.hash(w) : 0);
            hashed = true;
        }
        return hashCode;
    }

    @Override
    public int compareTo(Quaterniond q) {
        return (int) Math.signum(lengthSquared() - q.lengthSquared());
    }

    @Override
    public Quaterniond clone() {
        return new Quaterniond(this);
    }

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

    /**
     * Creates a new quaternion from the double real component.
     *
     *
The {@link #ZERO} constant is re-used when {@code w} is 0.

     *
     * @param w The w (real) component
     * @return The quaternion created from the double real component
     */
    public static Quaterniond fromReal(double w) {
         return w == 0 ? ZERO : new Quaterniond(0, 0, 0, w);
    }

    /**
     * Creates a new quaternion from the double imaginary components.
     *
     *
The {@link #ZERO} constant is re-used when {@code x}, {@code y}, and {@code z} are 0.

     *
     * @param x The x (imaginary) component
     * @param y The y (imaginary) component
     * @param z The z (imaginary) component
     * @return The quaternion created from the double imaginary components
     */
    public static Quaterniond fromImaginary(double x, double y, double z) {
         return x == 0 && y == 0 && z == 0 ? ZERO : new Quaterniond(x, y, z, 0);
    }

    /**
     * Creates a new quaternion from the double components.
     *
     *
The {@link #ZERO} constant is re-used when {@code x}, {@code y}, {@code z}, and {@code w} are 0.

     *
     * @param x The x (imaginary) component
     * @param y The y (imaginary) component
     * @param z The z (imaginary) component
     * @param w The w (real) component
     * @return The quaternion created from the double components
     */
    public static Quaterniond from(double x, double y, double z, double w) {
         return x == 0 && y == 0 && z == 0 && w == 0 ? ZERO : new Quaterniond(x, y, z, w);
    }

    /**
     * Creates a new quaternion from the float angles in degrees around the x, y and z axes.
     *
     * @param pitch The rotation around x
     * @param yaw The rotation around y
     * @param roll The rotation around z
     * @return The quaternion defined by the rotations around the axes
     */
    public static Quaterniond fromAxesAnglesDeg(float pitch, float yaw, float roll) {
        return fromAxesAnglesDeg((double) pitch, (double) yaw, (double) roll);
    }

    /**
     * Creates a new quaternion from the float angles in radians around the x, y and z axes.
     *
     * @param pitch The rotation around x
     * @param yaw The rotation around y
     * @param roll The rotation around z
     * @return The quaternion defined by the rotations around the axes
     */
    public static Quaterniond fromAxesAnglesRad(float pitch, float yaw, float roll) {
        return fromAxesAnglesRad((double) pitch, (double) yaw, (double) roll);
    }

    /**
     * Creates a new quaternion from the double angles in degrees around the x, y and z axes.
     *
     * @param pitch The rotation around x
     * @param yaw The rotation around y
     * @param roll The rotation around z
     * @return The quaternion defined by the rotations around the axes
     */
    public static Quaterniond fromAxesAnglesDeg(double pitch, double yaw, double roll) {
        return Quaterniond.fromAngleDegAxis(yaw, Vector3d.UNIT_Y).
                mul(Quaterniond.fromAngleDegAxis(pitch, Vector3d.UNIT_X)).
                mul(Quaterniond.fromAngleDegAxis(roll, Vector3d.UNIT_Z));
    }

    /**
     * Creates a new quaternion from the double angles in radians around the x, y and z axes.
     *
     * @param pitch The rotation around x
     * @param yaw The rotation around y
     * @param roll The rotation around z
     * @return The quaternion defined by the rotations around the axes
     */
    public static Quaterniond fromAxesAnglesRad(double pitch, double yaw, double roll) {
        return Quaterniond.fromAngleRadAxis(yaw, Vector3d.UNIT_Y).
                mul(Quaterniond.fromAngleRadAxis(pitch, Vector3d.UNIT_X)).
                mul(Quaterniond.fromAngleRadAxis(roll, Vector3d.UNIT_Z));
    }

    /**
     * Creates a new quaternion from the angle-axis rotation defined from the first to the second vector.
     *
     * @param from The first vector
     * @param to The second vector
     * @return The quaternion defined by the angle-axis rotation between the vectors
     */
    public static Quaterniond fromRotationTo(Vector3d from, Vector3d to) {
        return Quaterniond.fromAngleRadAxis(TrigMath.acos(from.dot(to) / (from.length() * to.length())), from.cross(to));
    }

    /**
     * Creates a new quaternion from the rotation float angle in degrees around the axis vector.
     *
     * @param angle The rotation angle in degrees
     * @param axis The axis of rotation
     * @return The quaternion defined by the rotation around the axis
     */
    public static Quaterniond fromAngleDegAxis(float angle, Vector3d axis) {
        return fromAngleRadAxis(Math.toRadians(angle), axis);
    }

    /**
     * Creates a new quaternion from the rotation float angle in radians around the axis vector.
     *
     * @param angle The rotation angle in radians
     * @param axis The axis of rotation
     * @return The quaternion defined by the rotation around the axis
     */
    public static Quaterniond fromAngleRadAxis(float angle, Vector3d axis) {
        return fromAngleRadAxis((double) angle, axis);
    }

    /**
     * Creates a new quaternion from the rotation double angle in degrees around the axis vector.
     *
     * @param angle The rotation angle in degrees
     * @param axis The axis of rotation
     * @return The quaternion defined by the rotation around the axis
     */
    public static Quaterniond fromAngleDegAxis(double angle, Vector3d axis) {
        return fromAngleRadAxis((double) Math.toRadians(angle), axis);
    }

    /**
     * Creates a new quaternion from the rotation double angle in radians around the axis vector.
     *
     * @param angle The rotation angle in radians
     * @param axis The axis of rotation
     * @return The quaternion defined by the rotation around the axis
     */
    public static Quaterniond fromAngleRadAxis(double angle, Vector3d axis) {
        return fromAngleRadAxis(angle, axis.getX(), axis.getY(), axis.getZ());
    }

    /**
     * Creates a new quaternion from the rotation float angle in degrees around the axis vector float components.
     *
     * @param angle The rotation angle in degrees
     * @param x The x component of the axis vector
     * @param y The y component of the axis vector
     * @param z The z component of the axis vector
     * @return The quaternion defined by the rotation around the axis
     */
    public static Quaterniond fromAngleDegAxis(float angle, float x, float y, float z) {
        return fromAngleRadAxis(Math.toRadians(angle), x, y, z);
    }

    /**
     * Creates a new quaternion from the rotation float angle in radians around the axis vector float components.
     *
     * @param angle The rotation angle in radians
     * @param x The x component of the axis vector
     * @param y The y component of the axis vector
     * @param z The z component of the axis vector
     * @return The quaternion defined by the rotation around the axis
     */
    public static Quaterniond fromAngleRadAxis(float angle, float x, float y, float z) {
        return fromAngleRadAxis((double) angle, (double) x, (double) y, (double) z);
    }

    /**
     * Creates a new quaternion from the rotation double angle in degrees around the axis vector double components.
     *
     * @param angle The rotation angle in degrees
     * @param x The x component of the axis vector
     * @param y The y component of the axis vector
     * @param z The z component of the axis vector
     * @return The quaternion defined by the rotation around the axis
     */
    public static Quaterniond fromAngleDegAxis(double angle, double x, double y, double z) {
        return fromAngleRadAxis((double) Math.toRadians(angle), x, y, z);
    }

    /**
     * Creates a new quaternion from the rotation double angle in radians around the axis vector double components.
     *
     * @param angle The rotation angle in radians
     * @param x The x component of the axis vector
     * @param y The y component of the axis vector
     * @param z The z component of the axis vector
     * @return The quaternion defined by the rotation around the axis
     */
    public static Quaterniond fromAngleRadAxis(double angle, double x, double y, double z) {
        final double halfAngle = angle / 2;
        final double q = TrigMath.sin(halfAngle) / (double) Math.sqrt(x * x + y * y + z * z);
        return new Quaterniond(x * q, y * q, z * q, TrigMath.cos(halfAngle));
    }

    /**
     * Creates a new quaternion from the rotation matrix. The matrix will be interpreted as a rotation matrix even if it is not.
     *
     * @param matrix The rotation matrix
     * @return The quaternion defined by the rotation matrix
     */
    public static Quaterniond fromRotationMatrix(Matrix3d matrix) {
        final double trace = matrix.trace();
        if (trace < 0) {
            if (matrix.get(1, 1) > matrix.get(0, 0)) {
                if (matrix.get(2, 2) > matrix.get(1, 1)) {
                    final double r = (double) Math.sqrt(matrix.get(2, 2) - matrix.get(0, 0) - matrix.get(1, 1) + 1);
                    final double s = 0.5f / r;
                    return new Quaterniond(
                            (matrix.get(2, 0) + matrix.get(0, 2)) * s,
                            (matrix.get(1, 2) + matrix.get(2, 1)) * s,
                            0.5f * r,
                            (matrix.get(1, 0) - matrix.get(0, 1)) * s);
                } else {
                    final double r = (double) Math.sqrt(matrix.get(1, 1) - matrix.get(2, 2) - matrix.get(0, 0) + 1);
                    final double s = 0.5f / r;
                    return new Quaterniond(
                            (matrix.get(0, 1) + matrix.get(1, 0)) * s,
                            0.5f * r,
                            (matrix.get(1, 2) + matrix.get(2, 1)) * s,
                            (matrix.get(0, 2) - matrix.get(2, 0)) * s);
                }
            } else if (matrix.get(2, 2) > matrix.get(0, 0)) {
                final double r = (double) Math.sqrt(matrix.get(2, 2) - matrix.get(0, 0) - matrix.get(1, 1) + 1);
                final double s = 0.5f / r;
                return new Quaterniond(
                        (matrix.get(2, 0) + matrix.get(0, 2)) * s,
                        (matrix.get(1, 2) + matrix.get(2, 1)) * s,
                        0.5f * r,
                        (matrix.get(1, 0) - matrix.get(0, 1)) * s);
            } else {
                final double r = (double) Math.sqrt(matrix.get(0, 0) - matrix.get(1, 1) - matrix.get(2, 2) + 1);
                final double s = 0.5f / r;
                return new Quaterniond(
                        0.5f * r,
                        (matrix.get(0, 1) + matrix.get(1, 0)) * s,
                        (matrix.get(2, 0) - matrix.get(0, 2)) * s,
                        (matrix.get(2, 1) - matrix.get(1, 2)) * s);
            }
        } else {
            final double r = (double) Math.sqrt(trace + 1);
            final double s = 0.5f / r;
            return new Quaterniond(
                    (matrix.get(2, 1) - matrix.get(1, 2)) * s,
                    (matrix.get(0, 2) - matrix.get(2, 0)) * s,
                    (matrix.get(1, 0) - matrix.get(0, 1)) * s,
                    0.5f * r);
        }
    }
}