/*
   Copyright (C) 1997,1998,1999
   Kenji Hiranabe, Eiwa System Management, Inc.

   This program is free software.
   Implemented by Kenji Hiranabe(hiranabe@esm.co.jp),
   conforming to the Java(TM) 3D API specification by Sun Microsystems.

   Permission to use, copy, modify, distribute and sell this software
   and its documentation for any purpose is hereby granted without fee,
   provided that the above copyright notice appear in all copies and
   that both that copyright notice and this permission notice appear
   in supporting documentation. Kenji Hiranabe and Eiwa System Management,Inc.
   makes no representations about the suitability of this software for any
   purpose.  It is provided "AS IS" with NO WARRANTY.
*/
package javajs.util;

/**
 * A single precision floating point 4 by 4 matrix.
 * 
 * @author Kenji hiranabe
 * 
 *         additions by Bob Hanson hansonr@stolaf.edu 9/30/2012 for unique
 *         constructor and method names for the optimization of compiled
 *         JavaScript using Java2Script
 */
public class M4 extends M34 {

  /**
   * The fourth element of the first row.
   */
  public float m03;

  /**
   * The fourth element of the second row.
   */
  public float m13;

  /**
   * The fourth element of the third row.
   */
  public float m23;

  /**
   * The first element of the fourth row.
   */
  public float m30;

  /**
   * The second element of the fourth row.
   */
  public float m31;

  /**
   * The third element of the fourth row.
   */
  public float m32;

  /**
   * The fourth element of the fourth row.
   */
  public float m33 = 0;

  /**
   * all zeros
   * @j2sIgnore
   */
  public M4() {
  }
  /**
   * Constructs and initializes a Matrix4f from the specified 16 element array.
   * this.m00 =v[0], this.m01=v[1], etc.
   * 
   * @param v
   *        the array of length 16 containing in order
   * @return m
   */
  public static M4 newA16(float[] v) {
    M4 m = new M4();
    m.m00 = v[0];
    m.m01 = v[1];
    m.m02 = v[2];
    m.m03 = v[3];

    m.m10 = v[4];
    m.m11 = v[5];
    m.m12 = v[6];
    m.m13 = v[7];

    m.m20 = v[8];
    m.m21 = v[9];
    m.m22 = v[10];
    m.m23 = v[11];

    m.m30 = v[12];
    m.m31 = v[13];
    m.m32 = v[14];
    m.m33 = v[15];

    return m;
  }

  /**
   * Constructs a new matrix with the same values as the Matrix4f parameter.
   * 
   * @param m1
   *        the source matrix
   * @return m
   */
  public static M4 newM4(M4 m1) {
    M4 m = new M4();
    if (m1 == null) {
      m.setIdentity();
      return m;
    }
    m.setToM3(m1);
    m.m03 = m1.m03;
    m.m13 = m1.m13;
    m.m23 = m1.m23;
    m.m30 = m1.m30;
    m.m31 = m1.m31;
    m.m32 = m1.m32;
    m.m33 = m1.m33;
    return m;
  }

  /**
   * Constructs and initializes a Matrix4f from the rotation matrix and
   * translation.
   * 
   * @param m1
   *        The rotation matrix representing the rotational components
   * @param t
   *        The translational components of the matrix
   * @return m
   */
  public static M4 newMV(M3 m1, T3 t) {
    M4 m = new M4();
    m.setMV(m1, t);
    return m;
  }

  /**
   * Sets this matrix to all zeros.
   */
  public void setZero() {
    clear33();
    m03 = m13 = m23 = m30 = m31 = m32 = m33 = 0.0f;
  }

  /**
   * Sets this Matrix4f to identity.
   */
  public void setIdentity() {
    setZero();
    m00 = m11 = m22 = m33 = 1.0f;
  }

  /**
   * Sets the value of this matrix to a copy of the passed matrix m1.
   * 
   * @param m1
   *        the matrix to be copied
   * @return this
   */
  public M4 setM4(M4 m1) {
    setM33(m1);
    m03 = m1.m03;
    m13 = m1.m13;
    m23 = m1.m23;
    m30 = m1.m30;
    m31 = m1.m31;
    m32 = m1.m32;
    m33 = m1.m33;
    return this;
  }

  /**
   * Initializes a Matrix4f from the rotation matrix and translation.
   * 
   * @param m1
   *        The rotation matrix representing the rotational components
   * @param t
   *        The translational components of the matrix
   */
  public void setMV(M3 m1, T3 t) {
    setM33(m1);
    setTranslation(t);
    m33 = 1;
  }

  /**
   * Sets the rotational component (upper 3x3) of this matrix to the matrix
   * values in the single precision Matrix3f argument; the other elements of
   * this matrix are initialized as if this were an identity matrix (ie, affine
   * matrix with no translational component).
   * 
   * @param m1
   *        the 3x3 matrix
   */
  public void setToM3(M34 m1) {
    setM33(m1);
    m03 = m13 = m23 = m30 = m31 = m32 = 0.0f;
    m33 = 1.0f;
  }

  /**
   * Sets the rotational component (upper 3x3) of this matrix 
   * to a rotation given by an axis angle
   * 
   * @param a
   *        the axis and angle to be converted
   */
  public void setToAA(A4 a) {
    setIdentity();
    setAA33(a);
  }

  /**
   * Sets the values in this Matrix4f equal to the row-major array parameter
   * (ie, the first four elements of the array will be copied into the first row
   * of this matrix, etc.).
   * 
   * @param m
   */
  public void setA(float m[]) {
    m00 = m[0];
    m01 = m[1];
    m02 = m[2];
    m03 = m[3];
    m10 = m[4];
    m11 = m[5];
    m12 = m[6];
    m13 = m[7];
    m20 = m[8];
    m21 = m[9];
    m22 = m[10];
    m23 = m[11];
    m30 = m[12];
    m31 = m[13];
    m32 = m[14];
    m33 = m[15];
  }

  /**
   * Modifies the translational components of this matrix to the values of the
   * Vector3f argument; the other values of this matrix are not modified.
   * 
   * @param trans
   *        the translational component
   */
  public void setTranslation(T3 trans) {
    m03 = trans.x;
    m13 = trans.y;
    m23 = trans.z;
  }

  /**
   * Sets the specified element of this matrix4f to the value provided.
   * 
   * @param row
   *        the row number to be modified (zero indexed)
   * @param col
   *        the column number to be modified (zero indexed)
   * @param v
   *        the new value
   */
  public void setElement(int row, int col, float v) {
    if (row < 3 && col < 3) {
      set33(row, col, v);
      return;
    }
    if (row > 3 || col > 3)
      err();
    switch (row) {
    case 0:
      m03 = v;
      return;
    case 1:
      m13 = v;
      return;
    case 2:
      m23 = v;
      return;
    }
    switch (col) {
    case 0:
      m30 = v;
      return;
    case 1:
      m31 = v;
      return;
    case 2:
      m32 = v;
      return;
    case 3:
      m33 = v;
      return;
    }
  }

  /**
   * Retrieves the value at the specified row and column of this matrix.
   * 
   * @param row
   *        the row number to be retrieved (zero indexed)
   * @param col
   *        the column number to be retrieved (zero indexed)
   * @return the value at the indexed element
   */
  public float getElement(int row, int col) {
    if (row < 3 && col < 3)
      return get33(row, col);
    if (row > 3 || col > 3) {
      err();
      return 0;
    }
    switch (row) {
    case 0:
      return m03;
    case 1:
      return m13;
    case 2:
      return m23;
    default:
      switch (col) {
      case 0:
        return m30;
      case 1:
        return m31;
      case 2:
        return m32;
      default:
        return m33;
      }
    }
  }

  /**
   * Retrieves the translational components of this matrix.
   * 
   * @param trans
   *        the vector that will receive the translational component
   */
  public void getTranslation(T3 trans) {
    trans.x = m03;
    trans.y = m13;
    trans.z = m23;
  }

  /**
   * Gets the upper 3x3 values of this matrix and places them into the matrix
   * m1.
   * 
   * @param m1
   *        The matrix that will hold the values
   */
  public void getRotationScale(M3 m1) {
    m1.m00 = m00;
    m1.m01 = m01;
    m1.m02 = m02;
    m1.m10 = m10;
    m1.m11 = m11;
    m1.m12 = m12;
    m1.m20 = m20;
    m1.m21 = m21;
    m1.m22 = m22;
  }

  /**
   * Replaces the upper 3x3 matrix values of this matrix with the values in the
   * matrix m1.
   * 
   * @param m1
   *        The matrix that will be the new upper 3x3
   */
  public void setRotationScale(M3 m1) {
    m00 = m1.m00;
    m01 = m1.m01;
    m02 = m1.m02;
    m10 = m1.m10;
    m11 = m1.m11;
    m12 = m1.m12;
    m20 = m1.m20;
    m21 = m1.m21;
    m22 = m1.m22;
  }

  /**
   * Sets the specified row of this matrix4f to the four values provided.
   * 
   * @param row
   *        the row number to be modified (zero indexed)
   * @param v
   *        the replacement row
   */
  public void setRowA(int row, float v[]) {
    if (row < 3)
      setRow33(row, v);
    switch (row) {
    case 0:
      m03 = v[3];
      return;
    case 1:
      m13 = v[3];
      return;
    case 2:
      m23 = v[3];
      return;
    case 3:
      m30 = v[0];
      m31 = v[1];
      m32 = v[2];
      m33 = v[3];
      return;
    }
    err();
  }

  /**
   * Copies the matrix values in the specified row into the array parameter.
   * 
   * @param row
   *        the matrix row
   * @param v
   *        The array into which the matrix row values will be copied
   */
  @Override
  public void getRow(int row, float v[]) {
    if (row < 3)
      getRow33(row, v);
    switch (row) {
    case 0:
      v[3] = m03;
      return;
    case 1:
      v[3] = m13;
      return;
    case 2:
      v[3] = m23;
      return;
    case 3:
      v[0] = m30;
      v[1] = m31;
      v[2] = m32;
      v[3] = m33;
      return;
    }
    err();
  }

  /**
   * Sets the specified column of this matrix4f to the four values provided.
   * 
   * @param column
   *        the column number to be modified (zero indexed)
   * @param x
   *        the first row element
   * @param y
   *        the second row element
   * @param z
   *        the third row element
   * @param w
   *        the fourth row element
   */
  public void setColumn4(int column, float x, float y, float z, float w) {
    if (column == 0) {
      m00 = x;
      m10 = y;
      m20 = z;
      m30 = w;
    } else if (column == 1) {
      m01 = x;
      m11 = y;
      m21 = z;
      m31 = w;
    } else if (column == 2) {
      m02 = x;
      m12 = y;
      m22 = z;
      m32 = w;
    } else if (column == 3) {
      m03 = x;
      m13 = y;
      m23 = z;
      m33 = w;
    } else {
      err();
    }
  }

  /**
   * Sets the specified column of this matrix4f to the four values provided.
   * 
   * @param column
   *        the column number to be modified (zero indexed)
   * @param v
   *        the replacement column
   */
  public void setColumnA(int column, float v[]) {
    if (column < 3)
      setColumn33(column, v);
    switch (column) {
    case 0:
      m30 = v[3];
      return;
    case 1:
      m31 = v[3];
      return;
    case 2:
      m32 = v[3];
      return;
    case 3:
      m03 = v[0];
      m13 = v[1];
      m23 = v[2];
      m33 = v[3];
      return;
    default:
      err();
    }
  }

  /**
   * Copies the matrix values in the specified column into the array parameter.
   * 
   * @param column
   *        the matrix column
   * @param v
   *        The array into which the matrix column values will be copied
   */
  public void getColumn(int column, float v[]) {
    if (column < 3)
      getColumn33(column, v);
    switch (column) {
    case 0:
      v[3] = m30;
      return;
    case 1:
      v[3] = m31;
      return;
    case 2:
      v[3] = m32;
      return;
    case 3:
      v[0] = m03;
      v[1] = m13;
      v[2] = m23;
      v[3] = m33;
      return;
    default:
      err();
    }
  }

  /**
   * Sets the value of this matrix to the matrix difference of itself and matrix
   * m1 (this = this - m1).
   * 
   * @param m1
   *        the other matrix
   */
  public void sub(M4 m1) {
    sub33(m1);
    m03 -= m1.m03;
    m13 -= m1.m13;
    m23 -= m1.m23;
    m30 -= m1.m30;
    m31 -= m1.m31;
    m32 -= m1.m32;
    m33 -= m1.m33;
  }
  
  /**
   * add to translation
   * 
   * @param pt
   */
  public void add(T3 pt) {
    m03 += pt.x;
    m13 += pt.y;
    m23 += pt.z;
  }

  /**
   * Sets the value of this matrix to its transpose.
   */
  public void transpose() {
    transpose33();
    float tmp = m03;
    m03 = m30;
    m30 = tmp;

    tmp = m13;
    m13 = m31;
    m31 = tmp;

    tmp = m23;
    m23 = m32;
    m32 = tmp;
  }

  /**
   * Sets the value of this matrix to its inverse.
   * @return this
   */
  public M4 invert() {
    float s = determinant4();
    if (s == 0.0)
      return this;
    s = 1 / s;
    // alias-safe way.
    // less *,+,- calculation than expanded expression.
    set(m11 * (m22 * m33 - m23 * m32) + m12 * (m23 * m31 - m21 * m33) + m13
        * (m21 * m32 - m22 * m31), m21 * (m02 * m33 - m03 * m32) + m22
        * (m03 * m31 - m01 * m33) + m23 * (m01 * m32 - m02 * m31), m31
        * (m02 * m13 - m03 * m12) + m32 * (m03 * m11 - m01 * m13) + m33
        * (m01 * m12 - m02 * m11), m01 * (m13 * m22 - m12 * m23) + m02
        * (m11 * m23 - m13 * m21) + m03 * (m12 * m21 - m11 * m22),

    m12 * (m20 * m33 - m23 * m30) + m13 * (m22 * m30 - m20 * m32) + m10
        * (m23 * m32 - m22 * m33), m22 * (m00 * m33 - m03 * m30) + m23
        * (m02 * m30 - m00 * m32) + m20 * (m03 * m32 - m02 * m33), m32
        * (m00 * m13 - m03 * m10) + m33 * (m02 * m10 - m00 * m12) + m30
        * (m03 * m12 - m02 * m13), m02 * (m13 * m20 - m10 * m23) + m03
        * (m10 * m22 - m12 * m20) + m00 * (m12 * m23 - m13 * m22),

    m13 * (m20 * m31 - m21 * m30) + m10 * (m21 * m33 - m23 * m31) + m11
        * (m23 * m30 - m20 * m33), m23 * (m00 * m31 - m01 * m30) + m20
        * (m01 * m33 - m03 * m31) + m21 * (m03 * m30 - m00 * m33), m33
        * (m00 * m11 - m01 * m10) + m30 * (m01 * m13 - m03 * m11) + m31
        * (m03 * m10 - m00 * m13), m03 * (m11 * m20 - m10 * m21) + m00
        * (m13 * m21 - m11 * m23) + m01 * (m10 * m23 - m13 * m20),

    m10 * (m22 * m31 - m21 * m32) + m11 * (m20 * m32 - m22 * m30) + m12
        * (m21 * m30 - m20 * m31), m20 * (m02 * m31 - m01 * m32) + m21
        * (m00 * m32 - m02 * m30) + m22 * (m01 * m30 - m00 * m31), m30
        * (m02 * m11 - m01 * m12) + m31 * (m00 * m12 - m02 * m10) + m32
        * (m01 * m10 - m00 * m11), m00 * (m11 * m22 - m12 * m21) + m01
        * (m12 * m20 - m10 * m22) + m02 * (m10 * m21 - m11 * m20));
    scale(s);
    return this;
  }

  /**
   * Sets 16 values
   * 
   * @param m00
   * @param m01
   * @param m02
   * @param m03
   * @param m10
   * @param m11
   * @param m12
   * @param m13
   * @param m20
   * @param m21
   * @param m22
   * @param m23
   * @param m30
   * @param m31
   * @param m32
   * @param m33
   */
  private void set(float m00, float m01, float m02, float m03, float m10,
                   float m11, float m12, float m13, float m20, float m21,
                   float m22, float m23, float m30, float m31, float m32,
                   float m33) {
    this.m00 = m00;
    this.m01 = m01;
    this.m02 = m02;
    this.m03 = m03;
    this.m10 = m10;
    this.m11 = m11;
    this.m12 = m12;
    this.m13 = m13;
    this.m20 = m20;
    this.m21 = m21;
    this.m22 = m22;
    this.m23 = m23;
    this.m30 = m30;
    this.m31 = m31;
    this.m32 = m32;
    this.m33 = m33;
  }
  /**
   * Computes the determinant of this matrix.
   * 
   * @return the determinant of the matrix
   */
  public float determinant4() {
    // less *,+,- calculation than expanded expression.
    return (m00 * m11 - m01 * m10) * (m22 * m33 - m23 * m32)
        - (m00 * m12 - m02 * m10) * (m21 * m33 - m23 * m31)
        + (m00 * m13 - m03 * m10) * (m21 * m32 - m22 * m31)
        + (m01 * m12 - m02 * m11) * (m20 * m33 - m23 * m30)
        - (m01 * m13 - m03 * m11) * (m20 * m32 - m22 * m30)
        + (m02 * m13 - m03 * m12) * (m20 * m31 - m21 * m30);

  }

  /**
   * Multiplies each element of this matrix by a scalar.
   * 
   * @param scalar
   *        The scalar multiplier.
   */
  public void scale(float scalar) {
    mul33(scalar);
    m03 *= scalar;
    m13 *= scalar;
    m23 *= scalar;
    m30 *= scalar;
    m31 *= scalar;
    m32 *= scalar;
    m33 *= scalar;
  }

  /**
   * Sets the value of this matrix to the result of multiplying itself with
   * matrix m1.
   * 
   * @param m1
   *        the other matrix
   */
  public void mul(M4 m1) {
    mul2(this, m1);
  }

  /**
   * Sets the value of this matrix to the result of multiplying the two argument
   * matrices together.
   * 
   * @param m1
   *        the first matrix
   * @param m2
   *        the second matrix
   */
  public void mul2(M4 m1, M4 m2) {
    // alias-safe way.
    set(m1.m00 * m2.m00 + m1.m01 * m2.m10 + m1.m02 * m2.m20 + m1.m03 * m2.m30,
        m1.m00 * m2.m01 + m1.m01 * m2.m11 + m1.m02 * m2.m21 + m1.m03 * m2.m31,
        m1.m00 * m2.m02 + m1.m01 * m2.m12 + m1.m02 * m2.m22 + m1.m03 * m2.m32,
        m1.m00 * m2.m03 + m1.m01 * m2.m13 + m1.m02 * m2.m23 + m1.m03 * m2.m33,

        m1.m10 * m2.m00 + m1.m11 * m2.m10 + m1.m12 * m2.m20 + m1.m13 * m2.m30,
        m1.m10 * m2.m01 + m1.m11 * m2.m11 + m1.m12 * m2.m21 + m1.m13 * m2.m31,
        m1.m10 * m2.m02 + m1.m11 * m2.m12 + m1.m12 * m2.m22 + m1.m13 * m2.m32,
        m1.m10 * m2.m03 + m1.m11 * m2.m13 + m1.m12 * m2.m23 + m1.m13 * m2.m33,

        m1.m20 * m2.m00 + m1.m21 * m2.m10 + m1.m22 * m2.m20 + m1.m23 * m2.m30,
        m1.m20 * m2.m01 + m1.m21 * m2.m11 + m1.m22 * m2.m21 + m1.m23 * m2.m31,
        m1.m20 * m2.m02 + m1.m21 * m2.m12 + m1.m22 * m2.m22 + m1.m23 * m2.m32,
        m1.m20 * m2.m03 + m1.m21 * m2.m13 + m1.m22 * m2.m23 + m1.m23 * m2.m33,

        m1.m30 * m2.m00 + m1.m31 * m2.m10 + m1.m32 * m2.m20 + m1.m33 * m2.m30,
        m1.m30 * m2.m01 + m1.m31 * m2.m11 + m1.m32 * m2.m21 + m1.m33 * m2.m31,
        m1.m30 * m2.m02 + m1.m31 * m2.m12 + m1.m32 * m2.m22 + m1.m33 * m2.m32,
        m1.m30 * m2.m03 + m1.m31 * m2.m13 + m1.m32 * m2.m23 + m1.m33 * m2.m33);
  }

  /**
   * Transform the vector vec using this Matrix4f and place the result back into
   * vec.
   * 
   * @param vec
   *        the single precision vector to be transformed
   */
  public void transform(T4 vec) {
    transform2(vec, vec);
  }

  /**
   * Transform the vector vec using this Matrix4f and place the result into
   * vecOut.
   * 
   * @param vec
   *        the single precision vector to be transformed
   * @param vecOut
   *        the vector into which the transformed values are placed
   */
  public void transform2(T4 vec, T4 vecOut) {
    // alias-safe
    vecOut.set4(m00 * vec.x + m01 * vec.y + m02 * vec.z + m03 * vec.w, m10
        * vec.x + m11 * vec.y + m12 * vec.z + m13 * vec.w, m20 * vec.x + m21
        * vec.y + m22 * vec.z + m23 * vec.w, m30 * vec.x + m31 * vec.y + m32
        * vec.z + m33 * vec.w);
  }

  /**
   * Transforms the point parameter with this Matrix4f and places the result
   * back into point. The fourth element of the point input parameter is assumed
   * to be one.
   * 
   * @param point
   *        the input point to be transformed.
   */
  public void rotTrans(T3 point) {
    rotTrans2(point, point);
  }

  /**
   * Transforms the point parameter with this Matrix4f and places the result
   * into pointOut. The fourth element of the point input parameter is assumed to
   * be one. point may be pointOut
   * 
   * @param point
   *        the input point to be transformed.
   * @param pointOut
   *        the transformed point
   * @return pointOut
   */
  public T3 rotTrans2(T3 point, T3 pointOut) {
      pointOut.set(
          m00 * point.x + m01 * point.y + m02 * point.z + m03, 
          m10 * point.x + m11 * point.y + m12 * point.z + m13, 
          m20 * point.x + m21 * point.y + m22 * point.z + m23);
      return pointOut;
  }

  /**
   * Sets the value of this matrix to a rotation matrix about the w axis by the
   * passed angle.
   * 
   * @param angle
   *        the angle to rotate about the W axis in radians
   * @return this
   */
  public M4 setAsXYRotation(float angle) {
    setIdentity();
    double c = Math.cos(angle);
    double s = Math.sin(angle);
    m22 = (float) c;
    m23 = (float) -s;
    m32 = (float) s;
    m33 = (float) c;
    return this;
  }

  /**
   * Sets the value of this matrix to a rotation matrix about the w axis by the
   * passed angle.
   * 
   * @param angle
   *        the angle to rotate about the W axis in radians
   * @return this
   */
  public M4 setAsYZRotation(float angle) {
    setIdentity();
    double c = Math.cos(angle);
    double s = Math.sin(angle);
    m00 = (float) c;
    m03 = (float) -s;
    m30 = (float) s;
    m33 = (float) c;
    return this;
  }

  /**
   * Sets the value of this matrix to a rotation matrix about the w axis by the
   * passed angle.
   * 
   * @param angle
   *        the angle to rotate about the W axis in radians
   * @return this
   */
  public M4 setAsXZRotation(float angle) {
    setIdentity();
    double c = Math.cos(angle);
    double s = Math.sin(angle);
    m11 = (float) c;
    m13 = (float) -s;
    m31 = (float) s;
    m33 = (float) c;
    return this;
  }

  /**
   * Returns true if the Object o is of type Matrix4f and all of the data
   * members of t1 are equal to the corresponding data members in this Matrix4f.
   * 
   * @param o
   *        the object with which the comparison is made.
   */
  @Override
  public boolean equals(Object o) {
    if (!(o instanceof M4))
      return false;
    M4 m = (M4) o;
    return (this.m00 == m.m00 && this.m01 == m.m01 && this.m02 == m.m02
        && this.m03 == m.m03 && this.m10 == m.m10 && this.m11 == m.m11
        && this.m12 == m.m12 && this.m13 == m.m13 && this.m20 == m.m20
        && this.m21 == m.m21 && this.m22 == m.m22 && this.m23 == m.m23
        && this.m30 == m.m30 && this.m31 == m.m31 && this.m32 == m.m32 && this.m33 == m.m33);
  }

  /**
   * Returns a hash number based on the data values in this object. Two
   * different Matrix4f objects with identical data values (ie, returns true for
   * equals(Matrix4f) ) will return the same hash number. Two objects with
   * different data members may return the same hash value, although this is not
   * likely.
   * 
   * @return the integer hash value
   */
  @Override
  public int hashCode() {
    return T3.floatToIntBits(m00) ^ T3.floatToIntBits(m01)
        ^ T3.floatToIntBits(m02) ^ T3.floatToIntBits(m03)
        ^ T3.floatToIntBits(m10) ^ T3.floatToIntBits(m11)
        ^ T3.floatToIntBits(m12) ^ T3.floatToIntBits(m13)
        ^ T3.floatToIntBits(m20) ^ T3.floatToIntBits(m21)
        ^ T3.floatToIntBits(m22) ^ T3.floatToIntBits(m23)
        ^ T3.floatToIntBits(m30) ^ T3.floatToIntBits(m31)
        ^ T3.floatToIntBits(m32) ^ T3.floatToIntBits(m33);
  }

  /**
   * Returns a string that contains the values of this Matrix4f.
   * 
   * @return the String representation
   */
  @Override
  public String toString() {
    return "[[" + m00 + "\t" + m01 + "\t" + m02 + "\t" + m03 + "]"
        + "\n  [" + m10 + "\t" + m11 + "\t" + m12 + "\t" + m13 + "]" + "\n  ["
        + m20 + "\t" + m21 + "\t" + m22 + "\t" + m23 + "]" + "\n  [" + m30
        + "\t" + m31 + "\t" + m32 + "\t" + m33 + "]]";
  }
  public M4 round(float f) {
    m00 = rnd(m00, f);
    m01 = rnd(m01, f);
    m02 = rnd(m02, f);
    m03 = rnd(m03, f);
    m10 = rnd(m10, f);
    m11 = rnd(m11, f);
    m12 = rnd(m12, f);
    m13 = rnd(m13, f);
    m20 = rnd(m20, f);
    m21 = rnd(m21, f);
    m22 = rnd(m22, f);
    m23 = rnd(m23, f);
    m30 = rnd(m30, f);
    m31 = rnd(m31, f);
    m32 = rnd(m32, f);
    m33 = rnd(m33, f);
    return this;
  }

  private float rnd(float n, float f) {
    return (Math.abs(n) < f ? 0 : n);
  }
}
