Matrix.java

package org.drip.numerical.linearalgebra;

/*
 * -*- mode: java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 */

/*!
 * Copyright (C) 2020 Lakshmi Krishnamurthy
 * Copyright (C) 2019 Lakshmi Krishnamurthy
 * Copyright (C) 2018 Lakshmi Krishnamurthy
 * Copyright (C) 2017 Lakshmi Krishnamurthy
 * Copyright (C) 2016 Lakshmi Krishnamurthy
 * Copyright (C) 2015 Lakshmi Krishnamurthy
 * Copyright (C) 2014 Lakshmi Krishnamurthy
 * Copyright (C) 2013 Lakshmi Krishnamurthy
 * 
 *  This file is part of DROP, an open-source library targeting analytics/risk, transaction cost analytics,
 *  	asset liability management analytics, capital, exposure, and margin analytics, valuation adjustment
 *  	analytics, and portfolio construction analytics within and across fixed income, credit, commodity,
 *  	equity, FX, and structured products. It also includes auxiliary libraries for algorithm support,
 *  	numerical analysis, numerical optimization, spline builder, model validation, statistical learning,
 *  	and computational support.
 *  
 *  	https://lakshmidrip.github.io/DROP/
 *  
 *  DROP is composed of three modules:
 *  
 *  - DROP Product Core - https://lakshmidrip.github.io/DROP-Product-Core/
 *  - DROP Portfolio Core - https://lakshmidrip.github.io/DROP-Portfolio-Core/
 *  - DROP Computational Core - https://lakshmidrip.github.io/DROP-Computational-Core/
 * 
 * 	DROP Product Core implements libraries for the following:
 * 	- Fixed Income Analytics
 * 	- Loan Analytics
 * 	- Transaction Cost Analytics
 * 
 * 	DROP Portfolio Core implements libraries for the following:
 * 	- Asset Allocation Analytics
 *  - Asset Liability Management Analytics
 * 	- Capital Estimation Analytics
 * 	- Exposure Analytics
 * 	- Margin Analytics
 * 	- XVA Analytics
 * 
 * 	DROP Computational Core implements libraries for the following:
 * 	- Algorithm Support
 * 	- Computation Support
 * 	- Function Analysis
 *  - Model Validation
 * 	- Numerical Analysis
 * 	- Numerical Optimizer
 * 	- Spline Builder
 *  - Statistical Learning
 * 
 * 	Documentation for DROP is Spread Over:
 * 
 * 	- Main                     => https://lakshmidrip.github.io/DROP/
 * 	- Wiki                     => https://github.com/lakshmiDRIP/DROP/wiki
 * 	- GitHub                   => https://github.com/lakshmiDRIP/DROP
 * 	- Repo Layout Taxonomy     => https://github.com/lakshmiDRIP/DROP/blob/master/Taxonomy.md
 * 	- Javadoc                  => https://lakshmidrip.github.io/DROP/Javadoc/index.html
 * 	- Technical Specifications => https://github.com/lakshmiDRIP/DROP/tree/master/Docs/Internal
 * 	- Release Versions         => https://lakshmidrip.github.io/DROP/version.html
 * 	- Community Credits        => https://lakshmidrip.github.io/DROP/credits.html
 * 	- Issues Catalog           => https://github.com/lakshmiDRIP/DROP/issues
 * 	- JUnit                    => https://lakshmidrip.github.io/DROP/junit/index.html
 * 	- Jacoco                   => https://lakshmidrip.github.io/DROP/jacoco/index.html
 * 
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *   	you may not use this file except in compliance with the License.
 *   
 *  You may obtain a copy of the License at
 *  	http://www.apache.org/licenses/LICENSE-2.0
 *  
 *  Unless required by applicable law or agreed to in writing, software
 *  	distributed under the License is distributed on an "AS IS" BASIS,
 *  	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  
 *  See the License for the specific language governing permissions and
 *  	limitations under the License.
 */

/**
 * <i>Matrix</i> implements Matrix manipulation routines. It exports the following functionality:
 *  <ul>
 *  	<li>
 * 			Matrix Inversion using Closed form solutions (for low-dimension matrices), or using Gaussian
 * 				elimination
 *  	</li>
 *  	<li>
 * 			Matrix Product
 *  	</li>
 *  	<li>
 * 			Matrix Diagonalization and Diagonal Pivoting
 *  	</li>
 *  	<li>
 * 			Matrix Regularization through Row Addition/Row Swap
 *  	</li>
 *  </ul>
 * 
 * <br><br>
 *  <ul>
 *		<li><b>Module </b> = <a href = "https://github.com/lakshmiDRIP/DROP/tree/master/ComputationalCore.md">Computational Core Module</a></li>
 *		<li><b>Library</b> = <a href = "https://github.com/lakshmiDRIP/DROP/tree/master/NumericalAnalysisLibrary.md">Numerical Analysis Library</a></li>
 *		<li><b>Project</b> = <a href = "https://github.com/lakshmiDRIP/DROP/tree/master/src/main/java/org/drip/numerical/README.md">Numerical Quadrature, Differentiation, Eigenization, Linear Algebra, and Utilities</a></li>
 *		<li><b>Package</b> = <a href = "https://github.com/lakshmiDRIP/DROP/tree/master/src/main/java/org/drip/numerical/linearalgebra/README.md">Linear Algebra Matrix Transform Library</a></li>
 *  </ul>
 * <br><br>
 *
 * @author Lakshmi Krishnamurthy
 */

public class Matrix {

	/**
	 * Lower Triangular Matrix
	 */

	public static int LOWER_TRIANGULAR = 1;

	/**
	 * Upper Triangular Matrix
	 */

	public static int UPPER_TRIANGULAR = 2;

	/**
	 * Lower+Upper Triangular Matrix
	 */

	public static int LOWER_AND_UPPER_TRIANGULAR = 3;

	/**
	 * Non Triangular Matrix
	 */

	public static int NON_TRIANGULAR = 0;

	/**
	 * Diagonalize the specified row in the source matrix, and apply comparable operations to the target
	 * 
	 * @param iQ Row in the Source Matrix
	 * @param aadblZ2XJack Source Matrix
	 * @param aadblZ2YJack Target Matrix
	 * 
	 * @return TRUE - Diagonalization was successful
	 */

	public static final boolean DiagonalizeRow (
		final int iQ,
		final double[][] aadblZ2XJack,
		final double[][] aadblZ2YJack)
	{
		if (0. != aadblZ2XJack[iQ][iQ]) return true;

		int iSize = aadblZ2XJack.length;
		int iP = iSize - 1;

		while (0. == aadblZ2XJack[iP][iQ] && iP >= 0) --iP;

		if (0 > iP) return false;

		for (int j = 0; j < iSize; ++j)
			aadblZ2XJack[iQ][j] += aadblZ2XJack[iP][j];

		aadblZ2YJack[iQ][iP] += 1.;
		return true;
	}

	/**
	 * Compute the Product of an Input Matrix and a Column
	 * 
	 * @param aadblA Matrix A
	 * @param adblB Array B
	 * 
	 * @return The Product
	 */

	public static final double[] Product (
		final double[][] aadblA,
		final double[] adblB)
	{
		if (null == aadblA || null == adblB) return null;

		int iNumACol = aadblA[0].length;
		int iNumProductCol = adblB.length;
		int iNumProductRow = aadblA.length;
		double[] adblProduct = new double[iNumProductRow];

		if (0 == iNumACol || iNumACol != adblB.length || 0 == iNumProductRow || 0 == iNumProductCol)
			return null;

		for (int iRow = 0; iRow < iNumProductRow; ++iRow) {
			adblProduct[iRow] = 0.;

			for (int i = 0; i < iNumACol; ++i) {
				if (!org.drip.numerical.common.NumberUtil.IsValid (aadblA[iRow][i]) ||
					!org.drip.numerical.common.NumberUtil.IsValid (adblB[i]))
					return null;

				adblProduct[iRow] += aadblA[iRow][i] * adblB[i];
			}
		}

		return adblProduct;
	}

	/**
	 * Compute the Product of an input column and a matrix
	 * 
	 * @param adblA Column A
	 * @param aadblB Matrix B
	 * 
	 * @return The Product
	 */

	public static final double[][] Product (
		final double[] adblA,
		final double[][] aadblB)
	{
		if (null == adblA || null == aadblB) return null;

		int iNumACol = adblA.length;
		int iNumProductCol = aadblB.length;
		double[][] aadblProduct = new double[iNumACol][iNumProductCol];

		if (0 == iNumACol || iNumACol != aadblB.length || 0 == iNumProductCol) return null;

		for (int iRow = 0; iRow < iNumACol; ++iRow) {
			for (int iCol = 0; iCol < iNumProductCol; ++iCol) {
				aadblProduct[iRow][iCol] = 0.;

				for (int i = 0; i < iNumACol; ++i) {
					if (!org.drip.numerical.common.NumberUtil.IsValid (adblA[iRow]) ||
						!org.drip.numerical.common.NumberUtil.IsValid (aadblB[i][iCol]))
						return null;

					aadblProduct[iRow][iCol] += adblA[iRow] * aadblB[i][iCol];
				}
			}
		}

		return aadblProduct;
	}

	/**
	 * Compute the Product of the input matrices
	 * 
	 * @param aadblA Matrix A
	 * @param aadblB Matrix B
	 * 
	 * @return The Product
	 */

	public static final double[][] Product (
		final double[][] aadblA,
		final double[][] aadblB)
	{
		if (null == aadblA || null == aadblB) return null;

		int iNumACol = aadblA[0].length;
		int iNumProductRow = aadblA.length;
		int iNumProductCol = aadblB[0].length;
		double[][] aadblProduct = new double[iNumProductRow][iNumProductCol];

		if (0 == iNumACol || iNumACol != aadblB.length || 0 == iNumProductRow || 0 == iNumProductCol)
			return null;

		for (int iRow = 0; iRow < iNumProductRow; ++iRow) {
			for (int iCol = 0; iCol < iNumProductCol; ++iCol) {
				aadblProduct[iRow][iCol] = 0.;

				for (int i = 0; i < iNumACol; ++i) {
					if (!org.drip.numerical.common.NumberUtil.IsValid (aadblA[iRow][i]) ||
						!org.drip.numerical.common.NumberUtil.IsValid (aadblB[i][iCol]))
						return null;

					aadblProduct[iRow][iCol] += aadblA[iRow][i] * aadblB[i][iCol];
				}
			}
		}

		return aadblProduct;
	}

	/**
	 * Make a Square Diagonal Matrix from a Row
	 * 
	 * @param adblA The Row Array
	 * 
	 * @return The corresponding Square Diagonal Matrix
	 */

	public static final double[][] MakeSquareDiagonal (
		final double[] adblA)
	{
		if (null == adblA) return null;

		int iNumElement = adblA.length;
		double[][] aadblDiagonal = 0 == iNumElement ? null : new double[iNumElement][iNumElement];

		if (0 == iNumElement) return null;

		for (int i = 0; i < iNumElement; ++i) {
			for (int j = 0; j < iNumElement; ++j)
				aadblDiagonal[i][j] = i == j ? adblA[i] : 0.;
		}

		return aadblDiagonal;
	}

	/**
	 * Invert a 2D Matrix using Cramer's Rule
	 * 
	 * @param aadblA Input 2D Matrix
	 * 
	 * @return The Inverted Matrix
	 */

	public static final double[][] Invert2DMatrixUsingCramerRule (
		final double[][] aadblA)
	{
		if (null == aadblA || 2 != aadblA.length || 2 != aadblA[0].length) return null;

		for (int i = 0; i < 2; ++i) {
			for (int j = 0; j < 2; ++j) {
				if (!org.drip.numerical.common.NumberUtil.IsValid (aadblA[i][j])) return null;
			}
		}

		double dblScale = aadblA[0][0] * aadblA[1][1] - aadblA[0][1] * aadblA[1][0];

		if (0. == dblScale) return null;

		return new double[][] {{aadblA[1][1] / dblScale, -1. * aadblA[0][1] / dblScale}, {-1. * aadblA[1][0]
			/ dblScale, aadblA[0][0] / dblScale}};
	}

	/**
	 * Regularize the specified diagonal entry of the input matrix using Row Swapping
	 * 
	 * @param mct The Input Matrix Complement Transform
	 * 
	 * @return The Regularization was successful
	 */

	public static final boolean RegularizeUsingRowSwap (
		final org.drip.numerical.linearalgebra.MatrixComplementTransform mct)
	{
		if (null == mct) return false;

		int iSize = mct.size();

		double[][] aadblSource = mct.getSource();

		double[][] aadblComplement = mct.getComplement();

		for (int iDiagonal = 0; iDiagonal < iSize; ++iDiagonal) {
			if (0. == aadblSource[iDiagonal][iDiagonal]) {
				int iSwapRow = iSize - 1;

				while (iSwapRow >= 0 && (0. == aadblSource[iSwapRow][iDiagonal] || 0. ==
					aadblSource[iDiagonal][iSwapRow]))
					--iSwapRow;

				if (0 > iSwapRow) {
					iSwapRow = 0;

					while (iSwapRow < iSize && 0. == aadblSource[iSwapRow][iDiagonal])
						++iSwapRow;

					if (iSwapRow >= iSize) return false;
				}

				for (int iCol = 0; iCol < iSize; ++iCol) {
					double dblComplementDiagonalEntry = aadblComplement[iDiagonal][iCol];
					aadblComplement[iDiagonal][iCol] = aadblComplement[iSwapRow][iCol];
					aadblComplement[iSwapRow][iCol] = dblComplementDiagonalEntry;
					double dblSourceDiagonalEntry = aadblSource[iDiagonal][iCol];
					aadblSource[iDiagonal][iCol] = aadblSource[iSwapRow][iCol];
					aadblSource[iSwapRow][iCol] = dblSourceDiagonalEntry;
				}
			}
		}

		/* for (int iDiagonal = 0; iDiagonal < iSize; ++iDiagonal) {
			if (0. == aadblSource[iDiagonal][iDiagonal]) {
				org.drip.quant.common.NumberUtil.Print2DArray ("ZERO DIAG!", aadblSource, false);

				return false;
			}
		} */

		return true;
	}

	/**
	 * Regularize the specified diagonal entry of the input matrix using Row Addition
	 * 
	 * @param mct The Input Matrix Complement Transform
	 * 
	 * @return The Regularization was successful
	 */

	public static final boolean RegularizeUsingRowAddition (
		final org.drip.numerical.linearalgebra.MatrixComplementTransform mct)
	{
		if (null == mct) return false;

		int iSize = mct.size();

		double[][] aadblSource = mct.getSource();

		double[][] aadblComplement = mct.getComplement();

		for (int iDiagonal = 0; iDiagonal < iSize; ++iDiagonal) {
			if (0. == aadblSource[iDiagonal][iDiagonal]) {
				int iPivotRow = iSize - 1;

				while (0. == aadblSource[iPivotRow][iDiagonal] && iPivotRow >= 0) --iPivotRow;

				if (0 > iPivotRow) return false;

				for (int iCol = 0; iCol < iSize; ++iCol) {
					aadblSource[iDiagonal][iCol] += aadblSource[iPivotRow][iCol];
					aadblComplement[iDiagonal][iCol] += aadblComplement[iPivotRow][iCol];
				}
			}
		}

		return true;
	}

	/**
	 * Pivot the Diagonal of the Input Matrix
	 * 
	 * @param aadblA The Input Matrix
	 * 
	 * @return The Matrix Complement Transform Instance
	 */

	public static final org.drip.numerical.linearalgebra.MatrixComplementTransform PivotDiagonal (
		final double[][] aadblA)
	{
		if (null == aadblA) return null;

		int iSize = aadblA.length;
		double[][] aadblSource = new double[iSize][iSize];
		double[][] aadblComplement = new double[iSize][iSize];
		org.drip.numerical.linearalgebra.MatrixComplementTransform mctOut = null;

		if (0 == iSize || null == aadblA[0] || iSize != aadblA[0].length) return null;

		for (int i = 0; i < iSize; ++i) {
			for (int j = 0; j < iSize; ++j) {
				aadblSource[i][j] = aadblA[i][j];
				aadblComplement[i][j] = i == j ? 1. : 0.;
			}
		}

		try {
			mctOut = new org.drip.numerical.linearalgebra.MatrixComplementTransform (aadblSource,
				aadblComplement);
		} catch (java.lang.Exception e) {
			e.printStackTrace();

			return null;
		}

		return RegularizeUsingRowSwap (mctOut) ? mctOut : null;
	}

	/**
	 * Invert the Source Matrix using Gaussian Elimination
	 * 
	 * @param aadblSource Source Matrix
	 * 
	 * @return The Inverted Matrix
	 */

	public static final double[][] InvertUsingGaussianElimination (
		final double[][] aadblSource)
	{
		org.drip.numerical.linearalgebra.MatrixComplementTransform mctRegularized =
			org.drip.numerical.linearalgebra.Matrix.PivotDiagonal (aadblSource);

		if (null == mctRegularized) return null;

		double[][] aadblRegularizedSource = mctRegularized.getSource();

		double[][] aadblRegularizedInverse = mctRegularized.getComplement();

		int iSize = aadblRegularizedSource.length;

		for (int iDiagonal = 0; iDiagonal < iSize; ++iDiagonal) {
			if (0. == aadblRegularizedSource[iDiagonal][iDiagonal]) return null;

			for (int iRow = 0; iRow < iSize; ++iRow) {
				if (iRow == iDiagonal || 0. == aadblRegularizedSource[iRow][iDiagonal]) continue;

				double dblColEntryEliminatorRatio = aadblRegularizedSource[iDiagonal][iDiagonal] /
					aadblRegularizedSource[iRow][iDiagonal];

				for (int iCol = 0; iCol < iSize; ++iCol) {
					aadblRegularizedSource[iRow][iCol] = aadblRegularizedSource[iRow][iCol] *
						dblColEntryEliminatorRatio - aadblRegularizedSource[iDiagonal][iCol];
					aadblRegularizedInverse[iRow][iCol] = aadblRegularizedInverse[iRow][iCol] *
						dblColEntryEliminatorRatio - aadblRegularizedInverse[iDiagonal][iCol];
				}
			}
		}

		for (int iDiagonal = 0; iDiagonal < iSize; ++iDiagonal) {
			double dblDiagScaleDown = aadblRegularizedSource[iDiagonal][iDiagonal];

			if (0. == dblDiagScaleDown) return null;

			for (int iCol = 0; iCol < iSize; ++iCol) {
				aadblRegularizedSource[iDiagonal][iCol] /= dblDiagScaleDown;
				aadblRegularizedInverse[iDiagonal][iCol] /= dblDiagScaleDown;
			}
		}

		return aadblRegularizedInverse;
	}

	/**
	 * Invert the input matrix using the specified Method
	 * 
	 * @param aadblA Input Matrix
	 * @param strMethod The Inversion Method
	 * 
	 * @return The Inverted Matrix
	 */

	public static final double[][] Invert (
		final double[][] aadblA,
		final java.lang.String strMethod)
	{
		if (null == aadblA) return null;

		int iSize = aadblA.length;
		double[][] aadblAInv = null;
		double[][] aadblASource = new double[iSize][iSize];
		double[][] aadblZ2YJack = new double[iSize][iSize];

		if (0 == iSize || iSize != aadblA[0].length) return null;

		for (int i = 0; i < iSize; ++i) {
			for (int j = 0; j < iSize; ++j) {
				if (!org.drip.numerical.common.NumberUtil.IsValid (aadblASource[i][j] = aadblA[i][j]))
					return null;

				aadblZ2YJack[i][j] = i == j ? 1. : 0.;
			}
		}

		for (int i = 0; i < iSize; ++i) {
			if (0. == aadblASource[i][i] && !DiagonalizeRow (i, aadblASource, aadblZ2YJack)) return null;
		}

		if (null == strMethod || strMethod.isEmpty() || strMethod.equalsIgnoreCase ("GaussianElimination"))
			aadblAInv = InvertUsingGaussianElimination (aadblASource);

		if (null == aadblAInv || iSize != aadblAInv.length || iSize != aadblAInv[0].length) return null;

		return Product (aadblAInv, aadblZ2YJack);
	}

	/**
	 * Compute the Rank of the Matrix
	 * 
	 * @param aadblSource Source Matrix
	 * 
	 * @return The Rank of the Matrix
	 * 
	 * @throws java.lang.Exception Thrown if the Rank Cannot be computed
	 */

	public static final int Rank (
		final double[][] aadblSource)
		throws java.lang.Exception
	{
		if (null == aadblSource) return 0;

		int iNumRow = aadblSource.length;

		if (iNumRow == 0) return 0;

		int iNumCol = aadblSource[0].length;

		for (int iScanRow = 0; iScanRow < iNumRow; ++iScanRow) {
			if (!org.drip.numerical.common.NumberUtil.IsValid (aadblSource[iScanRow]))
				throw new java.lang.Exception ("Matrix::Rank => Invalid Inputs");
		}

		double[][] aadblRegularizedSource = iNumRow < iNumCol ?
			org.drip.numerical.linearalgebra.Matrix.Transpose (aadblSource) : aadblSource;

		int iNumDependentRow = 0;
		int iProcessedRow = aadblRegularizedSource.length;
		int iProcessedCol = aadblRegularizedSource[0].length;

		if (1 == iNumRow || 1 == iNumCol) return iProcessedRow;

		for (int iScanRow = 0; iScanRow < iProcessedCol; ++iScanRow) {
			for (int iRow = 0; iRow < iProcessedCol; ++iRow) {
				if (iRow == iScanRow || 0. == aadblRegularizedSource[iRow][iScanRow]) continue;

				double dblColEntryEliminatorRatio = aadblRegularizedSource[iScanRow][iScanRow] /
					aadblRegularizedSource[iRow][iScanRow];

				for (int iCol = 0; iCol < iProcessedCol; ++iCol)
					aadblRegularizedSource[iRow][iCol] = aadblRegularizedSource[iRow][iCol] *
						dblColEntryEliminatorRatio - aadblRegularizedSource[iScanRow][iCol];
			}
		}

		for (int iScanRow = 0; iScanRow < iProcessedCol; ++iScanRow) {
			if (0. == org.drip.numerical.linearalgebra.Matrix.Modulus (aadblRegularizedSource[iScanRow]))
				++iNumDependentRow;
		}

		return iProcessedRow - iNumDependentRow;
	}

	/**
	 * Transpose the specified Square Matrix
	 * 
	 * @param aadblA The Input Square Matrix
	 * 
	 * @return The Transpose of the Square Matrix
	 */

	public static final double[][] Transpose (
		final double[][] aadblA)
	{
		if (null == aadblA) return null;

		int iRowSize = aadblA.length;

		if (0 == iRowSize || null == aadblA[0]) return null;

		int iColSize = aadblA[0].length;
		double[][] aadblATranspose = new double[iColSize][iRowSize];

		if (0 == iColSize) return null;

		for (int i = 0; i < iColSize; ++i) {
			for (int j = 0; j < iRowSize; ++j)
				aadblATranspose[i][j] = aadblA[j][i];
		}

		return aadblATranspose;
	}

	/**
	 * Compute the Cholesky-Banachiewicz Factorization of the specified Matrix.
	 * 
	 * @param aadblA The Input Matrix
	 * 
	 * @return The Factorized Matrix
	 */

	public static final double[][] CholeskyBanachiewiczFactorization (
		final double[][] aadblA)
	{
		if (null == aadblA) return null;

		int iSize = aadblA.length;
		double[][] aadblL = new double[iSize][iSize];

		if (0 == iSize || null == aadblA[0] || iSize != aadblA[0].length) return null;

		for (int i = 0; i < iSize; ++i) {
			for (int j = 0; j < iSize; ++j) {
				aadblL[i][j] = 0.;

				if (i == j) {
					for (int k = 0; k < j; ++k)
						aadblL[j][j] -= aadblL[j][k] * aadblL[j][k];

					aadblL[j][j] = java.lang.Math.sqrt (aadblL[j][j] + aadblA[j][j]);
				} else if (i > j) {
					for (int k = 0; k < j; ++k)
						aadblL[i][j] -= aadblL[i][k] * aadblL[j][k];

					aadblL[i][j] = (aadblA[i][j] + aadblL[i][j]) / aadblL[j][j];
				}
			}
		}

		return aadblL;
	}

	/**
	 * Dot Product of Vectors A and E
	 * 
	 * @param adblA Vector A
	 * @param adblE Vector E
	 * 
	 * @return The Dot Product
	 * 
	 * @throws java.lang.Exception Thrown if the Dot-Product cannot be computed
	 */

	public static final double DotProduct (
		final double[] adblA,
		final double[] adblE)
		throws java.lang.Exception
	{
		if (null == adblA || null == adblE)
			throw new java.lang.Exception ("Matrix::DotProduct => Invalid Inputs!");

		int iSize = adblA.length;
		double dblDotProduct = 0.;

		if (0 == iSize || iSize != adblE.length)
			throw new java.lang.Exception ("Matrix::DotProduct => Invalid Inputs!");

		for (int i = 0; i < iSize; ++i)
			dblDotProduct += adblE[i] * adblA[i];

		return dblDotProduct;
	}

	/**
	 * Compute the Cross Product between the Specified Vectors
	 * 
	 * @param vector1 Vector #1
	 * @param vector2 Vector #2
	 * 
	 * @return The Cross Product
	 */

	public static final double[][] CrossProduct (
		final double[] vector1,
		final double[] vector2)
	{
		if (null == vector1 || null == vector2)
		{
			return null;
		}

		int size1 = vector1.length;
		int size2 = vector2.length;
		double[][] crossProduct = 0 == size1 || 0 == size2 ? null : new double[size1][size2];

		if (null == crossProduct)
		{
			return null;
		}

		for (int index1 = 0; index1 < size1; ++index1)
		{
			for (int index2 = 0; index2 < size2; ++index2)
			{
				crossProduct[index1][index2] = vector1[index1] * vector2[index2];
			}
		}

		return crossProduct;
	}

	/**
	 * Project the Vector A along the Vector E
	 * 
	 * @param adblA Vector A
	 * @param adblE Vector E
	 * 
	 * @return The Vector of Projection of A along E
	 */

	public static final double[] Project (
		final double[] adblA,
		final double[] adblE)
	{
		if (null == adblA || null == adblE) return null;

		int iSize = adblA.length;
		double dblProjection = java.lang.Double.NaN;
		double[] adblProjectAOnE = new double[iSize];

		if (0 == iSize || iSize != adblE.length) return null;

		try {
			dblProjection = DotProduct (adblA, adblE) / DotProduct (adblE, adblE);
		} catch (java.lang.Exception e) {
			e.printStackTrace();

			return null;
		}

		for (int i = 0; i < iSize; ++i)
			adblProjectAOnE[i] = adblE[i] * dblProjection;

		return adblProjectAOnE;
	}

	/**
	 * Compute the Sum of the Input Vector
	 * 
	 * @param adbl The Input Vector
	 * 
	 * @return TRUE - The Sum of the Input Vector
	 * 
	 * @throws java.lang.Exception Thrown if the Inputs are Invalid
	 */

	public static final double Sum (
		final double[] adbl)
		throws java.lang.Exception
	{
		if (null == adbl || !org.drip.numerical.common.NumberUtil.IsValid (adbl))
			throw new java.lang.Exception ("Matrix::Sum => Invalid Inputs");

		double dblSum = 0.;
		int iSize = adbl.length;

		if (0 == iSize) throw new java.lang.Exception ("Matrix::Sum => Invalid Inputs");

		for (int i = 0; i < iSize; ++i)
			dblSum += adbl[i];

		return dblSum;
	}

	/**
	 * Compute the Modulus of the Input Vector
	 * 
	 * @param adbl The Input Vector
	 * 
	 * @return TRUE - The Modulus of the Input Vector
	 * 
	 * @throws java.lang.Exception Thrown if the Inputs are Invalid
	 */

	public static final double Modulus (
		final double[] adbl)
		throws java.lang.Exception
	{
		if (null == adbl || !org.drip.numerical.common.NumberUtil.IsValid (adbl))
			throw new java.lang.Exception ("Matrix::Modulus => Invalid Inputs");

		double dblModulus = 0.;
		int iSize = adbl.length;

		if (0 == iSize) throw new java.lang.Exception ("Matrix::Modulus => Invalid Inputs");

		for (int i = 0; i < iSize; ++i)
			dblModulus += adbl[i] * adbl[i];

		return java.lang.Math.sqrt (dblModulus);
	}

	/**
	 * Indicate if the Array Entries are Positive or Zero
	 * 
	 * @param adbl The Array
	 * 
	 * @return TRUE - The Array Entries are Positive or Zero
	 * 
	 * @throws java.lang.Exception Thrown if the Inputs are Invalid
	 */

	public static final boolean PositiveOrZero (
		final double[] adbl)
		throws java.lang.Exception
	{
		if (null == adbl || !org.drip.numerical.common.NumberUtil.IsValid (adbl))
			throw new java.lang.Exception ("Matrix::PositiveOrZero => Invalid Inputs");

		int iSize = adbl.length;

		if (0 == iSize) throw new java.lang.Exception ("Matrix::PositiveOrZero => Invalid Inputs");

		for (int i = 0; i < iSize; ++i) {
			if (0. > adbl[i]) return false;
		}

		return true;
	}

	/**
	 * Indicate if the Array Entries are Negative or Zero
	 * 
	 * @param adbl The Array
	 * 
	 * @return The Array Entries are Negative or Zero
	 * 
	 * @throws java.lang.Exception Thrown if the Inputs are Invalid
	 */

	public static final boolean NegativeOrZero (
		final double[] adbl)
		throws java.lang.Exception
	{
		if (null == adbl || !org.drip.numerical.common.NumberUtil.IsValid (adbl))
			throw new java.lang.Exception ("Matrix::NegativeOrZero => Invalid Inputs");

		int iSize = adbl.length;

		if (0 == iSize)  throw new java.lang.Exception ("Matrix::NegativeOrZero => Invalid Inputs");

		for (int i = 0; i < iSize; ++i) {
			if (0. < adbl[i]) return false;
		}

		return true;
	}

	/**
	 * Indicate if the Array Entries are Positive Linearly Independent
	 * 
	 * @param adbl The Array
	 * 
	 * @return TRUE - The Array Entries are Positive Linearly Independent
	 * 
	 * @throws java.lang.Exception Thrown if the Inputs are Invalid
	 */

	public static final boolean PositiveLinearlyIndependent (
		final double[] adbl)
		throws java.lang.Exception
	{
		return !PositiveOrZero (adbl) && !NegativeOrZero (adbl);
	}

	/**
	 * Normalize the Input Vector
	 * 
	 * @param adbl The Input Vector
	 * 
	 * @return The Normalized Vector
	 */

	public static final double[] Normalize (
		final double[] adbl)
	{
		if (null == adbl) return null;

		double dblNorm = 0.;
		int iSize = adbl.length;
		double[] adblNormalized = new double[iSize];

		if (0 == iSize) return null;

		for (int i = 0; i < iSize; ++i)
			dblNorm += adbl[i] * adbl[i];

		dblNorm = java.lang.Math.sqrt (dblNorm);

		for (int i = 0; i < iSize; ++i)
			adblNormalized[i] = adbl[i] / dblNorm;

		return adblNormalized;
	}

	/**
	 * Orthogonalize the Specified Matrix Using the Graham-Schmidt Method
	 * 
	 * @param aadblV The Input Matrix
	 * 
	 * @return The Orthogonalized Matrix
	 */

	public static final double[][] GrahamSchmidtOrthogonalization (
		final double[][] aadblV)
	{
		if (null == aadblV) return null;

		int iSize = aadblV.length;
		double[][] aadblU = new double[iSize][iSize];

		if (0 == iSize || null == aadblV[0] || iSize != aadblV[0].length) return null;

		for (int i = 0; i < iSize; ++i) {
			for (int j = 0; j < iSize; ++j)
				aadblU[i][j] = aadblV[i][j];

			for (int j = 0; j < i; ++j) {
				double dblProjectionAmplitude = java.lang.Double.NaN;

				try {
					dblProjectionAmplitude = DotProduct (aadblV[i], aadblU[j]) / DotProduct (aadblU[j],
						aadblU[j]);
				} catch (java.lang.Exception e) {
					e.printStackTrace();

					return null;
				}

				for (int k = 0; k < iSize; ++k)
					aadblU[i][k] -= dblProjectionAmplitude * aadblU[j][k];
			}
		}

		return aadblU;
	}

	/**
	 * Orthonormalize the Specified Matrix Using the Graham-Schmidt Method
	 * 
	 * @param aadblV The Input Matrix
	 * 
	 * @return The Orthonormalized Matrix
	 */

	public static final double[][] GrahamSchmidtOrthonormalization (
		final double[][] aadblV)
	{
		double[][] aadblVOrthogonal = GrahamSchmidtOrthogonalization (aadblV);

		if (null == aadblVOrthogonal) return null;

		int iSize = aadblVOrthogonal.length;

		double[][] aadblVOrthonormal = new double[iSize][];

		for (int i = 0; i < iSize; ++i)
			aadblVOrthonormal[i] = Normalize (aadblVOrthogonal[i]);

		return aadblVOrthonormal;
	}

	/**
	 * Perform a QR Decomposition on the Input Matrix
	 * 
	 * @param aadblA The Input Matrix
	 * 
	 * @return The Output of QR Decomposition
	 */

	public static final org.drip.numerical.linearalgebra.QR QRDecomposition (
		final double[][] aadblA)
	{
		double[][] aadblQ = GrahamSchmidtOrthonormalization (aadblA);

		if (null == aadblQ) return null;

		int iSize = aadblQ.length;
		double[][] aadblR = new double[iSize][iSize];

		try {
			for (int i = 0; i < iSize; ++i) {
				for (int j = 0; j < iSize; ++j)
					aadblR[i][j] = i > j ? DotProduct (aadblQ[i], aadblA[j]) : 0.;
			}

			return new org.drip.numerical.linearalgebra.QR (aadblQ, aadblR);
		} catch (java.lang.Exception e) {
			e.printStackTrace();
		}

		return null;
	}

	/**
	 * Retrieve the Triangular Type of the Matrix
	 * 
	 * @param aadblA The Input Matrix
	 * @param dblFloor The Floor Level that means "Zero"
	 * 
	 * @return The Triangular Type
	 */

	public static final int TriangularType (
		final double[][] aadblA,
		final double dblFloor)
	{
		if (null == aadblA || !org.drip.numerical.common.NumberUtil.IsValid (dblFloor) || dblFloor < 0.)
			return NON_TRIANGULAR;

		int iSize = aadblA.length;
		boolean bLowerTriangular = true;
		boolean bUpperTriangular = true;

		if (1 >= iSize || null == aadblA[0] || iSize != aadblA[0].length) return NON_TRIANGULAR;

		for (int i = 0; i < iSize; ++i) {
			for (int j = 0; j < iSize; ++j) {
				if (i > j) {
					if (java.lang.Math.abs (aadblA[i][j]) > dblFloor) bLowerTriangular = false;
				} else if (i < j) {
					if (java.lang.Math.abs (aadblA[i][j]) > dblFloor) bUpperTriangular = false;
				}
			}
		}

		if (bLowerTriangular && bUpperTriangular) return LOWER_AND_UPPER_TRIANGULAR;

		if (bLowerTriangular && !bUpperTriangular) return LOWER_TRIANGULAR;

		if (!bLowerTriangular && bUpperTriangular) return UPPER_TRIANGULAR;

		return NON_TRIANGULAR;
	}

	/**
	 * Compute the Rayleigh Quotient given the Matrix and one of its Eigenvector
	 * 
	 * @param matrix The Given Matrix
	 * @param eigenvector The corresponding Eigenvector
	 * 
	 * @return The Computed Rayleigh Quotient
	 * 
	 * @throws java.lang.Exception Thrown if the Inputs are Invalid
	 */

	public static final double RayleighQuotient (
		final double[][] matrix,
		final double[] eigenvector)
		throws java.lang.Exception
	{
		return org.drip.numerical.linearalgebra.Matrix.DotProduct (
			eigenvector,
			org.drip.numerical.linearalgebra.Matrix.Product (
				matrix,
				eigenvector
			)
		);
	}

	/**
	 * Scale the Entries of the Input Vector by the Factor
	 * 
	 * @param vector The Input Vector
	 * @param scaleFactor The Scale Factor
	 * 
	 * @return The Scaled Matrix
	 */

	public static final double[] Scale1D (
		final double[] vector,
		final double scaleFactor)
	{
		if (null == vector || !org.drip.numerical.common.NumberUtil.IsValid (scaleFactor))
		{
			return null;
		}

		int rowCount = vector.length;
		double[] scaledVector = 0 == rowCount ? null : new double[rowCount];

		for (int rowIndex = 0; rowIndex < rowCount ; ++rowIndex)
		{
			scaledVector[rowIndex] = vector[rowIndex] * scaleFactor;
		}

		return scaledVector;
	}

	/**
	 * Scale the Entries of the Input Matrix by the Factor
	 * 
	 * @param matrix The Input Matrix
	 * @param scaleFactor The Scale Factor
	 * 
	 * @return The Scaled Matrix
	 */

	public static final double[][] Scale2D (
		final double[][] matrix,
		final double scaleFactor)
	{
		if (null == matrix || !org.drip.numerical.common.NumberUtil.IsValid (scaleFactor))
		{
			return null;
		}

		int rowCount = matrix.length;
		int columnCount = 0 == rowCount || null == matrix[0] ? 0 : matrix[0].length;
		double[][] scaledMatrix = 0 == columnCount ? null : new double[rowCount][columnCount];

		for (int rowIndex = 0; rowIndex < rowCount ; ++rowIndex)
		{
			for (int columnIndex = 0; columnIndex < columnCount ; ++columnIndex)
			{
				scaledMatrix[rowIndex][columnIndex] = matrix[rowIndex][columnIndex] * scaleFactor;
			}
		}

		return scaledMatrix;
	}

    /**
     * Indicate if the Specified Matrix is Diagonal
     *
     * @param matrix The Matrix
     *
     * @return TRUE - The Specified Matrix is Diagonal
     */

    public static final boolean IsDiagonal (
    	final double[][] matrix)
    {
       if (null == matrix)
       {
    	   return false;
       }

       int rowCount = matrix.length;
       int columnCount = 0 == rowCount || null == matrix[0] ? 0 : matrix[0].length;

       for (int rowIndex = 0;
    		rowIndex < rowCount;
            ++rowIndex)
       {
    	   for (int columnIndex = 0;
                columnIndex < columnCount;
                ++columnIndex)
           {
    		   if (rowIndex != columnIndex && 0. != matrix[rowIndex][columnIndex])
    		   {
                   return false;
    		   }
           }
       }

       return true;
    }
}