package java3d;

public class Matrix3d {
	public double m00;
	public double m01;
	public double m02;
	public double m10;
	public double m11;
	public double m12;
	public double m20;
	public double m21;
	public double m22;

	// コンストラクタ
	public Matrix3d() {
		m00 = 0.0;
		m01 = 0.0;
		m02 = 0.0;
		m10 = 0.0;
		m11 = 0.0;
		m12 = 0.0;
		m20 = 0.0;
		m21 = 0.0;
		m22 = 0.0;
	}

	public Matrix3d(double[] v) {
		m00 = v[0];
		m01 = v[1];
		m02 = v[2];
		m10 = v[3];
		m11 = v[4];
		m12 = v[5];
		m20 = v[6];
		m21 = v[7];
		m22 = v[8];
	}

	public Matrix3d(double m00, double m01, double m02, double m10, double m11,
			double m12, double m20, double m21, double m22) {
		this.m00 = m00;
		this.m01 = m01;
		this.m02 = m02;
		this.m10 = m10;
		this.m11 = m11;
		this.m12 = m12;
		this.m20 = m20;
		this.m21 = m21;
		this.m22 = m22;
	}

	public Matrix3d(Matrix3d m1) {
		this.m00 = m1.m00;
		this.m01 = m1.m01;
		this.m02 = m1.m02;
		this.m10 = m1.m10;
		this.m11 = m1.m11;
		this.m12 = m1.m12;
		this.m20 = m1.m20;
		this.m21 = m1.m21;
		this.m22 = m1.m22;
	}

	public Matrix3d clone() {
		return new Matrix3d(m00, m01, m02, m10, m11, m12, m20, m21, m22);
	}

	/** 各要素に足す */
	public void add(Matrix3d m1) {
		this.m00 += m1.m00;
		this.m01 += m1.m01;
		this.m02 += m1.m02;
		this.m10 += m1.m10;
		this.m11 += m1.m11;
		this.m12 += m1.m12;
		this.m20 += m1.m20;
		this.m21 += m1.m21;
		this.m22 += m1.m22;
	}

	/** 二つの行列の和を格納する */
	public void add(Matrix3d m1, Matrix3d m2) {
		this.m00 = m1.m00 + m2.m00;
		this.m01 = m1.m01 + m2.m01;
		this.m02 = m1.m02 + m2.m02;
		this.m10 = m1.m10 + m2.m10;
		this.m11 = m1.m11 + m2.m11;
		this.m12 = m1.m12 + m2.m12;
		this.m20 = m1.m20 + m2.m20;
		this.m21 = m1.m21 + m2.m21;
		this.m22 = m1.m22 + m2.m22;
	}

	/** x軸周りにangle回転 */
	public void rotX(double angle) {
        double d1 = Math.sin(angle);
        double d2 = Math.cos(angle);
        m00 = 1.0D;
        m01 = 0.0D;
        m02 = 0.0D;
        m10 = 0.0D;
        m11 = d2;
        m12 = -d1;
        m20 = 0.0D;
        m21 = d1;
        m22 = d2;
	}

	/** y軸周りにangle回転 */
	public void rotY(double angle) {
        double d1 = Math.sin(angle);
        double d2 = Math.cos(angle);
        m00 = d2;
        m01 = 0.0D;
        m02 = d1;
        m10 = 0.0D;
        m11 = 1.0D;
        m12 = 0.0D;
        m20 = -d1;
        m21 = 0.0D;
        m22 = d2;
	}

	/** z軸周りにangle1回転 */
	public void rotZ(double angle) {
        double d1 = Math.sin(angle);
        double d2 = Math.cos(angle);
        m00 = d2;
        m01 = -d1;
        m02 = 0.0D;
        m10 = d1;
        m11 = d2;
        m12 = 0.0D;
        m20 = 0.0D;
        m21 = 0.0D;
        m22 = 1.0D;
	}

	/** 自分に引数の行列を掛けたものを格納する */
	public void mul(Matrix3d mat) {
		double t00 = m00 * mat.m00 + m01 * mat.m10 + m02 * mat.m20;
		double t01 = m00 * mat.m01 + m01 * mat.m11 + m02 * mat.m21;
		double t02 = m00 * mat.m02 + m01 * mat.m12 + m02 * mat.m22;
		double t10 = m10 * mat.m10 + m11 * mat.m10 + m12 * mat.m20;
		double t11 = m10 * mat.m11 + m11 * mat.m11 + m12 * mat.m21;
		double t12 = m10 * mat.m12 + m11 * mat.m12 + m12 * mat.m22;
		double t20 = m20 * mat.m20 + m21 * mat.m10 + m22 * mat.m20;
		double t21 = m20 * mat.m21 + m21 * mat.m11 + m22 * mat.m21;
		double t22 = m20 * mat.m22 + m21 * mat.m12 + m22 * mat.m22;

		set(t00, t01, t02, t10, t11, t12, t20, t21, t22);
	}
	
	public void mul(Matrix3d mat1, Matrix3d mat2) {
		this.set(mat1.m00, mat1.m01, mat1.m02, mat1.m10, mat1.m11, mat1.m12, mat1.m20, mat1.m21, mat1.m22);
		mul(mat2);
	}

	public void set(double t00, double t01, double t02, double t10, double t11,
			double t12, double t20, double t21, double t22) {
		m00 = t00;
		m01 = t01;
		m02 = t02;
		m10 = t10;
		m11 = t11;
		m12 = t12;
		m20 = t20;
		m21 = t21;
		m22 = t22;
	}

	/**
	 * Sets the value of this matrix to the matrix inverse of the passed matrix
	 * m1.
	 * 
	 * @param m1
	 *            the matrix to be inverted
	 */
	public final void invert(Matrix3d m1) {
		invertGeneral(m1);
	}

	/**
	 * Inverts this matrix in place.
	 */
	public final void invert() {
		invertGeneral(this);
	}

	/**
	 * General invert routine. Inverts m1 and places the result in "this". Note
	 * that this routine handles both the "this" version and the non-"this"
	 * version.
	 * 
	 * Also note that since this routine is slow anyway, we won't worry about
	 * allocating a little bit of garbage.
	 */
	private final void invertGeneral(Matrix3d m1) {
		double result[] = new double[9];
		int row_perm[] = new int[3];
		int i, r, c;
		double[] tmp = new double[9]; // scratch matrix

		// Use LU decomposition and backsubstitution code specifically
		// for floating-point 3x3 matrices.

		// Copy source matrix to t1tmp
		tmp[0] = m1.m00;
		tmp[1] = m1.m01;
		tmp[2] = m1.m02;

		tmp[3] = m1.m10;
		tmp[4] = m1.m11;
		tmp[5] = m1.m12;

		tmp[6] = m1.m20;
		tmp[7] = m1.m21;
		tmp[8] = m1.m22;

		// Calculate LU decomposition: Is the matrix singular?
		if (!luDecomposition(tmp, row_perm)) {
			// Matrix has no inverse
			// throw new
			// SingularMatrixException(VecMathI18N.getString("Matrix3d12"));
		}

		// Perform back substitution on the identity matrix
		for (i = 0; i < 9; i++)
			result[i] = 0.0;
		result[0] = 1.0;
		result[4] = 1.0;
		result[8] = 1.0;
		luBacksubstitution(tmp, row_perm, result);

		this.m00 = result[0];
		this.m01 = result[1];
		this.m02 = result[2];

		this.m10 = result[3];
		this.m11 = result[4];
		this.m12 = result[5];

		this.m20 = result[6];
		this.m21 = result[7];
		this.m22 = result[8];

	}

	/**
	 * Given a 3x3 array "matrix0", this function replaces it with the LU
	 * decomposition of a row-wise permutation of itself. The input parameters
	 * are "matrix0" and "dimen". The array "matrix0" is also an output
	 * parameter. The vector "row_perm[3]" is an output parameter that contains
	 * the row permutations resulting from partial pivoting. The output
	 * parameter "even_row_xchg" is 1 when the number of row exchanges is even,
	 * or -1 otherwise. Assumes data type is always double.
	 * 
	 * This function is similar to luDecomposition, except that it is tuned
	 * specifically for 3x3 matrices.
	 * 
	 * @return true if the matrix is nonsingular, or false otherwise.
	 */
	//
	// Reference: Press, Flannery, Teukolsky, Vetterling,
	// _Numerical_Recipes_in_C_, Cambridge University Press,
	// 1988, pp 40-45.
	//
	static boolean luDecomposition(double[] matrix0, int[] row_perm) {

		double row_scale[] = new double[3];

		// Determine implicit scaling information by looping over rows
		{
			int i, j;
			int ptr, rs;
			double big, temp;

			ptr = 0;
			rs = 0;

			// For each row ...
			i = 3;
			while (i-- != 0) {
				big = 0.0;

				// For each column, find the largest element in the row
				j = 3;
				while (j-- != 0) {
					temp = matrix0[ptr++];
					temp = Math.abs(temp);
					if (temp > big) {
						big = temp;
					}
				}

				// Is the matrix singular?
				if (big == 0.0) {
					return false;
				}
				row_scale[rs++] = 1.0 / big;
			}
		}

		{
			int j;
			int mtx;

			mtx = 0;

			// For all columns, execute Crout's method
			for (j = 0; j < 3; j++) {
				int i, imax, k;
				int target, p1, p2;
				double sum, big, temp;

				// Determine elements of upper diagonal matrix U
				for (i = 0; i < j; i++) {
					target = mtx + (3 * i) + j;
					sum = matrix0[target];
					k = i;
					p1 = mtx + (3 * i);
					p2 = mtx + j;
					while (k-- != 0) {
						sum -= matrix0[p1] * matrix0[p2];
						p1++;
						p2 += 3;
					}
					matrix0[target] = sum;
				}

				// Search for largest pivot element and calculate
				// intermediate elements of lower diagonal matrix L.
				big = 0.0;
				imax = -1;
				for (i = j; i < 3; i++) {
					target = mtx + (3 * i) + j;
					sum = matrix0[target];
					k = j;
					p1 = mtx + (3 * i);
					p2 = mtx + j;
					while (k-- != 0) {
						sum -= matrix0[p1] * matrix0[p2];
						p1++;
						p2 += 3;
					}
					matrix0[target] = sum;

					// Is this the best pivot so far?
					if ((temp = row_scale[i] * Math.abs(sum)) >= big) {
						big = temp;
						imax = i;
					}
				}

				if (imax < 0) {
					// throw new
					// RuntimeException(VecMathI18N.getString("Matrix3d13"));
				}

				// Is a row exchange necessary?
				if (j != imax) {
					// Yes: exchange rows
					k = 3;
					p1 = mtx + (3 * imax);
					p2 = mtx + (3 * j);
					while (k-- != 0) {
						temp = matrix0[p1];
						matrix0[p1++] = matrix0[p2];
						matrix0[p2++] = temp;
					}

					// Record change in scale factor
					row_scale[imax] = row_scale[j];
				}

				// Record row permutation
				row_perm[j] = imax;

				// Is the matrix singular
				if (matrix0[(mtx + (3 * j) + j)] == 0.0) {
					return false;
				}

				// Divide elements of lower diagonal matrix L by pivot
				if (j != (3 - 1)) {
					temp = 1.0 / (matrix0[(mtx + (3 * j) + j)]);
					target = mtx + (3 * (j + 1)) + j;
					i = 2 - j;
					while (i-- != 0) {
						matrix0[target] *= temp;
						target += 3;
					}
				}
			}
		}

		return true;
	}

	/**
	 * Solves a set of linear equations. The input parameters "matrix1", and
	 * "row_perm" come from luDecompostionD3x3 and do not change here. The
	 * parameter "matrix2" is a set of column vectors assembled into a 3x3
	 * matrix of floating-point values. The procedure takes each column of
	 * "matrix2" in turn and treats it as the right-hand side of the matrix
	 * equation Ax = LUx = b. The solution vector replaces the original column
	 * of the matrix.
	 * 
	 * If "matrix2" is the identity matrix, the procedure replaces its contents
	 * with the inverse of the matrix from which "matrix1" was originally
	 * derived.
	 */
	//
	// Reference: Press, Flannery, Teukolsky, Vetterling,
	// _Numerical_Recipes_in_C_, Cambridge University Press,
	// 1988, pp 44-45.
	//
	static void luBacksubstitution(double[] matrix1, int[] row_perm,
			double[] matrix2) {

		int i, ii, ip, j, k;
		int rp;
		int cv, rv;

		// rp = row_perm;
		rp = 0;

		// For each column vector of matrix2 ...
		for (k = 0; k < 3; k++) {
			// cv = &(matrix2[0][k]);
			cv = k;
			ii = -1;

			// Forward substitution
			for (i = 0; i < 3; i++) {
				double sum;

				ip = row_perm[rp + i];
				sum = matrix2[cv + 3 * ip];
				matrix2[cv + 3 * ip] = matrix2[cv + 3 * i];
				if (ii >= 0) {
					// rv = &(matrix1[i][0]);
					rv = i * 3;
					for (j = ii; j <= i - 1; j++) {
						sum -= matrix1[rv + j] * matrix2[cv + 3 * j];
					}
				} else if (sum != 0.0) {
					ii = i;
				}
				matrix2[cv + 3 * i] = sum;
			}

			// Backsubstitution
			// rv = &(matrix1[3][0]);
			rv = 2 * 3;
			matrix2[cv + 3 * 2] /= matrix1[rv + 2];

			rv -= 3;
			matrix2[cv + 3 * 1] = (matrix2[cv + 3 * 1] - matrix1[rv + 2]
					* matrix2[cv + 3 * 2])
					/ matrix1[rv + 1];

			rv -= 3;
			matrix2[cv + 4 * 0] = (matrix2[cv + 3 * 0] - matrix1[rv + 1]
					* matrix2[cv + 3 * 1] - matrix1[rv + 2]
					* matrix2[cv + 3 * 2])
					/ matrix1[rv + 0];

		}
	}
		
	/**
	* Multiply this matrix by the tuple t and place the result
	* back into the tuple (t = this*t).
	* @param t  the tuple to be multiplied by this matrix and then replaced
	*/
	public final void transform(Tuple3d t) {
		double x,y,z;
		x = m00* t.x + m01*t.y + m02*t.z;
		y = m10* t.x + m11*t.y + m12*t.z;
		z = m20* t.x + m21*t.y + m22*t.z;
		t.set(x,y,z);
	}
}
