[libejml-java] 01/04: Imported Upstream version 0.28

Andreas Tille tille at debian.org
Mon Nov 16 12:49:17 UTC 2015


This is an automated email from the git hooks/post-receive script.

tille pushed a commit to branch master
in repository libejml-java.

commit 2c80442766d3ac68f6124525d59189053044808f
Author: Andreas Tille <tille at debian.org>
Date:   Mon Nov 16 11:24:24 2015 +0100

    Imported Upstream version 0.28
---
 .idea/codeStyleSettings.xml                        |   14 +
 .idea/copyright/ejml.xml                           |    9 +
 .idea/copyright/profiles_settings.xml              |    7 +
 .idea/scopes/ejml.xml                              |    3 +
 .idea/scopes/scope_settings.xml                    |    5 +
 .svnignore                                         |    1 +
 .travis.yml                                        |    6 +
 LICENSE-2.0.txt                                    |  178 ++
 README.md                                          |  118 ++
 TODO_Algorithms.txt                                |   65 +
 build.gradle                                       |  209 ++
 build.xml                                          |  205 ++
 change.txt                                         |  481 +++++
 design_notes.txt                                   |   80 +
 docs/ManualEJML.pdf                                |  Bin 0 -> 171898 bytes
 docs/bottom.txt                                    |   12 +
 docs/header.txt                                    |    9 +
 docs/latex/ManualEJML.tex                          |  326 +++
 docs/logo/EJML_logo.pdf                            |  Bin 0 -> 55706 bytes
 docs/logo/EJML_logo.pptx                           |  Bin 0 -> 54724 bytes
 docs/logo/ejml_icon.gif                            |  Bin 0 -> 4414 bytes
 docs/logo/ejml_logo.gif                            |  Bin 0 -> 10354 bytes
 docs/logo/ejml_logo_153x51.gif                     |  Bin 0 -> 3525 bytes
 docs/release-checklist.txt                         |   23 +
 examples/build.gradle                              |    9 +
 examples/build.xml                                 |   84 +
 examples/readme.txt                                |   39 +
 .../ejml/example/BenchmarkKalmanPerformance.java   |  171 ++
 .../org/ejml/example/EquationCustomFunction.java   |   96 +
 .../src/org/ejml/example/ExampleComplexMath.java   |   60 +
 .../org/ejml/example/ExampleFixedSizedMatrix.java  |   84 +
 examples/src/org/ejml/example/ExampleMatrixIO.java |   86 +
 examples/src/org/ejml/example/KalmanFilter.java    |   83 +
 .../src/org/ejml/example/KalmanFilterEquation.java |  102 +
 .../org/ejml/example/KalmanFilterOperations.java   |  129 ++
 .../src/org/ejml/example/KalmanFilterSimple.java   |   94 +
 .../src/org/ejml/example/LevenbergMarquardt.java   |  331 +++
 examples/src/org/ejml/example/PolynomialFit.java   |  165 ++
 .../src/org/ejml/example/PolynomialRootFinder.java |   89 +
 .../ejml/example/PrincipalComponentAnalysis.java   |  258 +++
 .../src/org/ejml/example/QRExampleEquation.java    |  135 ++
 .../src/org/ejml/example/QRExampleOperations.java  |  151 ++
 examples/src/org/ejml/example/QRExampleSimple.java |  129 ++
 .../src/org/ejml/example/StatisticsMatrix.java     |  129 ++
 .../org/ejml/example/TestCompareKalmanResults.java |   90 +
 .../org/ejml/example/TestLevenbergMarquardt.java   |  140 ++
 .../test/org/ejml/example/TestPolynomialFit.java   |  108 +
 .../org/ejml/example/TestPolynomialRootFinder.java |   58 +
 .../example/TestPrincipleComponentAnalysis.java    |  147 ++
 .../org/ejml/example/TestQRExampleEquation.java    |   62 +
 .../test/org/ejml/example/TestQRExampleOps.java    |   63 +
 .../test/org/ejml/example/TestQRExampleSimple.java |   57 +
 main/all/EJML All.iml                              |   32 +
 main/all/build.gradle                              |   13 +
 main/all/src/org/ejml/All.java                     |   26 +
 main/build.gradle                                  |    6 +
 main/core/build.gradle                             |   11 +
 main/core/src/org/ejml/EjmlParameters.java         |   95 +
 main/core/src/org/ejml/UtilEjml.java               |  127 ++
 .../ejml/alg/dense/linsol/LinearSolverSafe.java    |  109 +
 main/core/src/org/ejml/data/BlockMatrix64F.java    |  179 ++
 main/core/src/org/ejml/data/CD1Matrix64F.java      |  124 ++
 main/core/src/org/ejml/data/CDenseMatrix64F.java   |  216 ++
 main/core/src/org/ejml/data/Complex64F.java        |  112 ++
 main/core/src/org/ejml/data/ComplexMatrix64F.java  |   91 +
 main/core/src/org/ejml/data/ComplexPolar64F.java   |   72 +
 main/core/src/org/ejml/data/D1Matrix32F.java       |  274 +++
 main/core/src/org/ejml/data/D1Matrix64F.java       |  274 +++
 main/core/src/org/ejml/data/D1Submatrix64F.java    |   99 +
 main/core/src/org/ejml/data/DenseMatrix32F.java    |  403 ++++
 main/core/src/org/ejml/data/DenseMatrix64F.java    |  403 ++++
 main/core/src/org/ejml/data/Eigenpair64F.java      |   36 +
 main/core/src/org/ejml/data/FixedMatrix2_64F.java  |  126 ++
 .../core/src/org/ejml/data/FixedMatrix2x2_64F.java |  135 ++
 main/core/src/org/ejml/data/FixedMatrix3_64F.java  |  134 ++
 .../core/src/org/ejml/data/FixedMatrix3x3_64F.java |  176 ++
 main/core/src/org/ejml/data/FixedMatrix4_64F.java  |  142 ++
 .../core/src/org/ejml/data/FixedMatrix4x4_64F.java |  231 +++
 main/core/src/org/ejml/data/FixedMatrix5_64F.java  |  150 ++
 .../core/src/org/ejml/data/FixedMatrix5x5_64F.java |  300 +++
 main/core/src/org/ejml/data/FixedMatrix64F.java    |   27 +
 main/core/src/org/ejml/data/FixedMatrix6_64F.java  |  158 ++
 .../core/src/org/ejml/data/FixedMatrix6x6_64F.java |  383 ++++
 main/core/src/org/ejml/data/Matrix.java            |   57 +
 main/core/src/org/ejml/data/MatrixIterator32F.java |  141 ++
 main/core/src/org/ejml/data/MatrixIterator64F.java |  141 ++
 main/core/src/org/ejml/data/RealMatrix32F.java     |   77 +
 main/core/src/org/ejml/data/RealMatrix64F.java     |   77 +
 main/core/src/org/ejml/data/ReshapeMatrix.java     |   34 +
 main/core/src/org/ejml/data/RowD1Matrix64F.java    |   28 +
 .../decomposition/BidiagonalDecomposition.java     |   77 +
 .../decomposition/CholeskyDecomposition.java       |   73 +
 .../decomposition/CholeskyLDLDecomposition.java    |   81 +
 .../decomposition/DecompositionInterface.java      |   63 +
 .../decomposition/EigenDecomposition.java          |   86 +
 .../interfaces/decomposition/LUDecomposition.java  |  110 +
 .../interfaces/decomposition/QRDecomposition.java  |   93 +
 .../interfaces/decomposition/QRPDecomposition.java |   71 +
 .../decomposition/SingularValueDecomposition.java  |  133 ++
 .../TridiagonalSimilarDecomposition.java           |   62 +
 .../org/ejml/interfaces/linsol/LinearSolver.java   |  157 ++
 .../interfaces/linsol/ReducedRowEchelonForm.java   |   59 +
 main/core/src/org/ejml/ops/ComplexMath64F.java     |  216 ++
 main/core/src/org/ejml/ops/ConvertMatrixType.java  |  744 +++++++
 .../src/org/ejml/ops/MatrixDimensionException.java |   36 +
 main/core/src/org/ejml/ops/MatrixIO.java           |  266 +++
 main/core/src/org/ejml/ops/ReadCsv.java            |  159 ++
 main/core/src/org/ejml/ops/ReadMatrixCsv.java      |  128 ++
 main/core/test/org/ejml/TestUtilEjml.java          |   73 +
 .../alg/dense/linsol/TestLinearSolverSafe.java     |  210 ++
 .../org/ejml/data/GenericTestsD1Matrix32F.java     |   63 +
 .../org/ejml/data/GenericTestsD1Matrix64F.java     |   63 +
 .../test/org/ejml/data/GenericTestsMatrix32F.java  |  108 +
 .../test/org/ejml/data/GenericTestsMatrix64F.java  |  108 +
 .../test/org/ejml/data/TestBlockMatrix64F.java     |   41 +
 main/core/test/org/ejml/data/TestCD1Matrix64F.java |   68 +
 .../test/org/ejml/data/TestCDenseMatrix64F.java    |  204 ++
 .../test/org/ejml/data/TestD1Submatrix64F.java     |   71 +
 .../test/org/ejml/data/TestDenseMatrix32F.java     |  232 +++
 .../test/org/ejml/data/TestDenseMatrix64F.java     |  227 +++
 .../test/org/ejml/data/TestMatrixIterator32F.java  |   98 +
 .../test/org/ejml/data/TestMatrixIterator64F.java  |   98 +
 main/core/test/org/ejml/data/UtilTestMatrix.java   |  121 ++
 .../core/test/org/ejml/ops/TestComplexMath64F.java |  220 ++
 .../test/org/ejml/ops/TestConvertMatrixType.java   |  192 ++
 main/core/test/org/ejml/ops/TestMatrixIO.java      |   71 +
 main/core/test/org/ejml/ops/TestReadMatrixCsv.java |   68 +
 main/dense64/build.gradle                          |   11 +
 .../generate/org/ejml/CodeGeneratorBase.java       |   74 +
 .../dense/misc/GenerateDeterminantFromMinor.java   |  230 +++
 .../alg/dense/misc/GenerateInverseFromMinor.java   |  240 +++
 .../alg/dense/mult/GeneratorMatrixMatrixMult.java  |  523 +++++
 .../org/ejml/alg/fixed/GenerateFixedOps.java       | 1395 +++++++++++++
 .../org/ejml/data/GenerateFixedMatrixN.java        |  202 ++
 .../org/ejml/data/GenerateFixedMatrixNxN.java      |  213 ++
 .../ejml/alg/block/BlockInnerMultiplication.java   |  562 ++++++
 .../org/ejml/alg/block/BlockInnerRankUpdate.java   |  343 ++++
 .../ejml/alg/block/BlockInnerTriangularSolver.java |  274 +++
 .../src/org/ejml/alg/block/BlockMatrixOps.java     |  620 ++++++
 .../org/ejml/alg/block/BlockMultiplication.java    |  337 ++++
 .../org/ejml/alg/block/BlockTriangularSolver.java  |  516 +++++
 .../src/org/ejml/alg/block/BlockVectorOps.java     |  359 ++++
 .../block/GeneratorBlockInnerMultiplication.java   |  314 +++
 .../decomposition/bidiagonal/BidiagonalHelper.java |   89 +
 .../decomposition/chol/CholeskyOuterForm_B64.java  |  204 ++
 .../decomposition/chol/InnerCholesky_B64.java      |  126 ++
 .../TridiagonalDecompositionHouseholder_B64.java   |  298 +++
 .../hessenberg/TridiagonalHelper_B64.java          |  343 ++++
 .../block/decomposition/qr/BlockHouseHolder.java   | 1034 ++++++++++
 .../qr/QRDecompositionHouseholder_B64.java         |  417 ++++
 .../linsol/chol/BlockCholeskyOuterSolver.java      |  158 ++
 .../block/linsol/qr/BlockQrHouseHolderSolver.java  |  164 ++
 .../BaseDecomposition_B64_to_D64.java              |   87 +
 .../alg/dense/decomposition/TriangularSolver.java  |  244 +++
 .../bidiagonal/BidiagonalDecompositionRow_D64.java |  393 ++++
 .../BidiagonalDecompositionTall_D64.java           |  155 ++
 .../chol/CholeskyBlockHelper_D64.java              |  107 +
 .../chol/CholeskyDecompositionBlock_D64.java       |  242 +++
 .../chol/CholeskyDecompositionCommon_D64.java      |  199 ++
 .../chol/CholeskyDecompositionInner_D64.java       |  119 ++
 .../chol/CholeskyDecompositionLDL_D64.java         |  175 ++
 .../chol/CholeskyDecomposition_B64_to_D64.java     |   66 +
 .../dense/decomposition/eig/EigenPowerMethod.java  |  218 ++
 .../decomposition/eig/EigenvalueExtractor.java     |   35 +
 .../dense/decomposition/eig/EigenvalueSmall.java   |  134 ++
 .../eig/SwitchingEigenDecomposition.java           |  104 +
 .../eig/SymmetricQRAlgorithmDecomposition_D64.java |  242 +++
 .../eig/WatchedDoubleStepQRDecomposition_D64.java  |  109 +
 .../eig/symm/SymmetricQREigenHelper.java           |  494 +++++
 .../eig/symm/SymmetricQrAlgorithm.java             |  206 ++
 .../eig/watched/WatchedDoubleStepQREigen.java      |  602 ++++++
 .../eig/watched/WatchedDoubleStepQREigenvalue.java |  137 ++
 .../watched/WatchedDoubleStepQREigenvector.java    |  320 +++
 .../HessenbergSimilarDecomposition_D64.java        |  240 +++
 ...ridiagonalDecompositionHouseholderOrig_D64.java |  276 +++
 .../TridiagonalDecompositionHouseholder_D64.java   |  300 +++
 .../TridiagonalDecomposition_B64_to_D64.java       |   96 +
 .../decomposition/lu/LUDecompositionAlt_D64.java   |  113 ++
 .../decomposition/lu/LUDecompositionBase_D64.java  |  246 +++
 ...QRColPivDecompositionHouseholderColumn_D64.java |  322 +++
 .../qr/QRDecompositionHouseholderColumn_D64.java   |  302 +++
 .../qr/QRDecompositionHouseholderTran_D64.java     |  345 ++++
 .../qr/QRDecompositionHouseholder_D64.java         |  367 ++++
 .../qr/QRDecomposition_B64_to_D64.java             |   85 +
 .../decomposition/qr/QrHelperFunctions_D64.java    |  334 ++++
 .../ejml/alg/dense/decomposition/qr/QrUpdate.java  |  426 ++++
 .../ejml/alg/dense/decomposition/svd/SafeSvd.java  |   96 +
 .../svd/SvdImplicitQrDecompose_D64.java            |  345 ++++
 .../svd/implicitqr/SvdImplicitQrAlgorithm.java     |  821 ++++++++
 .../alg/dense/linsol/AdjustableLinearSolver.java   |   52 +
 .../ejml/alg/dense/linsol/InvertUsingSolve.java    |   57 +
 .../alg/dense/linsol/LinearSolverAbstract_D64.java |   58 +
 .../alg/dense/linsol/LinearSolverUnrolled.java     |   76 +
 .../alg/dense/linsol/LinearSolver_B64_to_D64.java  |  115 ++
 .../dense/linsol/chol/LinearSolverCholLDL_D64.java |  175 ++
 .../dense/linsol/chol/LinearSolverChol_B64.java    |   57 +
 .../dense/linsol/chol/LinearSolverChol_D64.java    |  183 ++
 .../dense/linsol/lu/LinearSolverLuBase_D64.java    |  129 ++
 .../alg/dense/linsol/lu/LinearSolverLuKJI_D64.java |   97 +
 .../alg/dense/linsol/lu/LinearSolverLu_D64.java    |   75 +
 .../alg/dense/linsol/qr/AdjLinearSolverQr_D64.java |   96 +
 .../dense/linsol/qr/BaseLinearSolverQrp_D64.java   |  196 ++
 .../dense/linsol/qr/LinearSolverQrBlock64_D64.java |   36 +
 .../linsol/qr/LinearSolverQrHouseCol_D64.java      |  166 ++
 .../linsol/qr/LinearSolverQrHouseTran_D64.java     |  173 ++
 .../dense/linsol/qr/LinearSolverQrHouse_D64.java   |  164 ++
 .../alg/dense/linsol/qr/LinearSolverQr_D64.java    |  174 ++
 .../linsol/qr/LinearSolverQrpHouseCol_D64.java     |  112 ++
 .../dense/linsol/qr/SolvePseudoInverseQrp_D64.java |  117 ++
 .../dense/linsol/svd/SolvePseudoInverseSvd.java    |  160 ++
 .../ejml/alg/dense/misc/DeterminantFromMinor.java  |  242 +++
 .../dense/misc/ImplCommonOps_DenseMatrix64F.java   |   42 +
 .../alg/dense/misc/ImplCommonOps_Matrix64F.java    |   43 +
 .../alg/dense/misc/RrefGaussJordanRowPivot.java    |  106 +
 .../src/org/ejml/alg/dense/misc/TransposeAlgs.java |  116 ++
 .../dense/misc/UnrolledDeterminantFromMinor.java   |  209 ++
 .../alg/dense/misc/UnrolledInverseFromMinor.java   |  262 +++
 .../org/ejml/alg/dense/mult/MatrixMatrixMult.java  | 1239 ++++++++++++
 .../org/ejml/alg/dense/mult/MatrixMultProduct.java |  150 ++
 .../org/ejml/alg/dense/mult/MatrixVectorMult.java  |  351 ++++
 .../src/org/ejml/alg/dense/mult/SubmatrixOps.java  |   42 +
 .../org/ejml/alg/dense/mult/VectorVectorMult.java  |  307 +++
 main/dense64/src/org/ejml/alg/fixed/FixedOps2.java |  989 +++++++++
 main/dense64/src/org/ejml/alg/fixed/FixedOps3.java | 1141 +++++++++++
 main/dense64/src/org/ejml/alg/fixed/FixedOps4.java | 1355 +++++++++++++
 main/dense64/src/org/ejml/alg/fixed/FixedOps5.java | 1608 +++++++++++++++
 main/dense64/src/org/ejml/alg/fixed/FixedOps6.java | 1708 ++++++++++++++++
 .../org/ejml/alg/generic/CodeGeneratorMisc.java    |   46 +
 .../src/org/ejml/alg/generic/GenericMatrixOps.java |  128 ++
 .../src/org/ejml/factory/DecompositionFactory.java |  291 +++
 .../src/org/ejml/factory/LinearSolverFactory.java  |  196 ++
 .../org/ejml/factory/SingularMatrixException.java  |   38 +
 main/dense64/src/org/ejml/ops/CommonOps.java       | 2101 ++++++++++++++++++++
 main/dense64/src/org/ejml/ops/CovarianceOps.java   |  125 ++
 .../src/org/ejml/ops/CovarianceRandomDraw.java     |   86 +
 main/dense64/src/org/ejml/ops/EigenOps.java        |  303 +++
 main/dense64/src/org/ejml/ops/EjmlUnitTests.java   |  237 +++
 main/dense64/src/org/ejml/ops/MatrixComponent.java |   84 +
 main/dense64/src/org/ejml/ops/MatrixFeatures.java  |  684 +++++++
 .../src/org/ejml/ops/MatrixVisualization.java      |   69 +
 main/dense64/src/org/ejml/ops/NormOps.java         |  462 +++++
 main/dense64/src/org/ejml/ops/RandomMatrices.java  |  485 +++++
 main/dense64/src/org/ejml/ops/SingularOps.java     |  429 ++++
 main/dense64/src/org/ejml/ops/SpecializedOps.java  |  444 +++++
 main/dense64/src/overview.html                     |   10 +
 .../alg/block/TestBlockInnerMultiplication.java    |  168 ++
 .../ejml/alg/block/TestBlockInnerRankUpdate.java   |  136 ++
 .../alg/block/TestBlockInnerTriangularSolver.java  |  185 ++
 .../org/ejml/alg/block/TestBlockMatrixOps.java     |  559 ++++++
 .../ejml/alg/block/TestBlockMultiplication.java    |  193 ++
 .../ejml/alg/block/TestBlockTriangularSolver.java  |  255 +++
 .../org/ejml/alg/block/TestBlockVectorOps.java     |  210 ++
 .../bidiagonal/TestBidiagonalHelper.java           |   62 +
 .../chol/TestCholeskyOuterForm_B64.java            |  104 +
 .../decomposition/chol/TestInnerCholesky_B64.java  |  103 +
 ...estTridiagonalDecompositionHouseholder_B64.java |  131 ++
 .../hessenberg/TestTridiagonalHelper_B64.java      |  294 +++
 .../qr/GenericBlock64QrDecompositionTests.java     |  167 ++
 .../decomposition/qr/TestBlockHouseHolder.java     |  504 +++++
 .../qr/TestQRDecompositionHouseholder_B64.java     |   48 +
 .../linsol/chol/TestBlockCholeskyOuterSolver.java  |  197 ++
 .../linsol/qr/TestBlockQrHouseHolderSolver.java    |  163 ++
 .../decomposition/CheckDecompositionInterface.java |   63 +
 .../TestBaseDecomposition_B64_to_D64.java          |  104 +
 .../dense/decomposition/TestTriangularSolver.java  |  194 ++
 .../bidiagonal/GenericBidiagonalCheck.java         |  142 ++
 .../TestBidiagonalDecompositionRow_D64.java        |  207 ++
 .../TestBidiagonalDecompositionTall_D64.java       |   33 +
 .../chol/GenericCholeskyTests_D64.java             |  176 ++
 .../chol/TestCholeskyDecompositionBlock_D64.java   |  112 ++
 .../chol/TestCholeskyDecompositionInner_D64.java   |   43 +
 .../chol/TestCholeskyDecompositionLDL_D64.java     |   73 +
 .../chol/TestCholeskyDecomposition_B64_to_D64.java |   42 +
 .../eig/GeneralEigenDecompositionCheck.java        |  626 ++++++
 .../decomposition/eig/TestEigenPowerMethod.java    |  110 +
 .../eig/TestSwitchingEigenDecomposition.java       |   39 +
 .../TestSymmetricQRAlgorithmDecomposition_D64.java |   75 +
 .../TestWatchedDoubleStepQRDecomposition_D64.java  |   39 +
 .../eig/symm/TestSymmetricQrAlgorithm.java         |  142 ++
 .../hessenberg/StandardTridiagonalTests.java       |  110 +
 .../TestHessenbergSimilarDecomposition_D64.java    |  214 ++
 ...estTridiagonalDecompositionHouseholder_D64.java |   35 +
 .../TestTridiagonalDecomposition_B64_to_D64.java   |   35 +
 .../lu/GeneralLuDecompositionChecks.java           |  195 ++
 .../lu/TestLUDecompositionAlt_D64.java             |   34 +
 .../lu/TestLUDecompositionBase_D64.java            |  120 ++
 .../dense/decomposition/qr/GenericQrCheck_D64.java |  190 ++
 ...QRColPivDecompositionHouseholderColumn_D64.java |  167 ++
 .../TestQRDecompositionHouseholderColumn_D64.java  |  169 ++
 .../qr/TestQRDecompositionHouseholderTran_D64.java |  214 ++
 .../qr/TestQRDecompositionHouseholder_D64.java     |  171 ++
 .../qr/TestQRDecomposition_B64_to_D64.java         |   33 +
 .../alg/dense/decomposition/qr/TestQrUpdate.java   |  152 ++
 .../dense/decomposition/svd/StandardSvdChecks.java |  342 ++++
 .../alg/dense/decomposition/svd/TestSafeSvd.java   |  159 ++
 .../svd/TestSvdImplicitQrDecompose_D64.java        |  137 ++
 .../svd/implicitqr/TestSvdImplicitQrAlgorithm.java |  244 +++
 .../dense/linsol/GenericLinearSolverChecks.java    |  300 +++
 .../linsol/GenericSolvePseudoInverseChecks.java    |  175 ++
 .../alg/dense/linsol/TestInvertUsingSolve.java     |   69 +
 .../alg/dense/linsol/TestLinearSolverAbstract.java |   93 +
 .../dense/linsol/TestLinearSolver_B64_to_D64.java  |   41 +
 .../linsol/chol/BaseCholeskySolveTests_D64.java    |  172 ++
 .../linsol/chol/TestLinearSolverCholLDL_D64.java   |   56 +
 .../linsol/chol/TestLinearSolverChol_B64.java      |   34 +
 .../linsol/chol/TestLinearSolverChol_D64.java      |   37 +
 .../linsol/lu/TestLinearSolverLuBase_D64.java      |   63 +
 .../dense/linsol/lu/TestLinearSolverLuKJI_D64.java |   43 +
 .../dense/linsol/lu/TestLinearSolverLu_D64.java    |   43 +
 .../dense/linsol/qr/TestAdjLinearSolverQr_D64.java |  113 ++
 .../linsol/qr/TestLinearSolverQrBlock64_D64.java   |   34 +
 .../linsol/qr/TestLinearSolverQrHouseCol_D64.java  |   39 +
 .../linsol/qr/TestLinearSolverQrHouseTran_D64.java |   39 +
 .../linsol/qr/TestLinearSolverQrHouse_D64.java     |   39 +
 .../dense/linsol/qr/TestLinearSolverQr_D64.java    |   40 +
 .../qr/TestSolveLinearSolverQrpHouseCol_D64.java   |   58 +
 .../linsol/qr/TestSolvePseudoInverseQrp_D64.java   |   58 +
 .../linsol/svd/TestSolvePseudoInverseSvd.java      |   48 +
 .../misc/GeneralReducedRowEchelonFormChecks.java   |  165 ++
 .../alg/dense/misc/TestDeterminantFromMinor.java   |  130 ++
 .../misc/TestImplCommonOps_DenseMatrix64F.java     |   50 +
 .../dense/misc/TestImplCommonOps_Matrix64F.java    |   50 +
 .../dense/misc/TestRrefGaussJordanRowPivot.java    |   28 +
 .../org/ejml/alg/dense/misc/TestTransposeAlgs.java |   82 +
 .../misc/TestUnrolledDeterminantFromMinor.java     |   52 +
 .../dense/misc/TestUnrolledInverseFromMinor.java   |   66 +
 .../ejml/alg/dense/mult/CheckMatrixMultShape.java  |  171 ++
 .../alg/dense/mult/CheckMatrixVectorMultShape.java |  144 ++
 .../ejml/alg/dense/mult/TestMatrixMatrixMult.java  |  273 +++
 .../ejml/alg/dense/mult/TestMatrixMultProduct.java |   90 +
 .../ejml/alg/dense/mult/TestMatrixVectorMult.java  |  197 ++
 .../org/ejml/alg/dense/mult/TestSubmatrixOps.java  |   53 +
 .../ejml/alg/dense/mult/TestVectorVectorMult.java  |  190 ++
 .../ejml/alg/fixed/CompareFixedToCommonOps.java    |  257 +++
 .../test/org/ejml/alg/fixed/TestFixedOps2.java     |   46 +
 .../test/org/ejml/alg/fixed/TestFixedOps3.java     |   54 +
 .../test/org/ejml/alg/fixed/TestFixedOps4.java     |   51 +
 .../test/org/ejml/alg/fixed/TestFixedOps5.java     |   49 +
 .../test/org/ejml/alg/fixed/TestFixedOps6.java     |   52 +
 .../org/ejml/factory/TestDecompositionFactory.java |   78 +
 .../org/ejml/factory/TestLinearSolverFactory.java  |  125 ++
 main/dense64/test/org/ejml/ops/TestCommonOps.java  | 1183 +++++++++++
 .../test/org/ejml/ops/TestCovarianceOps.java       |   52 +
 .../org/ejml/ops/TestCovarianceRandomDraw.java     |   97 +
 main/dense64/test/org/ejml/ops/TestEigenOps.java   |  116 ++
 .../test/org/ejml/ops/TestMatrixFeatures.java      |  464 +++++
 main/dense64/test/org/ejml/ops/TestNormOps.java    |  242 +++
 .../test/org/ejml/ops/TestRandomMatrices.java      |  383 ++++
 .../dense64/test/org/ejml/ops/TestSingularOps.java |  418 ++++
 .../test/org/ejml/ops/TestSpecializedOps.java      |  292 +++
 main/denseC64/build.gradle                         |   10 +
 .../alg/dense/mult/GenerateCMatrixMatrixMult.java  |  266 +++
 .../alg/dense/decompose/CTriangularSolver.java     |  187 ++
 .../chol/CholeskyDecompositionCommon_CD64.java     |  175 ++
 .../chol/CholeskyDecompositionInner_CD64.java      |  175 ++
 .../decompose/lu/LUDecompositionAlt_CD64.java      |  139 ++
 .../decompose/lu/LUDecompositionBase_CD64.java     |  301 +++
 .../qr/QRDecompositionHouseholderColumn_CD64.java  |  331 +++
 .../qr/QRDecompositionHouseholderTran_CD64.java    |  384 ++++
 .../qr/QRDecompositionHouseholder_CD64.java        |  444 +++++
 .../dense/decompose/qr/QrHelperFunctions_CD64.java |  303 +++
 .../ejml/alg/dense/linsol/CInvertUsingSolve.java   |   56 +
 .../dense/linsol/LinearSolverAbstract_CD64.java    |   60 +
 .../dense/linsol/chol/LinearSolverChol_CD64.java   |  180 ++
 .../dense/linsol/lu/LinearSolverLuBase_CD64.java   |   95 +
 .../alg/dense/linsol/lu/LinearSolverLu_CD64.java   |   73 +
 .../linsol/qr/LinearSolverQrHouseCol_CD64.java     |  175 ++
 .../linsol/qr/LinearSolverQrHouseTran_CD64.java    |  195 ++
 .../dense/linsol/qr/LinearSolverQrHouse_CD64.java  |  185 ++
 .../alg/dense/linsol/qr/LinearSolverQr_CD64.java   |  179 ++
 .../org/ejml/alg/dense/misc/CTransposeAlgs.java    |  124 ++
 .../org/ejml/alg/dense/mult/CMatrixMatrixMult.java |  469 +++++
 .../org/ejml/alg/dense/mult/CVectorVectorMult.java |  184 ++
 .../org/ejml/factory/CDecompositionFactory.java    |   90 +
 .../src/org/ejml/factory/CLinearSolverFactory.java |   66 +
 main/denseC64/src/org/ejml/ops/CCommonOps.java     |  978 +++++++++
 .../denseC64/src/org/ejml/ops/CMatrixFeatures.java |  315 +++
 main/denseC64/src/org/ejml/ops/CNormOps.java       |   60 +
 .../denseC64/src/org/ejml/ops/CRandomMatrices.java |  203 ++
 .../denseC64/src/org/ejml/ops/CSpecializedOps.java |  195 ++
 .../CheckDecompositionInterface_CD64.java          |   53 +
 .../alg/dense/decompose/TestCTriangularSolver.java |  107 +
 .../decompose/chol/GenericCholeskyTests_CD64.java  |  144 ++
 .../chol/TestCholeskyDecompositionCommon_CD64.java |   74 +
 .../chol/TestCholeskyDecompositionInner_CD64.java  |   34 +
 .../lu/GeneralLuDecompositionChecks_CD64.java      |  215 ++
 .../decompose/lu/TestLUDecompositionAlt_CD64.java  |   29 +
 .../decompose/lu/TestLUDecompositionBase_CD64.java |  171 ++
 .../dense/decompose/qr/GenericQrCheck_CD64.java    |  227 +++
 .../TestQRDecompositionHouseholderColumn_CD64.java |  187 ++
 .../TestQRDecompositionHouseholderTran_CD64.java   |  226 +++
 .../qr/TestQRDecompositionHouseholder_CD64.java    |  194 ++
 .../decompose/qr/TestQrHelperFunctions_CD64.java   |  278 +++
 .../dense/linsol/GenericCLinearSolverChecks.java   |  319 +++
 .../alg/dense/linsol/TestCInvertUsingSolve.java    |   63 +
 .../linsol/chol/BaseCholeskySolveTests_CD64.java   |  182 ++
 .../linsol/chol/TestLinearSolverChol_CD64.java     |   35 +
 .../dense/linsol/lu/TestLinearSolverLu_CD64.java   |   43 +
 .../linsol/qr/TestLinearSolverQrHouseCol_CD64.java |   39 +
 .../qr/TestLinearSolverQrHouseTran_CD64.java       |   39 +
 .../linsol/qr/TestLinearSolverQrHouse_CD64.java    |   39 +
 .../dense/linsol/qr/TestLinearSolverQr_CD64.java   |   40 +
 .../ejml/alg/dense/misc/TestCTransposeAlgs.java    |  120 ++
 .../ejml/alg/dense/mult/TestCMatrixMatrixMult.java |  152 ++
 .../ejml/alg/dense/mult/TestCVectorVectorMult.java |  101 +
 .../denseC64/test/org/ejml/ops/TestCCommonOps.java |  707 +++++++
 .../test/org/ejml/ops/TestCMatrixFeatures.java     |  204 ++
 main/denseC64/test/org/ejml/ops/TestCNormOps.java  |   54 +
 .../test/org/ejml/ops/TestCRandomMatrices.java     |  171 ++
 .../test/org/ejml/ops/TestCSpecializedOps.java     |  160 ++
 main/denseC64/todo.txt                             |    7 +
 main/equation/build.gradle                         |   11 +
 main/equation/src/org/ejml/equation/Equation.java  | 1220 ++++++++++++
 main/equation/src/org/ejml/equation/Function.java  |   46 +
 .../src/org/ejml/equation/ManagerFunctions.java    |  361 ++++
 .../org/ejml/equation/ManagerTempVariables.java    |   49 +
 .../src/org/ejml/equation/MatrixConstructor.java   |  171 ++
 main/equation/src/org/ejml/equation/Operation.java | 1401 +++++++++++++
 main/equation/src/org/ejml/equation/Sequence.java  |   46 +
 main/equation/src/org/ejml/equation/Symbol.java    |   77 +
 main/equation/src/org/ejml/equation/TokenList.java |  347 ++++
 main/equation/src/org/ejml/equation/Variable.java  |   40 +
 .../src/org/ejml/equation/VariableDouble.java      |   37 +
 .../src/org/ejml/equation/VariableInteger.java     |   37 +
 .../src/org/ejml/equation/VariableMatrix.java      |   59 +
 .../src/org/ejml/equation/VariableScalar.java      |   33 +
 .../src/org/ejml/equation/VariableSpecial.java     |   45 +
 .../src/org/ejml/equation/VariableType.java        |   30 +
 .../test/org/ejml/equation/TestEquation.java       |  815 ++++++++
 .../org/ejml/equation/TestMatrixConstructor.java   |   93 +
 .../test/org/ejml/equation/TestOperation.java      | 1064 ++++++++++
 .../test/org/ejml/equation/TestSequence.java       |   62 +
 .../test/org/ejml/equation/TestTokenList.java      |  216 ++
 .../benchmarks/src/org/ejml/BenchmarkElement.java  |   59 +
 .../benchmarks/src/org/ejml/BenchmarkGenerics.java |  101 +
 .../src/org/ejml/BenchmarkInheritanceCall.java     |  182 ++
 .../src/org/ejml/BenchmarkInliningGetSet.java      |  156 ++
 .../src/org/ejml/BenchmarkInstanceOf.java          |  118 ++
 .../ejml/alg/block/BenchmarkBlockTranspose.java    |   92 +
 .../complex/mult/BenchmarkMatrixMatrixMult.java    |  194 ++
 .../BenchmarkBidiagonalDecomposition.java          |   97 +
 .../StabilityBidiagonalDecomposition.java          |   97 +
 .../chol/BenchmarkCholeskyDecomposition.java       |  157 ++
 .../chol/StabilityCholeksyDecomposition.java       |   83 +
 .../eig/BenchmarkEigenDecomposition.java           |   68 +
 .../eig/RealEigenDecompositionStressTest.java      |   26 +
 .../symm/BenchmarkSymmetricEigenDecomposition.java |  145 ++
 .../decomposition/eig/symm/StabilitySymmEigen.java |   73 +
 .../eig/symm/SymmetricEigenStressTest.java         |  115 ++
 .../eig/watched/WatchDoubleStepQR.java             |  131 ++
 .../hessenberg/BenchmarkHessenberg.java            |   89 +
 .../hessenberg/BenchmarkTridiagonal.java           |  110 +
 .../hessenberg/StabilityTridiagonal.java           |   85 +
 .../decomposition/lu/BenchmarkLuDecomposition.java |   74 +
 .../qr/BenchmarkQrDecomposition_CD64.java          |   80 +
 .../qr/BenchmarkQrDecomposition_D64.java           |  102 +
 .../decomposition/qr/StabilityQRDecomposition.java |  105 +
 .../alg/dense/decomposition/svd/BenchmarkSvd.java  |  116 ++
 .../svd/StabilitySvdlDecomposition.java            |   90 +
 .../dense/linsol/BenchmarkInverseStability.java    |  205 ++
 .../alg/dense/linsol/BenchmarkInvertSquare.java    |  109 +
 .../alg/dense/linsol/BenchmarkInvertSymPosDef.java |  114 ++
 .../ejml/alg/dense/linsol/BenchmarkRectSolve.java  |   99 +
 .../ejml/alg/dense/linsol/BenchmarkSolveEq.java    |  108 +
 .../ejml/alg/dense/linsol/BenchmarkSolveOver.java  |  125 ++
 .../dense/linsol/BenchmarkSolvePseudoInverse.java  |  111 ++
 .../alg/dense/linsol/BenchmarkSolveSymPosDef.java  |   93 +
 .../ejml/alg/dense/misc/BenchmarkDeterminant.java  |  183 ++
 .../alg/dense/misc/BenchmarkImplCommonOps.java     |   90 +
 .../ejml/alg/dense/misc/BenchmarkTranspose.java    |  128 ++
 .../alg/dense/mult/BenchmarkMatrixMatrixMult.java  |  192 ++
 .../dense/mult/BenchmarkMatrixMatrixMultAdd.java   |  151 ++
 .../dense/mult/BenchmarkMatrixMatrixMultQuad.java  |   88 +
 .../mult/BenchmarkMatrixMatrixMultTransA.java      |  137 ++
 .../mult/BenchmarkMatrixMatrixMultTransAB.java     |  137 ++
 .../dense/mult/BenchmarkMatrixMultAccessors.java   |  160 ++
 .../alg/dense/mult/BenchmarkMatrixMultProduct.java |   98 +
 .../alg/dense/mult/BenchmarkMatrixVectorOps.java   |  149 ++
 .../org/ejml/alg/fixed/BenchmarkInverseFixed.java  |  120 ++
 .../alg/fixed/BenchmarkMultiplicationFixed.java    |  148 ++
 .../src/org/ejml/data/BenchmarkFunctionReturn.java |   86 +
 .../src/org/ejml/ops/BenchmarkEquality.java        |   76 +
 .../src/org/ejml/ops/BenchmarkMultAndAddOps.java   |  333 ++++
 .../src/org/ejml/ops/BenchmarkVariousOps.java      |  203 ++
 .../eig/RealEigenvalueHessenbergStressTest.java    |  361 ++++
 main/experimental/build.gradle                     |   17 +
 .../src/org/ejml/alg/blockd3/BlockD3MatrixOps.java |  239 +++
 .../BidiagonalDecompositionNaive_D64.java          |  198 ++
 .../decomposition/lu/LUDecompositionNR_CD64.java   |  175 ++
 .../decomposition/lu/LUDecompositionNR_D64.java    |  130 ++
 .../decomposition/svd/SmartRotatorUpdate.java      |   72 +
 .../svd/SvdImplicitQrAlgorithmSmart.java           |   58 +
 .../svd/SvdImplicitQrDecompose_Ultimate.java       |  312 +++
 .../svd/SvdImplicitQrDecompose_UltimateS.java      |  315 +++
 .../org/ejml/alg/dense/misc/NaiveDeterminant.java  |  120 ++
 .../src/org/ejml/alg/dense/misc/PermuteArray.java  |  184 ++
 .../org/ejml/alg/dense/mult/MatrixMultQuad.java    |   70 +
 .../ejml/alg/densed2/mult/MatrixMatrixMult_D2.java |   96 +
 .../src/org/ejml/data/BlockD3Matrix64F.java        |  151 ++
 .../src/org/ejml/data/DenseD2Matrix64F.java        |  118 ++
 .../org/ejml/alg/blockd3/TestBlockD3MatrixOps.java |  123 ++
 .../TestBidiagonalDecompositionNaive_D64.java      |   74 +
 .../decompose/lu/TestLUDecompositionNR_CD64.java   |   31 +
 .../decompose/lu/TestLUDecompositionNR_D64.java    |   34 +
 .../ejml/alg/dense/misc/TestNaiveDeterminant.java  |   63 +
 .../org/ejml/alg/dense/misc/TestPermuteArray.java  |  100 +
 .../test/org/ejml/data/TestBlockD3Matrix64F.java   |   41 +
 .../test/org/ejml/data/TestDenseD2Matrix64F.java   |   43 +
 main/simple/EJML Simple.iml                        |   31 +
 main/simple/build.gradle                           |   11 +
 main/simple/src/org/ejml/simple/SimpleBase.java    | 1125 +++++++++++
 main/simple/src/org/ejml/simple/SimpleEVD.java     |  155 ++
 main/simple/src/org/ejml/simple/SimpleMatrix.java  |  326 +++
 main/simple/src/org/ejml/simple/SimpleSVD.java     |  173 ++
 .../src/org/ejml/simple/SimpleUnitTests.java       |   94 +
 .../src/org/ejml/simple/UtilSimpleMatrix.java      |   40 +
 .../test/org/ejml/simple/TestSimpleMatrix.java     |  767 +++++++
 settings.gradle                                    |    2 +
 518 files changed, 96835 insertions(+)

diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml
new file mode 100644
index 0000000..c4deb56
--- /dev/null
+++ b/.idea/codeStyleSettings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectCodeStyleSettingsManager">
+    <option name="PER_PROJECT_SETTINGS">
+      <value>
+        <XML>
+          <option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
+        </XML>
+      </value>
+    </option>
+    <option name="USE_PER_PROJECT_SETTINGS" value="true" />
+  </component>
+</project>
+
diff --git a/.idea/copyright/ejml.xml b/.idea/copyright/ejml.xml
new file mode 100644
index 0000000..ace4aae
--- /dev/null
+++ b/.idea/copyright/ejml.xml
@@ -0,0 +1,9 @@
+<component name="CopyrightManager">
+  <copyright>
+    <option name="notice" value="Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.

This file is part of Efficient Java Matrix Library (EJML).

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
 [...]
+    <option name="keyword" value="Copyright" />
+    <option name="allowReplaceKeyword" value="" />
+    <option name="myName" value="ejml" />
+    <option name="myLocal" value="true" />
+  </copyright>
+</component>
\ No newline at end of file
diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..2d45e92
--- /dev/null
+++ b/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,7 @@
+<component name="CopyrightManager">
+  <settings default="">
+    <module2copyright>
+      <element module="ejml" copyright="ejml" />
+    </module2copyright>
+  </settings>
+</component>
\ No newline at end of file
diff --git a/.idea/scopes/ejml.xml b/.idea/scopes/ejml.xml
new file mode 100644
index 0000000..c165566
--- /dev/null
+++ b/.idea/scopes/ejml.xml
@@ -0,0 +1,3 @@
+<component name="DependencyValidationManager">
+  <scope name="ejml" pattern="src:*..*||test:*..*" />
+</component>
\ No newline at end of file
diff --git a/.idea/scopes/scope_settings.xml b/.idea/scopes/scope_settings.xml
new file mode 100644
index 0000000..922003b
--- /dev/null
+++ b/.idea/scopes/scope_settings.xml
@@ -0,0 +1,5 @@
+<component name="DependencyValidationManager">
+  <state>
+    <option name="SKIP_IMPORT_STATEMENTS" value="false" />
+  </state>
+</component>
\ No newline at end of file
diff --git a/.svnignore b/.svnignore
new file mode 100644
index 0000000..01871f8
--- /dev/null
+++ b/.svnignore
@@ -0,0 +1 @@
+*.iws
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..3e5f722
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,6 @@
+language: java
+jdk:
+  - openjdk6
+  - oraclejdk7
+  - oraclejdk8
+script:
diff --git a/LICENSE-2.0.txt b/LICENSE-2.0.txt
new file mode 100644
index 0000000..e454a52
--- /dev/null
+++ b/LICENSE-2.0.txt
@@ -0,0 +1,178 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8ea6387
--- /dev/null
+++ b/README.md
@@ -0,0 +1,118 @@
+# Efficient Java Matrix Library
+
+                    Author: Peter Abeles
+                            peter.abeles at gmail.com 
+
+#####  Project Website: http://ejml.org
+
+[![Build Status](https://travis-ci.org/lessthanoptimal/ejml.svg?branch=master)](https://travis-ci.org/lessthanoptimal/ejml)
+
+## Introduction
+
+Efficient Java Matrix Library (EJML) is a linear algebra library for manipulating dense matrices. Its design goals are; 1) to be as computationally and memory efficient as possible for both small and large matrices, and 2) to be accessible to both novices and experts. These goals are accomplished by dynamically selecting the best algorithms to use at runtime, clean API, and multiple interfaces. EJML is free, written in 100% Java and has been released under an Apache v2.0 license.
+
+EJML has three distinct ways to interact with it: 1) procedural, 2) SimpleMatrix, and 3) Equations. Procedure provides all capabilities of EJML and almost complete control over memory creation, speed, and specific algorithms. SimpleMatrix provides a simplified subset of the core capabilities in an easy to use flow styled object-oriented API, inspired by Jama. Equations is a symbolic interface, similar in spirit to Matlab and other CAS, that provides a compact way of writing equations.
+The following functionality is provided:
+
+* Basic operators (addition, multiplication, ...)
+* Matrix Manipulation (extract, insert, combine, ...)
+* Linear Solvers (linear, least squares,incremental, ...)
+* Decompositions (LU, QR, Cholesky, SVD, Eigenvalue, ...)
+* Matrix Features (rank, symmetric, definitiveness, ...)
+* Random Matrices (covariance, orthogonal, symmetric, ...)
+* Different Internal Formats (row-major, block)
+* Unit Testing
+
+Unit tests are extensively used to ensure correctness of each algorithm's implementation.  Internal benchmarks and Java Matrix Benchmark are both used to ensure the speed of this library.
+
+==========================================================================
+## Documentation
+
+For a more detailed explanation of how to use the library see:
+
+http://ejml.org/wiki/index.php?title=Manual
+
+The JavaDoc has also been posted online at:
+
+http://ejml.org/javadoc/
+
+==========================================================================
+## Including in Gradle and Maven Projects
+
+EJML is on the Maven central repository and can easily be included in projects by adding the following code to the dependency section of your Maven or Gradle project.  This will include all the modules in EJML.
+
+Gradle:
+```
+compile group: 'org.ejml', name: 'all', version: '0.27'
+```
+
+Maven:
+```
+<groupId>org.ejml</groupId>
+<artifactId>all</artifactId>
+<version>0.27</version>
+```
+Or you can include the required modules individually
+
+     Name        |                 Description
+-----------------|-------------------------------------------------------
+core             | Contains core data structures
+dense64          | Algorithms for dense real 64-bit floats
+denseC64         | Algorithms for dense complex 64-bit floats
+equation         | Equations interface
+simple           | Object oriented SimpleMatrix interface
+
+==========================================================================
+## Building
+
+Gradle is the official build environment for EJML.  In addition to all the standard commands the following are also available.
+
+* createLibraryDirectory : To build all the modules as jars and save them in ejml/libraries
+* oneJar : To compile all the modules into a single jar at ejml/EJML.jar
+
+==========================================================================
+## File System
+
+* **docs/** :
+         Documentation for this library. This documentation is often out of date and online is the best place to get the latest.
+* **examples/** :
+         Contains several examples of how EJML can be used to solve different problems or how EJML can be modified for different applications.
+* **main/core** :
+         Contains all essential data structures
+* **main/dense64** :
+         Algorithms for real dense 64-bit floating point matrices
+* **main/denseC64** :
+         Algorithms for complex dense 64-bit floating point matrices
+* **main/equation** :
+         Contains source code for Equations API
+* **main/simple** :
+         Contains source code for SimpleMatrix
+* **main/experimental/** :
+         Where experimental or alternative approaches and possibly buggy code goes that is not ready to be used by most users.
+* **change.txt** :
+         History of what changed between each version.
+* **TODO_Algorithms.txt** :
+         Contains a list of what needs to be added to this library.
+
+==========================================================================
+## Questions and Comments
+
+A public message board has been created for asking questions and making comments:
+
+http://groups.google.com/group/efficient-java-matrix-library-discuss
+
+Bugs can either be posted on that message board or at:
+
+https://github.com/lessthanoptimal/ejml/issues
+
+==========================================================================
+## Acknowledgements
+
+I would like to thank all the people have made various comments, suggestions, and reported bugs.  Also David Watkins
+for writing "Fundamentals of Matrix Computations", which clearly explains algorithms and yet addresses important
+implementation issues.
+
+==========================================================================
+## License
+
+EJML is released under the Apache v2.0 open source license
diff --git a/TODO_Algorithms.txt b/TODO_Algorithms.txt
new file mode 100644
index 0000000..5dc0a13
--- /dev/null
+++ b/TODO_Algorithms.txt
@@ -0,0 +1,65 @@
+Feature TODO
+- a = [5:10]
+- first: b = [1 2 50 69]
+  then:  c(b) = 1
+
+Maybe do this by parsing "5:10" and "1 2 3 4 5" into IntegerSequence with specific types range and list?
+Have an interator in IntegerSequence for generic processing, but allow for specialized for each sequence type?
+
+
+- Add to CommonOps invertSPD()
+- Unroll symmetric inverse by minor
+- Unroll multiplication for square matrices
+- Make QrUpdate more friendly and accessible
+
+For some future Release
+- Block SVD
+- Block Hessenberg
+- Adapt QR-tran into LQ
+- Remove QRDecompositionHouseholder?
+- Remove chol-block for dense with chol-block64?
+  * reduce cache misses in invert and see if its faster
+- Merge inner triangular solver code
+- Require LinearSolverFactory to take in a matrix so it can figure out alg to use?
+- Improve cholesky block inverse by taking advantage of symmetry
+- Add a function for sorting eigenvalues.
+
+----------------------------------------------------------
+
+- LU
+  - block
+
+- Cholesky
+  - unwrap for small matrices.  improve accuracy
+  - improve stability
+
+- Linear Solver
+  * Iterative
+  * Add condition(), use Hager's method? pg 132
+  * Put this new condition into NormOps since it should be much faster
+
+- SVD
+  - Save up rotators, multiply against each other, then multiply against U and V
+  - Divide and conquer algorithm
+  - An implementation that just finds zero singular values
+
+- Incremental SVD
+
+- Eigen decomposition
+  - Divide and conquer algorithm.
+
+- Accurate version of symmetric eigenvalue for 2 by 2
+  - SVD
+  - SymmEig
+
+- Fast Matrix Multiply
+
+- hard code cholesky decomposition for small matrices
+- hard code symmetric inverse for small matrices
+
+- Matrix Multiplication:
+  - Try a variant on mult_aux that does the vector mult up to block size then goes down a row.
+  - Finish vector vector multiply
+  - Code generator for matrix vector ops
+  - Add matrix vector multiply
+  - Auto switch to all of above in CommonOps
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..cdad6f6
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,209 @@
+allprojects {
+    apply plugin: 'idea'
+    apply plugin: 'eclipse'
+
+    group = 'org.ejml'
+    version = '0.28'
+}
+
+subprojects {
+    apply plugin: 'java'
+    apply plugin: 'osgi'
+    apply plugin: 'maven'
+    apply plugin: 'signing'
+
+    sourceCompatibility = 1.6
+    targetCompatibility = 1.6
+
+    javadoc.failOnError = false
+
+    repositories {
+        mavenCentral()
+        mavenLocal()
+
+        maven {
+            url = "https://oss.sonatype.org/content/repositories/snapshots/"
+        }
+    }
+
+    sourceSets {
+        main {
+            java {
+                srcDir 'src'
+            }
+            resources {
+                srcDir 'resources/src'
+            }
+        }
+
+        test {
+            java {
+                srcDir 'test'
+                srcDir 'generate'
+                srcDir 'benchmarks/src'
+                srcDir 'benchmarks/test'
+            }
+            resources {
+                srcDir 'resources/test'
+            }
+        }
+    }
+
+    dependencies {
+        testCompile group: 'junit', name: 'junit', version: '4.11'
+    }
+
+    jar {
+        manifest { // the manifest of the default jar is of type OsgiManifest
+            instruction 'Bundle-Vendor', 'EJML'
+//            instruction 'Bundle-Description', 'EJML'
+            instruction 'Bundle-DocURL', 'http://ejml.org/'
+        }
+    }
+
+    task javadocJar(type: Jar) {
+        classifier = 'javadoc'
+        from javadoc
+    }
+
+    task sourcesJar(type: Jar) {
+        classifier = 'sources'
+        from sourceSets.main.allSource
+    }
+
+    artifacts {
+        archives javadocJar, sourcesJar
+    }
+
+    // if Maven central isn't setup in gradle.properties skip all of this
+    if( project.hasProperty('ossrhUsername') ) {
+        signing {
+            sign configurations.archives
+        }
+
+        uploadArchives {
+            repositories {
+                mavenDeployer {
+                    beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
+
+                    repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
+                        authentication(userName: ossrhUsername, password: ossrhPassword)
+                    }
+
+                    snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") {
+                        authentication(userName: ossrhUsername, password: ossrhPassword)
+                    }
+
+                    pom.project {
+                        name 'EJML'
+                        packaging 'pom'
+                        // optionally artifactId can be defined here
+                        description 'A fast and easy to use dense matrix linear algebra library written in Java.'
+                        url 'http://ejml.org/'
+
+                        scm {
+                            connection 'git at github.com:lessthanoptimal/ejml.git'
+                            developerConnection 'git at github.com:lessthanoptimal/ejml.git'
+                            url 'https://github.com/lessthanoptimal/ejml'
+                        }
+
+                        licenses {
+                            license {
+                                name 'The Apache Software License, Version 2.0'
+                                url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
+                            }
+                        }
+
+                        developers {
+                            developer {
+                                id 'pabeles'
+                                name 'Peter Abeles'
+                                email 'peter.abeles at gmail.com'
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+def allModules = [
+        ':main:core',
+        ':main:dense64',
+        ':main:denseC64',
+        ':main:equation',
+        ':main:simple'
+]
+
+// Creates a directory with all the comiled jars
+task createLibraryDirectory( dependsOn: allModules.collect{ it+":jar"}+allModules.collect{ it+":sourcesJar"}) << {
+
+    // Create lists of .class jars and source jars
+    ext.listJars = files(allModules.collect{ project(it).tasks.jar.archivePath })
+    ext.listSource = files(allModules.collect{ project(it).tasks.sourcesJar.archivePath })
+
+    file('libraries').deleteDir()
+    file('libraries').mkdir()
+
+    copy {
+        from ext.listJars
+        from ext.listSource
+        into 'libraries'
+
+        // append on BoofCV so it's clear which jars are part of BoofCV and which are not
+        rename { String fileName ->
+            "EJML-" + fileName
+        }
+    }
+}
+
+idea {
+    project {
+        jdkName = '1.6 (64bit)'
+        languageLevel = '1.6'
+    }
+}
+
+def javadocProjects = [
+        ':main:denseC64',
+        ':main:dense64',
+        ':main:core',
+        ':main:equation'
+]
+task alljavadoc(type: Javadoc) {
+    // only include source code in src directory to avoid including 3rd party code which some projects do as a hack
+    source = javadocProjects.collect { project(it).fileTree('src').include('**/*.java') }
+//    source = javadocProjects.collect { project(it).sourceSets.main.allJava }
+    classpath = files(javadocProjects.collect { project(it).sourceSets.main.compileClasspath })
+
+    destinationDir = file("${buildDir}/docs/javadoc")
+    configure(options) {
+        docTitle = "Efficient Java Matrix Library (EJML) v$project.version"
+        links = [ 'http://docs.oracle.com/javase/7/docs/api/' ]
+        header = file('docs/header.txt').text
+        bottom = file('docs/bottom.txt').text // the quotes messes up javadoc still create bug report
+    }
+}
+
+task oneJarBin(type: Jar, dependsOn: javadocProjects.collect { it + ":compileJava" }) {
+    baseName = 'EJML'
+
+    from files(javadocProjects.collect { project(it).sourceSets.main.output })
+}
+
+// Disable the creation of jars for distribution.  If you don't do this it will crash
+[':main',':examples'].each {String a ->
+    project(a) {
+        if( project.hasProperty('ossrhUsername') ) {
+            signArchives.enabled = false
+        }
+        sourcesJar.enabled = false
+        javadocJar.enabled = false
+        jar.enabled = false
+        uploadArchives.enabled = false
+        install.enabled = false
+    }
+}
+
+
diff --git a/build.xml b/build.xml
new file mode 100644
index 0000000..1ae4f22
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,205 @@
+<project name="EJML" basedir="." default="main">
+
+
+    <property name="src.dir"     value="src"/>
+    <property name="experimental.src.dir"     value="experimental/src"/>
+    <property name="test.dir"     value="test"/>
+
+    <property name="build.dir"   value="build"/>
+    <property name="classes.dir" value="${build.dir}/classes"/>
+    <property name="jar.dir"     value="${build.dir}/jar"/>
+    <property name="generate.dir"   value="generate/src"/>
+    <property name="testbuild.dir"   value="build/test"/>
+    <property name="testclasses.dir"   value="${testbuild.dir}/classes"/>
+    <property name="testreport.dir"  value="${testbuild.dir}/report"/>
+
+    <property name="junit.dir"  value="lib/"/>
+
+    <path id="project.classpath">
+        <!--<fileset dir="${lib.dir}" includes="**/*.jar"/>-->
+    </path>
+
+    <path id="test.classpath">
+        <path refid="project.classpath"/>
+        <fileset dir="${junit.dir}" includes="junit*.jar"/>
+        <fileset dir="${jar.dir}" includes="**/${ant.project.name}.jar"/>
+    </path>
+
+
+    <target name="clean">
+        <delete dir="${build.dir}"/>
+        <delete dir="docs/api"/>
+    </target>
+
+    <target name="compile">
+
+        <!-- Capture the path as a delimited property using the refid attribute -->
+        <!--<property name="myclasspath" refid="project.classpath"/>-->
+        <!-- Emit the property to the ant console -->
+        <!--<echo message="Classpath = ${myclasspath}"/>-->
+
+
+        <mkdir dir="${classes.dir}"/>
+        <javac srcdir="${src.dir}" destdir="${classes.dir}" includeantruntime="false">
+            <classpath refid="project.classpath"/>
+        </javac>
+    </target>
+
+    <target name="compile experimental">
+        <mkdir dir="${classes.dir}"/>
+        <javac srcdir="${experimental.src.dir}" destdir="${classes.dir}" includeantruntime="false">
+            <classpath refid="project.classpath"/>
+        </javac>
+    </target>
+
+    <target name="jar" depends="compile">
+        <mkdir dir="${jar.dir}"/>
+        <jar destfile="${jar.dir}/${ant.project.name}.jar" basedir="${classes.dir}"/>
+    </target>
+
+    <target name="jar experimental" depends="compile,compile experimental">
+        <mkdir dir="${jar.dir}"/>
+        <jar destfile="${jar.dir}/${ant.project.name}.jar" basedir="${classes.dir}"/>
+    </target>
+
+    <target name="test" depends="jar">
+        <mkdir dir="${testbuild.dir}"/>
+        <mkdir dir="${testreport.dir}"/>
+        <mkdir dir="${testclasses.dir}"/>
+
+        <javac destdir="${testclasses.dir}" includeantruntime="false">
+            <src path="${generate.dir}"/>
+            <src path="${test.dir}"/>
+            <classpath>
+                <path refid="test.classpath"/>
+            </classpath>
+        </javac>
+
+        <junit printsummary="yes" showoutput="yes">
+            <classpath>
+                <path refid="test.classpath"/>
+                <pathelement location="${testclasses.dir}"/>
+            </classpath>
+
+            <formatter type="xml"/>
+
+            <batchtest fork="yes" todir="${testreport.dir}">
+                <fileset dir="${test.dir}" includes="**/Test*.java"/>
+            </batchtest>
+        </junit>
+    </target>
+
+    <target name="testreport">
+        <junitreport todir="${testreport.dir}">
+            <fileset dir="${testreport.dir}" includes="TEST-*.xml"/>
+            <report todir="${testreport.dir}"/>
+        </junitreport>
+    </target>
+
+    <!-- Creates a jar file with the project's source code -->
+    <target name="srcjar">
+      <mkdir dir="${jar.dir}"/>
+      <jar destfile="${jar.dir}/${ant.project.name}-src.jar">
+	<fileset dir="${src.dir}" includes="**/*.java"/>
+      </jar>
+    </target>
+
+    <target name="javadoc">
+        <javadoc
+                destdir="docs/api"
+                author="true"
+                version="true"
+                use="true"
+                windowtitle="Efficient Java Matrix Library"
+                overview="src/overview.html">
+
+            <link offline="false" href="http://docs.oracle.com/javase/7/docs/api/" packagelistloc="package-list" />
+
+            <packageset dir="main/core/src" defaultexcludes="yes">
+                <include name="org/ejml/**"/>
+            </packageset>
+            <packageset dir="main/dense64/src" defaultexcludes="yes">
+                <include name="org/ejml/**"/>
+            </packageset>
+            <packageset dir="main/denseC64/src" defaultexcludes="yes">
+                <include name="org/ejml/**"/>
+            </packageset>
+            <packageset dir="main/equation/src" defaultexcludes="yes">
+                <include name="org/ejml/**"/>
+            </packageset>
+            <packageset dir="main/simple/src" defaultexcludes="yes">
+                <include name="org/ejml/**"/>
+            </packageset>
+
+            <doctitle><![CDATA[<h1>Efficient Java Matrix Library API Specification</h1>]]></doctitle>
+            <bottom><![CDATA[<i>Copyright © 2009-2015 Peter Abeles All Rights Reserved.</i>]]></bottom>
+            <!--<group title="Group 1 Packages" packages="com.dummy.test.a*"/>-->
+            <!--<group title="Group 2 Packages" packages="com.dummy.test.b*:com.dummy.test.c*"/>-->
+            <!--<link offline="true" href="http://java.sun.com/j2se/1.5.0/docs/api/" packagelistLoc="C:\tmp"/>-->
+            <!--<link href="http://developer.java.sun.com/developer/products/xml/docs/api/"/>-->
+        </javadoc>
+    </target>
+
+    <!-- Generates JavaDOC but with tracking information for google analytics -->
+    <target name="javadocWeb">
+        <javadoc
+                destdir="docs/api"
+                author="true"
+                version="true"
+                use="true"
+                windowtitle="Efficient Java Matrix Library"
+                overview="src/overview.html">
+
+            <link offline="false" href="http://docs.oracle.com/javase/7/docs/api/" packagelistloc="package-list" />
+
+            <packageset dir="main/core/src" defaultexcludes="yes">
+                <include name="org/ejml/**"/>
+            </packageset>
+            <packageset dir="main/dense64/src" defaultexcludes="yes">
+                <include name="org/ejml/**"/>
+            </packageset>
+            <packageset dir="main/denseC64/src" defaultexcludes="yes">
+                <include name="org/ejml/**"/>
+            </packageset>
+            <packageset dir="main/equation/src" defaultexcludes="yes">
+                <include name="org/ejml/**"/>
+            </packageset>
+            <packageset dir="main/simple/src" defaultexcludes="yes">
+                <include name="org/ejml/**"/>
+            </packageset>
+
+            <doctitle><![CDATA[<h1>Efficient Java Matrix Library API Specification:</h1>]]></doctitle>
+            <header><![CDATA[<script type="text/javascript"><!--
+google_ad_client = "ca-pub-0419821832107208";
+/* boofcv javadoc banner */
+google_ad_slot = "3257872658";
+google_ad_width = 468;
+google_ad_height = 60;
+//-->
+</script>
+<script type="text/javascript"
+src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
+</script>
+]]>
+            </header>
+            <bottom><![CDATA[<script>
+  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
+
+  ga('create', 'UA-10413214-7', 'ejml.org');
+  ga('send', 'pageview');
+
+</script>
+<br>
+<b>Copyright © 2009-2015 Peter Abeles</b>
+]]>
+            </bottom>
+        </javadoc>
+    </target>
+
+    <target name="clean-build" depends="clean,jar"/>
+
+    <target name="main" depends="clean,jar"/>
+</project>
diff --git a/change.txt b/change.txt
new file mode 100644
index 0000000..6ed67f3
--- /dev/null
+++ b/change.txt
@@ -0,0 +1,481 @@
+Change log for EJML
+
+Date format: year/month/day
+
+----- Version 0.28
+
+2015/07/09
+
+- Equations
+  * Fixed bug where bounds for a submatrix-scalar assignment was being checked using col,row instead of row,col
+    Thanks lenhhoxung for reporting this bug
+- FixedOps
+  * Added vector equivalents for all element-wise matrix operations
+  * Added multAdd operators
+
+----- Version 0.27
+
+2015/04/01
+
+- Added SimpleMatrix.randomNormal() for drawing numbers from a normal distribution with zero mean
+- Added EjmlUnitTests.assertEquals() and similar for SimpleMatrix
+- Removed DenseMatrix64F.setReshape()
+  * Matrix.set(matrix) will now reshape the matrix that's being assigned
+- Triangle quality now just uses diagonal elements to scale results
+- Support for complex matrices
+  * Thanks IHMC (http://ihmc.us) for funding parts of this addition
+  * Basic operations (e.g. multiplication, addition, ... etc)
+  * LU Decomposition + Linear Solver
+  * QR Decomposition + Linear Solver
+  * Cholesky Decomposition + Linear Solver
+  * Square Matrices: inverse, solve, determinant
+  * Overdetermined: solve
+- ComplexMath64F
+  * Added sqrt(Complex64F)
+- Tweaked matrix inheritance to better support the addition of complex matrices
+- Added RandomMatrices setGaussian() and createGaussian()
+- Changed how SimpleMatrix computes its threshold for singular values
+  * Farley Lai noticed this issue
+- Added SingularOps.singularThreshold()
+- Added no argument rank and nullity for SVD using default threshold.
+- SimpleMatrix.loadCSV() now supports derived types
+- Added primitive 32bit data structures to make adding 32bit support in the future smoother
+- Equation
+  * 1x1 matrix can be assigned to a double scalar
+  * When referencing a single element in a matrix it will be extracted as a scalar and not a 1x1 matrix.
+  * Added sqrt() to parser
+  * lookupDouble() will now work on 1x1 matrices
+- CommonOps
+  * Added dot(a,b) for dot product between two vectors
+  * Added extractRow and extractColumn
+- FixedOps
+  * Added extractRow and extractColumn.  Thanks nknize for inspiring this modification with a pull request
+  * Added subtract and subtractEquals.  Thanks nknize for the pull request
+- Added determinant to Cholesky decomposition interface
+- Added getDecomposition() to LinearSolver to provide access to internal classes,
+  which can be useful in some specialized cases.  Alternatives were very ugly.
+
+----- Version 0.26
+
+2014/9/14
+
+- Switched most of the build system over to Gradle
+- Equations
+  * Symbolic way to perform linear algebra
+  * Similar to Matlab/Octave
+- SimpleMatrix
+  * Added plus( double )
+  * Added elementDiv()
+- Linear Solvers
+  * Exposed inner decomposition algorithms and some data structures
+- CommonOps
+  * Trace will now work on non-square matrices
+  * changeSign() has a two argument version
+  * left and right double-matrix division.
+      *** WARNING **** The meaning of CommonOps.divide(double,Matrix) and similar has changed
+  * Added subtract() matrix-double and double-matrix
+- QRP yet again uses a relative threshold.  No idea why it was set to a global one
+- Matrix Multiplication now fully supports multiplication by rows or columns with zero
+- Element-wise pow, exp, log to CommonOps and SimpleMatrix
+- High level interface for LDL
+- Renamed Principle to Principal in PCA
+  * Thanks sergei.skarupo for being the first person to point out this error
+- Fixed case where SolvePseudoInverseSvd would blow up if input matrix was zero
+- SVD and EVD can gracefully handle matrix with size 0
+
+----- Version 0.25
+
+2014/6/13
+
+- Fixed bug in CovarianceRandomDraw where it was modifying the input matrix
+  * Thanks Alexandre Bouchard for finding this bug
+- Added suffix to low level implementations so that you can tell by looking at the class name the type
+  of matrix it takes in as input
+  * D64 = DenseMatrix64F
+  * B64 = BlockMatrix64F
+- Moved interfaces into their own package.  Before they resided in dense.
+- Fixed bad matrix dimension check in LinearSolverChol
+  * Added unit tests which can catch the error
+  * Thanks Illya Kokshenev for finding and reporting
+- SimpleMatrix
+  * inverse and solve now check to see if output contains uncountable numbers.
+  * Thanks sylvain.rouard for the suggestion
+
+----- Version 0.24
+
+2013/12/26
+
+- CHANGED LICENSE FROM LGPL to Apache 2.0
+  * Don't agree with Free Software Foundation's interpretation for how the LGPL applies to Java jars.
+  * Even if it did behave the way I wanted it to, it wouldn't do much other than scare some people away.
+- Added fixed sized square matrices and vectors
+  * Auto code generator for standard operations
+  * Matrix 2x2, 3x3, 4x4, 5x5, 6x6
+  * Vector 2, 3, 4, 5, 6
+- Created an interface for Matrix64F and renamed the old one into ReshapeMatrix64F
+- CommonOps:  Added checks which ensure the input matrix is not modified for det() and inv()
+
+----- Version 0.23
+
+2013/06/21
+
+- Modified Matrix64F so that it can be serialized using beans.  Added setNumRows() and setNumCols()
+- Added user configurable threshold in SolvePseudoInverseSvd
+
+----- Version 0.22
+
+2013/04/09
+
+- Fixed bug in MatrixFeatures.isPositiveDefinite() where the input was being modified
+  * Added a check for isModified() flag in this and several other functions
+  * Thanks Eriklan Dodinh for reporting the bug
+- MatrixIO.loadCSV() now will throw IOException() if an incorrect matrix size is specified
+  * Thanks Ioannis P. for reporting this bug
+- LUDecompositionNR now returns false for singular matrices.
+  * Thanks Luke Nezda for reporting the bug
+- Moved LUDecompositionNR into experimental directory since its use is not recommended, but is still a good algorithm.
+- Clarified ordering of coefficients in polynomial root example
+  * Thanks Rahul for getting confused by the lack of documentation
+
+----- Version 0.21
+
+2012/12/04
+
+- Removed Android build option from ant because there is no need for such an option
+- Changed behavior of CommonOps.rref() so that numUnknowns <= 0 causes it to be set to a reasonable default
+- Fixed bug in SimpleSVD where getSingularValue() did not return ordered singular values
+- SingularOps.nullVector() now lets you specify if it is the left or right null space
+- Added SafeSvd, which is a wrapper class that ensures that the input to SVD is not modified
+
+----- Version 0.20
+
+2012/08/22
+
+- Fixed bug in SingularOps.nullity where it did not handle wide matrices correctly. 
+  * Thanks arnavkumar for pointing out the bug
+- Reworked null-space
+  * SingularOps.nullSpace() is now nullVector()
+  * SingularOps.nullSpace now returns the entire null space not just one vector
+  * Thanks arnavkumar for pointing out the non-standard implementation
+- Changed behavior of MatrixFeatures.isDiagonalPositive() so that it will return false if NaN is found
+- Under certain conditions SvdImplicitQrDecompose will use BidiagonalDecompositionTall instead of the default
+  algorithm.
+- Moved factories and related interfaces into their own package outside of the "alg" package, which was intended for
+  specific implementations.
+- DecompositionFactory has been cleaned up by reducing the number of functions, making input parameters more consistent,
+  and improved JavaDoc
+- Changed CommonOps.set(matrix,value) to CommonOps.fill(matrix,value)
+- Added function QRPDecomposition.setSingularThreshold() so that the user has more control over this important threshold
+  * The singular threshold in QRP is now absolute and not automatically relative.
+  * To replicate the original behavior do the following:
+       decomp.setSingularThreshold( CommonOps.elementMaxAbs(A)*UtilEjml.EPS )
+- SvdImplicitQrDecompose now takes in a parameter which specifies if tall bidiagonal decomposition can be used over
+  concerns of its stability.  These concerns are mostly theoretical since a few simple tests show the stability to be
+  almost the same.
+- SimpleBase now implements Serializable
+- Accessors in SingularValueDecomposition now can take in an optional storage matrix.
+- Removed support for LinearSolver with Gauss-Jordan elimination
+  * The provided implementation was very slow and didn't utilize any of the advantages of GJ
+- Added support for transforming an augmented matrix into Reduced Row Echelon Form (RREF)
+  * Implementation using Gauss-Jordan with row pivots
+  * Added CommonOps.rref()
+
+----- Version 0.19
+
+2012/04/23
+
+- Added example showing file IO to examples directory
+- Changed MatrixIO.saveXML and MatrixIO.loadXML to saveBin and loadBin because it uses a binary format not XML
+- Added save/load CSV to SimpleBase
+- Implemented VectorVectorMult.rank1Update
+  * Before the function did nothing
+- Fixed bug in LinearSolverQrHouseCol where it was not handled a change in matrix size correctly
+- Added new variant of SingularOps.descendingOrder() which takes an array of singular values as input
+- Added reshape(row,cols) because adding false to reshape(row,cols) all the time was annoying me.
+- Added multInner() and multOuter() for performing inner and outer matrix products.  Faster than
+  using generalized algorithms.
+- Changed behavior of VectorVectorMult.innerProdA so that it does not require 'A' to be square
+- Improved Pseudo Inverse
+  * Refactored by changing package and renaming
+  * Added QR pivot based pseudo inverse classes
+- Improved QR Pivot so that it has better stability
+  * Normalization is done on a column by column basis instead of by the whole matrix.
+- QR decomposition using extracted columns now invokes more generalized code for householder operations
+  * Should improve performance for larger matrices, and slightly degrade for small
+- Added SimpleMatrix.setRow() SimpleMatrix.setColumn()
+  * Writes elements in an array to a row/column.
+- Moved decomposition and linear solver factories and interfaces into a factories package.
+  * Was placed in alg.dense package, which is for specific implementations
+
+----- Version 0.18
+
+2011/12/04
+
+- Added support for reading and writing CSV formatted matrices
+  * Requested by various people.
+- Changed SolvePseudoInverse to use SVD, making it much more robust and will never fail now.
+  * Same behavior as matlab pinv() function
+  * Requested by mwolff.
+  * Updated CommonOps.pinv() to use SolvePseudoInverse
+  * Added pinv() to SimpleMatrix
+- Added getSingularValue() to SimpleSVD class.
+- Rewrote JavaDOC for reshape() because it was miss leading
+  * sh10151 pointed this out
+- Removed one of the CommonOps.insert() functions since it was identical to CommonOps.extract()
+- Created optimized versions of CommonOps.extract
+  * Seems to boost performance on small matrices but not large
+- Added QR decomposition with column pivots
+- Added solver which uses QR with column pivots
+- BidiagonalDecompositionTall now passes all of its unit tests
+
+----- Version 0.17
+
+2011/06/28
+
+- Moved general and symmetric EVD to DecompositionFactory from EigenOps
+- Moved show() from MatrixIO to MatrixVisualization to help with Android development.
+  * added "jar nogui" to ant build script which will exclude GUI related files.
+- Added CommonOps.elementDiv()
+  * Thanks to leokury for the suggestion
+- Many functions now handle NaN more intelligently in MatrixFeatures
+  * Thanks to kaspar.thommen for pointing out these flaws.
+- Adding Maven support
+  * Can now download from the central repository
+  * Requested by soren
+- Added sumRows() and sumCols() to CommonOps
+  * Requested by b.broeksema
+- Fixed bug in inducedP2 where the max singular value was not always returned
+  * Found by Antonino Freno
+
+----- Version 0.16
+
+2011/02/23
+
+- Removed SVD code based on NR
+- Fixed bug in BlockMatrix64HouseholderQR.applyQ()
+ * improved unit test to detect the bug
+- Changed contract for LinearSolver and added functions to determine if the input
+  is being modified or not.
+- Genericified LinearSolver
+  * Removed LinearBlockSolver
+- Genericified DecompositionInterface
+  * Removed block decomposition interfaces
+- Clarified behavior of functions in MatrixFeatures.
+- Added EjmlUnitTests
+  * converted many internal unit tests to use this class
+- Changed MatrixIO to take a PrintStream as an input
+- Adding support for toString()
+- EVD and SVD now modify their inputs by default
+- Added unit test for PCA
+- Added in-place converting to and from row-major to block matrices.
+- Trangular block solver can handle unaligned input
+- Modified isIdentical and added isEquals to MatrixFeatures.
+  * Resulting from a discussion with Kaspar Thommen
+- SimpleMatrix has been made more easy to extend by adding a protected function that declares
+  new instances of SimpleMatrix. Now function calls will return the correct type.
+- TridiagonalSimilarDecomposition is now an interface and related code has been made more
+  general purpose.
+- Added a QR decomposition that supports bidiagonalization for tall matrices
+  * Results in significantly faster SVD when U is not needed.
+  * Not used yet in SVD because QR decomposition without column pivots is unstable for singular matrices.
+- Modified BidiagonalizeDecomposition interface
+- SVD and EVD QR algorithm take in diagonal and off diagonal as inputs not the matrix.
+- Added dense matrix copy triangle and isEqualsTriangle
+- Changed logo to more accurately represent a Householder reflector.
+- Pushed SimpleMatrix into its own package.
+  * To make it easy to extend, SimpleMatrix now extends SimpleBase
+  * Added dot() and isVector() is SimpleMatrix.
+- LinearSolverFactory and DecompositionFactory now take in the matrix's size
+- SimpleMatrix now accepts variable arguments.
+
+----- Version 0.15
+
+2010/11/20
+
+- Minor speed ups in LU, determinant, transpose
+- Fixed a bug in MatrixComponent.  Negative elements were not handled correctly.
+- More clearly defined what LinearSolver.quality() is and made it invariant of the matrix's scale.
+- Added wrap to DenseMatrix64F for handling raw data.
+- Added PrincipleComponentAnalysis example
+- Changed DecompositionInterface
+- Added unsafe_get() and unsafe_set() to Matrix64F and implementing classes.
+- Cholesky and QR decompositions for a block matrix and associated helper functions.
+- Simplistic functions have had the hand inlining removed. Suggested by Kaspar Thommen.
+  * After marking it was found that sometimes more complex functions had their performance
+    significantly degraded after they started to use very light weight wrappers.
+  * In some cases it seems to slightly improve performance when setters/getters are used.
+  * Yes these changes were statistically significant.
+- Changed behavior of CommonOps.extract() to make its behavior match other submatrix operations better
+- Added the ability to create a new matrix if a null dst is provided in several CommonOps functions.
+- Moved examples into their own java package.  Makes selectively running those unit tests easier in IntelliJ
+- Added elementMult() to SimpleMatrix. Suggested by Nikita Rokotyan.
+- Fixed bug in the different QR decomposition implementations for compact QR.
+  * Added compact QR to generic tests
+- Fixed bug in LinearSolverFactory.symmetric() where LU instead of cholesky was being created.
+- EjmlParameters.MEMORY provides a way for it to select algorithms which are fast but memory hogs or
+  slower but more memory efficient.
+
+----- Version 0.14
+
+2010/08/07
+
+- Removed left over debugging print statement from SvdImplicitQrAlgorithm.
+- For reflectors (used in QR) code has been added that can switch between two normalization
+  methods.  This can significant speed things up and improve accuracy.
+- Added underflow stability benchmarks for some decompositions
+- Correct some issues in SVD and Symm EVD
+- Seed up SVD by removing need for some divisions
+- Symm EVD now uses the Wilkinson shift
+- Fixed a bug in kron().
+- Added several new functions to SimpleMatrix and cleaned up function names.
+- Fixed a bug in QR decomposition where compact flag was incorrectly handled.
+- Added QRExample* to example source code to demonstrate using extract and insert
+- All QR decompositions now works on matrices with any shape
+- Added set() to MatrixIterator
+- Added additional checks to matrix multiply that makes sure 'c' is not the same matrix as 'a' or ''b'
+  - Suggested by xcolwell
+- Moved IO functions to MatrixIO
+  - Provide a way to save/read DenseMatrix64F and SimpleMatrix to/from a file.
+- Fixed and added more block matrix operations.
+- Creates a new class to store transpose algorithms.
+   * Including a new block one that is about 40% faster on large non-square matrices.
+- Added quality() to LinearSolver to provide a quick way to validate results.
+- Added MatrixIO.show() for visually showing a matrix's state.
+
+----- Version 0.13
+
+2010/07/08
+
+- Fixed SingularOps.descendingOrder() bug.
+   * Thanks xcolwell for reporting it.
+- Fixed a bug in RandomMatrices.createOrthogonal()
+- Cleaned up Matrix64F and D1Matrix64F
+- Updated java doc for several classes
+- Added SVD and EVD to SimpleMatrix
+- SVD can now selectively compute U and V
+- SVD is computed all at once.
+   - Seems to perform slightly faster than original code and is simpler.
+   - There is a loss in performance for 2 by 2 matrices.  
+- SVD getU() and getV() can now indicate if the transpose is returned or not.
+- SVD fixed issue dealing with very small numbers canceling out and causing a divided by zero error
+- SVD changed exceptional shift to use a random rotation
+- Moved quality() functions inside of DecompositionFactory
+- Symmetric EVD
+  * Fixed rare problem that could happen if two eigenvalues are identical and off diagonal element is small it will never converge.
+  * Added code to make it more robust to very small numbers
+- Generic EVD
+  * Fixed long standing issue with eigenvectors and repeat eigenvalues
+  * Made unit test tolerant to small round off errors causing an eigen value to become imaginary
+- Added a version string to UtilEjml
+
+
+
+----- Version 0.12
+
+2010/06/17
+
+- Added PolynomialFit example code.
+- Updated comments to reflect the addition of LinearSolvers
+- Added MatrixIterator to provide another way of accessing elements inside a matrix
+- Can just compute eigenvalues.
+- Fixed an array index out of bounds error when computing general eigenvectors
+- Improved accuracy of general EVD when computing eigenvalues and eigenvectors
+  when dealing with nearly identical eigenvalues.  Thanks to Ex'ratt for finding this and the previous problem.
+- Auto code for unrolling determinant by minor.
+- Auto code generator for unrolling inverse by minor
+- Created experimental code directory
+  
+----- Version 0.11
+
+2010/04/07
+
+- Updated LevenbergMarquardt example.
+- Added the ability to save the old data in reshape() if the matrix grows in size.
+- Added Kronecker product
+- Added LinearSolverFactory
+- Changed constructor in DenseMatrix64F so that var-args can be used.
+- Can add random numbers to a matrix
+- Added null space to SingularOps
+- Added a function in SingularOps to rearrange the SVD such that the singular values are in descending order.
+
+----- Version 0.10.1
+
+2010/02/18
+
+- Fixed a bug with large symmetric matrices where eigen pairs would be incorrectly associated with each other.
+- Made the general eigenvalue decomposition algorithm less buggy.  It can now handle the case where
+  eigenvalues appear in a different order the second time, but doesn't solve for repeat values correctly.
+
+----- Version 0.10
+
+2010/02/16
+
+- fixed a LU pivot matrix bug
+- SVD Implicit QR algorithm (much faster than old)
+- Optimized rank1update for large matrices
+- Various refactoring and other tweaks.
+
+----- Version 0.9
+
+2010/01/29
+
+- Added the QR Algorithm for eigenvalue decomposition for Symmetric matrices.
+- improved eigenvalue unit tests exposing some problems with generic EVD
+- various refactoring
+
+----- Version 0.8
+
+2010/01/15
+
+- Usability improvements
+- Fixed matrix multiply switching rules for some of the operators
+- Added automatic switching to matrix vector multiply to some operators in CommonOps
+- More random matrices: orthonormal, orthogonal, normal span, and with specific singular values.
+- More matrix norms, including induced norms and all the p vector norms.
+- Eigenvalue decomposition for general matrices.
+- More functions in EigenOps and preexisting ones improved
+- LU now works for rectangular matrices
+- LU now generates a pivot matrix (I guess it is really PLU)
+- various refactoring
+
+----- Version 0.7
+
+2009/11/10
+
+- Created a linear solver interface.
+- QR Update.
+  * Two implementations.  One is more straight forward and the other is much faster.
+- Created a linear solver interface that allows data points to be added and removed.
+- Made decomposition and linear solver algorithms use lazy initialization.
+
+----- Version 0.6
+
+2009/10/26
+
+- Added QR Decomposition
+- Added Leibniz formula for determinants.
+  * This required a permutation algorithm to be added also.
+- Fixed various bugs
+- Improved SimpleMatrix to make it more useful
+
+
+----- Version 0.5
+
+2009/9/19
+
+- Added another type of matrix multiplication that creates a temporary column
+  * This significant improved performance in some areas
+
+----- Version 0.4
+
+2009/9/5
+
+- Added two more Cholesky decomposition algorithm; block and LDL.
+- Moved significant magic numbers in to EjmlParamters to provide a central place for configuration.
+- Various other refactorings and bug fixes.
+
+----- Version 0.3
+
+2009/8/27
+
+- This is the initial public distribution.  Has most of the final functionality.
diff --git a/design_notes.txt b/design_notes.txt
new file mode 100644
index 0000000..fadcbc9
--- /dev/null
+++ b/design_notes.txt
@@ -0,0 +1,80 @@
+Block Size
+---------------------------
+- Transpose
+  * Q6600 ran fast at 200 for 10k matrices.  didn't check higher
+  * Pentium-M degrades performance slightly for 2k matrix.
+  * Selected 60 as a compromise between Q6600 and pentium-m performance.
+- Block Cholesky
+  * 20 seems to be optimal, 60 slows it down a lot.
+
+
+Matrix Size optimization and Decompositions/Linear Solvers
+---------------------------
+
+Once a new instance of a Linear Solver or Decomposition has been created no more dynamics adjustments should
+happen for the input matrix size.  This is done to simply the code and testing for correctness by reducing
+the number of permutations.
+
+
+Block Cholesky Decomposition
+---------------------------
+The DenseMatrix64F based CholeskyDecompositionBlock class is easier to tune and offers equivalent
+performance to BlockMatrix64F based BlockCholeskyOuter class. CholeskyDecompositionBlock achieves
+optimal performance with munch smaller block sizes, but its performance actually degrades when
+larger blocks that are optimal for BlockMatrix64F algorithms are used.  CholeskyDecompositionBlock
+also works directly with DenseMatrix64F and avoids the need to convert matrix types.
+
+Block matrix solver is much faster than the row-major solver.
+
+However selecting the default class for Cholesky decomposition is not obvious because using
+CholeskyDecompositionBlock would require an additional tuning parameter making the library
+even more difficult to use.
+
+Block QR Decomposition
+--------------------------
+
+Saving the W matrix instead of recomputing it each time was tried.  For smaller matrices it has
+a noticeable speed improvement when solving.  Anything over 2k it seems to be negligible and almost double 
+the memory required.  Block QR Decomposition is only used on larger matrices so there is no point in
+saving W for later reuse when solving.  Based on profiling results W can save about 5% of runtime when solving
+a system.
+
+Block Matrix Multiply
+---------------------------
+- Block matrix multiplication does have fewer cache misses.
+- Converting from row major to block and multiplying causes too many cache misses.
+- Two different types of block matrix were created.
+  * single continuous array
+  * N*M arrays
+- After making the code ugly and barely readable they had comparable performance to multReorder(),
+  when multiplying two block matrices together.
+- The code currently committed and that resides in experimental has
+  not been optimized as much, but is readable.
+
+Unrolled Matrix Multiplication
+---------------------------
+- Tried to unroll either a row or column in either of the inputs
+- Does result in improve performance of small square matrices
+- Does not always translate to tall or wide matrices
+  * can be much slower than other orders
+- Did not integrate into library because of added complexity
+
+Combine matrix for DenseMatrix64
+---------------------------
+combine() is a function in SimpleMatrix that combines two matrices together and grows if needed.
+No equivalent is directly provided in CommonOps since it is horribly memory inefficient.  Why
+use DenseMatrix64 if you are going to do that.
+
+QR Decomposition: Column Major vs. Transpose
+---------------------------
+Two variants of QR decomposition currently exist in the code.  One converts the input matrix
+into a 2D array in a column major format and the other creates a transposed matrix.  QR
+decomposition has fewer cache misses when internally it reformats matrices like this.
+
+The column major 2D array format is about 10% faster than the transpose algorithm due
+to less array traversal overhead.  However, it requires specialized code and increases
+maintenance overhead.  It will also not benefit from improvements to more common operations.
+
+Deleting the 2D array format was being considered for sake of simplifying the code base.
+However, it is actually an idea format for QR with column pivots and results in simpler
+faster code.  So it was decided to keep both variants.
\ No newline at end of file
diff --git a/docs/ManualEJML.pdf b/docs/ManualEJML.pdf
new file mode 100644
index 0000000..1fef248
Binary files /dev/null and b/docs/ManualEJML.pdf differ
diff --git a/docs/bottom.txt b/docs/bottom.txt
new file mode 100644
index 0000000..0f1f021
--- /dev/null
+++ b/docs/bottom.txt
@@ -0,0 +1,12 @@
+<script>
+  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
+
+  ga('create', 'UA-10413214-7', 'ejml.org');
+  ga('send', 'pageview');
+
+</script>
+<br>
+<b>Copyright © 2009-2015 Peter Abeles</b>
diff --git a/docs/header.txt b/docs/header.txt
new file mode 100644
index 0000000..14cb655
--- /dev/null
+++ b/docs/header.txt
@@ -0,0 +1,9 @@
+<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
+<!-- boofcv javadoc banner -->
+<ins class="adsbygoogle"
+     style="display:inline-block;width:468px;height:60px"
+     data-ad-client="ca-pub-0419821832107208"
+     data-ad-slot="3257872658"></ins>
+<script>
+(adsbygoogle = window.adsbygoogle || []).push({});
+</script>
\ No newline at end of file
diff --git a/docs/latex/ManualEJML.tex b/docs/latex/ManualEJML.tex
new file mode 100644
index 0000000..678b230
--- /dev/null
+++ b/docs/latex/ManualEJML.tex
@@ -0,0 +1,326 @@
+\documentclass[12pt]{article}%book report
+
+\usepackage{bm}
+\usepackage{amssymb}
+\usepackage{amsmath}
+\usepackage[pdftex]{graphicx}
+
+\title{Efficient Java Matrix Library}
+\author{Peter Abeles}
+\date{\today}
+
+\begin{document}
+
+
+\maketitle
+
+\tableofcontents
+
+\begin{abstract}
+Efficient Java Matrix Library (EJML) is a linear algebra library for manipulating dense matrices. Its design goals are; 1) to be as computationally efficient as possible for both small and large matrices, and 2) to be accessible to both novices and experts. These goals are accomplished by dynamically selecting the best algorithms to use at runtime and by designing a clean API. EJML is free, written in 100\% Java and has been released under an LGPL license.
+
+http://code.google.com/p/efficient-java-matrix-library/
+\end{abstract}
+
+\section{IMPORANT}
+
+This document is horribly out of date but for some reason its a top result on google.  To get the latest documentation go to: 
+
+http://code.google.com/p/efficient-java-matrix-library/
+
+\section{Introduction}
+
+Efficient Java Matrix Library (EJML) is a linear algebra library for manipulating dense matrices. Its design goals are; 1) to be as computationally efficient as possible for both small and large matrices, and 2) to be accessible to both novices and experts. These goals are accomplished by dynamically selecting the best algorithms to use at runtime and by designing a clean API. EJML is free, written in 100\% Java and has been released under an LGPL license.
+
+EJML has three distinct ways to interact with it. This allows a programmer to choose between simplicity and efficiency. 1) A simplified interface that allows a more object oriented way of programming. 2) Letting EJML select the best algorithm to use. 3) Directly calling specialized algorithms. In general EJML is one the fastest single threaded pure Java library. See Java Matrix Benchmark for a detailed comparison of different libraries.
+
+The following is provided:
+\begin{itemize}
+\item Basic operators (addition, multiplication, ... ).
+\item Linear Solvers (batch and incremental).
+\item Decompositions (LU, QR, Cholesky, SVD, Eigenvalue).
+\item Matrix Features (rank, symmetric, definitiveness, ... ).
+\item Random Matrices (covariance, orthogonal, symmetric, ..
+\end{itemize}
+
+
+In addition there are many specialized algorithms for specific matrix sizes and types.  
+
+\section{Data Structures and Algorithms}
+\label{sec:structures}
+
+DenseMatrix64F is the most basic data structure in EJML.  It is a dense matrix composed of doubles.  Internally the matrix is stored as a single array using a row-major format, see Figure \ref{fig:rowmajor}.  SimpleMatrix is a wrapper on top of DenseMatrix64F that provides a simplified interface.
+
+\begin{figure}[h]
+\begin{displaymath}
+\begin{array}{|c|c|c|}
+\hline
+a_{11} & a_{12} & a_{13} \\  
+\hline
+a_{21} & a_{22} & a_{23} \\  
+\hline
+\end{array}
+\rightarrow
+\begin{array}{|c|c|c|c|c|c|}
+\hline
+a_{11} & a_{12} & a_{13} & a_{21} & a_{22} & a_{23} \\  
+\hline
+\end{array}
+\end{displaymath}
+\caption{\label{fig:rowmajor}A matrix encoded in row-major format.}
+\end{figure}
+
+An operation, in this context, is just a mathematical function that takes at least one matrix in as an input.  For example addition and subtraction are both operators.  CommonOps and SpecializedOps are two classes which have several static functions that are operators for DenseMatrix64F.  SimpleMatrix has all of its operators as part of its class for convenience.  For a comparision of using operators with DenseMatrix64F and SimpleMatrix see Figure \ref{fig:ops_vs_simple}.
+
+There are many operators in EJML.  Figure \ref{fig:operators} contains a list of some of the operators in this libraries.  Figure \ref{fig:simplefuncs} contains a list of operators that can be directly performed by SimpleMatrix.  To make SimpleMatrix easier to program with, only a subset of the most essential operators are provided.  However, by directly accessing the internal DenseMatrix64 in a SimpleMatrix, all the operators can be called on it.
+
+\begin{figure}[h]
+\begin{center}
+\begin{tabular}{c}
+Matrix multiplcation using operations. \\
+\hline \\
+\begin{minipage}[c]{10cm}
+\begin{verbatim}
+DenseMatrix64F c = 
+  new DenseMatrix64F(a.numRows,b.numCols);
+CommonOps.mult(a,b,c);
+\end{verbatim}
+\end{minipage}
+\\
+\vspace{1cm}\\
+Matrix multiplcation using SimpleMatrix \\
+\hline \\
+\begin{minipage}[c]{10cm}
+\begin{verbatim}
+SimpleMatrix c = a.times(b);
+\end{verbatim}
+\end{minipage}
+\end{tabular}
+\end{center}
+\caption{\label{fig:ops_vs_simple}Comparision operations and SimpleMatrix.}
+\end{figure}
+
+Some of the operators do not specify which specific algorithm is used and some do.  For example, many of the operators in CommonOps can call different algorithms depending on the structure of the matrix passed in.  This allows the most efficient algorithm to be called.  However, many the functions in MatrixMatrixMult are designed to be optimal for matrices of different sizes.  Unless there is a very good reason not to, all calls should be made to the more generic operators. 
+
+One good reason not to use the generic operators is if you, the programmer, knows something they do not.  For example, if a matrix is positive definite, then the generic inverse function is suboptimal.  In that situation you should use CholeskyDecomposition directly because it is much faster.
+
+\begin{figure}[h]
+\begin{tabular}{cll}
+Class & function & description \\
+\hline
+CommonOps & mult & $C = AB$ and $C = \alpha AB$ and $C = A^TB$ \\ 
+CommonOps & multTranA & $C = A^TB$ and $C = \alpha A^TB$\\
+CommonOps & multTranAB & $C = A^TB^T$ and $C = \alpha A^TB^T$\\
+CommonOps & multTranB & $C = AB^T$ and $C = \alpha AB^T$\\
+CommonOps & multAdd & $C = C+AB$ and $C = C+\alpha AB$\\
+CommonOps & multAddTranA & $C = C+A^TB$ and $C = C+\alpha A^TB$\\
+CommonOps & multAddTranAB & $C = C+A^TB^T$ and $C = C+\alpha A^TB^T$\\
+CommonOps & multAddTranB & $C = C+AB^T$ and $C = C+\alpha AB^T$\\
+CommonOps & multElement & $c_{ij} = a_{ij} b_{ij}$ \\
+CommonOps & add & $C = A+B$, $C = \alpha A+\beta B$\\
+CommonOps & addEquals & $A = A+B$, $A = A + \beta B$\\
+CommonOps & sub & $C = A-B$ \\
+CommonOps & subEquals & $A = A-B$\\
+CommonOps & invert & Matrix inverse $A^{-1}$\\
+CommonOps & solve & Solves for $X = A^{-1} B$\\
+CommonOps & normF & Matrix norm \\
+CommonOps & scale & $a_{ij} = \alpha a_{ij}$ \\
+CommonOps & transpose & $a_{ij} = a_{ji}$ \\
+CommonOps & trace & $\sum_i a_{ii}$ \\
+SpecializedOps & identity & Creates an identity matrix \\
+SpecializedOps & diag & Creates a diagonal matrix \\
+SpecializedOps & submatrix & Creates a submatrix of the original \\
+SpecializedOps & diffNormF & $\sqrt{\sum_{ij}(a_{ij}-b_{ij})^2 }$ \\
+SpecializedOps & diffNormP1 & $\sum_{ij}|a_{ij}-b_{ij}|$ \\
+SpecializedOps & copyChangeRow & Swaps the rows of the original matrix \\
+MatrixFeatures & hasNaN & Does the matrix contain a NaN \\
+MatrixFeatures & hasUncountable & Does the matrix contain a NaN or Infinity\\
+MatrixFeatures & isVector & Is the matrix a vector? \\
+MatrixFeatures & isPositiveDefinite & Is the matrix positive definite? \\
+MatrixFeatures & isPositiveSemidefinite & Is the matrix positive semidefinite? \\
+MatrixFeatures & isSquare & Is the matrix square? \\
+MatrixFeatures & isSymmetric & Is the matrix symmetric? \\
+MatrixFeatures & isSimilar & $|a_{ij}-b_{ij}|\le \sigma \; \forall \; i,j$ \\
+CovarianceOps & isValidFast & Quick covariance validity check. \\
+CovarianceOps & isValid & Rigerous covariance validity check. \\
+CovarianceOps & invert & Faster covariance matrix inversion. \\
+CovarianceOps & randomVector & Draws a random vector from the covariance. \\
+\end{tabular}
+\caption{\label{fig:operators}Some of the matrix operators included in EJML}
+\end{figure}
+
+
+\begin{figure}[h]
+\begin{tabular}{ll}
+function & description \\
+\hline
+wrap & Allows a DenseMatrix64F to be manipulated as a SimpleMatrix. \\
+identity & Creates an identity matrix.\\
+random & Creates a matrix with random elements.\\
+diag & Creates a diagonal matrix. \\
+getMatrix & Returns the internal DenseMatrix64F.\\
+transpose & Returns the transpose of this matrix.\\
+mult(B) & Returns $AB$\\
+plus(B) & Returns $A+B$\\
+minus(B) & Returns $A-B$\\
+plus($\beta$,B) & Returns $A+\beta B$\\
+scale($\alpha$) & Returns $\alpha A$\\
+invert() & Returns $A^{-1}$\\
+solve(B) & Returns $X=A^{-1}B$\\
+set(val) & $a_{ij} = val$\\
+zero & $a_{ij} = 0$ \\
+norm & Returns the matrix norm. \\
+determinant & Returns the determinant. \\
+trace & Returns the matrix's trace. \\
+reshape & Changes the matrix's shape with out declaring new data. \\
+computeSVD & Singular Value Decomposition \\
+set(i,j,val) & $a_{ij} = val$ \\
+get(i,j) & Returns $a_{ij}$ \\
+numRows() & Returns number of rows.\\
+numCols() & Returns number of columns.\\
+\end{tabular}
+\caption{\label{fig:simplefuncs}Functions provided by SimpleMatrix.}
+\end{figure}
+
+
+\section{Examples}
+In the examples directory three different implementations of a Kalman filter are provided along with a Levenberg-Marquardt optimization algorithm.  These two algorithms are popular in various engineering disciplines.
+
+The three different implementations of the Kalman filter are provided.  Each one demonstrates different ways that EJML can be used. to show off the interfaces that one can use in EJML.  In the following subsections the update function is shown and discussed.
+
+\subsection{KalmanFilterSimple.java}
+Figure \ref{fig:update_simple} shows several concepts related to the simple interface.  The wrap() function on the top demonstrates how a regular DenseMatrix64F can be changed into a SimpleMatrix with ease.  Memory management is at a minimal since new matrices of the correct size are automatically created by each operation.  The code is also noticeably easier to read.
+
+\begin{figure}[h]
+\begin{verbatim}
+public void update(DenseMatrix64F _z, DenseMatrix64F _R) {
+   // a fast way to make the matrices usable by SimpleMatrix
+   SimpleMatrix z = SimpleMatrix.wrap(_z);
+   SimpleMatrix R = SimpleMatrix.wrap(_R);
+
+   // y = z - H x
+   SimpleMatrix y = z.minus(H.mult(x));
+
+   // S = H P H' + R
+   SimpleMatrix S = H.mult(P).mult(H.transpose()).plus(R);
+
+   // K = PH'S^(-1)
+   SimpleMatrix K = P.mult(H.transpose().mult(S.invert()));
+
+   // x = x + Ky
+   x = x.plus(K.mult(y));
+
+   // P = (I-kH)P = P - KHP
+   P = P.minus(K.mult(H).mult(P));
+}
+\end{verbatim} 
+\caption{\label{fig:update_simple}Code from KalmanFilterSimple.java}
+\end{figure}
+
+\subsection{KalmanFilterOps.java}
+The operation interface, shown in Figure \ref{fig:update_ops}, is noticeably more complex.  It requires matrices to be predeclared.  For this to work the programmer needs to figure out the exact shape of these intermediate matrices.  What was on one line now takes up multiple lines.
+
+Memory management is more complicated in this example.  In Figure \ref{fig:predeclare} matrices that store the intermediate results are declared in the constructor.  This requires more though from the programmer, but greatly reduces the amount of memory created and destroyed each processing cycle. 
+
+\begin{figure}[h]
+\begin{verbatim}
+public void update(DenseMatrix64F z, DenseMatrix64F R) {
+   // y = z - H x
+   mult(H,x,y);
+   sub(z,y,y);
+
+   // S = H P H' + R
+   mult(H,P,c);
+   multTransB(c,H,S);
+   addEquals(S,R);
+
+   // K = PH'S^(-1)
+   if( !invert(S,S_inv) ) throw new RuntimeException("Invert failed");
+   multTransA(H,S_inv,d);
+   mult(P,d,K);
+
+   // x = x + Ky
+   mult(K,y,a);
+   addEquals(x,a);
+
+   // P = (I-kH)P = P - (KH)P = P-K(HP)
+   mult(H,P,c);
+   mult(K,c,b);
+   subEquals(P,b);
+}
+\end{verbatim} 
+\caption{\label{fig:update_ops}Code from KalmanFilterOps.java}
+\end{figure}
+
+\begin{figure}
+\begin{verbatim}
+a = new DenseMatrix64F(dimenX,1);
+b = new DenseMatrix64F(dimenX,dimenX);
+y = new DenseMatrix64F(dimenZ,1);
+S = new DenseMatrix64F(dimenZ,dimenZ);
+S_inv = new DenseMatrix64F(dimenZ,dimenZ);
+c = new DenseMatrix64F(dimenZ,dimenX);
+d = new DenseMatrix64F(dimenX,dimenZ);
+K = new DenseMatrix64F(dimenX,dimenZ);
+\end{verbatim}
+\caption{\label{fig:predeclare}When directly working with the operation functions, matrices that contain the intermediate results need to be declared.}
+\end{figure}
+
+\subsection{KalmanFilterAlg.java}
+Finally there is the algorithm interface, Figure \ref{fig:update_alg}.  Here specific algorithms are called.  The most important is the use of the Cholesky decomposition, which speeds up the matrix inverse.  In other cases it takes advantage of it knowing before hand the size and shape of the matrix, which results in a slight performance gain.
+
+\subsection{Notes}
+What is the benefit of the added complexity of using the operator and algorithm interfaces over the simplistic interface?  The operator interface runs about 23\% faster than the simplistic interface and the algorithm interface runs about 28\% faster than the simplistic interface.  The exact performance differences will vary greatly depending on the application.
+
+\begin{figure}[h]
+\begin{verbatim}
+public void update(DenseMatrix64F z, DenseMatrix64F R) {
+   // y = z - H x
+   MatrixVectorMult.mult(H,x,y);
+   sub(z,y,y);
+
+   // S = H P H' + R
+   MatrixMatrixMult.mult_small(H,P,c);
+   MatrixMatrixMult.multTransB(c,H,S);
+   addEquals(S,R);
+
+   // K = PH'S^(-1)
+   if( !chol.decompose(S) ) throw new RuntimeException("Invert failed");
+   chol.setToInverse(S_inv);
+   MatrixMatrixMult.multTransA_small(H,S_inv,d);
+   MatrixMatrixMult.mult_small(P,d,K);
+
+   // x = x + Ky
+   MatrixVectorMult.mult(K,y,a);
+   addEquals(x,a);
+
+   // P = (I-kH)P = P - (KH)P = P-K(HP)
+   MatrixMatrixMult.mult_small(H,P,c);
+   MatrixMatrixMult.mult_small(K,c,b);
+   subEquals(P,b);
+}
+\end{verbatim} 
+\caption{\label{fig:update_alg}Code from KalmanFilterAlg.java}
+\end{figure}
+
+\appendix
+\section{Design Philosophy}
+This library was written in response to perceived weaknesses in other Java matrix libraries.  The three biggest are, 1) unnecessarily slow performance in small matrices, 2) lack of flexibility in memory management, and 3) needing to choose between ease of use and performance.
+
+One of the areas EJML performs very well is when dealing with small matrices.  This is accomplished by having very low overhead and by using specialized algorithms for small matrices.  The overhead is reduce by not having abstraction that allow native code or java code to be run, or generic algorithms that can work on a wide variety of matrix types.  Specialized algorithms vary from switching the order the matrix is traversed depending on its size to having hand coded algorithms for spec [...]
+
+One way to write efficient Java algorithms is to minimize the amount of memory that is created and destroyed.  This smooths out the processing time by not having the garbage collector needing to run as often and reduces the number of cycles spent reinitializing the data.  All of the algorithms in EJML have been coded such that they predeclared all the data they use.  The reshape function is also provided.  While extremely easy to implement, it is often neglected in other libraries forcin [...]
+
+Jama\footnote{http://math.nist.gov/javanumerics/jama/} is still a popular library despite no longer being actively developed.  The primary reason for this is that it is easy to use.  Matrix Toolkit Java (MTJ)\footnote{http://code.google.com/p/matrix-toolkits-java/} gives the developer more control, but is harder to use.  EJML uses ideas from both libraries to create a simple yet robust interface.  A user can program in EJML using the following interfaces; simple, operator, and algorithm. 
+
+Simple is a wrapper on top of the operator interface.  It hides much of the memory management from the user and allows more readable code. The operator interface simplifies the selection of which algorithm to use from the user by automatically selecting what it thinks is the best one.  The algorithm interface gives complete control to the programmer, but requires more skill and knowledge to user effectively.
+
+\subsection{Why multiple implementations?}
+Optimizing an algorithm for processing small and large matrices requires different strategies.  An optimal small matrix algorithm typically minimizes the number of operations.  Large matrix algorithms need to minimize the number of operations and cache misses.
+
+On a modern desktop computer missing a CPU cache entails a large performance hit.  A cache miss is when a computer needs to access memory that is not available on one of its high speed memory caches.  This forces it to access the much slower main memory.
+
+Small matrices can often be stored entirely in the CPU's cache, making it much less likely to have a cache miss.  Large matrix algorithms are designed to avoid cache misses, often by processing the data in a less straight forward way that takes more operations.  Typically running a small matrix algorithm on a large matrix will result in a very large peformance hit, but the inverse only results in a relatively small performance hit.
+
+
+\end{document}
diff --git a/docs/logo/EJML_logo.pdf b/docs/logo/EJML_logo.pdf
new file mode 100644
index 0000000..f9897a9
Binary files /dev/null and b/docs/logo/EJML_logo.pdf differ
diff --git a/docs/logo/EJML_logo.pptx b/docs/logo/EJML_logo.pptx
new file mode 100755
index 0000000..e54e896
Binary files /dev/null and b/docs/logo/EJML_logo.pptx differ
diff --git a/docs/logo/ejml_icon.gif b/docs/logo/ejml_icon.gif
new file mode 100644
index 0000000..275071d
Binary files /dev/null and b/docs/logo/ejml_icon.gif differ
diff --git a/docs/logo/ejml_logo.gif b/docs/logo/ejml_logo.gif
new file mode 100755
index 0000000..81077d1
Binary files /dev/null and b/docs/logo/ejml_logo.gif differ
diff --git a/docs/logo/ejml_logo_153x51.gif b/docs/logo/ejml_logo_153x51.gif
new file mode 100644
index 0000000..5c241ca
Binary files /dev/null and b/docs/logo/ejml_logo_153x51.gif differ
diff --git a/docs/release-checklist.txt b/docs/release-checklist.txt
new file mode 100644
index 0000000..bea2118
--- /dev/null
+++ b/docs/release-checklist.txt
@@ -0,0 +1,23 @@
+- Update version number in UtilEjml.VERSION
+- Update change.txt
+- Update version in build.gradle
+- Commit and tag release
+- Create zip of source code
+- Create jar file "gradle createLibraryDirectory"
+- Update documentation on website
+
+-----
+Push to central:
+
+- gradle uploadArchives
+- Then go to the website  https://oss.sonatype.org/
+  * click close button
+  * double check the files
+  * click release button
+
+
+---------
+Once a year:
+- Update copy right
+- Update year in ant script
+- update year on docs/bottom.txt
diff --git a/examples/build.gradle b/examples/build.gradle
new file mode 100644
index 0000000..2c89225
--- /dev/null
+++ b/examples/build.gradle
@@ -0,0 +1,9 @@
+dependencies {
+    compile project(':main:all')
+}
+
+idea {
+    module {
+        name = "EJML Examples"
+    }
+}
\ No newline at end of file
diff --git a/examples/build.xml b/examples/build.xml
new file mode 100644
index 0000000..8237201
--- /dev/null
+++ b/examples/build.xml
@@ -0,0 +1,84 @@
+<project name="examples" basedir="." default="main">
+
+
+    <property name="src.dir"     value="src"/>
+    <property name="test.dir"     value="test"/>
+
+    <property name="build.dir"   value="build"/>
+    <property name="classes.dir" value="${build.dir}/classes"/>
+    <property name="jar.dir"     value="${build.dir}/jar"/>
+    <property name="testbuild.dir"   value="build/test"/>
+    <property name="testclasses.dir"   value="${testbuild.dir}/classes"/>
+
+    <property name="junit.dir"  value="../lib/"/>
+
+    <path id="project.classpath">
+        <fileset dir="../build/jar" includes="**/*.jar"/>
+    </path>
+
+    <path id="test.classpath">
+        <path refid="project.classpath"/>
+        <fileset dir="${junit.dir}" includes="junit*.jar"/>
+        <fileset dir="${jar.dir}" includes="**/${ant.project.name}.jar"/>
+        <dirset dir="../build/test" includes="**/classes"/>
+    </path>
+
+
+    <target name="clean">
+        <delete dir="${build.dir}"/>
+    </target>
+
+    <target name="compile">
+        <!-- Capture the path as a delimited property using the refid attribute -->
+        <!--<property name="myclasspath" refid="project.classpath"/>-->
+
+        <!-- Emit the property to the ant console -->
+        <!--<echo message="Classpath = ${myclasspath}"/>-->
+
+        <mkdir dir="${classes.dir}"/>
+        <javac srcdir="${src.dir}" destdir="${classes.dir}">
+            <classpath refid="project.classpath"/>
+        </javac>
+    </target>
+
+    <target name="jar" depends="compile">
+        <mkdir dir="${jar.dir}"/>
+        <jar destfile="${jar.dir}/${ant.project.name}.jar" basedir="${classes.dir}"/>
+    </target>
+
+    <target name="test" depends="jar">
+        <mkdir dir="${testbuild.dir}"/>
+        <mkdir dir="${testclasses.dir}"/>
+
+        <javac srcdir="${test.dir}" destdir="${testclasses.dir}">
+            <classpath>
+                <path refid="test.classpath"/>
+            </classpath>
+        </javac>
+
+        <junit printsummary="yes" showoutput="no">
+            <classpath>
+                <path refid="test.classpath"/>
+                <pathelement location="${testclasses.dir}"/>
+            </classpath>
+
+            <batchtest fork="yes">
+                <fileset dir="${test.dir}" includes="**/Test*.java"/>
+            </batchtest>
+        </junit>
+    </target>
+
+    <target name="clean-build" depends="clean,jar"/>
+
+    <target name="main" depends="clean,jar"/>
+
+    <target name="kalman-benchmark" depends="clean-build">
+        <java classname="BenchmarkKalmanPerformance" fork="true">
+            <classpath>
+                <path refid="project.classpath"/>
+                <fileset dir="${jar.dir}" includes="**/${ant.project.name}.jar"/>
+            </classpath>
+        </java>
+    </target>
+
+</project>
diff --git a/examples/readme.txt b/examples/readme.txt
new file mode 100644
index 0000000..db34d77
--- /dev/null
+++ b/examples/readme.txt
@@ -0,0 +1,39 @@
+Here are some examples of common algorithms created using EJML library.  Each example is designed
+to provided a practicle example of how the library can be used.
+
+There are two different algorithms provided as examples; Kalman filter
+and Levenberg-Marquardt.  The Kalman filter is implemented different ways to show the library
+can be used to write code easily and/or very efficiently.  Levenberg-Marquardt shows another
+ type of algorithm and how to effectively use reshaping to avoid creating new memory unnecisarily.
+
+-------------------------------
+
+Three different example of how to program a Kalman filter using this library are provided.
+One example uses the simplified interface, the other uses generic matrix operation functions,
+the last one uses direct calls to an algorithm to speed things up.  These represent three
+different levels in terms of coding complexity and performance.
+
+KalmanFilterSimple
+- uses the simplified interface.
+- easiest to program.
+- slowest to run.
+
+KalmanFilterOps
+- Uses generic functions.
+- Reduces the amount of memory created/destroyed significantly.
+- Requires more work to program.
+- Runs about 23% faster than the simplified case
+
+KalmanFilterAlg
+- Uses a specialized algorithm for performing the matrix inversion operation.
+- Requires knowedge of which algorithms to use and how to use them.
+- No new memory is created after initialization.
+- Runs about 28% faster than the simplified case.
+
+To run the speed benchmark application tell ant to run "kalman-benchmark".
+
+---------------------------------
+
+The provided ant script can compile all the example code and run the unit tests.
+
+- Peter Abeles
\ No newline at end of file
diff --git a/examples/src/org/ejml/example/BenchmarkKalmanPerformance.java b/examples/src/org/ejml/example/BenchmarkKalmanPerformance.java
new file mode 100644
index 0000000..f0e6813
--- /dev/null
+++ b/examples/src/org/ejml/example/BenchmarkKalmanPerformance.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.example;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Compares how fast the filters all run relative to each other.
+ *
+ * @author Peter Abeles
+ */
+public class BenchmarkKalmanPerformance {
+
+    private static final int NUM_TRIALS = 200;
+    private static final int MAX_STEPS = 1000;
+    private static final double T = 1.0;
+
+    private static int measDOF = 8;
+
+    List<KalmanFilter> filters = new ArrayList<KalmanFilter>();
+
+    public void run() {
+        DenseMatrix64F priorX = new DenseMatrix64F(9,1, true, 0.5, -0.2, 0, 0, 0.2, -0.9, 0, 0.2, -0.5);
+        DenseMatrix64F priorP = CommonOps.identity(9);
+
+        DenseMatrix64F trueX = new DenseMatrix64F(9,1, true, 0, 0, 0, 0.2, 0.2, 0.2, 0.5, 0.1, 0.6);
+
+        List<DenseMatrix64F> meas = createSimulatedMeas(trueX);
+
+        DenseMatrix64F F = createF(T);
+        DenseMatrix64F Q = createQ(T,0.1);
+        DenseMatrix64F H = createH();
+
+        for(KalmanFilter f : filters ) {
+
+            long timeBefore = System.currentTimeMillis();
+
+            f.configure(F,Q,H);
+
+            for( int trial = 0; trial < NUM_TRIALS; trial++ ) {
+                f.setState(priorX,priorP);
+                processMeas(f,meas);
+            }
+
+            long timeAfter = System.currentTimeMillis();
+
+            System.out.println("Filter = "+f.getClass().getSimpleName());
+            System.out.println("Elapsed time: "+(timeAfter-timeBefore));
+
+            System.gc();
+        }
+    }
+
+    private List<DenseMatrix64F> createSimulatedMeas( DenseMatrix64F x ) {
+
+        List<DenseMatrix64F> ret = new ArrayList<DenseMatrix64F>();
+
+        DenseMatrix64F F = createF(T);
+        DenseMatrix64F H = createH();
+
+//        UtilEjml.print(F);
+//        UtilEjml.print(H);
+
+        DenseMatrix64F x_next = new DenseMatrix64F(x);
+        DenseMatrix64F z = new DenseMatrix64F(H.numRows,1);
+
+        for( int i = 0; i < MAX_STEPS; i++ ) {
+            CommonOps.mult(F,x,x_next);
+            CommonOps.mult(H,x_next,z);
+            ret.add(z.copy());
+            x.set(x_next);
+        }
+
+        return ret;
+    }
+
+    private void processMeas( KalmanFilter f ,
+                              List<DenseMatrix64F> meas )
+    {
+        DenseMatrix64F R = CommonOps.identity(measDOF);
+
+        for(DenseMatrix64F z : meas ) {
+            f.predict();
+            f.update(z,R);
+        }
+    }
+
+
+    public static DenseMatrix64F createF( double T ) {
+        double []a = new double[]{
+                1, 0 , 0 , T , 0 , 0 , 0.5*T*T , 0 , 0 ,
+                0, 1 , 0 , 0 , T , 0 , 0 , 0.5*T*T , 0 ,
+                0, 0 , 1 , 0 , 0 , T , 0 , 0 , 0.5*T*T ,
+                0, 0 , 0 , 1 , 0 , 0 , T , 0 , 0 ,
+                0, 0 , 0 , 0 , 1 , 0 , 0 , T , 0 ,
+                0, 0 , 0 , 0 , 0 , 1 , 0 , 0 , T ,
+                0, 0 , 0 , 0 , 0 , 0 , 1 , 0 , 0 ,
+                0, 0 , 0 , 0 , 0 , 0 , 0 , 1 , 0 ,
+                0, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 };
+
+        return new DenseMatrix64F(9,9, true, a);
+    }
+
+    public static DenseMatrix64F createQ( double T , double var ) {
+        DenseMatrix64F Q = new DenseMatrix64F(9,9);
+
+        double a00 = (1.0/4.0)*T*T*T*T*var;
+        double a01 = (1.0/2.0)*T*T*T*var;
+        double a02 = (1.0/2.0)*T*T*var;
+        double a11 = T*T*var;
+        double a12 = T*var;
+        double a22 = var;
+
+        for( int i = 0; i < 3; i++ ) {
+            Q.set(i,i,a00);
+            Q.set(i,3+i,a01);
+            Q.set(i,6+i,a02);
+            Q.set(3+i,3+i,a11);
+            Q.set(3+i,6+i,a12);
+            Q.set(6+i,6+i,a22);
+        }
+
+        for( int y = 1; y < 9; y++ ) {
+            for( int x = 0; x < y; x++ ) {
+                Q.set(y,x, Q.get(x,y));
+            }
+        }
+
+        return Q;
+    }
+
+    public static DenseMatrix64F createH() {
+        DenseMatrix64F H = new DenseMatrix64F(measDOF,9);
+        for( int i = 0; i < measDOF; i++ ) {
+            H.set(i,i,1.0);
+        }
+
+        return H;
+    }
+
+    public static void main( String args[] ) {
+        BenchmarkKalmanPerformance benchmark = new BenchmarkKalmanPerformance();
+
+        benchmark.filters.add( new KalmanFilterOperations());
+        benchmark.filters.add( new KalmanFilterSimple());
+        benchmark.filters.add( new KalmanFilterEquation());
+
+
+        benchmark.run();
+    }
+}
diff --git a/examples/src/org/ejml/example/EquationCustomFunction.java b/examples/src/org/ejml/example/EquationCustomFunction.java
new file mode 100644
index 0000000..0f9ccf6
--- /dev/null
+++ b/examples/src/org/ejml/example/EquationCustomFunction.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.example;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.equation.*;
+import org.ejml.ops.CommonOps;
+import org.ejml.simple.SimpleMatrix;
+
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Demonstration on how to create and use a custom function in Equation.  A custom function must implement
+ * ManagerFunctions.Input1 or ManagerFunctions.InputN, depending on the number of inputs it takes.
+ *
+ * @author Peter Abeles
+ */
+public class EquationCustomFunction {
+
+    public static void main(String[] args) {
+        Random rand = new Random(234);
+
+        Equation eq = new Equation();
+        eq.getFunctions().add("multTransA",createMultTransA());
+
+        SimpleMatrix A = new SimpleMatrix(1,1); // will be resized
+        SimpleMatrix B = SimpleMatrix.random(3,4,-1,1,rand);
+        SimpleMatrix C = SimpleMatrix.random(3,4,-1,1,rand);
+
+        eq.alias(A,"A",B,"B",C,"C");
+
+        eq.process("A=multTransA(B,C)");
+
+        System.out.println("Found");
+        System.out.println(A);
+        System.out.println("Expected");
+        B.transpose().mult(C).print();
+    }
+
+    /**
+     * Create the function.  Be sure to handle all possible input types and combinations correctly and provide
+     * meaningful error messages.  The output matrix should be resized to fit the inputs.
+     */
+    public static ManagerFunctions.InputN createMultTransA() {
+        return new ManagerFunctions.InputN() {
+            @Override
+            public Operation.Info create(List<Variable> inputs, ManagerTempVariables manager ) {
+                if( inputs.size() != 2 )
+                    throw new RuntimeException("Two inputs required");
+
+                final Variable varA = inputs.get(0);
+                final Variable varB = inputs.get(1);
+
+                Operation.Info ret = new Operation.Info();
+
+                if( varA instanceof VariableMatrix && varB instanceof VariableMatrix ) {
+
+                    // The output matrix or scalar variable must be created with the provided manager
+                    final VariableMatrix output = manager.createMatrix();
+                    ret.output = output;
+                    ret.op = new Operation("multTransA-mm") {
+                        @Override
+                        public void process() {
+                            DenseMatrix64F mA = ((VariableMatrix)varA).matrix;
+                            DenseMatrix64F mB = ((VariableMatrix)varB).matrix;
+                            output.matrix.reshape(mA.numCols,mB.numCols);
+
+                            CommonOps.multTransA(mA,mB,output.matrix);
+                        }
+                    };
+                } else {
+                    throw new IllegalArgumentException("Expected both inputs to be a matrix");
+                }
+
+                return ret;
+            }
+        };
+    }
+}
diff --git a/examples/src/org/ejml/example/ExampleComplexMath.java b/examples/src/org/ejml/example/ExampleComplexMath.java
new file mode 100644
index 0000000..f760f65
--- /dev/null
+++ b/examples/src/org/ejml/example/ExampleComplexMath.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.example;
+
+import org.ejml.data.Complex64F;
+import org.ejml.data.ComplexPolar64F;
+import org.ejml.ops.ComplexMath64F;
+
+/**
+ * Demonstration of different operations that can be performed on complex numbers.
+ *
+ * @author Peter Abeles
+ */
+public class ExampleComplexMath {
+
+    public static void main( String []args ) {
+        Complex64F a = new Complex64F(1,2);
+        Complex64F b = new Complex64F(-1,-0.6);
+        Complex64F c = new Complex64F();
+        ComplexPolar64F polarC = new ComplexPolar64F();
+
+        System.out.println("a = "+a);
+        System.out.println("b = "+b);
+        System.out.println("------------------");
+
+        ComplexMath64F.plus(a, b, c);
+        System.out.println("a + b = "+c);
+        ComplexMath64F.minus(a, b, c);
+        System.out.println("a - b = "+c);
+        ComplexMath64F.multiply(a, b, c);
+        System.out.println("a * b = "+c);
+        ComplexMath64F.divide(a, b, c);
+        System.out.println("a / b = "+c);
+
+        System.out.println("------------------");
+        ComplexPolar64F polarA = new ComplexPolar64F();
+        ComplexMath64F.convert(a, polarA);
+        System.out.println("polar notation of a = "+polarA);
+        ComplexMath64F.pow(polarA, 3, polarC);
+        System.out.println("a ** 3 = "+polarC);
+        ComplexMath64F.convert(polarC, c);
+        System.out.println("a ** 3 = "+c);
+    }
+}
diff --git a/examples/src/org/ejml/example/ExampleFixedSizedMatrix.java b/examples/src/org/ejml/example/ExampleFixedSizedMatrix.java
new file mode 100644
index 0000000..e9482db
--- /dev/null
+++ b/examples/src/org/ejml/example/ExampleFixedSizedMatrix.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2009-2013, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.example;
+
+import org.ejml.alg.fixed.FixedOps3;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.data.FixedMatrix3_64F;
+import org.ejml.data.FixedMatrix3x3_64F;
+import org.ejml.ops.ConvertMatrixType;
+import org.ejml.simple.SimpleMatrix;
+
+/**
+ * In some applications a small fixed sized matrix can speed things up a lot, e.g. 8 times faster.  One application
+ * which uses small matrices is graphics and rigid body motion, which extensively uses 3x3 and 4x4 matrices.  This
+ * example is to show some examples of how you can use a fixed sized matrix.
+ *
+ * @author Peter Abeles
+ */
+public class ExampleFixedSizedMatrix {
+
+    public static void main( String args[] ) {
+        // declare the matrix
+        FixedMatrix3x3_64F a = new FixedMatrix3x3_64F();
+        FixedMatrix3x3_64F b = new FixedMatrix3x3_64F();
+
+        // Can assign values the usual way
+        for( int i = 0; i < 3; i++ ) {
+            for( int j = 0; j < 3; j++ ) {
+                a.set(i,j,i+j+1);
+            }
+        }
+
+        // Direct manipulation of each value is the fastest way to assign/read values
+        a.a11 = 12;
+        a.a23 = 64;
+
+        // can print the usual way too
+        a.print();
+
+        // most of the standard operations are support
+        FixedOps3.transpose(a,b);
+        b.print();
+
+        System.out.println("Determinant = "+FixedOps3.det(a));
+
+        // matrix-vector operations are also supported
+        // Constructors for vectors and matrices can be used to initialize its value
+        FixedMatrix3_64F v = new FixedMatrix3_64F(1,2,3);
+        FixedMatrix3_64F result = new FixedMatrix3_64F();
+
+        FixedOps3.mult(a,v,result);
+
+        // Conversion into DenseMatrix64F can also be done
+        DenseMatrix64F dm = ConvertMatrixType.convert(a,null);
+
+        dm.print();
+
+        // This can be useful if you need do more advanced operations
+        SimpleMatrix sv = SimpleMatrix.wrap(dm).svd().getV();
+
+        // can then convert it back into a fixed matrix
+        FixedMatrix3x3_64F fv = ConvertMatrixType.convert(sv.getMatrix(),(FixedMatrix3x3_64F)null);
+
+        System.out.println("Original simple matrix and converted fixed matrix");
+        sv.print();
+        fv.print();
+    }
+}
diff --git a/examples/src/org/ejml/example/ExampleMatrixIO.java b/examples/src/org/ejml/example/ExampleMatrixIO.java
new file mode 100644
index 0000000..d4682cd
--- /dev/null
+++ b/examples/src/org/ejml/example/ExampleMatrixIO.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.example;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.MatrixIO;
+import org.ejml.simple.SimpleMatrix;
+
+import java.io.IOException;
+
+/**
+ * Examples for reading and writing matrices to files in different formats
+ *
+ * @author Peter Abeles
+ */
+public class ExampleMatrixIO {
+
+    public static void csv() {
+        DenseMatrix64F A = new DenseMatrix64F(2,3,true,new double[]{1,2,3,4,5,6});
+
+        try {
+            MatrixIO.saveCSV(A, "matrix_file.csv");
+            DenseMatrix64F B = MatrixIO.loadCSV("matrix_file.csv");
+            B.print();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static void csv_simple() {
+        SimpleMatrix A = new SimpleMatrix(2,3,true,new double[]{1,2,3,4,5,6});
+
+        try {
+            A.saveToFileCSV("matrix_file.csv");
+            SimpleMatrix B = new SimpleMatrix().loadCSV("matrix_file.csv");
+            B.print();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static void serializedBinary() {
+        DenseMatrix64F A = new DenseMatrix64F(2,3,true,new double[]{1,2,3,4,5,6});
+
+        try {
+            MatrixIO.saveBin(A, "matrix_file.data");
+            DenseMatrix64F B = MatrixIO.loadBin("matrix_file.data");
+            B.print();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static void csv_serializedBinary() {
+        SimpleMatrix A = new SimpleMatrix(2,3,true,new double[]{1,2,3,4,5,6});
+
+        try {
+            A.saveToFileBinary("matrix_file.data");
+            SimpleMatrix B = SimpleMatrix.loadBinary("matrix_file.data");
+            B.print();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+    
+    public static void main( String args[] ) {
+        csv();
+        serializedBinary();
+    }
+}
diff --git a/examples/src/org/ejml/example/KalmanFilter.java b/examples/src/org/ejml/example/KalmanFilter.java
new file mode 100644
index 0000000..6485f77
--- /dev/null
+++ b/examples/src/org/ejml/example/KalmanFilter.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2009-2013, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.example;
+
+import org.ejml.data.DenseMatrix64F;
+
+/**
+ * <p>
+ * This is an interface for a discrete time Kalman filter with no control input:<br>
+ * <br>
+ * x<sub>k</sub> = F<sub>k</sub> x<sub>k-1</sub> + w<sub>k</sub><br>
+ * z<sub>k</sub> = H<sub>k</sub> x<sub>k</sub> + v<sub>k</sub> <br>
+ * <br>
+ * w<sub>k</sub> ~ N(0,Q<sub>k</sub>)<br>
+ * v<sub>k</sub> ~ N(0,R<sub>k</sub>)<br>
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public interface KalmanFilter {
+
+    /**
+     * Specify the kinematics model of the Kalman filter.  This must be called
+     * first before any other functions.
+     *
+     * @param F State transition matrix.
+     * @param Q plant noise.
+     * @param H measurement projection matrix.
+     */
+    public void configure( DenseMatrix64F F, DenseMatrix64F Q ,
+                           DenseMatrix64F H);
+
+    /**
+     * The prior state estimate and covariance.
+     *
+     * @param x The estimated system state.
+     * @param P The covariance of the estimated system state.
+     */
+    public void setState( DenseMatrix64F x , DenseMatrix64F P );
+
+    /**
+     * Predicts the state of the system forward one time step.
+     */
+    public void predict();
+
+    /**
+     * Updates the state provided the observation from a sensor.
+     *
+     * @param z Measurement.
+     * @param R Measurement covariance.
+     */
+    public void update( DenseMatrix64F z , DenseMatrix64F R );
+
+    /**
+     * Returns the current estimated state of the system.
+     *
+     * @return The state.
+     */
+    public DenseMatrix64F getState();
+
+    /**
+     * Returns the estimated state's covariance matrix.
+     *
+     * @return The covariance.
+     */
+    public DenseMatrix64F getCovariance();
+}
diff --git a/examples/src/org/ejml/example/KalmanFilterEquation.java b/examples/src/org/ejml/example/KalmanFilterEquation.java
new file mode 100644
index 0000000..f19d888
--- /dev/null
+++ b/examples/src/org/ejml/example/KalmanFilterEquation.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.example;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.equation.Equation;
+import org.ejml.equation.Sequence;
+
+/**
+ * Example of how the equation interface can greatly simplify code
+ *
+ * @author Peter Abeles
+ */
+public class KalmanFilterEquation implements KalmanFilter{
+
+    // system state estimate
+    private DenseMatrix64F x,P;
+
+    private Equation eq;
+
+    // Storage for precompiled code for predict and update
+    Sequence predictX,predictP;
+    Sequence updateY,updateK,updateX,updateP;
+
+    @Override
+    public void configure(DenseMatrix64F F, DenseMatrix64F Q, DenseMatrix64F H) {
+        int dimenX = F.numCols;
+
+        x = new DenseMatrix64F(dimenX,1);
+        P = new DenseMatrix64F(dimenX,dimenX);
+
+        eq = new Equation();
+
+        // Provide aliases between the symbolic variables and matrices we normally interact with
+        // The names do not have to be the same.
+        eq.alias(x,"x",P,"P",Q,"Q",F,"F",H,"H");
+
+        // Dummy matrix place holder to avoid compiler errors.  Will be replaced later on
+        eq.alias(new DenseMatrix64F(1,1),"z");
+        eq.alias(new DenseMatrix64F(1,1),"R");
+
+        // Pre-compile so that it doesn't have to compile it each time it's invoked.  More cumbersome
+        // but for small matrices the overhead is significant
+        predictX = eq.compile("x = F*x");
+        predictP = eq.compile("P = F*P*F' + Q");
+
+        updateY = eq.compile("y = z - H*x");
+        updateK = eq.compile("K = P*H'*inv( H*P*H' + R )");
+        updateX = eq.compile("x = x + K*y");
+        updateP = eq.compile("P = P-K*(H*P)");
+    }
+
+    @Override
+    public void setState(DenseMatrix64F x, DenseMatrix64F P) {
+        this.x.set(x);
+        this.P.set(P);
+    }
+
+    @Override
+    public void predict() {
+        predictX.perform();
+        predictP.perform();
+    }
+
+    @Override
+    public void update(DenseMatrix64F z, DenseMatrix64F R) {
+
+        // Alias will overwrite the reference to the previous matrices with the same name
+        eq.alias(z,"z"); eq.alias(R,"R");
+
+        updateY.perform();
+        updateK.perform();
+        updateX.perform();
+        updateP.perform();
+    }
+
+    @Override
+    public DenseMatrix64F getState() {
+        return x;
+    }
+
+    @Override
+    public DenseMatrix64F getCovariance() {
+        return P;
+    }
+}
\ No newline at end of file
diff --git a/examples/src/org/ejml/example/KalmanFilterOperations.java b/examples/src/org/ejml/example/KalmanFilterOperations.java
new file mode 100644
index 0000000..b59b470
--- /dev/null
+++ b/examples/src/org/ejml/example/KalmanFilterOperations.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.example;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.LinearSolverFactory;
+import org.ejml.interfaces.linsol.LinearSolver;
+
+import static org.ejml.ops.CommonOps.*;
+
+/**
+ * A Kalman filter that is implemented using the operations API, which is procedural.  Much of the excessive
+ * memory creation/destruction has been reduced from the KalmanFilterSimple. A specialized solver is
+ * under to invert the SPD matrix.
+ *
+ * @author Peter Abeles
+ */
+public class KalmanFilterOperations implements KalmanFilter{
+
+    // kinematics description
+    private DenseMatrix64F F,Q,H;
+
+    // system state estimate
+    private DenseMatrix64F x,P;
+
+    // these are predeclared for efficiency reasons
+    private DenseMatrix64F a,b;
+    private DenseMatrix64F y,S,S_inv,c,d;
+    private DenseMatrix64F K;
+
+    private LinearSolver<DenseMatrix64F> solver;
+
+    @Override
+    public void configure(DenseMatrix64F F, DenseMatrix64F Q, DenseMatrix64F H) {
+        this.F = F;
+        this.Q = Q;
+        this.H = H;
+
+        int dimenX = F.numCols;
+        int dimenZ = H.numRows;
+
+        a = new DenseMatrix64F(dimenX,1);
+        b = new DenseMatrix64F(dimenX,dimenX);
+        y = new DenseMatrix64F(dimenZ,1);
+        S = new DenseMatrix64F(dimenZ,dimenZ);
+        S_inv = new DenseMatrix64F(dimenZ,dimenZ);
+        c = new DenseMatrix64F(dimenZ,dimenX);
+        d = new DenseMatrix64F(dimenX,dimenZ);
+        K = new DenseMatrix64F(dimenX,dimenZ);
+
+        x = new DenseMatrix64F(dimenX,1);
+        P = new DenseMatrix64F(dimenX,dimenX);
+
+        // covariance matrices are symmetric positive semi-definite
+        solver = LinearSolverFactory.symmPosDef(dimenX);
+    }
+
+    @Override
+    public void setState(DenseMatrix64F x, DenseMatrix64F P) {
+        this.x.set(x);
+        this.P.set(P);
+    }
+
+    @Override
+    public void predict() {
+
+        // x = F x
+        mult(F,x,a);
+        x.set(a);
+
+        // P = F P F' + Q
+        mult(F,P,b);
+        multTransB(b,F, P);
+        addEquals(P,Q);
+    }
+
+    @Override
+    public void update(DenseMatrix64F z, DenseMatrix64F R) {
+        // y = z - H x
+        mult(H,x,y);
+        subtract(z, y, y);
+
+        // S = H P H' + R
+        mult(H,P,c);
+        multTransB(c,H,S);
+        addEquals(S,R);
+
+        // K = PH'S^(-1)
+        if( !solver.setA(S) ) throw new RuntimeException("Invert failed");
+        solver.invert(S_inv);
+        multTransA(H,S_inv,d);
+        mult(P,d,K);
+
+        // x = x + Ky
+        mult(K,y,a);
+        addEquals(x,a);
+
+        // P = (I-kH)P = P - (KH)P = P-K(HP)
+        mult(H,P,c);
+        mult(K,c,b);
+        subtractEquals(P, b);
+    }
+
+    @Override
+    public DenseMatrix64F getState() {
+        return x;
+    }
+
+    @Override
+    public DenseMatrix64F getCovariance() {
+        return P;
+    }
+}
\ No newline at end of file
diff --git a/examples/src/org/ejml/example/KalmanFilterSimple.java b/examples/src/org/ejml/example/KalmanFilterSimple.java
new file mode 100644
index 0000000..9ccc563
--- /dev/null
+++ b/examples/src/org/ejml/example/KalmanFilterSimple.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.example;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.simple.SimpleMatrix;
+
+/**
+ * A Kalman filter implemented using SimpleMatrix.  The code tends to be easier to
+ * read and write, but the performance is degraded due to excessive creation/destruction of
+ * memory and the use of more generic algorithms.  This also demonstrates how code can be
+ * seamlessly implemented using both SimpleMatrix and DenseMatrix64F.  This allows code
+ * to be quickly prototyped or to be written either by novices or experts.
+ *
+ * @author Peter Abeles
+ */
+public class KalmanFilterSimple implements KalmanFilter{
+
+    // kinematics description
+    private SimpleMatrix F,Q,H;
+
+    // sytem state estimate
+    private SimpleMatrix x,P;
+
+    @Override
+    public void configure(DenseMatrix64F F, DenseMatrix64F Q, DenseMatrix64F H) {
+        this.F = new SimpleMatrix(F);
+        this.Q = new SimpleMatrix(Q);
+        this.H = new SimpleMatrix(H);
+    }
+
+    @Override
+    public void setState(DenseMatrix64F x, DenseMatrix64F P) {
+        this.x = new SimpleMatrix(x);
+        this.P = new SimpleMatrix(P);
+    }
+
+    @Override
+    public void predict() {
+        // x = F x
+        x = F.mult(x);
+
+        // P = F P F' + Q
+        P = F.mult(P).mult(F.transpose()).plus(Q);
+    }
+
+    @Override
+    public void update(DenseMatrix64F _z, DenseMatrix64F _R) {
+        // a fast way to make the matrices usable by SimpleMatrix
+        SimpleMatrix z = SimpleMatrix.wrap(_z);
+        SimpleMatrix R = SimpleMatrix.wrap(_R);
+
+        // y = z - H x
+        SimpleMatrix y = z.minus(H.mult(x));
+
+        // S = H P H' + R
+        SimpleMatrix S = H.mult(P).mult(H.transpose()).plus(R);
+
+        // K = PH'S^(-1)
+        SimpleMatrix K = P.mult(H.transpose().mult(S.invert()));
+
+        // x = x + Ky
+        x = x.plus(K.mult(y));
+
+        // P = (I-kH)P = P - KHP
+        P = P.minus(K.mult(H).mult(P));
+    }
+
+    @Override
+    public DenseMatrix64F getState() {
+        return x.getMatrix();
+    }
+
+    @Override
+    public DenseMatrix64F getCovariance() {
+        return P.getMatrix();
+    }
+}
diff --git a/examples/src/org/ejml/example/LevenbergMarquardt.java b/examples/src/org/ejml/example/LevenbergMarquardt.java
new file mode 100644
index 0000000..29661f9
--- /dev/null
+++ b/examples/src/org/ejml/example/LevenbergMarquardt.java
@@ -0,0 +1,331 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.example;
+
+import org.ejml.data.DenseMatrix64F;
+
+import static org.ejml.ops.CommonOps.*;
+import static org.ejml.ops.SpecializedOps.diffNormF;
+
+/**
+ * <p>
+ * This is a straight forward implementation of the Levenberg-Marquardt (LM) algorithm. LM is used to minimize
+ * non-linear cost functions:<br>
+ * <br>
+ * S(P) = Sum{ i=1:m , [y<sub>i</sub> - f(x<sub>i</sub>,P)]<sup>2</sup>}<br>
+ * <br>
+ * where P is the set of parameters being optimized.
+ * </p>
+ *
+ * <p>
+ * In each iteration the parameters are updated using the following equations:<br>
+ * <br>
+ * P<sub>i+1</sub> = (H + λ I)<sup>-1</sup> d <br>
+ * d =  (1/N) Sum{ i=1..N , (f(x<sub>i</sub>;P<sub>i</sub>) - y<sub>i</sub>) * jacobian(:,i) } <br>
+ * H =  (1/N) Sum{ i=1..N , jacobian(:,i) * jacobian(:,i)<sup>T</sup> }
+ * </p>
+ * <p>
+ * Whenever possible the allocation of new memory is avoided.  This is accomplished by reshaping matrices.
+ * A matrix that is reshaped won't grow unless the new shape requires more memory than it has available.
+ * </p>
+ * @author Peter Abeles
+ */
+public class LevenbergMarquardt {
+    // how much the numerical jacobian calculation perturbs the parameters by.
+    // In better implementation there are better ways to compute this delta.  See Numerical Recipes.
+    private final static double DELTA = 1e-8;
+
+    private double initialLambda;
+
+    // the function that is optimized
+    private Function func;
+
+    // the optimized parameters and associated costs
+    private DenseMatrix64F param;
+    private double initialCost;
+    private double finalCost;
+
+    // used by matrix operations
+    private DenseMatrix64F d;
+    private DenseMatrix64F H;
+    private DenseMatrix64F negDelta;
+    private DenseMatrix64F tempParam;
+    private DenseMatrix64F A;
+
+    // variables used by the numerical jacobian algorithm
+    private DenseMatrix64F temp0;
+    private DenseMatrix64F temp1;
+    // used when computing d and H variables
+    private DenseMatrix64F tempDH;
+
+    // Where the numerical Jacobian is stored.
+    private DenseMatrix64F jacobian;
+
+    /**
+     * Creates a new instance that uses the provided cost function.
+     *
+     * @param funcCost Cost function that is being optimized.
+     */
+    public LevenbergMarquardt( Function funcCost )
+    {
+        this.initialLambda = 1;
+
+        // declare data to some initial small size. It will grow later on as needed.
+        int maxElements = 1;
+        int numParam = 1;
+
+        this.temp0 = new DenseMatrix64F(maxElements,1);
+        this.temp1 = new DenseMatrix64F(maxElements,1);
+        this.tempDH = new DenseMatrix64F(maxElements,1);
+        this.jacobian = new DenseMatrix64F(numParam,maxElements);
+
+        this.func = funcCost;
+
+        this.param = new DenseMatrix64F(numParam,1);
+        this.d = new DenseMatrix64F(numParam,1);
+        this.H = new DenseMatrix64F(numParam,numParam);
+        this.negDelta = new DenseMatrix64F(numParam,1);
+        this.tempParam = new DenseMatrix64F(numParam,1);
+        this.A = new DenseMatrix64F(numParam,numParam);
+    }
+
+
+    public double getInitialCost() {
+        return initialCost;
+    }
+
+    public double getFinalCost() {
+        return finalCost;
+    }
+
+    public DenseMatrix64F getParameters() {
+        return param;
+    }
+
+    /**
+     * Finds the best fit parameters.
+     *
+     * @param initParam The initial set of parameters for the function.
+     * @param X The inputs to the function.
+     * @param Y The "observed" output of the function
+     * @return true if it succeeded and false if it did not.
+     */
+    public boolean optimize( DenseMatrix64F initParam ,
+                             DenseMatrix64F X ,
+                             DenseMatrix64F Y )
+    {
+        configure(initParam,X,Y);
+
+        // save the cost of the initial parameters so that it knows if it improves or not
+        initialCost = cost(param,X,Y);
+
+        // iterate until the difference between the costs is insignificant
+        // or it iterates too many times
+        if( !adjustParam(X, Y, initialCost) ) {
+            finalCost = Double.NaN;
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Iterate until the difference between the costs is insignificant
+     * or it iterates too many times
+     */
+    private boolean adjustParam(DenseMatrix64F X, DenseMatrix64F Y,
+                                double prevCost) {
+        // lambda adjusts how big of a step it takes
+        double lambda = initialLambda;
+        // the difference between the current and previous cost
+        double difference = 1000;
+
+        for( int iter = 0; iter < 20 || difference < 1e-6 ; iter++ ) {
+            // compute some variables based on the gradient
+            computeDandH(param,X,Y);
+
+            // try various step sizes and see if any of them improve the
+            // results over what has already been done
+            boolean foundBetter = false;
+            for( int i = 0; i < 5; i++ ) {
+                computeA(A,H,lambda);
+
+                if( !solve(A,d,negDelta) ) {
+                    return false;
+                }
+                // compute the candidate parameters
+                subtract(param, negDelta, tempParam);
+
+                double cost = cost(tempParam,X,Y);
+                if( cost < prevCost ) {
+                    // the candidate parameters produced better results so use it
+                    foundBetter = true;
+                    param.set(tempParam);
+                    difference = prevCost - cost;
+                    prevCost = cost;
+                    lambda /= 10.0;
+                } else {
+                    lambda *= 10.0;
+                }
+            }
+
+            // it reached a point where it can't improve so exit
+            if( !foundBetter )
+                break;
+        }
+        finalCost = prevCost;
+        return true;
+    }
+
+    /**
+     * Performs sanity checks on the input data and reshapes internal matrices.  By reshaping
+     * a matrix it will only declare new memory when needed.
+     */
+    protected void configure( DenseMatrix64F initParam , DenseMatrix64F X , DenseMatrix64F Y )
+    {
+        if( Y.getNumRows() != X.getNumRows() ) {
+            throw new IllegalArgumentException("Different vector lengths");
+        } else if( Y.getNumCols() != 1 || X.getNumCols() != 1 ) {
+            throw new IllegalArgumentException("Inputs must be a column vector");
+        }
+
+        int numParam = initParam.getNumElements();
+        int numPoints = Y.getNumRows();
+
+        if( param.getNumElements() != initParam.getNumElements() ) {
+            // reshaping a matrix means that new memory is only declared when needed
+            this.param.reshape(numParam,1, false);
+            this.d.reshape(numParam,1, false);
+            this.H.reshape(numParam,numParam, false);
+            this.negDelta.reshape(numParam,1, false);
+            this.tempParam.reshape(numParam,1, false);
+            this.A.reshape(numParam,numParam, false);
+        }
+
+        param.set(initParam);
+
+        // reshaping a matrix means that new memory is only declared when needed
+        temp0.reshape(numPoints,1, false);
+        temp1.reshape(numPoints,1, false);
+        tempDH.reshape(numPoints,1, false);
+        jacobian.reshape(numParam,numPoints, false);
+
+
+    }
+
+    /**
+     * Computes the d and H parameters.  Where d is the average error gradient and
+     * H is an approximation of the hessian.
+     */
+    private void computeDandH( DenseMatrix64F param , DenseMatrix64F x , DenseMatrix64F y )
+    {
+        func.compute(param,x, tempDH);
+        subtractEquals(tempDH, y);
+
+        computeNumericalJacobian(param,x,jacobian);
+
+        int numParam = param.getNumElements();
+        int length = x.getNumElements();
+
+        // d = average{ (f(x_i;p) - y_i) * jacobian(:,i) }
+        for( int i = 0; i < numParam; i++ ) {
+            double total = 0;
+            for( int j = 0; j < length; j++ ) {
+                total += tempDH.get(j,0)*jacobian.get(i,j);
+            }
+            d.set(i,0,total/length);
+        }
+
+        // compute the approximation of the hessian
+        multTransB(jacobian,jacobian,H);
+        scale(1.0/length,H);
+    }
+
+    /**
+     * A = H + lambda*I <br>
+     * <br>
+     * where I is an identity matrix.
+     */
+    private void computeA( DenseMatrix64F A , DenseMatrix64F H , double lambda )
+    {
+        final int numParam = param.getNumElements();
+
+        A.set(H);
+        for( int i = 0; i < numParam; i++ ) {
+            A.set(i,i, A.get(i,i) + lambda);
+        }
+    }
+
+    /**
+     * Computes the "cost" for the parameters given.
+     *
+     * cost = (1/N) Sum (f(x;p) - y)^2
+     */
+    private double cost( DenseMatrix64F param , DenseMatrix64F X , DenseMatrix64F Y)
+    {
+        func.compute(param,X, temp0);
+
+        double error = diffNormF(temp0,Y);
+
+        return error*error / (double)X.numRows;
+    }
+
+    /**
+     * Computes a simple numerical Jacobian.
+     *
+     * @param param The set of parameters that the Jacobian is to be computed at.
+     * @param pt The point around which the Jacobian is to be computed.
+     * @param deriv Where the jacobian will be stored
+     */
+    protected void computeNumericalJacobian( DenseMatrix64F param ,
+                                             DenseMatrix64F pt ,
+                                             DenseMatrix64F deriv )
+    {
+        double invDelta = 1.0/DELTA;
+
+        func.compute(param,pt, temp0);
+
+        // compute the jacobian by perturbing the parameters slightly
+        // then seeing how it effects the results.
+        for( int i = 0; i < param.numRows; i++ ) {
+            param.data[i] += DELTA;
+            func.compute(param,pt, temp1);
+            // compute the difference between the two parameters and divide by the delta
+            add(invDelta,temp1,-invDelta,temp0,temp1);
+            // copy the results into the jacobian matrix
+            System.arraycopy(temp1.data,0,deriv.data,i*pt.numRows,pt.numRows);
+
+            param.data[i] -= DELTA;
+        }
+    }
+
+    /**
+     * The function that is being optimized.
+     */
+    public interface Function {
+        /**
+         * Computes the output for each value in matrix x given the set of parameters.
+         *
+         * @param param The parameter for the function.
+         * @param x the input points.
+         * @param y the resulting output.
+         */
+        public void compute( DenseMatrix64F param , DenseMatrix64F x , DenseMatrix64F y );
+    }
+}
diff --git a/examples/src/org/ejml/example/PolynomialFit.java b/examples/src/org/ejml/example/PolynomialFit.java
new file mode 100644
index 0000000..e3d6f49
--- /dev/null
+++ b/examples/src/org/ejml/example/PolynomialFit.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2009-2013, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.example;
+
+import org.ejml.alg.dense.linsol.AdjustableLinearSolver;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.LinearSolverFactory;
+
+/**
+ * <p>
+ * This example demonstrates how a polynomial can be fit to a set of data.  This is done by
+ * using a least squares solver that is adjustable.  By using an adjustable solver elements
+ * can be inexpensively removed and the coefficients recomputed.  This is much less expensive
+ * than resolving the whole system from scratch.
+ * </p>
+ * <p>
+ * The following is demonstrated:<br>
+ * <ol>
+ *  <li>Creating a solver using LinearSolverFactory</li>
+ *  <li>Using an adjustable solver</li>
+ *  <li>reshaping</li>
+ * </ol>
+ * @author Peter Abeles
+ */
+public class PolynomialFit {
+
+    // Vandermonde matrix
+    DenseMatrix64F A;
+    // matrix containing computed polynomial coefficients
+    DenseMatrix64F coef;
+    // observation matrix
+    DenseMatrix64F y;
+
+    // solver used to compute
+    AdjustableLinearSolver solver;
+
+    /**
+     * Constructor.
+     *
+     * @param degree The polynomial's degree which is to be fit to the observations.
+     */
+    public PolynomialFit( int degree ) {
+        coef = new DenseMatrix64F(degree+1,1);
+        A = new DenseMatrix64F(1,degree+1);
+        y = new DenseMatrix64F(1,1);
+
+        // create a solver that allows elements to be added or removed efficiently
+        solver = LinearSolverFactory.adjustable();
+    }
+
+    /**
+     * Returns the computed coefficients
+     *
+     * @return polynomial coefficients that best fit the data.
+     */
+    public double[] getCoef() {
+        return coef.data;
+    }
+
+    /**
+     * Computes the best fit set of polynomial coefficients to the provided observations.
+     *
+     * @param samplePoints where the observations were sampled.
+     * @param observations A set of observations.
+     */
+    public void fit( double samplePoints[] , double[] observations ) {
+        // Create a copy of the observations and put it into a matrix
+        y.reshape(observations.length,1,false);
+        System.arraycopy(observations,0, y.data,0,observations.length);
+
+        // reshape the matrix to avoid unnecessarily declaring new memory
+        // save values is set to false since its old values don't matter
+        A.reshape(y.numRows, coef.numRows,false);
+
+        // set up the A matrix
+        for( int i = 0; i < observations.length; i++ ) {
+
+            double obs = 1;
+
+            for( int j = 0; j < coef.numRows; j++ ) {
+                A.set(i,j,obs);
+                obs *= samplePoints[i];
+            }
+        }
+
+        // process the A matrix and see if it failed
+        if( !solver.setA(A) )
+            throw new RuntimeException("Solver failed");
+
+        // solver the the coefficients
+        solver.solve(y,coef);
+    }
+
+    /**
+     * Removes the observation that fits the model the worst and recomputes the coefficients.
+     * This is done efficiently by using an adjustable solver.  Often times the elements with
+     * the largest errors are outliers and not part of the system being modeled.  By removing them
+     * a more accurate set of coefficients can be computed.
+     */
+    public void removeWorstFit() {
+        // find the observation with the most error
+        int worstIndex=-1;
+        double worstError = -1;
+
+        for( int i = 0; i < y.numRows; i++ ) {
+            double predictedObs = 0;
+
+            for( int j = 0; j < coef.numRows; j++ ) {
+                predictedObs += A.get(i,j)*coef.get(j,0);
+            }
+
+            double error = Math.abs(predictedObs- y.get(i,0));
+
+            if( error > worstError ) {
+                worstError = error;
+                worstIndex = i;
+            }
+        }
+
+        // nothing left to remove, so just return
+        if( worstIndex == -1 )
+            return;
+
+        // remove that observation
+        removeObservation(worstIndex);
+
+        // update A
+        solver.removeRowFromA(worstIndex);
+
+        // solve for the parameters again
+        solver.solve(y,coef);
+    }
+
+    /**
+     * Removes an element from the observation matrix.
+     *
+     * @param index which element is to be removed
+     */
+    private void removeObservation( int index ) {
+        final int N = y.numRows-1;
+        final double d[] = y.data;
+
+        // shift
+        for( int i = index; i < N; i++ ) {
+            d[i] = d[i+1];
+        }
+        y.numRows--;
+    }
+}
diff --git a/examples/src/org/ejml/example/PolynomialRootFinder.java b/examples/src/org/ejml/example/PolynomialRootFinder.java
new file mode 100644
index 0000000..a6af302
--- /dev/null
+++ b/examples/src/org/ejml/example/PolynomialRootFinder.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.example;
+
+import org.ejml.data.Complex64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.DecompositionFactory;
+import org.ejml.interfaces.decomposition.EigenDecomposition;
+
+/**
+ * <p>
+ * Eigenvalue decomposition can be used to find the roots in a polynomial by constructing the
+ * so called companion matrix.  While faster techniques do exist for root finding, this is
+ * one of the most stable and probably the easiest to implement.
+ * </p>
+ *
+ * <p>
+ * Because the companion matrix is not symmetric a generalized eigenvalue decomposition is needed.
+ * The roots of the polynomial may also be complex.  Complex eigenvalues is the only instance in
+ * which EJML supports complex arithmetic.  Depending on the application one might need to check
+ * to see if the eigenvalues are real or complex.
+ * </p>
+ *
+ * <p>
+ * For more algorithms and robust solution for finding polynomial roots check out http://ddogleg.org
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class PolynomialRootFinder {
+
+    /**
+     * <p>
+     * Given a set of polynomial coefficients, compute the roots of the polynomial.  Depending on
+     * the polynomial being considered the roots may contain complex number.  When complex numbers are
+     * present they will come in pairs of complex conjugates.
+     * </p>
+     *
+     * <p>
+     * Coefficients are ordered from least to most significant, e.g: y = c[0] + x*c[1] + x*x*c[2].
+     * </p>
+     *
+     * @param coefficients Coefficients of the polynomial.
+     * @return The roots of the polynomial
+     */
+    public static Complex64F[] findRoots(double... coefficients) {
+        int N = coefficients.length-1;
+
+        // Construct the companion matrix
+        DenseMatrix64F c = new DenseMatrix64F(N,N);
+
+        double a = coefficients[N];
+        for( int i = 0; i < N; i++ ) {
+            c.set(i,N-1,-coefficients[i]/a);
+        }
+        for( int i = 1; i < N; i++ ) {
+            c.set(i,i-1,1);
+        }
+
+        // use generalized eigenvalue decomposition to find the roots
+        EigenDecomposition<DenseMatrix64F> evd =  DecompositionFactory.eig(N,false);
+
+        evd.decompose(c);
+
+        Complex64F[] roots = new Complex64F[N];
+
+        for( int i = 0; i < N; i++ ) {
+            roots[i] = evd.getEigenvalue(i);
+        }
+
+        return roots;
+    }
+}
diff --git a/examples/src/org/ejml/example/PrincipalComponentAnalysis.java b/examples/src/org/ejml/example/PrincipalComponentAnalysis.java
new file mode 100644
index 0000000..d76fb1f
--- /dev/null
+++ b/examples/src/org/ejml/example/PrincipalComponentAnalysis.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.example;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.DecompositionFactory;
+import org.ejml.interfaces.decomposition.SingularValueDecomposition;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.NormOps;
+import org.ejml.ops.SingularOps;
+
+/**
+ * <p>
+ * The following is a simple example of how to perform basic principal component analysis in EJML.
+ * </p>
+ *
+ * <p>
+ * Principal Component Analysis (PCA) is typically used to develop a linear model for a set of data
+ * (e.g. face images) which can then be used to test for membership.  PCA works by converting the
+ * set of data to a new basis that is a subspace of the original set.  The subspace is selected
+ * to maximize information.
+ * </p>
+ * <p>
+ * PCA is typically derived as an eigenvalue problem.  However in this implementation {@link org.ejml.interfaces.decomposition.SingularValueDecomposition SVD}
+ * is used instead because it will produce a more numerically stable solution.  Computation using EVD requires explicitly
+ * computing the variance of each sample set. The variance is computed by squaring the residual, which can
+ * cause loss of precision.
+ * </p>
+ *
+ * <p>
+ * Usage:<br>
+ * 1) call setup()<br>
+ * 2) For each sample (e.g. an image ) call addSample()<br>
+ * 3) After all the samples have been added call computeBasis()<br>
+ * 4) Call  sampleToEigenSpace() , eigenToSampleSpace() , errorMembership() , response()
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class PrincipalComponentAnalysis {
+
+    // principal component subspace is stored in the rows
+    private DenseMatrix64F V_t;
+
+    // how many principal components are used
+    private int numComponents;
+
+    // where the data is stored
+    private DenseMatrix64F A = new DenseMatrix64F(1,1);
+    private int sampleIndex;
+
+    // mean values of each element across all the samples
+    double mean[];
+
+    public PrincipalComponentAnalysis() {
+    }
+
+    /**
+     * Must be called before any other functions. Declares and sets up internal data structures.
+     *
+     * @param numSamples Number of samples that will be processed.
+     * @param sampleSize Number of elements in each sample.
+     */
+    public void setup( int numSamples , int sampleSize ) {
+        mean = new double[ sampleSize ];
+        A.reshape(numSamples,sampleSize,false);
+        sampleIndex = 0;
+        numComponents = -1;
+    }
+
+    /**
+     * Adds a new sample of the raw data to internal data structure for later processing.  All the samples
+     * must be added before computeBasis is called.
+     *
+     * @param sampleData Sample from original raw data.
+     */
+    public void addSample( double[] sampleData ) {
+        if( A.getNumCols() != sampleData.length )
+            throw new IllegalArgumentException("Unexpected sample size");
+        if( sampleIndex >= A.getNumRows() )
+            throw new IllegalArgumentException("Too many samples");
+
+        for( int i = 0; i < sampleData.length; i++ ) {
+            A.set(sampleIndex,i,sampleData[i]);
+        }
+        sampleIndex++;
+    }
+
+    /**
+     * Computes a basis (the principal components) from the most dominant eigenvectors.
+     *
+     * @param numComponents Number of vectors it will use to describe the data.  Typically much
+     * smaller than the number of elements in the input vector.
+     */
+    public void computeBasis( int numComponents ) {
+        if( numComponents > A.getNumCols() )
+            throw new IllegalArgumentException("More components requested that the data's length.");
+        if( sampleIndex != A.getNumRows() )
+            throw new IllegalArgumentException("Not all the data has been added");
+        if( numComponents > sampleIndex )
+            throw new IllegalArgumentException("More data needed to compute the desired number of components");
+
+        this.numComponents = numComponents;
+
+        // compute the mean of all the samples
+        for( int i = 0; i < A.getNumRows(); i++ ) {
+            for( int j = 0; j < mean.length; j++ ) {
+                mean[j] += A.get(i,j);
+            }
+        }
+        for( int j = 0; j < mean.length; j++ ) {
+            mean[j] /= A.getNumRows();
+        }
+
+        // subtract the mean from the original data
+        for( int i = 0; i < A.getNumRows(); i++ ) {
+            for( int j = 0; j < mean.length; j++ ) {
+                A.set(i,j,A.get(i,j)-mean[j]);
+            }
+        }
+
+        // Compute SVD and save time by not computing U
+        SingularValueDecomposition<DenseMatrix64F> svd =
+                DecompositionFactory.svd(A.numRows, A.numCols, false, true, false);
+        if( !svd.decompose(A) )
+            throw new RuntimeException("SVD failed");
+
+        V_t = svd.getV(null,true);
+        DenseMatrix64F W = svd.getW(null);
+
+        // Singular values are in an arbitrary order initially
+        SingularOps.descendingOrder(null,false,W,V_t,true);
+
+        // strip off unneeded components and find the basis
+        V_t.reshape(numComponents,mean.length,true);
+    }
+
+    /**
+     * Returns a vector from the PCA's basis.
+     *
+     * @param which Which component's vector is to be returned.
+     * @return Vector from the PCA basis.
+     */
+    public double[] getBasisVector( int which ) {
+        if( which < 0 || which >= numComponents )
+            throw new IllegalArgumentException("Invalid component");
+
+        DenseMatrix64F v = new DenseMatrix64F(1,A.numCols);
+        CommonOps.extract(V_t,which,which+1,0,A.numCols,v,0,0);
+
+        return v.data;
+    }
+
+    /**
+     * Converts a vector from sample space into eigen space.
+     *
+     * @param sampleData Sample space data.
+     * @return Eigen space projection.
+     */
+    public double[] sampleToEigenSpace( double[] sampleData ) {
+        if( sampleData.length != A.getNumCols() )
+            throw new IllegalArgumentException("Unexpected sample length");
+        DenseMatrix64F mean = DenseMatrix64F.wrap(A.getNumCols(),1,this.mean);
+
+        DenseMatrix64F s = new DenseMatrix64F(A.getNumCols(),1,true,sampleData);
+        DenseMatrix64F r = new DenseMatrix64F(numComponents,1);
+
+        CommonOps.subtract(s, mean, s);
+
+        CommonOps.mult(V_t,s,r);
+
+        return r.data;
+    }
+
+    /**
+     * Converts a vector from eigen space into sample space.
+     *
+     * @param eigenData Eigen space data.
+     * @return Sample space projection.
+     */
+    public double[] eigenToSampleSpace( double[] eigenData ) {
+        if( eigenData.length != numComponents )
+            throw new IllegalArgumentException("Unexpected sample length");
+
+        DenseMatrix64F s = new DenseMatrix64F(A.getNumCols(),1);
+        DenseMatrix64F r = DenseMatrix64F.wrap(numComponents,1,eigenData);
+        
+        CommonOps.multTransA(V_t,r,s);
+
+        DenseMatrix64F mean = DenseMatrix64F.wrap(A.getNumCols(),1,this.mean);
+        CommonOps.add(s,mean,s);
+
+        return s.data;
+    }
+
+
+    /**
+     * <p>
+     * The membership error for a sample.  If the error is less than a threshold then
+     * it can be considered a member.  The threshold's value depends on the data set.
+     * </p>
+     * <p>
+     * The error is computed by projecting the sample into eigenspace then projecting
+     * it back into sample space and
+     * </p>
+     * 
+     * @param sampleA The sample whose membership status is being considered.
+     * @return Its membership error.
+     */
+    public double errorMembership( double[] sampleA ) {
+        double[] eig = sampleToEigenSpace(sampleA);
+        double[] reproj = eigenToSampleSpace(eig);
+
+
+        double total = 0;
+        for( int i = 0; i < reproj.length; i++ ) {
+            double d = sampleA[i] - reproj[i];
+            total += d*d;
+        }
+
+        return Math.sqrt(total);
+    }
+
+    /**
+     * Computes the dot product of each basis vector against the sample.  Can be used as a measure
+     * for membership in the training sample set.  High values correspond to a better fit.
+     *
+     * @param sample Sample of original data.
+     * @return Higher value indicates it is more likely to be a member of input dataset.
+     */
+    public double response( double[] sample ) {
+        if( sample.length != A.numCols )
+            throw new IllegalArgumentException("Expected input vector to be in sample space");
+
+        DenseMatrix64F dots = new DenseMatrix64F(numComponents,1);
+        DenseMatrix64F s = DenseMatrix64F.wrap(A.numCols,1,sample);
+
+        CommonOps.mult(V_t,s,dots);
+
+        return NormOps.normF(dots);
+    }
+}
diff --git a/examples/src/org/ejml/example/QRExampleEquation.java b/examples/src/org/ejml/example/QRExampleEquation.java
new file mode 100644
index 0000000..9c5d33b
--- /dev/null
+++ b/examples/src/org/ejml/example/QRExampleEquation.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.example;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.equation.Equation;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.NormOps;
+
+/**
+ * <p>
+ * Example code for computing the QR decomposition of a matrix. It demonstrates how to
+ * extract a submatrix and insert one matrix into another one using the operator interface.
+ * </p>
+ *
+ * Note: This code is horribly inefficient and is for demonstration purposes only.
+ *
+ * @author Peter Abeles
+ */
+public class QRExampleEquation {
+
+    // where the QR decomposition is stored
+    private DenseMatrix64F QR;
+
+    // used for computing Q
+    private double gammas[];
+
+    /**
+     * Computes the QR decomposition of the provided matrix.
+     *
+     * @param A Matrix which is to be decomposed.  Not modified.
+     */
+    public void decompose( DenseMatrix64F A ) {
+
+        Equation eq = new Equation();
+
+        this.QR = A.copy();
+
+        int N = Math.min(A.numCols,A.numRows);
+
+        gammas = new double[ A.numCols ];
+
+        for( int i = 0; i < N; i++ ) {
+            // update temporary variables
+            eq.alias(QR.numRows-i,"Ni",QR,"QR",i,"i");
+
+            // Place the column that should be zeroed into v
+            eq.process("v=QR(i:,i)");
+            // Note that v is lazily created above.  Need direct access to it, which is done below.
+            DenseMatrix64F v = eq.lookupMatrix("v");
+
+            double maxV = CommonOps.elementMaxAbs(v);
+            eq.alias(maxV,"maxV");
+
+            if( maxV > 0 && v.getNumElements() > 1 ) {
+                // normalize to reduce overflow issues
+                eq.process("v=v/maxV");
+
+                // compute the magnitude of the vector
+                double tau = NormOps.normF(v);
+
+                if( v.get(0) < 0 )
+                    tau *= -1.0;
+
+                eq.alias(tau,"tau");
+                eq.process("u_0 = v(0,0)+tau");
+                eq.process("gamma = u_0/tau");
+                eq.process("v=v/u_0");
+                eq.process("v(0,0)=1");
+                eq.process("QR(i:,i:) = (eye(Ni) - gamma*v*v')*QR(i:,i:)");
+                eq.process("QR(i:,i) = v");
+                eq.process("QR(i,i) = -1*tau*maxV");
+
+                // save gamma for recomputing Q later on
+                gammas[i] = eq.lookupDouble("gamma");
+            }
+        }
+    }
+
+    /**
+     * Returns the Q matrix.
+     */
+    public DenseMatrix64F getQ() {
+        Equation eq = new Equation();
+
+        DenseMatrix64F Q = CommonOps.identity(QR.numRows);
+        DenseMatrix64F u = new DenseMatrix64F(QR.numRows,1);
+
+        int N = Math.min(QR.numCols,QR.numRows);
+
+        eq.alias(u,"u",Q,"Q",QR,"QR",QR.numRows,"r");
+
+        // compute Q by first extracting the householder vectors from the columns of QR and then applying it to Q
+        for( int j = N-1; j>= 0; j-- ) {
+            eq.alias(j,"j",gammas[j],"gamma");
+
+            eq.process("u(j:,0) = [1 ; QR(j+1:,j)]");
+            eq.process("Q=(eye(r)-gamma*u*u')*Q");
+        }
+
+        return Q;
+    }
+
+    /**
+     * Returns the R matrix.
+     */
+    public DenseMatrix64F getR() {
+        DenseMatrix64F R = new DenseMatrix64F(QR.numRows,QR.numCols);
+        int N = Math.min(QR.numCols,QR.numRows);
+
+        for( int i = 0; i < N; i++ ) {
+            for( int j = i; j < QR.numCols; j++ ) {
+                R.unsafe_set(i,j, QR.unsafe_get(i,j));
+            }
+        }
+
+        return R;
+    }
+}
\ No newline at end of file
diff --git a/examples/src/org/ejml/example/QRExampleOperations.java b/examples/src/org/ejml/example/QRExampleOperations.java
new file mode 100644
index 0000000..75b581e
--- /dev/null
+++ b/examples/src/org/ejml/example/QRExampleOperations.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.example;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.NormOps;
+
+/**
+ * <p>
+ * Example code for computing the QR decomposition of a matrix. It demonstrates how to
+ * extract a submatrix and insert one matrix into another one using the operator interface.
+ * </p>
+ *
+ * Note: This code is horribly inefficient and is for demonstration purposes only.
+ *
+ * @author Peter Abeles
+ */
+public class QRExampleOperations {
+
+    // where the QR decomposition is stored
+    private DenseMatrix64F QR;
+
+    // used for computing Q
+    private double gammas[];
+
+    /**
+     * Computes the QR decomposition of the provided matrix.
+     *
+     * @param A Matrix which is to be decomposed.  Not modified.
+     */
+    public void decompose( DenseMatrix64F A ) {
+
+        this.QR = A.copy();
+
+        int N = Math.min(A.numCols,A.numRows);
+
+        gammas = new double[ A.numCols ];
+
+        DenseMatrix64F A_small = new DenseMatrix64F(A.numRows,A.numCols);
+        DenseMatrix64F A_mod = new DenseMatrix64F(A.numRows,A.numCols);
+        DenseMatrix64F v = new DenseMatrix64F(A.numRows,1);
+        DenseMatrix64F Q_k = new DenseMatrix64F(A.numRows,A.numRows);
+
+        for( int i = 0; i < N; i++ ) {
+            // reshape temporary variables
+            A_small.reshape(QR.numRows-i,QR.numCols-i,false);
+            A_mod.reshape(A_small.numRows,A_small.numCols,false);
+            v.reshape(A_small.numRows,1,false);
+            Q_k.reshape(v.getNumElements(),v.getNumElements(),false);
+
+            // use extract matrix to get the column that is to be zeroed
+            CommonOps.extract(QR,i,QR.numRows,i,i+1,v,0,0);
+
+            double max = CommonOps.elementMaxAbs(v);
+
+            if( max > 0 && v.getNumElements() > 1 ) {
+                // normalize to reduce overflow issues
+                CommonOps.divide(v,max);
+
+                // compute the magnitude of the vector
+                double tau = NormOps.normF(v);
+
+                if( v.get(0) < 0 )
+                    tau *= -1.0;
+
+                double u_0 = v.get(0) + tau;
+                double gamma = u_0/tau;
+
+                CommonOps.divide(v,u_0);
+                v.set(0,1.0);
+
+                // extract the submatrix of A which is being operated on
+                CommonOps.extract(QR,i,QR.numRows,i,QR.numCols,A_small,0,0);
+
+                // A = (I - γ*u*u<sup>T</sup>)A
+                CommonOps.setIdentity(Q_k);
+                CommonOps.multAddTransB(-gamma,v,v,Q_k);
+                CommonOps.mult(Q_k,A_small,A_mod);
+
+                // save the results
+                CommonOps.insert(A_mod, QR, i,i);
+                CommonOps.insert(v, QR, i,i);
+                QR.unsafe_set(i,i,-tau*max);
+
+                // save gamma for recomputing Q later on
+                gammas[i] = gamma;
+            }
+        }
+    }
+
+    /**
+     * Returns the Q matrix.
+     */
+    public DenseMatrix64F getQ() {
+        DenseMatrix64F Q = CommonOps.identity(QR.numRows);
+        DenseMatrix64F Q_k = new DenseMatrix64F(QR.numRows,QR.numRows);
+        DenseMatrix64F u = new DenseMatrix64F(QR.numRows,1);
+
+        DenseMatrix64F temp = new DenseMatrix64F(QR.numRows,QR.numRows);
+
+        int N = Math.min(QR.numCols,QR.numRows);
+
+        // compute Q by first extracting the householder vectors from the columns of QR and then applying it to Q
+        for( int j = N-1; j>= 0; j-- ) {
+            CommonOps.extract(QR,j, QR.numRows,j,j+1,u,j,0);
+            u.set(j,1.0);
+
+            // A = (I - γ*u*u<sup>T</sup>)*A<br>
+            CommonOps.setIdentity(Q_k);
+            CommonOps.multAddTransB(-gammas[j],u,u,Q_k);
+            CommonOps.mult(Q_k,Q,temp);
+            Q.set(temp);
+        }
+
+        return Q;
+    }
+
+    /**
+     * Returns the R matrix.
+     */
+    public DenseMatrix64F getR() {
+        DenseMatrix64F R = new DenseMatrix64F(QR.numRows,QR.numCols);
+
+        int N = Math.min(QR.numCols,QR.numRows);
+
+        for( int i = 0; i < N; i++ ) {
+            for( int j = i; j < QR.numCols; j++ ) {
+                R.unsafe_set(i,j, QR.unsafe_get(i,j));
+            }
+        }
+
+        return R;
+    }
+}
\ No newline at end of file
diff --git a/examples/src/org/ejml/example/QRExampleSimple.java b/examples/src/org/ejml/example/QRExampleSimple.java
new file mode 100644
index 0000000..0e263a6
--- /dev/null
+++ b/examples/src/org/ejml/example/QRExampleSimple.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.example;
+
+import org.ejml.simple.SimpleMatrix;
+
+import static org.ejml.simple.SimpleMatrix.END;
+
+/**
+ * <p>
+ * Example code for computing the QR decomposition of a matrix. It demonstrates how to
+ * extract a submatrix and insert one matrix into another one using SimpleMatrix.
+ * </p>
+ *
+ * Note: This code is horribly inefficient and is for demonstration purposes only.
+ *
+ * @author Peter Abeles
+ */
+public class QRExampleSimple {
+
+    // where the QR decomposition is stored
+    private SimpleMatrix QR;
+
+    // used for computing Q
+    private double gammas[];
+
+    /**
+     * Computes the QR decomposition of the provided matrix.
+     *
+     * @param A Matrix which is to be decomposed.  Not modified.
+     */
+    public void decompose( SimpleMatrix A ) {
+
+        this.QR = A.copy();
+
+        int N = Math.min(A.numCols(),A.numRows());
+        gammas = new double[ A.numCols() ];
+
+        for( int i = 0; i < N; i++ ) {
+            // use extract matrix to get the column that is to be zeroed
+            SimpleMatrix v = QR.extractMatrix(i, END,i,i+1);
+            double max = v.elementMaxAbs();
+
+            if( max > 0 && v.getNumElements() > 1 ) {
+                // normalize to reduce overflow issues
+                v = v.divide(max);
+
+                // compute the magnitude of the vector
+                double tau = v.normF();
+
+                if( v.get(0) < 0 )
+                    tau *= -1.0;
+
+                double u_0 = v.get(0) + tau;
+                double gamma = u_0/tau;
+
+                v = v.divide(u_0);
+                v.set(0,1.0);
+
+                // extract the submatrix of A which is being operated on
+                SimpleMatrix A_small = QR.extractMatrix(i,END,i,END);
+
+                // A = (I - γ*u*u<sup>T</sup>)A
+                A_small = A_small.plus(-gamma,v.mult(v.transpose()).mult(A_small));
+
+                // save the results
+                QR.insertIntoThis(i,i,A_small);
+                QR.insertIntoThis(i+1,i,v.extractMatrix(1,END,0,1));
+
+                // save gamma for recomputing Q later on
+                gammas[i] = gamma;
+            }
+        }
+    }
+
+    /**
+     * Returns the Q matrix.
+     */
+    public SimpleMatrix getQ() {
+        SimpleMatrix Q = SimpleMatrix.identity(QR.numRows());
+
+        int N = Math.min(QR.numCols(),QR.numRows());
+
+        // compute Q by first extracting the householder vectors from the columns of QR and then applying it to Q
+        for( int j = N-1; j>= 0; j-- ) {
+            SimpleMatrix u = new SimpleMatrix(QR.numRows(),1);
+            u.insertIntoThis(j,0,QR.extractMatrix(j, END,j,j+1));
+            u.set(j,1.0);
+
+            // A = (I - γ*u*u<sup>T</sup>)*A<br>
+            Q = Q.plus(-gammas[j],u.mult(u.transpose()).mult(Q));
+        }
+
+        return Q;
+    }
+
+    /**
+     * Returns the R matrix.
+     */
+    public SimpleMatrix getR() {
+        SimpleMatrix R = new SimpleMatrix(QR.numRows(),QR.numCols());
+
+        int N = Math.min(QR.numCols(),QR.numRows());
+
+        for( int i = 0; i < N; i++ ) {
+            for( int j = i; j < QR.numCols(); j++ ) {
+                R.set(i,j, QR.get(i,j));
+            }
+        }
+
+        return R;
+    }
+}
\ No newline at end of file
diff --git a/examples/src/org/ejml/example/StatisticsMatrix.java b/examples/src/org/ejml/example/StatisticsMatrix.java
new file mode 100644
index 0000000..d105ec9
--- /dev/null
+++ b/examples/src/org/ejml/example/StatisticsMatrix.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2009-2013, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.example;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.RandomMatrices;
+import org.ejml.simple.SimpleBase;
+
+import java.util.Random;
+
+
+/**
+ * Example of how to extend "SimpleMatrix" and add your own functionality.  In this case
+ * two basic statistic operations are added.  Since SimpleBase is extended and StatisticsMatrix
+ * is specified as the generics type, all "SimpleMatrix" operations return a matrix of
+ * type StatisticsMatrix, ensuring strong typing.
+ *
+ * @author Peter Abeles
+ */
+public class StatisticsMatrix extends SimpleBase<StatisticsMatrix> {
+
+    public StatisticsMatrix( int numRows , int numCols ) {
+        super(numRows,numCols);
+    }
+
+    protected StatisticsMatrix(){}
+
+    /**
+     * Wraps a StatisticsMatrix around 'm'.  Does NOT create a copy of 'm' but saves a reference
+     * to it.
+     */
+    public static StatisticsMatrix wrap( DenseMatrix64F m ) {
+        StatisticsMatrix ret = new StatisticsMatrix();
+        ret.mat = m;
+
+        return ret;
+    }
+
+    /**
+     * Computes the mean or average of all the elements.
+     *
+     * @return mean
+     */
+    public double mean() {
+        double total = 0;
+
+        final int N = getNumElements();
+        for( int i = 0; i < N; i++ ) {
+            total += get(i);
+        }
+
+        return total/N;
+    }
+
+    /**
+     * Computes the unbiased standard deviation of all the elements.
+     *
+     * @return standard deviation
+     */
+    public double stdev() {
+        double m = mean();
+
+        double total = 0;
+
+        final int N = getNumElements();
+        if( N <= 1 )
+            throw new IllegalArgumentException("There must be more than one element to compute stdev");
+
+
+        for( int i = 0; i < N; i++ ) {
+            double x = get(i);
+
+            total += (x - m)*(x - m);
+        }
+
+        total /= (N-1);
+
+        return Math.sqrt(total);
+    }
+
+    /**
+     * Returns a matrix of StatisticsMatrix type so that SimpleMatrix functions create matrices
+     * of the correct type.
+     */
+    @Override
+    protected StatisticsMatrix createMatrix(int numRows, int numCols) {
+        return new StatisticsMatrix(numRows,numCols);
+    }
+
+    public static void main( String args[] ) {
+        Random rand = new Random(24234);
+
+        int N = 500;
+
+        // create two vectors whose elements are drawn from uniform distributions
+        StatisticsMatrix A = StatisticsMatrix.wrap(RandomMatrices.createRandom(N,1,0,1,rand));
+        StatisticsMatrix B = StatisticsMatrix.wrap(RandomMatrices.createRandom(N,1,1,2,rand));
+
+        // the mean should be about 0.5
+        System.out.println("Mean of A is               "+A.mean());
+        // the mean should be about 1.5
+        System.out.println("Mean of B is               "+B.mean());
+
+        StatisticsMatrix C = A.plus(B);
+
+        // the mean should be about 2.0
+        System.out.println("Mean of C = A + B is       "+C.mean());
+
+        System.out.println("Standard deviation of A is "+A.stdev());
+        System.out.println("Standard deviation of B is "+B.stdev());
+        System.out.println("Standard deviation of C is "+C.stdev());
+    }
+}
diff --git a/examples/test/org/ejml/example/TestCompareKalmanResults.java b/examples/test/org/ejml/example/TestCompareKalmanResults.java
new file mode 100644
index 0000000..13e956b
--- /dev/null
+++ b/examples/test/org/ejml/example/TestCompareKalmanResults.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.example;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.EjmlUnitTests;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Make sure all the filters produce exactly the same results.
+ *
+ * @author Peter Abeles
+ */
+public class TestCompareKalmanResults {
+
+    private static final double T = 0.5;
+
+    /**
+     * See if all the filters produce the same reslts.
+     */
+    @Test
+    public void checkIdentical() {
+        KalmanFilterSimple simple = new KalmanFilterSimple();
+
+        List<KalmanFilter> all = new ArrayList<KalmanFilter>();
+        all.add( new KalmanFilterOperations() );
+        all.add( new KalmanFilterEquation() );
+        all.add( simple );
+
+        DenseMatrix64F priorX = new DenseMatrix64F(9,1, true, 0.5, -0.2, 0, 0, 0.2, -0.9, 0, 0.2, -0.5);
+        DenseMatrix64F priorP = CommonOps.identity(9);
+
+        DenseMatrix64F F = BenchmarkKalmanPerformance.createF(T);
+        DenseMatrix64F Q = BenchmarkKalmanPerformance.createQ(T,0.1);
+        DenseMatrix64F H = BenchmarkKalmanPerformance.createH();
+
+
+        for( KalmanFilter f : all ) {
+            f.configure(F,Q,H);
+            f.setState(priorX,priorP);
+            f.predict();
+        }
+
+        for( KalmanFilter f : all ) {
+            compareFilters(simple,f);
+        }
+
+        DenseMatrix64F z = new DenseMatrix64F(H.numRows,1);
+        DenseMatrix64F R = CommonOps.identity(H.numRows);
+
+        for( KalmanFilter f : all ) {
+            f.update(z,R);
+        }
+
+        for( KalmanFilter f : all ) {
+            compareFilters(simple,f);
+        }
+    }
+
+    private void compareFilters( KalmanFilter a, KalmanFilter b ) {
+            DenseMatrix64F testX = b.getState();
+            DenseMatrix64F testP = b.getCovariance();
+
+            DenseMatrix64F X = a.getState();
+            DenseMatrix64F P = a.getCovariance();
+
+            EjmlUnitTests.assertEquals(testX,X,1e-8);
+            EjmlUnitTests.assertEquals(testP,P,1e-8);
+    }
+}
\ No newline at end of file
diff --git a/examples/test/org/ejml/example/TestLevenbergMarquardt.java b/examples/test/org/ejml/example/TestLevenbergMarquardt.java
new file mode 100644
index 0000000..13d65b5
--- /dev/null
+++ b/examples/test/org/ejml/example/TestLevenbergMarquardt.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2009-2013, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.example;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.EjmlUnitTests;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestLevenbergMarquardt {
+    int NUM_PTS = 50;
+
+    Random rand = new Random(7264);
+
+    /**
+     * Give it a simple function and see if it computes something close to it for its results.
+     */
+    @Test
+    public void testNumericalJacobian() {
+        JacobianTestFunction func = new JacobianTestFunction();
+
+        DenseMatrix64F param = new DenseMatrix64F(3,1, true, 2, -1, 4);
+
+        LevenbergMarquardt alg = new LevenbergMarquardt(func);
+
+        DenseMatrix64F X = RandomMatrices.createRandom(NUM_PTS,1,rand);
+
+        DenseMatrix64F numJacobian = new DenseMatrix64F(3,NUM_PTS);
+        DenseMatrix64F analyticalJacobian = new DenseMatrix64F(3,NUM_PTS);
+
+        alg.configure(param,X,new DenseMatrix64F(NUM_PTS,1));
+        alg.computeNumericalJacobian(param,X,numJacobian);
+        func.deriv(X,analyticalJacobian);
+
+        EjmlUnitTests.assertEquals(analyticalJacobian,numJacobian,1e-6);
+    }
+
+    /**
+     * See if it can solve an easy optimization problem.
+     */
+    @Test
+    public void testTrivial() {
+        // the number of sample points is equal to the max allowed points
+        runTrivial(NUM_PTS);
+        // do the same thing but with a different number of poitns from the max allowed
+        runTrivial(20);
+    }
+
+    /**
+     * Runs the simple optimization problem with a set of randomly generated inputs.
+     *
+     * @param numPoints How many sample points there are.
+     */
+    public void runTrivial( int numPoints ) {
+        JacobianTestFunction func = new JacobianTestFunction();
+
+        DenseMatrix64F paramInit = new DenseMatrix64F(3,1);
+        DenseMatrix64F param = new DenseMatrix64F(3,1, true, 2, -1, 4);
+
+        LevenbergMarquardt alg = new LevenbergMarquardt(func);
+
+        DenseMatrix64F X = RandomMatrices.createRandom(numPoints,1,rand);
+        DenseMatrix64F Y = new DenseMatrix64F(numPoints,1);
+        func.compute(param,X,Y);
+
+        alg.optimize(paramInit,X,Y);
+
+        DenseMatrix64F foundParam = alg.getParameters();
+
+        assertEquals(0,alg.getFinalCost(),1e-8);
+        EjmlUnitTests.assertEquals(param,foundParam,1e-6);
+    }
+
+    /**
+     * A very simple function to test how well the numerical jacobian is computed.
+     */
+    private static class JacobianTestFunction implements LevenbergMarquardt.Function
+    {
+
+        public void deriv( DenseMatrix64F x, DenseMatrix64F deriv) {
+            double dataX[] = x.data;
+
+            int length = x.numRows;
+
+            for( int j = 0; j < length; j++ ) {
+                double v = dataX[j];
+
+                double dA = 1;
+                double dB = v;
+                double dC = v*v;
+
+                deriv.set(0,j,dA);
+                deriv.set(1,j,dB);
+                deriv.set(2,j,dC);
+            }
+
+        }
+
+        @Override
+        public void compute(DenseMatrix64F param, DenseMatrix64F x, DenseMatrix64F y) {
+            double a = param.data[0];
+            double b = param.data[1];
+            double c = param.data[2];
+
+            double dataX[] = x.data;
+            double dataY[] = y.data;
+
+            int length = x.numRows;
+
+            for( int i = 0; i < length; i++ ) {
+                double v = dataX[i];
+
+                dataY[i] = a + b*v + c*v*v;
+            }
+        }
+    }
+}
diff --git a/examples/test/org/ejml/example/TestPolynomialFit.java b/examples/test/org/ejml/example/TestPolynomialFit.java
new file mode 100644
index 0000000..9f133c0
--- /dev/null
+++ b/examples/test/org/ejml/example/TestPolynomialFit.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2009-2013, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.example;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestPolynomialFit {
+
+    /**
+     * Test with perfect data
+     */
+    @Test
+    public void testPerfect() {
+        double coef[] = new double[]{1,-2,3};
+
+        double x[] = new double[]{-2,1,0.5,2,3,4,5,7,8,9.2,10.2,4.3,6.7};
+        double y[] = new double[ x.length ];
+
+        for( int i = 0; i < y.length; i++ ) {
+            double v = 0;
+            double xx = 1;
+            for (double c : coef) {
+                v += c * xx;
+                xx *= x[i];
+            }
+
+            y[i] = v;
+        }
+
+        PolynomialFit alg = new PolynomialFit(2);
+
+        alg.fit(x,y);
+
+        double found[] = alg.getCoef();
+
+        for( int i = 0; i < coef.length; i++ ) {
+            assertEquals(coef[i],found[i],1e-8);
+        }
+    }
+
+    /**
+     * Make one of the observations way off and see if it is removed
+     */
+    @Test
+    public void testNoise() {
+        double coef[] = new double[]{1,-2,3};
+
+        double x[] = new double[]{-2,1,0.5,2,3,4,5,7,8,9.2,10.2,4.3,6.7};
+        double y[] = new double[ x.length ];
+
+        for( int i = 0; i < y.length; i++ ) {
+            double v = 0;
+            double xx = 1;
+            for (double c : coef) {
+                v += c * xx;
+                xx *= x[i];
+            }
+
+            y[i] = v;
+        }
+
+        y[4] += 3.5;
+
+        PolynomialFit alg = new PolynomialFit(2);
+
+        alg.fit(x,y);
+
+        double found[] = alg.getCoef();
+
+        // the coefficients that it initialy computes should be incorrect
+
+        for( int i = 0; i < coef.length; i++ ) {
+            assertTrue(Math.abs(coef[i]-found[i])>1e-8);
+        }
+
+        //remove the outlier
+        alg.removeWorstFit();
+
+        // now see if the solution is perfect
+        found = alg.getCoef();
+
+        for( int i = 0; i < coef.length; i++ ) {
+            assertEquals(coef[i],found[i],1e-8);
+        }
+    }
+}
diff --git a/examples/test/org/ejml/example/TestPolynomialRootFinder.java b/examples/test/org/ejml/example/TestPolynomialRootFinder.java
new file mode 100644
index 0000000..3ba3587
--- /dev/null
+++ b/examples/test/org/ejml/example/TestPolynomialRootFinder.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2009-2013, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.example;
+
+import org.ejml.data.Complex64F;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestPolynomialRootFinder {
+
+    @Test
+    public void findRoots() {
+        Complex64F[] roots = PolynomialRootFinder.findRoots(4, 3, 2, 1);
+
+        int numReal = 0;
+        for( Complex64F c : roots ) {
+            if( c.isReal() ) {
+                checkRoot(c.real,4,3,2,1);
+                numReal++;
+            }
+        }
+
+        assertTrue(numReal>0);
+    }
+
+    private void checkRoot( double root , double ...coefs ) {
+        double total = 0;
+
+        double a = 1;
+        for( double c : coefs ) {
+            total += a*c;
+            a *= root;
+        }
+
+        assertEquals(0,total,1e-8);
+    }
+}
\ No newline at end of file
diff --git a/examples/test/org/ejml/example/TestPrincipleComponentAnalysis.java b/examples/test/org/ejml/example/TestPrincipleComponentAnalysis.java
new file mode 100644
index 0000000..34f05b3
--- /dev/null
+++ b/examples/test/org/ejml/example/TestPrincipleComponentAnalysis.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.example;
+
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestPrincipleComponentAnalysis {
+    Random rand = new Random(234345);
+
+    /**
+     * Sees if the projection error increases as the DOF decreases in the number of basis vectors.
+     */
+    @Test
+    public void checkBasisError() {
+        int M = 30;
+        int N = 5;
+
+        double obs[][] = new double[M][];
+
+        PrincipalComponentAnalysis pca = new PrincipalComponentAnalysis();
+
+        // add observations
+        pca.setup(M,N);
+
+        for( int i = 0; i < M; i++ ) {
+            obs[i] = RandomMatrices.createRandom(N,1,-1,1,rand).data;
+            pca.addSample(obs[i]);
+        }
+
+        // as a more crude estimate is made of the input data the error should increase
+        pca.computeBasis(N);
+        double errorPrev = computeError(pca,obs);
+        assertEquals(errorPrev,0,1e-8);
+
+        for( int i = N-1; i >= 1; i-- ) {
+            pca.computeBasis(i);
+            double error = computeError(pca,obs);
+            assertTrue(error > errorPrev );
+            errorPrev = error;
+        }
+    }
+
+    private double computeError(PrincipalComponentAnalysis pca, double[][] obs ) {
+        double error = 0;
+        for (double[] o : obs) {
+            error += pca.errorMembership(o);
+        }
+        return error;
+    }
+
+    /**
+     * Checks sampleToEigenSpace and sampleToEigenSpace when the basis vectors can
+     * fully describe the vector.
+     */
+    @Test
+    public void sampleToEigenSpace() {
+        int M = 30;
+        int N = 5;
+
+        double obs[][] = new double[M][];
+
+        PrincipalComponentAnalysis pca = new PrincipalComponentAnalysis();
+
+        // add observations
+        pca.setup(M,N);
+
+        for( int i = 0; i < M; i++ ) {
+            obs[i] = RandomMatrices.createRandom(N,1,-1,1,rand).data;
+            pca.addSample(obs[i]);
+        }
+
+        // when the basis is N vectors it should perfectly describe the vector
+        pca.computeBasis(N);
+
+        for( int i = 0; i < M; i++ ) {
+            double s[] = pca.sampleToEigenSpace(obs[i]);
+            assertTrue(error(s,obs[i]) > 1e-8 );
+            double o[] = pca.eigenToSampleSpace(s);
+            assertTrue(error(o,obs[i]) <= 1e-8 );
+        }
+    }
+
+    private double error( double[] a , double []b ) {
+        double ret = 0;
+
+        for( int i = 0; i < a.length; i++ ) {
+            ret += Math.abs(a[i]-b[i]);
+        }
+
+        return ret;
+    }
+
+    /**
+     * Makes sure the response is not zero.  Perhaps this is too simple of a test
+     */
+    @Test
+    public void response() {
+        int M = 30;
+        int N = 5;
+
+        double obs[][] = new double[M][];
+
+        PrincipalComponentAnalysis pca = new PrincipalComponentAnalysis();
+
+        // add observations
+        pca.setup(M,N);
+
+        for( int i = 0; i < M; i++ ) {
+            obs[i] = RandomMatrices.createRandom(N,1,-1,1,rand).data;
+            pca.addSample(obs[i]);
+        }
+
+        pca.computeBasis(N-2);
+
+        for( int i = 0; i < M; i++ ) {
+            double responseObs = pca.response(obs[i]);
+
+            assertTrue(responseObs > 0 );
+        }
+    }
+}
diff --git a/examples/test/org/ejml/example/TestQRExampleEquation.java b/examples/test/org/ejml/example/TestQRExampleEquation.java
new file mode 100644
index 0000000..6fb1549
--- /dev/null
+++ b/examples/test/org/ejml/example/TestQRExampleEquation.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.example;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestQRExampleEquation {
+
+    Random rand = new Random(23423);
+
+    @Test
+    public void basic() {
+        checkMatrix(7,5);
+        checkMatrix(5,5);
+        checkMatrix(7,7);
+    }
+
+    private void checkMatrix( int numRows , int numCols ) {
+        DenseMatrix64F A = RandomMatrices.createRandom(numRows,numCols,-1,1,rand);
+
+        QRExampleEquation alg = new QRExampleEquation();
+
+        alg.decompose(A);
+
+        DenseMatrix64F Q = alg.getQ();
+        DenseMatrix64F R = alg.getR();
+
+        DenseMatrix64F A_found = new DenseMatrix64F(numRows,numCols);
+        CommonOps.mult(Q,R,A_found);
+
+        assertTrue( MatrixFeatures.isIdentical(A,A_found,1e-8));
+    }
+
+
+}
\ No newline at end of file
diff --git a/examples/test/org/ejml/example/TestQRExampleOps.java b/examples/test/org/ejml/example/TestQRExampleOps.java
new file mode 100644
index 0000000..a97dfd1
--- /dev/null
+++ b/examples/test/org/ejml/example/TestQRExampleOps.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.example;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestQRExampleOps {
+
+    Random rand = new Random(23423);
+
+
+    @Test
+    public void basic() {
+        checkMatrix(7,5);
+        checkMatrix(5,5);
+        checkMatrix(7,7);
+    }
+
+    private void checkMatrix( int numRows , int numCols ) {
+        DenseMatrix64F A = RandomMatrices.createRandom(numRows,numCols,-1,1,rand);
+
+        QRExampleOperations alg = new QRExampleOperations();
+
+        alg.decompose(A);
+
+        DenseMatrix64F Q = alg.getQ();
+        DenseMatrix64F R = alg.getR();
+
+        DenseMatrix64F A_found = new DenseMatrix64F(numRows,numCols);
+        CommonOps.mult(Q,R,A_found);
+
+        assertTrue( MatrixFeatures.isIdentical(A,A_found,1e-8));
+    }
+
+
+}
\ No newline at end of file
diff --git a/examples/test/org/ejml/example/TestQRExampleSimple.java b/examples/test/org/ejml/example/TestQRExampleSimple.java
new file mode 100644
index 0000000..93a19c2
--- /dev/null
+++ b/examples/test/org/ejml/example/TestQRExampleSimple.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2009-2013, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.example;import org.ejml.simple.SimpleMatrix;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestQRExampleSimple {
+
+    Random rand = new Random(23423);
+
+
+    @Test
+    public void basic() {
+        checkMatrix(7,5);
+        checkMatrix(5,5);
+        checkMatrix(7,7);
+    }
+
+    private void checkMatrix( int numRows , int numCols ) {
+        SimpleMatrix A = SimpleMatrix.random(numRows,numCols,-1,1,rand);
+
+        QRExampleSimple alg = new QRExampleSimple();
+
+        alg.decompose(A);
+
+        SimpleMatrix Q = alg.getQ();
+        SimpleMatrix R = alg.getR();
+
+        SimpleMatrix A_found = Q.mult(R);
+
+        assertTrue( A.isIdentical(A_found,1e-8));
+    }
+
+
+}
diff --git a/main/all/EJML All.iml b/main/all/EJML All.iml
new file mode 100644
index 0000000..3453a5d
--- /dev/null
+++ b/main/all/EJML All.iml	
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module external.linked.project.id=":main:all" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="org.ejml" external.system.module.version="0.27" type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="false">
+    <output url="file://$MODULE_DIR$/build/classes/main" />
+    <output-test url="file://$MODULE_DIR$/build/classes/test" />
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/benchmarks/src" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/benchmarks/test" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/generate" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/resources/src" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/resources/test" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
+      <excludeFolder url="file://$MODULE_DIR$/.gradle" />
+      <excludeFolder url="file://$MODULE_DIR$/build" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="module" module-name="EJML Core" exported="" />
+    <orderEntry type="module" module-name="EJML Dense 64" exported="" />
+    <orderEntry type="module" module-name="EJML Dense C64" exported="" />
+    <orderEntry type="module" module-name="EJML Equation" exported="" />
+    <orderEntry type="module" module-name="EJML Simple" exported="" />
+    <orderEntry type="library" scope="TEST" name="Gradle: junit:junit:4.11" level="project" />
+    <orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-core:1.3" level="project" />
+  </component>
+</module>
\ No newline at end of file
diff --git a/main/all/build.gradle b/main/all/build.gradle
new file mode 100644
index 0000000..21ae72d
--- /dev/null
+++ b/main/all/build.gradle
@@ -0,0 +1,13 @@
+dependencies {
+    compile project(':main:core')
+    compile project(':main:dense64')
+    compile project(':main:denseC64')
+    compile project(':main:equation')
+    compile project(':main:simple')
+}
+
+idea {
+    module {
+        name = "EJML All"
+    }
+}
\ No newline at end of file
diff --git a/main/all/src/org/ejml/All.java b/main/all/src/org/ejml/All.java
new file mode 100644
index 0000000..6dc137c
--- /dev/null
+++ b/main/all/src/org/ejml/All.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml;
+
+/**
+ * A class which does nothing so that an All jar is created and gradle uploadArchive will not bitch
+ * @author Peter Abeles
+ */
+public class All {
+}
diff --git a/main/build.gradle b/main/build.gradle
new file mode 100644
index 0000000..9d7214a
--- /dev/null
+++ b/main/build.gradle
@@ -0,0 +1,6 @@
+subprojects {
+
+}
+
+//dependencies {
+//}
diff --git a/main/core/build.gradle b/main/core/build.gradle
new file mode 100644
index 0000000..34d554a
--- /dev/null
+++ b/main/core/build.gradle
@@ -0,0 +1,11 @@
+dependencies {
+    testCompile project(':main:dense64')
+    testCompile project(':main:denseC64')
+    testCompile project(':main:experimental')
+}
+
+idea {
+    module {
+        name = "EJML Core"
+    }
+}
\ No newline at end of file
diff --git a/main/core/src/org/ejml/EjmlParameters.java b/main/core/src/org/ejml/EjmlParameters.java
new file mode 100644
index 0000000..455ac8e
--- /dev/null
+++ b/main/core/src/org/ejml/EjmlParameters.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml;
+
+
+/**
+ * This is a list of parameters that are used across the code.  To tune performance
+ * for a particular system change these values.
+ *
+ * @author Peter Abeles
+ */
+public class EjmlParameters {
+
+    public static final float TOL32 = 1e-4f;
+    public static final double TOL64 = 1e-8;
+
+
+    /**
+     * Used to adjust which algorithms are used.  Often there is a trade off between memory usage
+     * and speed.
+     */
+    public static MemoryUsage MEMORY = MemoryUsage.FASTER;
+
+    /**
+     * <p>
+     * In modern computers there are high speed memory caches.  It is assumed that a square
+     * block with this width can be contained entirely in one of those caches.  Settings this
+     * value too large can have a dramatic effect on performance in some situations.  Setting
+     * it too low results in a less dramatic performance hit.  The optimal value is dependent
+     * on the computer's memory architecture.
+     * </p>
+     */
+    // See design notes
+    public static int BLOCK_WIDTH = 60;
+    public static int BLOCK_WIDTH_CHOL = 20;
+
+    /**
+     * Number of elements in a block.
+     */
+    public static int BLOCK_SIZE = BLOCK_WIDTH*BLOCK_WIDTH;
+
+    public static int TRANSPOSE_SWITCH = 375;
+
+    /**
+     * At what point does it switch from a small matrix multiply to the reorder version.
+     */
+    public static int MULT_COLUMN_SWITCH = 15;
+    public static int MULT_TRANAB_COLUMN_SWITCH = 40;
+    public static int MULT_INNER_SWITCH = 100;
+
+    public static int CMULT_COLUMN_SWITCH = 7;
+    
+    /**
+     * <p>
+     * At which point should it switch to the block cholesky algorithm.
+     * </p>
+     * <p>
+     * In benchmarks  the basic actually performed slightly better at 1000
+     * but in JVM 1.6 it some times get stuck in a mode where the basic version was very slow
+     * in that case the block performed much better.
+     * </p>
+     */
+    public static int SWITCH_BLOCK64_CHOLESKY = 1000;
+
+    public static int SWITCH_BLOCK64_QR = 1500;
+
+    public static enum MemoryUsage
+    {
+        /**
+         * Use lower memory algorithm while not totally sacrificing speed.
+         */
+        LOW_MEMORY,
+        /**
+         * Always favor faster algorithms even if they use more memory.
+         */
+        FASTER
+
+    }
+}
diff --git a/main/core/src/org/ejml/UtilEjml.java b/main/core/src/org/ejml/UtilEjml.java
new file mode 100644
index 0000000..52bd86b
--- /dev/null
+++ b/main/core/src/org/ejml/UtilEjml.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml;
+
+import org.ejml.data.DenseMatrix64F;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+
+/**
+ * Various functions that are useful but don't have a clear location that they belong in.
+ *
+ * @author Peter Abeles
+ */
+public class UtilEjml {
+
+    /**
+     * Version string used to indicate which version of EJML is being used.
+     */
+    public static String VERSION = "0.28";
+
+    /**
+     * Default tolerance.
+     */
+    public static double TOLERANCE = 1e-8;
+
+    public static double EPS = Math.pow(2,-52);
+
+    public static boolean isUncountable( double val ) {
+        return Double.isNaN(val) || Double.isInfinite(val);
+    }
+
+    public static void memset( double[] data , double val ) {
+        for( int i = 0; i < data.length; i++ ) {
+            data[i] = val;
+        }
+    }
+
+    public static void memset( double[] data , double val , int length ) {
+        for( int i = 0; i < length; i++ ) {
+            data[i] = val;
+        }
+    }
+
+    public static void memset( int[] data , int val , int length ) {
+        for( int i = 0; i < length; i++ ) {
+            data[i] = val;
+        }
+    }
+
+    public static <T> void setnull( T[] array )  {
+        for( int i = 0; i < array.length; i++ ) {
+            array[i] = null;
+        }
+    }
+
+    public static double max( double array[], int start , int length ) {
+        double max = array[start];
+        final int end = start+length;
+
+        for( int i = start+1; i < end; i++ ) {
+            double v = array[i];
+            if( v > max ) {
+                max = v;
+            }
+        }
+
+        return max;
+    }
+
+    /**
+     * Give a string of numbers it returns a DenseMatrix
+     */
+    public static DenseMatrix64F parseMatrix( String s , int numColumns )
+    {
+        String []vals = s.split("(\\s)+");
+
+        // there is the possibility the first element could be empty
+        int start = vals[0].isEmpty() ? 1 : 0;
+
+        // covert it from string to doubles
+        int numRows = (vals.length-start) / numColumns;
+
+        DenseMatrix64F ret = new DenseMatrix64F(numRows,numColumns);
+
+        int index = start;
+        for( int i = 0; i < numRows; i++ ) {
+            for( int j = 0; j < numColumns; j++ ) {
+                ret.set(i,j, Double.parseDouble(vals[ index++ ]));
+            }
+        }
+
+        return ret;
+    }
+
+    public static Integer[] sortByIndex( final double []data , int size ) {
+        Integer[] idx = new Integer[size];
+        for( int i = 0; i < size; i++ ) {
+            idx[i] = i;
+        }
+
+        Arrays.sort(idx, new Comparator<Integer>() {
+            @Override public int compare(final Integer o1, final Integer o2) {
+                return Double.compare(data[o1], data[o2]);
+            }
+        });
+
+        return idx;
+    }
+}
diff --git a/main/core/src/org/ejml/alg/dense/linsol/LinearSolverSafe.java b/main/core/src/org/ejml/alg/dense/linsol/LinearSolverSafe.java
new file mode 100644
index 0000000..bac4abd
--- /dev/null
+++ b/main/core/src/org/ejml/alg/dense/linsol/LinearSolverSafe.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol;
+
+import org.ejml.data.ReshapeMatrix;
+import org.ejml.interfaces.decomposition.DecompositionInterface;
+import org.ejml.interfaces.linsol.LinearSolver;
+
+
+/**
+ * Ensures that any linear solver it is wrapped around will never modify
+ * the input matrices.
+ *
+ * @author Peter Abeles
+ */
+ at SuppressWarnings({"unchecked"})
+public class LinearSolverSafe<T extends ReshapeMatrix> implements LinearSolver<T> {
+
+    // the solver it is wrapped around
+    private LinearSolver<T> alg;
+
+    // local copies of input matrices that can be modified.
+    private T A;
+    private T B;
+
+    /**
+     *
+     * @param alg The solver it is wrapped around.
+     */
+    public LinearSolverSafe(LinearSolver<T> alg) {
+        this.alg = alg;
+    }
+
+    @Override
+    public boolean setA(T A) {
+
+        if( alg.modifiesA() ) {
+            if( this.A == null ) {
+                this.A = (T)A.copy();
+            } else {
+                if( this.A.getNumRows() != A.getNumRows() || this.A.getNumCols() != A.getNumCols() ) {
+                    this.A.reshape(A.getNumRows(),A.getNumCols());
+                }
+                this.A.set(A);
+            }
+            return alg.setA(this.A);
+        }
+
+        return alg.setA(A);
+    }
+
+    @Override
+    public double quality() {
+        return alg.quality();
+    }
+
+    @Override
+    public void solve(T B, T X) {
+        if( alg.modifiesB() ) {
+            if( this.B == null ) {
+                this.B = (T)B.copy();
+            } else {
+                if( this.B.getNumRows() != B.getNumRows() || this.B.getNumCols() != B.getNumCols() ) {
+                    this.B.reshape(A.getNumRows(),B.getNumCols());
+                }
+                this.B.set(B);
+            }
+            B = this.B;
+        }
+
+        alg.solve(B,X);
+    }
+
+    @Override
+    public void invert(T A_inv) {
+        alg.invert(A_inv);
+    }
+
+    @Override
+    public boolean modifiesA() {
+        return false;
+    }
+
+    @Override
+    public boolean modifiesB() {
+        return false;
+    }
+
+    @Override
+    public <D extends DecompositionInterface> D getDecomposition() {
+        return alg.getDecomposition();
+    }
+}
diff --git a/main/core/src/org/ejml/data/BlockMatrix64F.java b/main/core/src/org/ejml/data/BlockMatrix64F.java
new file mode 100644
index 0000000..ec00547
--- /dev/null
+++ b/main/core/src/org/ejml/data/BlockMatrix64F.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import org.ejml.EjmlParameters;
+import org.ejml.ops.MatrixIO;
+
+
+/**
+ * A row-major block matrix declared on to one continuous array.
+ *
+ * @author Peter Abeles
+ */
+public class BlockMatrix64F extends D1Matrix64F {
+    public int blockLength;
+
+    public BlockMatrix64F( int numRows , int numCols , int blockLength)
+    {
+        this.data = new double[ numRows * numCols ];
+        this.blockLength = blockLength;
+        this.numRows = numRows;
+        this.numCols = numCols;
+    }
+
+    public BlockMatrix64F( int numRows , int numCols )
+    {
+        this(numRows,numCols, EjmlParameters.BLOCK_WIDTH);
+    }
+
+    public BlockMatrix64F(){}
+
+    public void set( BlockMatrix64F A ) {
+        this.blockLength = A.blockLength;
+        this.numRows = A.numRows;
+        this.numCols = A.numCols;
+
+        int N = numCols*numRows;
+
+        if( data.length < N )
+            data = new double[ N ];
+
+        System.arraycopy(A.data,0,data,0,N);
+    }
+
+    public static BlockMatrix64F wrap( double data[] , int numRows , int numCols , int blockLength )
+    {
+        BlockMatrix64F ret = new BlockMatrix64F();
+        ret.data = data;
+        ret.numRows = numRows;
+        ret.numCols = numCols;
+        ret.blockLength = blockLength;
+
+        return ret;
+    }
+
+    @Override
+    public double[] getData() {
+        return data;
+    }
+
+    @Override
+    public void reshape(int numRows, int numCols, boolean saveValues)
+    {
+        if( numRows*numCols <= data.length  ) {
+            this.numRows = numRows;
+            this.numCols = numCols;
+        } else {
+            double[] data = new double[ numRows*numCols ];
+
+            if( saveValues ) {
+                System.arraycopy(this.data,0,data,0,getNumElements());
+            }
+
+            this.numRows = numRows;
+            this.numCols = numCols;
+            this.data = data;
+        }
+    }
+
+    public void reshape(int numRows, int numCols, int blockLength , boolean saveValues) {
+        this.blockLength = blockLength;
+        this.reshape(numRows,numCols,saveValues);
+    }
+
+    @Override
+    public int getIndex( int row, int col ) {
+        // find the block it is inside
+        int blockRow = row / blockLength;
+        int blockCol = col / blockLength;
+
+        int localHeight = Math.min(numRows - blockRow*blockLength , blockLength);
+
+        int index = blockRow*blockLength*numCols + blockCol* localHeight * blockLength;
+
+        int localLength = Math.min(numCols - blockLength*blockCol , blockLength);
+
+        row = row % blockLength;
+        col = col % blockLength;
+        
+        return index + localLength * row + col;
+    }
+
+    @Override
+    public double get( int row, int col) {
+        return data[ getIndex(row,col)];
+    }
+
+    @Override
+    public double unsafe_get( int row, int col) {
+        return data[ getIndex(row,col)];
+    }
+
+    @Override
+    public void set( int row, int col, double val) {
+        data[ getIndex(row,col)] = val;
+    }
+
+    @Override
+    public void unsafe_set( int row, int col, double val) {
+        data[ getIndex(row,col)] = val;
+    }
+
+    @Override
+    public int getNumRows() {
+        return numRows;
+    }
+
+    @Override
+    public int getNumCols() {
+        return numCols;
+    }
+
+    @Override
+    public void set(Matrix original) {
+        if( original instanceof BlockMatrix64F ) {
+            set((BlockMatrix64F)original);
+        } else {
+            RealMatrix64F m = (RealMatrix64F) original;
+
+            for (int i = 0; i < numRows; i++) {
+                for (int j = 0; j < numCols; j++) {
+                    set(i, j, m.get(i, j));
+                }
+            }
+        }
+    }
+
+    @Override
+    public int getNumElements() {
+        return numRows*numCols;
+    }
+
+    @Override
+    public void print() {
+        MatrixIO.print(System.out,this);
+    }
+
+    public BlockMatrix64F copy() {
+        BlockMatrix64F A = new BlockMatrix64F(numRows,numCols,blockLength);
+        A.set(this);
+        return A;
+    }
+}
diff --git a/main/core/src/org/ejml/data/CD1Matrix64F.java b/main/core/src/org/ejml/data/CD1Matrix64F.java
new file mode 100644
index 0000000..137d68c
--- /dev/null
+++ b/main/core/src/org/ejml/data/CD1Matrix64F.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import org.ejml.ops.MatrixDimensionException;
+
+
+/**
+ * A generic abstract class for matrices whose data is stored in a single 1D array of doubles.  The
+ * format of the elements in this array is not specified.  For example row major, column major,
+ * and block row major are all common formats.
+ *
+ * @author Peter Abeles
+ */
+public abstract class CD1Matrix64F implements ComplexMatrix64F, ReshapeMatrix{
+    /**
+     * Where the raw data for the matrix is stored.  The format is type dependent.
+     */
+    public double[] data;
+
+    /**
+     * Number of rows in the matrix.
+     */
+    public int numRows;
+    /**
+     * Number of columns in the matrix.
+     */
+    public int numCols;
+
+    /**
+     * Used to get a reference to the internal data.
+     *
+     * @return Reference to the matrix's data.
+     */
+    public double[] getData() {
+        return data;
+    }
+
+    /**
+     * Changes the internal array reference.
+     */
+    public void setData( double[] data ) {
+        this.data = data;
+    }
+
+    /**
+     * Returns the internal array index for the specified row and column.
+     *
+     * @param row Row index.
+     * @param col Column index.
+     * @return Internal array index.
+     */
+    public abstract int getIndex( int row, int col );
+
+    /**
+     * Sets the value of this matrix to be the same as the value of the provided matrix.  Both
+     * matrices must have the same shape:<br>
+     * <br>
+     * a<sub>ij</sub> = b<sub>ij</sub><br>
+     * <br>
+     *
+     * @param b The matrix that this matrix is to be set equal to.
+     */
+    public void set( CD1Matrix64F b )
+    {
+        if( numRows != b.numRows || numCols != b.numCols ) {
+            throw new MatrixDimensionException("The two matrices do not have compatible shapes.");
+        }
+
+        int dataLength = b.getDataLength();
+
+        System.arraycopy(b.data, 0, this.data, 0, dataLength);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int getNumRows() {
+        return numRows;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int getNumCols() {
+        return numCols;
+    }
+
+    /**
+     * Sets the number of rows.
+     *
+     * @param numRows Number of rows
+     */
+    public void setNumRows(int numRows) {
+        this.numRows = numRows;
+    }
+
+    /**
+     * Sets the number of columns.
+     *
+     * @param numCols Number of columns
+     */
+    public void setNumCols(int numCols) {
+        this.numCols = numCols;
+    }
+}
\ No newline at end of file
diff --git a/main/core/src/org/ejml/data/CDenseMatrix64F.java b/main/core/src/org/ejml/data/CDenseMatrix64F.java
new file mode 100644
index 0000000..827fb11
--- /dev/null
+++ b/main/core/src/org/ejml/data/CDenseMatrix64F.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import org.ejml.ops.MatrixIO;
+
+/**
+ * Dense matrix for complex numbers.  Internally it stores its data in a single row-major array with the real
+ * and imaginary components interlaces, in that order.  The total number of elements in the array will be
+ * numRows*numColumns*2.
+ *
+ * @author Peter Abeles
+ */
+public class CDenseMatrix64F extends CD1Matrix64F {
+
+    /**
+     * <p>
+     * Creates a matrix with the values and shape defined by the 2D array 'data'.
+     * It is assumed that 'data' has a row-major formatting:<br>
+     *  <br>
+     * data[ row ][ column ]
+     * </p>
+     * @param data 2D array representation of the matrix. Not modified.
+     */
+    public CDenseMatrix64F( double data[][] ) {
+
+        this.numRows = data.length;
+        this.numCols = data[0].length/2;
+
+        this.data = new double[ numRows * numCols * 2 ];
+
+        for (int i = 0; i < numRows; i++) {
+            double[] row = data[i];
+            if( row.length != numCols*2 )
+                throw new IllegalArgumentException("Unexpected row size in input data at row "+i);
+
+            System.arraycopy(row,0,this.data,i*numCols*2,row.length);
+        }
+    }
+
+    public CDenseMatrix64F(int numRows, int numCols, boolean rowMajor, double... data) {
+        if( data.length != numRows*numCols*2 )
+            throw new RuntimeException("Unexpected length for data");
+
+        this.data = new double[ numRows * numCols * 2 ];
+
+        this.numRows = numRows;
+        this.numCols = numCols;
+
+        set(numRows,numCols, rowMajor, data);
+    }
+
+    /**
+     * Creates a new {@link org.ejml.data.CDenseMatrix64F} which is a copy of the passed in matrix.
+     * @param original Matrix which is to be copied
+     */
+    public CDenseMatrix64F(CDenseMatrix64F original) {
+        this(original.numRows, original.numCols);
+        set(original);
+    }
+
+    /**
+     * Creates a new matrix with the specified number of rows and columns
+     *
+     * @param numRows number of rows
+     * @param numCols number of columns
+     */
+    public CDenseMatrix64F(int numRows, int numCols) {
+        this.numRows = numRows;
+        this.numCols = numCols;
+        this.data = new double[numRows*numCols*2];
+    }
+
+    @Override
+    public int getIndex(int row, int col) {
+        return row*numCols*2 + col*2;
+    }
+
+    @Override
+    public void reshape( int numRows , int numCols ) {
+        int newLength = numRows*numCols*2;
+
+        if( newLength > data.length ) {
+            data = new double[newLength];
+        }
+
+        this.numRows = numRows;
+        this.numCols = numCols;
+    }
+
+    @Override
+    public void get(int row, int col, Complex64F output) {
+        int index = row*numCols*2 + col*2;
+        output.real = data[index];
+        output.imaginary = data[index+1];
+    }
+
+    @Override
+    public void set(int row, int col, double real, double imaginary) {
+        int index = row*numCols*2 + col*2;
+        data[index] = real;
+        data[index+1] = imaginary;
+    }
+
+    @Override
+    public double getReal(int row, int col) {
+        return data[row*numCols*2 + col*2];
+    }
+
+    @Override
+    public void setReal(int row, int col, double val) {
+        data[row*numCols*2 + col*2] = val;
+    }
+
+    @Override
+    public double getImaginary(int row, int col) {
+        return data[row*numCols*2 + col*2 + 1];
+    }
+
+    @Override
+    public void setImaginary(int row, int col, double val) {
+        data[row*numCols*2 + col*2 + 1] = val;
+    }
+
+    @Override
+    public int getDataLength() {
+        return numRows*numCols*2;
+    }
+
+    public void set( CDenseMatrix64F original ) {
+        reshape(original.numRows,original.numCols);
+        int columnSize = numCols*2;
+        for (int y = 0; y < numRows; y++) {
+            int index = y*numCols*2;
+            System.arraycopy(original.data,index,data,index,columnSize);
+        }
+    }
+
+    @Override
+    public CDenseMatrix64F copy() {
+        return new CDenseMatrix64F(this);
+    }
+
+    @Override
+    public void set(Matrix original) {
+        reshape(original.getNumRows(),original.getNumCols());
+
+        ComplexMatrix64F n = (ComplexMatrix64F)original;
+
+        Complex64F c = new Complex64F();
+        for (int i = 0; i < numRows; i++) {
+            for (int j = 0; j < numCols; j++) {
+                n.get(i,j,c);
+                set(i,j,c.real,c.imaginary);
+            }
+        }
+    }
+
+    @Override
+    public void print() {
+        MatrixIO.print(System.out, this);
+    }
+
+    /**
+     * Number of array elements in the matrix's row.
+     */
+    public int getRowStride() {
+        return numCols*2;
+    }
+
+    /**
+     * Sets this matrix equal to the matrix encoded in the array.
+     *
+     * @param numRows The number of rows.
+     * @param numCols The number of columns.
+     * @param rowMajor If the array is encoded in a row-major or a column-major format.
+     * @param data The formatted 1D array. Not modified.
+     */
+    public void set(int numRows, int numCols, boolean rowMajor, double ...data)
+    {
+        reshape(numRows,numCols);
+        int length = numRows*numCols*2;
+
+        if( length > data.length )
+            throw new RuntimeException("Passed in array not long enough");
+
+        if( rowMajor ) {
+            System.arraycopy(data,0,this.data,0,length);
+        } else {
+            int index = 0;
+            int stride = numRows*2;
+            for( int i = 0; i < numRows; i++ ) {
+                for( int j = 0; j < numCols; j++ ) {
+                    this.data[index++] = data[j*stride+i*2];
+                    this.data[index++] = data[j*stride+i*2+1];
+                }
+            }
+        }
+    }
+}
diff --git a/main/core/src/org/ejml/data/Complex64F.java b/main/core/src/org/ejml/data/Complex64F.java
new file mode 100644
index 0000000..ee7a5e1
--- /dev/null
+++ b/main/core/src/org/ejml/data/Complex64F.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import org.ejml.ops.ComplexMath64F;
+
+import java.io.Serializable;
+
+/**
+ * <p>
+ * Represents a complex number using 64bit floating point numbers.  A complex number is composed of
+ * a real and imaginary components.
+ * </p>
+ */
+public class Complex64F implements Serializable {
+    public double real;
+    public double imaginary;
+
+    public Complex64F(double real, double imaginary) {
+        this.real = real;
+        this.imaginary = imaginary;
+    }
+
+    public Complex64F() {
+    }
+
+    public double getReal() {
+        return real;
+    }
+
+    public double getMagnitude() {
+        return Math.sqrt(real*real + imaginary*imaginary);
+    }
+
+    public double getMagnitude2() {
+        return real*real + imaginary*imaginary;
+    }
+
+    public void setReal(double real) {
+        this.real = real;
+    }
+
+    public double getImaginary() {
+        return imaginary;
+    }
+
+    public void setImaginary(double imaginary) {
+        this.imaginary = imaginary;
+    }
+
+    public void set(double real, double imaginary) {
+        this.real = real;
+        this.imaginary = imaginary;
+    }
+
+    public void set(Complex64F a) {
+        this.real = a.real;
+        this.imaginary = a.imaginary;
+    }
+
+    public boolean isReal() {
+        return imaginary == 0.0;
+    }
+
+    public String toString() {
+        if( imaginary == 0 ) {
+            return ""+real;
+        } else {
+            return real+" "+imaginary+"i";
+        }
+    }
+
+    public Complex64F plus( Complex64F a ) {
+        Complex64F ret = new Complex64F();
+        ComplexMath64F.plus(this,a,ret);
+        return ret;
+    }
+
+    public Complex64F minus( Complex64F a ) {
+        Complex64F ret = new Complex64F();
+        ComplexMath64F.minus(this, a, ret);
+        return ret;
+    }
+
+    public Complex64F times( Complex64F a ) {
+        Complex64F ret = new Complex64F();
+        ComplexMath64F.multiply(this,a,ret);
+        return ret;
+    }
+
+    public Complex64F divide( Complex64F a ) {
+        Complex64F ret = new Complex64F();
+        ComplexMath64F.divide(this,a,ret);
+        return ret;
+    }
+}
diff --git a/main/core/src/org/ejml/data/ComplexMatrix64F.java b/main/core/src/org/ejml/data/ComplexMatrix64F.java
new file mode 100644
index 0000000..ea24672
--- /dev/null
+++ b/main/core/src/org/ejml/data/ComplexMatrix64F.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+/**
+ * Interface for all complex 64 bit floating point rectangular matrices.
+ *
+ * @author Peter Abeles
+ */
+public interface ComplexMatrix64F extends Matrix {
+
+    /**
+     * Returns the complex value of the matrix's element
+     * @param row Matrix element's row index..
+     * @param col Matrix element's column index.
+     * @param output Storage for the complex number
+     */
+    public void get( int row , int col , Complex64F output );
+
+    /**
+     * Set's the complex value of the matrix's element
+     *
+     * @param row Matrix element's row index..
+     * @param col Matrix element's column index.
+     * @param real The real component
+     * @param imaginary The imaginary component
+     */
+    public void set( int row , int col , double real , double imaginary );
+
+    /**
+     * Returns the real component of the matrix's element.
+     *
+     * @param row Matrix element's row index..
+     * @param col Matrix element's column index.
+     * @return The specified element's value.
+     */
+    public double getReal(int row, int col);
+
+
+    /**
+     * Sets the real component of the matrix's element.
+     *
+     * @param row Matrix element's row index..
+     * @param col Matrix element's column index.
+     * @param val  The element's new value.
+     */
+    public void setReal(int row, int col, double val);
+
+    /**
+     * Returns the imaginary component of the matrix's element.
+     *
+     * @param row Matrix element's row index..
+     * @param col Matrix element's column index.
+     * @return The specified element's value.
+     */
+    public double getImaginary(int row, int col);
+
+
+    /**
+     * Sets the imaginary component of the matrix's element.
+     *
+     * @param row Matrix element's row index..
+     * @param col Matrix element's column index.
+     * @param val  The element's new value.
+     */
+    public void setImaginary(int row, int col, double val);
+
+    /**
+     * Returns the number of elements in the internal data array
+     *
+     * @return Number of elements in the data array.
+     */
+    public int getDataLength();
+
+}
diff --git a/main/core/src/org/ejml/data/ComplexPolar64F.java b/main/core/src/org/ejml/data/ComplexPolar64F.java
new file mode 100644
index 0000000..0023cec
--- /dev/null
+++ b/main/core/src/org/ejml/data/ComplexPolar64F.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import org.ejml.ops.ComplexMath64F;
+
+/**
+ * <p>
+ * {@link Complex64F} number in polar notation.<br>
+ * z = r*(cos(θ) + i*sin(θ))<br>
+ * where r and θ are polar coordinate parameters
+ * </p>
+ * @author Peter Abeles
+ */
+public class ComplexPolar64F {
+    public double r;
+	public double theta;
+
+	public ComplexPolar64F(double r, double theta) {
+		this.r = r;
+		this.theta = theta;
+	}
+
+	public ComplexPolar64F( Complex64F n ) {
+		ComplexMath64F.convert(n, this);
+	}
+
+	public ComplexPolar64F() {
+	}
+
+	public Complex64F toStandard() {
+		Complex64F ret = new Complex64F();
+		ComplexMath64F.convert(this, ret);
+		return ret;
+	}
+
+    public double getR() {
+        return r;
+    }
+
+    public void setR(double r) {
+        this.r = r;
+    }
+
+    public double getTheta() {
+        return theta;
+    }
+
+    public void setTheta(double theta) {
+        this.theta = theta;
+    }
+
+    public String toString() {
+		return "( r = "+r+" theta = "+theta+" )";
+	}
+}
diff --git a/main/core/src/org/ejml/data/D1Matrix32F.java b/main/core/src/org/ejml/data/D1Matrix32F.java
new file mode 100644
index 0000000..8bbb60b
--- /dev/null
+++ b/main/core/src/org/ejml/data/D1Matrix32F.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+/**
+ * A generic abstract class for matrices whose data is stored in a single 1D array of floats.  The
+ * format of the elements in this array is not specified.  For example row major, column major,
+ * and block row major are all common formats.
+ *
+ * @author Peter Abeles
+ */
+public abstract class D1Matrix32F implements ReshapeMatrix, RealMatrix32F {
+    /**
+     * Where the raw data for the matrix is stored.  The format is type dependent.
+     */
+    public float[] data;
+
+    /**
+     * Number of rows in the matrix.
+     */
+    public int numRows;
+    /**
+     * Number of columns in the matrix.
+     */
+    public int numCols;
+
+    /**
+     * Used to get a reference to the internal data.
+     *
+     * @return Reference to the matrix's data.
+     */
+    public float[] getData() {
+        return data;
+    }
+
+	/**
+	 * Changes the internal array reference.
+	 */
+	public void setData( float[] data ) {
+		this.data = data;
+	}
+
+    /**
+     * Returns the internal array index for the specified row and column.
+     *
+     * @param row Row index.
+     * @param col Column index.
+     * @return Internal array index.
+     */
+    public abstract int getIndex( int row, int col );
+
+    /**
+     * Sets the value of this matrix to be the same as the value of the provided matrix.  Both
+     * matrices must have the same shape:<br>
+     * <br>
+     * a<sub>ij</sub> = b<sub>ij</sub><br>
+     * <br>
+     *
+     * @param b The matrix that this matrix is to be set equal to.
+     */
+    public void set( D1Matrix32F b )
+    {
+        this.reshape(b.numRows,b.numCols);
+
+        int dataLength = b.getNumElements();
+
+        System.arraycopy(b.data, 0, this.data, 0, dataLength);
+    }
+
+    /**
+     * Returns the value of the matrix at the specified internal array index. The element at which row and column
+     * returned by this function depends upon the matrix's internal structure, e.g. row-major, column-major, or block.
+     *
+     * @param index Internal array index.
+     * @return Value at the specified index.
+     */
+    public float get( int index ) {
+        return data[index];
+    }
+
+    /**
+     * Sets the element's value at the specified index.  The element at which row and column
+     * modified by this function depends upon the matrix's internal structure, e.g. row-major, column-major, or block.
+     *
+     * @param index Index of element that is to be set.
+     * @param val The new value of the index.
+     */
+    public float set( int index , float val ) {
+        // See benchmarkFunctionReturn.  Pointless return does not degrade performance.  Tested on JDK 1.6.0_21
+        return data[index] = val;
+    }
+
+    /**
+     * <p>
+     * Adds the specified value to the internal data array at the specified index.<br>
+     * <br>
+     * Equivalent to: this.data[index] += val;
+     * </p>
+     *
+     * <p>
+     * Intended for use in highly optimized code.  The  row/column coordinate of the modified element is
+     * dependent upon the matrix's internal structure.
+     * </p>
+     *
+     * @param index The index which is being modified.
+     * @param val The value that is being added.
+     */
+    public float plus( int index , float val ) {
+        // See benchmarkFunctionReturn.  Pointless return does not degrade performance.  Tested on JDK 1.6.0_21
+        return data[index] += val;
+    }
+
+    /**
+     * <p>
+     * Subtracts the specified value to the internal data array at the specified index.<br>
+     * <br>
+     * Equivalent to: this.data[index] -= val;
+     * </p>
+     *
+     * <p>
+     * Intended for use in highly optimized code.  The  row/column coordinate of the modified element is
+     * dependent upon the matrix's internal structure.
+     * </p>
+     *
+     * @param index The index which is being modified.
+     * @param val The value that is being subtracted.
+     */
+    public float minus( int index , float val ) {
+        // See benchmarkFunctionReturn.  Pointless return does not degrade performance.  Tested on JDK 1.6.0_21
+        return data[index] -= val;
+    }
+
+    /**
+     * <p>
+     * Multiplies the specified value to the internal data array at the specified index.<br>
+     * <br>
+     * Equivalent to: this.data[index] *= val;
+     * </p>
+     *
+     * <p>
+     * Intended for use in highly optimized code.  The  row/column coordinate of the modified element is
+     * dependent upon the matrix's internal structure.
+     * </p>
+     *
+     * @param index The index which is being modified.
+     * @param val The value that is being multiplied.
+     */
+    public float times( int index , float val ) {
+        // See benchmarkFunctionReturn.  Pointless return does not degrade performance.  Tested on JDK 1.6.0_21
+        return data[index] *= val;
+    }
+
+    /**
+     * <p>
+     * Divides the specified value to the internal data array at the specified index.<br>
+     * <br>
+     * Equivalent to: this.data[index] /= val;
+     * </p>
+     *
+     * <p>
+     * Intended for use in highly optimized code.  The  row/column coordinate of the modified element is
+     * dependent upon the matrix's internal structure.
+     * </p>
+     *
+     * @param index The index which is being modified.
+     * @param val The value that is being divided.
+     */
+    public float div( int index , float val ) {
+        // See benchmarkFunctionReturn.  Pointless return does not degrade performance.  Tested on JDK 1.6.0_21
+        return data[index] /= val;
+    }
+
+    /**
+     * <p>
+     * Changes the number of rows and columns in the matrix, allowing its size to grow or shrink.
+     * If the saveValues flag is set to true, then the previous values will be maintained, but
+     * reassigned to new elements in a row-major ordering.  If saveValues is false values will only
+     * be maintained when the requested size is less than or equal to the internal array size.
+     * The primary use for this function is to encourage data reuse and avoid unnecessarily declaring
+     * and initialization of new memory.
+     * </p>
+     *
+     * <p>
+     * Examples:<br>
+     * [ 1 2 ; 3 4 ] -> reshape( 2 , 3 , true ) = [ 1 2 3 ; 4 0 0 ]<br>
+     * [ 1 2 ; 3 4 ] -> reshape( 1 , 2 , true ) = [ 1 2 ]<br>
+     * [ 1 2 ; 3 4 ] -> reshape( 1 , 2 , false ) = [ 1 2 ]<br>
+     * [ 1 2 ; 3 4 ] -> reshape( 2 , 3 , false ) = [ 0 0 0 ; 0 0 0 ]
+     * </p>
+     *
+     * @param numRows The new number of rows in the matrix.
+     * @param numCols The new number of columns in the matrix.
+     * @param saveValues If true then the value of each element will be save using a row-major reordering.  Typically this should be false.
+     */
+    public abstract void reshape(int numRows, int numCols, boolean saveValues);
+
+    /**
+     * Equivalent to invoking reshape(numRows,numCols,false);
+     *
+     * @param numRows The new number of rows in the matrix.
+     * @param numCols The new number of columns in the matrix.
+     */
+    @Override
+    public void reshape( int numRows , int numCols ) {
+        reshape(numRows,numCols,false);
+    }
+
+    /**
+     * Creates a new iterator for traversing through a submatrix inside this matrix.  It can be traversed
+     * by row or by column.  Range of elements is inclusive, e.g. minRow = 0 and maxRow = 1 will include rows
+     * 0 and 1.  The iteration starts at (minRow,minCol) and ends at (maxRow,maxCol)
+     *
+     * @param rowMajor true means it will traverse through the submatrix by row first, false by columns.
+     * @param minRow first row it will start at.
+     * @param minCol first column it will start at.
+     * @param maxRow last row it will stop at.
+     * @param maxCol last column it will stop at.
+     * @return A new MatrixIterator
+     */
+    public MatrixIterator32F iterator(boolean rowMajor, int minRow, int minCol, int maxRow, int maxCol)
+    {
+        return new MatrixIterator32F(this,rowMajor, minRow, minCol, maxRow, maxCol);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int getNumRows() {
+        return numRows;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int getNumCols() {
+        return numCols;
+    }
+
+    /**
+     * Sets the number of rows.
+     *
+     * @param numRows Number of rows
+     */
+    public void setNumRows(int numRows) {
+        this.numRows = numRows;
+    }
+
+    /**
+     * Sets the number of columns.
+     *
+     * @param numCols Number of columns
+     */
+    public void setNumCols(int numCols) {
+        this.numCols = numCols;
+    }
+}
\ No newline at end of file
diff --git a/main/core/src/org/ejml/data/D1Matrix64F.java b/main/core/src/org/ejml/data/D1Matrix64F.java
new file mode 100644
index 0000000..d80e982
--- /dev/null
+++ b/main/core/src/org/ejml/data/D1Matrix64F.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+/**
+ * A generic abstract class for matrices whose data is stored in a single 1D array of doubles.  The
+ * format of the elements in this array is not specified.  For example row major, column major,
+ * and block row major are all common formats.
+ *
+ * @author Peter Abeles
+ */
+public abstract class D1Matrix64F implements ReshapeMatrix, RealMatrix64F {
+    /**
+     * Where the raw data for the matrix is stored.  The format is type dependent.
+     */
+    public double[] data;
+
+    /**
+     * Number of rows in the matrix.
+     */
+    public int numRows;
+    /**
+     * Number of columns in the matrix.
+     */
+    public int numCols;
+
+    /**
+     * Used to get a reference to the internal data.
+     *
+     * @return Reference to the matrix's data.
+     */
+    public double[] getData() {
+        return data;
+    }
+
+	/**
+	 * Changes the internal array reference.
+	 */
+	public void setData( double[] data ) {
+		this.data = data;
+	}
+
+    /**
+     * Returns the internal array index for the specified row and column.
+     *
+     * @param row Row index.
+     * @param col Column index.
+     * @return Internal array index.
+     */
+    public abstract int getIndex( int row, int col );
+
+    /**
+     * Sets the value of this matrix to be the same as the value of the provided matrix.  Both
+     * matrices must have the same shape:<br>
+     * <br>
+     * a<sub>ij</sub> = b<sub>ij</sub><br>
+     * <br>
+     *
+     * @param b The matrix that this matrix is to be set equal to.
+     */
+    public void set( D1Matrix64F b )
+    {
+        this.reshape(b.numRows,b.numCols);
+
+        int dataLength = b.getNumElements();
+
+        System.arraycopy(b.data, 0, this.data, 0, dataLength);
+    }
+
+    /**
+     * Returns the value of the matrix at the specified internal array index. The element at which row and column
+     * returned by this function depends upon the matrix's internal structure, e.g. row-major, column-major, or block.
+     *
+     * @param index Internal array index.
+     * @return Value at the specified index.
+     */
+    public double get( int index ) {
+        return data[index];
+    }
+
+    /**
+     * Sets the element's value at the specified index.  The element at which row and column
+     * modified by this function depends upon the matrix's internal structure, e.g. row-major, column-major, or block.
+     *
+     * @param index Index of element that is to be set.
+     * @param val The new value of the index.
+     */
+    public double set( int index , double val ) {
+        // See benchmarkFunctionReturn.  Pointless return does not degrade performance.  Tested on JDK 1.6.0_21
+        return data[index] = val;
+    }
+
+    /**
+     * <p>
+     * Adds the specified value to the internal data array at the specified index.<br>
+     * <br>
+     * Equivalent to: this.data[index] += val;
+     * </p>
+     *
+     * <p>
+     * Intended for use in highly optimized code.  The  row/column coordinate of the modified element is
+     * dependent upon the matrix's internal structure.
+     * </p>
+     *
+     * @param index The index which is being modified.
+     * @param val The value that is being added.
+     */
+    public double plus( int index , double val ) {
+        // See benchmarkFunctionReturn.  Pointless return does not degrade performance.  Tested on JDK 1.6.0_21
+        return data[index] += val;
+    }
+
+    /**
+     * <p>
+     * Subtracts the specified value to the internal data array at the specified index.<br>
+     * <br>
+     * Equivalent to: this.data[index] -= val;
+     * </p>
+     *
+     * <p>
+     * Intended for use in highly optimized code.  The  row/column coordinate of the modified element is
+     * dependent upon the matrix's internal structure.
+     * </p>
+     *
+     * @param index The index which is being modified.
+     * @param val The value that is being subtracted.
+     */
+    public double minus( int index , double val ) {
+        // See benchmarkFunctionReturn.  Pointless return does not degrade performance.  Tested on JDK 1.6.0_21
+        return data[index] -= val;
+    }
+
+    /**
+     * <p>
+     * Multiplies the specified value to the internal data array at the specified index.<br>
+     * <br>
+     * Equivalent to: this.data[index] *= val;
+     * </p>
+     *
+     * <p>
+     * Intended for use in highly optimized code.  The  row/column coordinate of the modified element is
+     * dependent upon the matrix's internal structure.
+     * </p>
+     *
+     * @param index The index which is being modified.
+     * @param val The value that is being multiplied.
+     */
+    public double times( int index , double val ) {
+        // See benchmarkFunctionReturn.  Pointless return does not degrade performance.  Tested on JDK 1.6.0_21
+        return data[index] *= val;
+    }
+
+    /**
+     * <p>
+     * Divides the specified value to the internal data array at the specified index.<br>
+     * <br>
+     * Equivalent to: this.data[index] /= val;
+     * </p>
+     *
+     * <p>
+     * Intended for use in highly optimized code.  The  row/column coordinate of the modified element is
+     * dependent upon the matrix's internal structure.
+     * </p>
+     *
+     * @param index The index which is being modified.
+     * @param val The value that is being divided.
+     */
+    public double div( int index , double val ) {
+        // See benchmarkFunctionReturn.  Pointless return does not degrade performance.  Tested on JDK 1.6.0_21
+        return data[index] /= val;
+    }
+
+    /**
+     * <p>
+     * Changes the number of rows and columns in the matrix, allowing its size to grow or shrink.
+     * If the saveValues flag is set to true, then the previous values will be maintained, but
+     * reassigned to new elements in a row-major ordering.  If saveValues is false values will only
+     * be maintained when the requested size is less than or equal to the internal array size.
+     * The primary use for this function is to encourage data reuse and avoid unnecessarily declaring
+     * and initialization of new memory.
+     * </p>
+     *
+     * <p>
+     * Examples:<br>
+     * [ 1 2 ; 3 4 ] -> reshape( 2 , 3 , true ) = [ 1 2 3 ; 4 0 0 ]<br>
+     * [ 1 2 ; 3 4 ] -> reshape( 1 , 2 , true ) = [ 1 2 ]<br>
+     * [ 1 2 ; 3 4 ] -> reshape( 1 , 2 , false ) = [ 1 2 ]<br>
+     * [ 1 2 ; 3 4 ] -> reshape( 2 , 3 , false ) = [ 0 0 0 ; 0 0 0 ]
+     * </p>
+     *
+     * @param numRows The new number of rows in the matrix.
+     * @param numCols The new number of columns in the matrix.
+     * @param saveValues If true then the value of each element will be save using a row-major reordering.  Typically this should be false.
+     */
+    public abstract void reshape(int numRows, int numCols, boolean saveValues);
+
+    /**
+     * Equivalent to invoking reshape(numRows,numCols,false);
+     *
+     * @param numRows The new number of rows in the matrix.
+     * @param numCols The new number of columns in the matrix.
+     */
+    @Override
+    public void reshape( int numRows , int numCols ) {
+        reshape(numRows,numCols,false);
+    }
+
+    /**
+     * Creates a new iterator for traversing through a submatrix inside this matrix.  It can be traversed
+     * by row or by column.  Range of elements is inclusive, e.g. minRow = 0 and maxRow = 1 will include rows
+     * 0 and 1.  The iteration starts at (minRow,minCol) and ends at (maxRow,maxCol)
+     *
+     * @param rowMajor true means it will traverse through the submatrix by row first, false by columns.
+     * @param minRow first row it will start at.
+     * @param minCol first column it will start at.
+     * @param maxRow last row it will stop at.
+     * @param maxCol last column it will stop at.
+     * @return A new MatrixIterator
+     */
+    public MatrixIterator64F iterator(boolean rowMajor, int minRow, int minCol, int maxRow, int maxCol)
+    {
+        return new MatrixIterator64F(this,rowMajor, minRow, minCol, maxRow, maxCol);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int getNumRows() {
+        return numRows;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int getNumCols() {
+        return numCols;
+    }
+
+    /**
+     * Sets the number of rows.
+     *
+     * @param numRows Number of rows
+     */
+    public void setNumRows(int numRows) {
+        this.numRows = numRows;
+    }
+
+    /**
+     * Sets the number of columns.
+     *
+     * @param numCols Number of columns
+     */
+    public void setNumCols(int numCols) {
+        this.numCols = numCols;
+    }
+}
\ No newline at end of file
diff --git a/main/core/src/org/ejml/data/D1Submatrix64F.java b/main/core/src/org/ejml/data/D1Submatrix64F.java
new file mode 100644
index 0000000..516a1e3
--- /dev/null
+++ b/main/core/src/org/ejml/data/D1Submatrix64F.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import org.ejml.ops.MatrixIO;
+
+/**
+ * <p>
+ * Describes a rectangular submatrix inside of a {@link D1Matrix64F}.
+ * </p>
+ *
+ * <p>
+ * Rows are row0 <= i < row1 and Columns are col0 <= j < col1
+ * </p>
+ * 
+ * @author Peter Abeles
+ */
+public class D1Submatrix64F {
+    public D1Matrix64F original;
+
+    // bounding rows and columns
+    public int row0,col0;
+    public int row1,col1;
+
+    public D1Submatrix64F() {
+    }
+
+    public D1Submatrix64F(D1Matrix64F original) {
+        set(original);
+    }
+
+    public D1Submatrix64F(D1Matrix64F original,
+                          int row0, int row1, int col0, int col1) {
+        set(original,row0,row1,col0,col1);
+    }
+
+    public void set(D1Matrix64F original,
+                    int row0, int row1, int col0, int col1) {
+        this.original = original;
+        this.row0 = row0;
+        this.col0 = col0;
+        this.row1 = row1;
+        this.col1 = col1;
+    }
+
+    public void set(D1Matrix64F original) {
+        this.original = original;
+        row1 = original.numRows;
+        col1 = original.numCols;
+    }
+
+    public int getRows() {
+        return row1 - row0;
+    }
+
+    public int getCols() {
+        return col1 - col0;
+    }
+
+    public double get(int row, int col ) {
+        return original.get(row+row0,col+col0);
+    }
+
+    public void set(int row, int col, double value) {
+        original.set(row+row0,col+col0,value);
+    }
+
+    public DenseMatrix64F extract() {
+        DenseMatrix64F ret = new DenseMatrix64F(row1-row0,col1-col0);
+
+        for( int i = 0; i < ret.numRows; i++ ) {
+            for( int j = 0; j < ret.numCols; j++ ) {
+                ret.set(i,j,get(i,j));
+            }
+        }
+
+        return ret;
+    }
+
+    public void print() {
+        MatrixIO.print(System.out,original,"%6.3f",row0,row1,col0,col1);
+    }
+}
diff --git a/main/core/src/org/ejml/data/DenseMatrix32F.java b/main/core/src/org/ejml/data/DenseMatrix32F.java
new file mode 100644
index 0000000..ae0fbcc
--- /dev/null
+++ b/main/core/src/org/ejml/data/DenseMatrix32F.java
@@ -0,0 +1,403 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import org.ejml.ops.MatrixIO;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.Arrays;
+
+
+/**
+ * <p>
+ * DenseMatrix64F is a dense matrix with elements that are 32-bit floats.  A matrix
+ * is the fundamental data structure in linear algebra.  Unlike a sparse matrix, there is no
+ * compression in a dense matrix and every element is stored in memory.  This allows for fast
+ * reads and writes to the matrix.
+ * </p>
+ *
+ * <p>
+ * The matrix is stored internally in a row-major 1D array format:<br>
+ * <br>
+ * data[ y*numCols + x ] = data[y][x]<br>
+ * <br>
+ * For example:<br>
+ * data =
+ * </p>
+ * <pre>
+ * a[0]  a[1]   a[2]   a[3]
+ * a[4]  a[5]   a[6]   a[7]
+ * a[8]  a[9]   a[10]  a[11]
+ * a[12] a[13]  a[14]  a[15]
+ * </pre>
+ *
+ * @author Peter Abeles
+ */
+public class DenseMatrix32F extends D1Matrix32F {
+
+    /**
+     * <p>
+     * Creates a new matrix which has the same value as the matrix encoded in the
+     * provided array.  The input matrix's format can either be row-major or
+     * column-major.
+     * </p>
+     *
+     * <p>
+     * Note that 'data' is a variable argument type, so either 1D arrays or a set of numbers can be
+     * passed in:<br>
+     * DenseMatrix a = new DenseMatrix(2,2,true,new float[]{1,2,3,4});<br>
+     * DenseMatrix b = new DenseMatrix(2,2,true,1,2,3,4);<br>
+     * <br>
+     * Both are equivalent.
+     * </p>
+     *
+     * @param numRows The number of rows.
+     * @param numCols The number of columns.
+     * @param rowMajor If the array is encoded in a row-major or a column-major format.
+     * @param data The formatted 1D array. Not modified.
+     */
+    public DenseMatrix32F(int numRows, int numCols, boolean rowMajor, float... data) {
+        final int length = numRows * numCols;
+        this.data = new float[ length ];
+
+        this.numRows = numRows;
+        this.numCols = numCols;
+
+        set(numRows,numCols, rowMajor, data);
+    }
+
+    /**
+     * <p>
+     * Creates a matrix with the values and shape defined by the 2D array 'data'.
+     * It is assumed that 'data' has a row-major formatting:<br>
+     *  <br>
+     * data[ row ][ column ]
+     * </p>
+     * @param data 2D array representation of the matrix. Not modified.
+     */
+    public DenseMatrix32F(float data[][]) {
+        this.numRows = data.length;
+        this.numCols = data[0].length;
+
+        this.data = new float[ numRows*numCols ];
+
+        int pos = 0;
+        for( int i = 0; i < numRows; i++ ) {
+            float []row = data[i];
+
+            if( row.length != numCols ) {
+                throw new IllegalArgumentException("All rows must have the same length");
+            }
+
+            System.arraycopy(row,0,this.data,pos,numCols);
+
+            pos += numCols;
+        }
+    }
+
+    /**
+     * Creates a new Matrix with the specified shape whose elements initially
+     * have the value of zero.
+     *
+     * @param numRows The number of rows in the matrix.
+     * @param numCols The number of columns in the matrix.
+     */
+    public DenseMatrix32F(int numRows, int numCols) {
+        data = new float[ numRows * numCols ];
+
+        this.numRows = numRows;
+        this.numCols = numCols;
+    }
+
+    /**
+     * Creates a new matrix which is equivalent to the provided matrix.  Note that
+     * the length of the data will be determined by the shape of the matrix.
+     *
+     * @param orig The matrix which is to be copied.  This is not modified or saved.
+     */
+    public DenseMatrix32F(DenseMatrix32F orig) {
+        this(orig.numRows,orig.numCols);
+        System.arraycopy(orig.data, 0, this.data, 0, orig.getNumElements());
+    }
+
+    /**
+     * This declares an array that can store a matrix up to the specified length.  This is use full
+     * when a matrix's size will be growing and it is desirable to avoid reallocating memory.
+     *
+     * @param length The size of the matrice's data array.
+     */
+    public DenseMatrix32F(int length) {
+        data = new float[ length ];
+    }
+
+    /**
+     * Default constructor in which nothing is configured.  THIS IS ONLY PUBLICLY ACCESSIBLE SO THAT THIS
+     * CLASS CAN BE A JAVA BEAN. DON'T USE IT UNLESS YOU REALLY KNOW WHAT YOU'RE DOING!
+     */
+    public DenseMatrix32F(){}
+
+    /**
+     * Creates a new DenseMatrix64F which contains the same information as the provided Matrix64F.
+     *
+     * @param mat Matrix whose values will be copied.  Not modified.
+     */
+    public DenseMatrix32F(RealMatrix32F mat) {
+        this(mat.getNumRows(),mat.getNumCols());
+        for( int i = 0; i < numRows; i++ ) {
+            for( int j = 0; j < numCols; j++ ) {
+                set(i,j, mat.get(i,j));
+            }
+        }
+    }
+
+    /**
+     * Creates a new DenseMatrix64F around the provided data.  The data must encode
+     * a row-major matrix.  Any modification to the returned matrix will modify the
+     * provided data.
+     *
+     * @param numRows Number of rows in the matrix.
+     * @param numCols Number of columns in the matrix.
+     * @param data Data that is being wrapped. Referenced Saved.
+     * @return A matrix which references the provided data internally.
+     */
+    public static DenseMatrix32F wrap( int numRows , int numCols , float []data ) {
+        DenseMatrix32F s = new DenseMatrix32F();
+        s.data = data;
+        s.numRows = numRows;
+        s.numCols = numCols;
+
+        return s;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    @Override
+    public void reshape(int numRows, int numCols, boolean saveValues) {
+        if( data.length < numRows * numCols ) {
+            float []d = new float[ numRows*numCols ];
+
+            if( saveValues ) {
+                System.arraycopy(data,0,d,0,getNumElements());
+            }
+
+            this.data = d;
+        }
+
+        this.numRows = numRows;
+        this.numCols = numCols;
+    }
+
+    /**
+     * <p>
+     * Assigns the element in the Matrix to the specified value.  Performs a bounds check to make sure
+     * the requested element is part of the matrix. <br>
+     * <br>
+     * a<sub>ij</sub> = value<br>
+     * </p>
+     *
+     * @param row The row of the element.
+     * @param col The column of the element.
+     * @param value The element's new value.
+     */
+    @Override
+    public void set( int row , int col , float value ) {
+        if( col < 0 || col >= numCols || row < 0 || row >= numRows ) {
+            throw new IllegalArgumentException("Specified element is out of bounds: ("+row+" , "+col+")");
+        }
+
+        data[ row * numCols + col ] = value;
+    }
+
+    @Override
+    public void unsafe_set( int row , int col , float value ) {
+        data[ row * numCols + col ] = value;
+    }
+
+    /**
+     * <p>
+     * Adds 'value' to the specified element in the matrix.<br>
+     * <br>
+     * a<sub>ij</sub> = a<sub>ij</sub> + value<br>
+     * </p>
+     *
+     * @param row The row of the element.
+     * @param col The column of the element.
+     * @param value The value that is added to the element
+     */
+    // todo move to commonops
+    public void add( int row , int col , float value ) {
+        if( col < 0 || col >= numCols || row < 0 || row >= numRows ) {
+            throw new IllegalArgumentException("Specified element is out of bounds");
+        }
+
+        data[ row * numCols + col ] += value;
+    }
+
+    /**
+     * Returns the value of the specified matrix element.  Performs a bounds check to make sure
+     * the requested element is part of the matrix.
+     *
+     * @param row The row of the element.
+     * @param col The column of the element.
+     * @return The value of the element.
+     */
+    @Override
+    public float get( int row , int col ) {
+        if( col < 0 || col >= numCols || row < 0 || row >= numRows ) {
+            throw new IllegalArgumentException("Specified element is out of bounds: "+row+" "+col);
+        }
+
+        return data[ row * numCols + col ];
+    }
+
+    @Override
+    public float unsafe_get( int row , int col ) {
+        return data[ row * numCols + col ];
+    }
+
+    @Override
+    public int getIndex( int row , int col ) {
+        return row * numCols + col;
+    }
+
+    /**
+     * Determins if the specified element is inside the bounds of the Matrix.
+     *
+     * @param row The element's row.
+     * @param col The elements' column.
+     * @return True if it is inside the matrices bound, false otherwise.
+     */
+    public boolean isInBounds( int row  , int col ) {
+        return( col >= 0 && col < numCols && row >= 0 && row < numRows );
+    }
+
+    /**
+     * Returns the number of elements in this matrix, which is equal to
+     * the number of rows times the number of columns.
+     *
+     * @return The number of elements in the matrix.
+     */
+    @Override
+    public int getNumElements() {
+        return numRows*numCols;
+    }
+
+    /**
+     * Sets this matrix equal to the matrix encoded in the array.
+     *
+     * @param numRows The number of rows.
+     * @param numCols The number of columns.
+     * @param rowMajor If the array is encoded in a row-major or a column-major format.
+     * @param data The formatted 1D array. Not modified.
+     */
+    public void set(int numRows, int numCols, boolean rowMajor, float ...data)
+    {
+        reshape(numRows,numCols);
+        int length = numRows*numCols;
+
+        if( length > this.data.length )
+            throw new IllegalArgumentException("The length of this matrix's data array is too small.");
+
+        if( rowMajor ) {
+            System.arraycopy(data,0,this.data,0,length);
+        } else {
+            int index = 0;
+            for( int i = 0; i < numRows; i++ ) {
+                for( int j = 0; j < numCols; j++ ) {
+                    this.data[index++] = data[j*numRows+i];
+                }
+            }
+        }
+    }
+
+    /**
+     * Sets all elements equal to zero.
+     */
+    public void zero() {
+        Arrays.fill(data, 0, getNumElements(), 0.0f);
+    }
+
+    /**
+     * Creates and returns a matrix which is idential to this one.
+     *
+     * @return A new identical matrix.
+     */
+    @SuppressWarnings({"unchecked"})
+    public DenseMatrix32F copy() {
+        return new DenseMatrix32F(this);
+    }
+
+    @Override
+    public void set(Matrix original) {
+        RealMatrix32F m = (RealMatrix32F)original;
+
+        reshape(original.getNumRows(),original.getNumCols());
+
+        if( original instanceof DenseMatrix32F) {
+            // do a faster copy if its of type DenseMatrix64F
+            System.arraycopy(((DenseMatrix32F)m).data,0,data,0,numRows*numCols);
+        } else {
+            int index = 0;
+            for (int i = 0; i < numRows; i++) {
+                for (int j = 0; j < numCols; j++) {
+                    data[index++] = m.get(i, j);
+                }
+            }
+        }
+    }
+
+    /**
+     * Prints the value of this matrix to the screen.  For more options see
+     * {@link org.ejml.UtilEjml}
+     *
+     */
+    @Override
+    public void print() {
+        MatrixIO.print(System.out,this);
+    }
+
+    /**
+     * <p>
+     * Prints the value of this matrix to the screen using the same format as {@link java.io.PrintStream#printf).
+     * </p>
+     *
+     * @param format The format which each element is printed uses.
+     */
+    public void print( String format ) {
+        MatrixIO.print(System.out,this,format);
+    }
+
+    /**
+     * <p>
+     * Converts the array into a string format for display purposes.
+     * The conversion is done using {@link org.ejml.ops.MatrixIO#print(java.io.PrintStream, org.ejml.data.RealMatrix64F)}.
+     * </p>
+     *
+     * @return String representation of the matrix.
+     */
+    @Override
+    public String toString() {
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+        MatrixIO.print(new PrintStream(stream),this);
+
+        return stream.toString();
+    }
+}
diff --git a/main/core/src/org/ejml/data/DenseMatrix64F.java b/main/core/src/org/ejml/data/DenseMatrix64F.java
new file mode 100644
index 0000000..233451a
--- /dev/null
+++ b/main/core/src/org/ejml/data/DenseMatrix64F.java
@@ -0,0 +1,403 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import org.ejml.UtilEjml;
+import org.ejml.ops.MatrixIO;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.Arrays;
+
+
+/**
+ * <p>
+ * DenseMatrix64F is a dense matrix with real elements that are 64-bit floats.  A matrix
+ * is the fundamental data structure in linear algebra.  Unlike a sparse matrix, there is no
+ * compression in a dense matrix and every element is stored in memory.  This allows for fast
+ * reads and writes to the matrix.
+ * </p>
+ *
+ * <p>
+ * The matrix is stored internally in a row-major 1D array format:<br>
+ * <br>
+ * data[ y*numCols + x ] = data[y][x]<br>
+ * <br>
+ * For example:<br>
+ * data =
+ * </p>
+ * <pre>
+ * a[0]  a[1]   a[2]   a[3]
+ * a[4]  a[5]   a[6]   a[7]
+ * a[8]  a[9]   a[10]  a[11]
+ * a[12] a[13]  a[14]  a[15]
+ * </pre>
+ * @author Peter Abeles
+ */
+public class DenseMatrix64F extends RowD1Matrix64F {
+
+    /**
+     * <p>
+     * Creates a new matrix which has the same value as the matrix encoded in the
+     * provided array.  The input matrix's format can either be row-major or
+     * column-major.
+     * </p>
+     *
+     * <p>
+     * Note that 'data' is a variable argument type, so either 1D arrays or a set of numbers can be
+     * passed in:<br>
+     * DenseMatrix a = new DenseMatrix(2,2,true,new double[]{1,2,3,4});<br>
+     * DenseMatrix b = new DenseMatrix(2,2,true,1,2,3,4);<br>
+     * <br>
+     * Both are equivalent.
+     * </p>
+     *
+     * @param numRows The number of rows.
+     * @param numCols The number of columns.
+     * @param rowMajor If the array is encoded in a row-major or a column-major format.
+     * @param data The formatted 1D array. Not modified.
+     */
+    public DenseMatrix64F(int numRows, int numCols, boolean rowMajor, double... data) {
+        final int length = numRows * numCols;
+        this.data = new double[ length ];
+
+        this.numRows = numRows;
+        this.numCols = numCols;
+
+        set(numRows,numCols, rowMajor, data);
+    }
+
+    /**
+     * <p>
+     * Creates a matrix with the values and shape defined by the 2D array 'data'.
+     * It is assumed that 'data' has a row-major formatting:<br>
+     *  <br>
+     * data[ row ][ column ]
+     * </p>
+     * @param data 2D array representation of the matrix. Not modified.
+     */
+    public DenseMatrix64F( double data[][] ) {
+        this.numRows = data.length;
+        this.numCols = data[0].length;
+
+        this.data = new double[ numRows*numCols ];
+
+        int pos = 0;
+        for( int i = 0; i < numRows; i++ ) {
+            double []row = data[i];
+
+            if( row.length != numCols ) {
+                throw new IllegalArgumentException("All rows must have the same length");
+            }
+
+            System.arraycopy(row,0,this.data,pos,numCols);
+
+            pos += numCols;
+        }
+    }
+
+    /**
+     * Creates a new Matrix with the specified shape whose elements initially
+     * have the value of zero.
+     *
+     * @param numRows The number of rows in the matrix.
+     * @param numCols The number of columns in the matrix.
+     */
+    public DenseMatrix64F( int numRows  , int numCols ) {
+        data = new double[ numRows * numCols ];
+
+        this.numRows = numRows;
+        this.numCols = numCols;
+    }
+
+    /**
+     * Creates a new matrix which is equivalent to the provided matrix.  Note that
+     * the length of the data will be determined by the shape of the matrix.
+     *
+     * @param orig The matrix which is to be copied.  This is not modified or saved.
+     */
+    public DenseMatrix64F( DenseMatrix64F orig ) {
+        this(orig.numRows,orig.numCols);
+        System.arraycopy(orig.data, 0, this.data, 0, orig.getNumElements());
+    }
+
+    /**
+     * This declares an array that can store a matrix up to the specified length.  This is use full
+     * when a matrix's size will be growing and it is desirable to avoid reallocating memory.
+     *
+     * @param length The size of the matrice's data array.
+     */
+    public DenseMatrix64F( int length ) {
+        data = new double[ length ];
+    }
+
+    /**
+     * Default constructor in which nothing is configured.  THIS IS ONLY PUBLICLY ACCESSIBLE SO THAT THIS
+     * CLASS CAN BE A JAVA BEAN. DON'T USE IT UNLESS YOU REALLY KNOW WHAT YOU'RE DOING!
+     */
+    public DenseMatrix64F(){}
+
+    /**
+     * Creates a new DenseMatrix64F which contains the same information as the provided Matrix64F.
+     *
+     * @param mat Matrix whose values will be copied.  Not modified.
+     */
+    public DenseMatrix64F(RealMatrix64F mat) {
+        this(mat.getNumRows(),mat.getNumCols());
+        for( int i = 0; i < numRows; i++ ) {
+            for( int j = 0; j < numCols; j++ ) {
+                set(i,j, mat.get(i,j));
+            }
+        }
+    }
+
+    /**
+     * Creates a new DenseMatrix64F around the provided data.  The data must encode
+     * a row-major matrix.  Any modification to the returned matrix will modify the
+     * provided data.
+     *
+     * @param numRows Number of rows in the matrix.
+     * @param numCols Number of columns in the matrix.
+     * @param data Data that is being wrapped. Referenced Saved.
+     * @return A matrix which references the provided data internally.
+     */
+    public static DenseMatrix64F wrap( int numRows , int numCols , double []data ) {
+        DenseMatrix64F s = new DenseMatrix64F();
+        s.data = data;
+        s.numRows = numRows;
+        s.numCols = numCols;
+
+        return s;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    @Override
+    public void reshape(int numRows, int numCols, boolean saveValues) {
+        if( data.length < numRows * numCols ) {
+            double []d = new double[ numRows*numCols ];
+
+            if( saveValues ) {
+                System.arraycopy(data,0,d,0,getNumElements());
+            }
+
+            this.data = d;
+        }
+
+        this.numRows = numRows;
+        this.numCols = numCols;
+    }
+
+    /**
+     * <p>
+     * Assigns the element in the Matrix to the specified value.  Performs a bounds check to make sure
+     * the requested element is part of the matrix. <br>
+     * <br>
+     * a<sub>ij</sub> = value<br>
+     * </p>
+     *
+     * @param row The row of the element.
+     * @param col The column of the element.
+     * @param value The element's new value.
+     */
+    @Override
+    public void set( int row , int col , double value ) {
+        if( col < 0 || col >= numCols || row < 0 || row >= numRows ) {
+            throw new IllegalArgumentException("Specified element is out of bounds: ("+row+" , "+col+")");
+        }
+
+        data[ row * numCols + col ] = value;
+    }
+
+    @Override
+    public void unsafe_set( int row , int col , double value ) {
+        data[ row * numCols + col ] = value;
+    }
+
+    /**
+     * <p>
+     * Adds 'value' to the specified element in the matrix.<br>
+     * <br>
+     * a<sub>ij</sub> = a<sub>ij</sub> + value<br>
+     * </p>
+     *
+     * @param row The row of the element.
+     * @param col The column of the element.
+     * @param value The value that is added to the element
+     */
+    // todo move to commonops
+    public void add( int row , int col , double value ) {
+        if( col < 0 || col >= numCols || row < 0 || row >= numRows ) {
+            throw new IllegalArgumentException("Specified element is out of bounds");
+        }
+
+        data[ row * numCols + col ] += value;
+    }
+
+    /**
+     * Returns the value of the specified matrix element.  Performs a bounds check to make sure
+     * the requested element is part of the matrix.
+     *
+     * @param row The row of the element.
+     * @param col The column of the element.
+     * @return The value of the element.
+     */
+    @Override
+    public double get( int row , int col ) {
+        if( col < 0 || col >= numCols || row < 0 || row >= numRows ) {
+            throw new IllegalArgumentException("Specified element is out of bounds: "+row+" "+col);
+        }
+
+        return data[ row * numCols + col ];
+    }
+
+    @Override
+    public double unsafe_get( int row , int col ) {
+        return data[ row * numCols + col ];
+    }
+
+    @Override
+    public int getIndex( int row , int col ) {
+        return row * numCols + col;
+    }
+
+    /**
+     * Determins if the specified element is inside the bounds of the Matrix.
+     *
+     * @param row The element's row.
+     * @param col The elements' column.
+     * @return True if it is inside the matrices bound, false otherwise.
+     */
+    public boolean isInBounds( int row  , int col ) {
+        return( col >= 0 && col < numCols && row >= 0 && row < numRows );
+    }
+
+    /**
+     * Returns the number of elements in this matrix, which is equal to
+     * the number of rows times the number of columns.
+     *
+     * @return The number of elements in the matrix.
+     */
+    @Override
+    public int getNumElements() {
+        return numRows*numCols;
+    }
+
+    /**
+     * Sets this matrix equal to the matrix encoded in the array.
+     *
+     * @param numRows The number of rows.
+     * @param numCols The number of columns.
+     * @param rowMajor If the array is encoded in a row-major or a column-major format.
+     * @param data The formatted 1D array. Not modified.
+     */
+    public void set(int numRows, int numCols, boolean rowMajor, double ...data)
+    {
+        reshape(numRows,numCols);
+        int length = numRows*numCols;
+
+        if( length > this.data.length )
+            throw new IllegalArgumentException("The length of this matrix's data array is too small.");
+
+        if( rowMajor ) {
+            System.arraycopy(data,0,this.data,0,length);
+        } else {
+            int index = 0;
+            for( int i = 0; i < numRows; i++ ) {
+                for( int j = 0; j < numCols; j++ ) {
+                    this.data[index++] = data[j*numRows+i];
+                }
+            }
+        }
+    }
+
+    /**
+     * Sets all elements equal to zero.
+     */
+    public void zero() {
+        Arrays.fill(data, 0, getNumElements(), 0.0);
+    }
+
+    /**
+     * Creates and returns a matrix which is idential to this one.
+     *
+     * @return A new identical matrix.
+     */
+    @SuppressWarnings({"unchecked"})
+    public DenseMatrix64F copy() {
+        return new DenseMatrix64F(this);
+    }
+
+    @Override
+    public void set(Matrix original) {
+        RealMatrix64F m = (RealMatrix64F)original;
+
+        reshape(original.getNumRows(),original.getNumCols());
+
+        if( original instanceof DenseMatrix64F ) {
+            // do a faster copy if its of type DenseMatrix64F
+            System.arraycopy(((DenseMatrix64F)m).data,0,data,0,numRows*numCols);
+        } else {
+            int index = 0;
+            for (int i = 0; i < numRows; i++) {
+                for (int j = 0; j < numCols; j++) {
+                    data[index++] = m.get(i, j);
+                }
+            }
+        }
+    }
+
+    /**
+     * Prints the value of this matrix to the screen.  For more options see
+     * {@link UtilEjml}
+     *
+     */
+    @Override
+    public void print() {
+        MatrixIO.print(System.out,this);
+    }
+
+    /**
+     * <p>
+     * Prints the value of this matrix to the screen using the same format as {@link java.io.PrintStream#printf).
+     * </p>
+     *
+     * @param format The format which each element is printed uses.
+     */
+    public void print( String format ) {
+        MatrixIO.print(System.out,this,format);
+    }
+
+    /**
+     * <p>
+     * Converts the array into a string format for display purposes.
+     * The conversion is done using {@link MatrixIO#print(java.io.PrintStream, RealMatrix64F)}.
+     * </p>
+     *
+     * @return String representation of the matrix.
+     */
+    @Override
+    public String toString() {
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+        MatrixIO.print(new PrintStream(stream),this);
+
+        return stream.toString();
+    }
+}
diff --git a/main/core/src/org/ejml/data/Eigenpair64F.java b/main/core/src/org/ejml/data/Eigenpair64F.java
new file mode 100644
index 0000000..142cb52
--- /dev/null
+++ b/main/core/src/org/ejml/data/Eigenpair64F.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+
+/**
+ * An eigenpair is a set composed of an eigenvalue and an eigenvector.  In this library since only real
+ * matrices are supported, all eigenpairs are real valued.
+ *
+ * @author Peter Abeles
+ */
+public class Eigenpair64F {
+    public double value;
+    public DenseMatrix64F vector;
+
+    public Eigenpair64F(double value, DenseMatrix64F vector) {
+        this.value = value;
+        this.vector = vector;
+    }
+}
diff --git a/main/core/src/org/ejml/data/FixedMatrix2_64F.java b/main/core/src/org/ejml/data/FixedMatrix2_64F.java
new file mode 100644
index 0000000..2fcdb82
--- /dev/null
+++ b/main/core/src/org/ejml/data/FixedMatrix2_64F.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+package org.ejml.data;
+
+import org.ejml.ops.MatrixIO;
+
+/**
+ * Fixed sized vector with 2 elements.  Can represent a 2 x 1 or 1 x 2 matrix, context dependent.
+ *
+ * @author Peter Abeles
+ */
+public class FixedMatrix2_64F implements FixedMatrix64F {
+    public double a1,a2;
+
+    public FixedMatrix2_64F() {
+    }
+
+    public FixedMatrix2_64F(double a1,double a2)
+    {
+        this.a1 = a1;
+        this.a2 = a2;
+    }
+
+    public FixedMatrix2_64F(FixedMatrix2_64F o) {
+        this.a1 = o.a1;
+        this.a2 = o.a2;
+    }
+
+    @Override
+    public double get(int row, int col) {
+        return unsafe_get(row,col);
+    }
+
+    @Override
+    public double unsafe_get(int row, int col) {
+        if( row != 0 && col != 0 )
+            throw new IllegalArgumentException("Row or column must be zero since this is a vector");
+
+        int w = Math.max(row,col);
+
+        if( w == 0 ) {
+            return a1;
+        } else if( w == 1 ) {
+            return a2;
+        } else {
+            throw new IllegalArgumentException("Out of range.  "+w);
+        }
+    }
+
+    @Override
+    public void set(int row, int col, double val) {
+        unsafe_set(row,col,val);
+    }
+
+    @Override
+    public void unsafe_set(int row, int col, double val) {
+        if( row != 0 && col != 0 )
+            throw new IllegalArgumentException("Row or column must be zero since this is a vector");
+
+        int w = Math.max(row,col);
+
+        if( w == 0 ) {
+            a1 = val;
+        } else if( w == 1 ) {
+            a2 = val;
+        } else {
+            throw new IllegalArgumentException("Out of range.  "+w);
+        }
+    }
+
+    @Override
+    public void set(Matrix original) {
+        RealMatrix64F m = (RealMatrix64F)original;
+
+        if( m.getNumCols() == 1 && m.getNumRows() == 2 ) {
+            a1 = m.get(0,0);
+            a2 = m.get(1,0);
+        } else if( m.getNumRows() == 1 && m.getNumCols() == 2 ){
+            a1 = m.get(0,0);
+            a2 = m.get(0,1);
+        } else {
+            throw new IllegalArgumentException("Incompatible shape");
+        }
+    }
+
+    @Override
+    public int getNumRows() {
+        return 2;
+    }
+
+    @Override
+    public int getNumCols() {
+        return 1;
+    }
+
+    @Override
+    public int getNumElements() {
+        return 2;
+    }
+
+    @Override
+    public <T extends Matrix> T copy() {
+        return (T)new FixedMatrix2_64F(this);
+    }
+
+    @Override
+    public void print() {
+        MatrixIO.print(System.out, this);
+    }
+}
+
diff --git a/main/core/src/org/ejml/data/FixedMatrix2x2_64F.java b/main/core/src/org/ejml/data/FixedMatrix2x2_64F.java
new file mode 100644
index 0000000..92b73d7
--- /dev/null
+++ b/main/core/src/org/ejml/data/FixedMatrix2x2_64F.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+package org.ejml.data;
+
+import org.ejml.ops.MatrixIO;
+
+/**
+ * Fixed sized 2 by FixedMatrix2x2_64F matrix.  The matrix is stored as class variables for very fast read/write.  aXY is the
+ * value of row = X and column = Y.
+ *
+ * @author Peter Abeles
+ */
+public class FixedMatrix2x2_64F implements FixedMatrix64F {
+
+    public double a11,a12;
+    public double a21,a22;
+
+    public FixedMatrix2x2_64F() {
+    }
+
+    public FixedMatrix2x2_64F( double a11,double a12,
+                              double a21,double a22)
+    {
+        this.a11 = a11;
+        this.a12 = a12;
+        this.a21 = a21;
+        this.a22 = a22;
+    }
+
+    public FixedMatrix2x2_64F( FixedMatrix2x2_64F o ) {
+        this.a11 = o.a11;
+        this.a12 = o.a12;
+        this.a21 = o.a21;
+        this.a22 = o.a22;
+    }
+
+    @Override
+    public double get(int row, int col) {
+        return unsafe_get(row,col);
+    }
+
+    @Override
+    public double unsafe_get(int row, int col) {
+        if( row == 0 ) {
+            if( col == 0 ) {
+                return a11;
+            } else if( col == 1 ) {
+                return a12;
+            }
+        } else if( row == 1 ) {
+            if( col == 0 ) {
+                return a21;
+            } else if( col == 1 ) {
+                return a22;
+            }
+        }
+        throw new IllegalArgumentException("Row and/or column out of range. "+row+" "+col);
+    }
+
+    @Override
+    public void set(int row, int col, double val) {
+        unsafe_set(row,col,val);
+    }
+
+    @Override
+    public void unsafe_set(int row, int col, double val) {
+        if( row == 0 ) {
+            if( col == 0 ) {
+                a11 = val; return;
+            } else if( col == 1 ) {
+                a12 = val; return;
+            }
+        } else if( row == 1 ) {
+            if( col == 0 ) {
+                a21 = val; return;
+            } else if( col == 1 ) {
+                a22 = val; return;
+            }
+        }
+        throw new IllegalArgumentException("Row and/or column out of range. "+row+" "+col);
+    }
+
+    @Override
+    public void set(Matrix original) {
+        if( original.getNumCols() != 2 || original.getNumRows() != 2 )
+            throw new IllegalArgumentException("Rows and/or columns do not match");
+        RealMatrix64F m = (RealMatrix64F)original;
+        
+        a11 = m.get(0,0);
+        a12 = m.get(0,1);
+        a21 = m.get(1,0);
+        a22 = m.get(1,1);
+    }
+
+    @Override
+    public int getNumRows() {
+        return 2;
+    }
+
+    @Override
+    public int getNumCols() {
+        return 2;
+    }
+
+    @Override
+    public int getNumElements() {
+        return 4;
+    }
+
+    @Override
+    public <T extends Matrix> T copy() {
+        return (T)new FixedMatrix2x2_64F(this);
+    }
+
+    @Override
+    public void print() {
+        MatrixIO.print(System.out, this);
+    }
+}
+
diff --git a/main/core/src/org/ejml/data/FixedMatrix3_64F.java b/main/core/src/org/ejml/data/FixedMatrix3_64F.java
new file mode 100644
index 0000000..cf70d0c
--- /dev/null
+++ b/main/core/src/org/ejml/data/FixedMatrix3_64F.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+package org.ejml.data;
+
+import org.ejml.ops.MatrixIO;
+
+/**
+ * Fixed sized vector with 3 elements.  Can represent a 3 x 1 or 1 x 3 matrix, context dependent.
+ *
+ * @author Peter Abeles
+ */
+public class FixedMatrix3_64F implements FixedMatrix64F {
+    public double a1,a2,a3;
+
+    public FixedMatrix3_64F() {
+    }
+
+    public FixedMatrix3_64F(double a1,double a2,double a3)
+    {
+        this.a1 = a1;
+        this.a2 = a2;
+        this.a3 = a3;
+    }
+
+    public FixedMatrix3_64F(FixedMatrix3_64F o) {
+        this.a1 = o.a1;
+        this.a2 = o.a2;
+        this.a3 = o.a3;
+    }
+
+    @Override
+    public double get(int row, int col) {
+        return unsafe_get(row,col);
+    }
+
+    @Override
+    public double unsafe_get(int row, int col) {
+        if( row != 0 && col != 0 )
+            throw new IllegalArgumentException("Row or column must be zero since this is a vector");
+
+        int w = Math.max(row,col);
+
+        if( w == 0 ) {
+            return a1;
+        } else if( w == 1 ) {
+            return a2;
+        } else if( w == 2 ) {
+            return a3;
+        } else {
+            throw new IllegalArgumentException("Out of range.  "+w);
+        }
+    }
+
+    @Override
+    public void set(int row, int col, double val) {
+        unsafe_set(row,col,val);
+    }
+
+    @Override
+    public void unsafe_set(int row, int col, double val) {
+        if( row != 0 && col != 0 )
+            throw new IllegalArgumentException("Row or column must be zero since this is a vector");
+
+        int w = Math.max(row,col);
+
+        if( w == 0 ) {
+            a1 = val;
+        } else if( w == 1 ) {
+            a2 = val;
+        } else if( w == 2 ) {
+            a3 = val;
+        } else {
+            throw new IllegalArgumentException("Out of range.  "+w);
+        }
+    }
+
+    @Override
+    public void set(Matrix original) {
+        RealMatrix64F m = (RealMatrix64F)original;
+
+        if( m.getNumCols() == 1 && m.getNumRows() == 3 ) {
+            a1 = m.get(0,0);
+            a2 = m.get(1,0);
+            a3 = m.get(2,0);
+        } else if( m.getNumRows() == 1 && m.getNumCols() == 3 ){
+            a1 = m.get(0,0);
+            a2 = m.get(0,1);
+            a3 = m.get(0,2);
+        } else {
+            throw new IllegalArgumentException("Incompatible shape");
+        }
+    }
+
+    @Override
+    public int getNumRows() {
+        return 3;
+    }
+
+    @Override
+    public int getNumCols() {
+        return 1;
+    }
+
+    @Override
+    public int getNumElements() {
+        return 3;
+    }
+
+    @Override
+    public <T extends Matrix> T copy() {
+        return (T)new FixedMatrix3_64F(this);
+    }
+
+    @Override
+    public void print() {
+        MatrixIO.print(System.out, this);
+    }
+}
+
diff --git a/main/core/src/org/ejml/data/FixedMatrix3x3_64F.java b/main/core/src/org/ejml/data/FixedMatrix3x3_64F.java
new file mode 100644
index 0000000..c08cbee
--- /dev/null
+++ b/main/core/src/org/ejml/data/FixedMatrix3x3_64F.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+package org.ejml.data;
+
+import org.ejml.ops.MatrixIO;
+
+/**
+ * Fixed sized 3 by FixedMatrix3x3_64F matrix.  The matrix is stored as class variables for very fast read/write.  aXY is the
+ * value of row = X and column = Y.
+ *
+ * @author Peter Abeles
+ */
+public class FixedMatrix3x3_64F implements FixedMatrix64F {
+
+    public double a11,a12,a13;
+    public double a21,a22,a23;
+    public double a31,a32,a33;
+
+    public FixedMatrix3x3_64F() {
+    }
+
+    public FixedMatrix3x3_64F( double a11,double a12,double a13,
+                              double a21,double a22,double a23,
+                              double a31,double a32,double a33)
+    {
+        this.a11 = a11;
+        this.a12 = a12;
+        this.a13 = a13;
+        this.a21 = a21;
+        this.a22 = a22;
+        this.a23 = a23;
+        this.a31 = a31;
+        this.a32 = a32;
+        this.a33 = a33;
+    }
+
+    public FixedMatrix3x3_64F( FixedMatrix3x3_64F o ) {
+        this.a11 = o.a11;
+        this.a12 = o.a12;
+        this.a13 = o.a13;
+        this.a21 = o.a21;
+        this.a22 = o.a22;
+        this.a23 = o.a23;
+        this.a31 = o.a31;
+        this.a32 = o.a32;
+        this.a33 = o.a33;
+    }
+
+    @Override
+    public double get(int row, int col) {
+        return unsafe_get(row,col);
+    }
+
+    @Override
+    public double unsafe_get(int row, int col) {
+        if( row == 0 ) {
+            if( col == 0 ) {
+                return a11;
+            } else if( col == 1 ) {
+                return a12;
+            } else if( col == 2 ) {
+                return a13;
+            }
+        } else if( row == 1 ) {
+            if( col == 0 ) {
+                return a21;
+            } else if( col == 1 ) {
+                return a22;
+            } else if( col == 2 ) {
+                return a23;
+            }
+        } else if( row == 2 ) {
+            if( col == 0 ) {
+                return a31;
+            } else if( col == 1 ) {
+                return a32;
+            } else if( col == 2 ) {
+                return a33;
+            }
+        }
+        throw new IllegalArgumentException("Row and/or column out of range. "+row+" "+col);
+    }
+
+    @Override
+    public void set(int row, int col, double val) {
+        unsafe_set(row,col,val);
+    }
+
+    @Override
+    public void unsafe_set(int row, int col, double val) {
+        if( row == 0 ) {
+            if( col == 0 ) {
+                a11 = val; return;
+            } else if( col == 1 ) {
+                a12 = val; return;
+            } else if( col == 2 ) {
+                a13 = val; return;
+            }
+        } else if( row == 1 ) {
+            if( col == 0 ) {
+                a21 = val; return;
+            } else if( col == 1 ) {
+                a22 = val; return;
+            } else if( col == 2 ) {
+                a23 = val; return;
+            }
+        } else if( row == 2 ) {
+            if( col == 0 ) {
+                a31 = val; return;
+            } else if( col == 1 ) {
+                a32 = val; return;
+            } else if( col == 2 ) {
+                a33 = val; return;
+            }
+        }
+        throw new IllegalArgumentException("Row and/or column out of range. "+row+" "+col);
+    }
+
+    @Override
+    public void set(Matrix original) {
+        if( original.getNumCols() != 3 || original.getNumRows() != 3 )
+            throw new IllegalArgumentException("Rows and/or columns do not match");
+        RealMatrix64F m = (RealMatrix64F)original;
+        
+        a11 = m.get(0,0);
+        a12 = m.get(0,1);
+        a13 = m.get(0,2);
+        a21 = m.get(1,0);
+        a22 = m.get(1,1);
+        a23 = m.get(1,2);
+        a31 = m.get(2,0);
+        a32 = m.get(2,1);
+        a33 = m.get(2,2);
+    }
+
+    @Override
+    public int getNumRows() {
+        return 3;
+    }
+
+    @Override
+    public int getNumCols() {
+        return 3;
+    }
+
+    @Override
+    public int getNumElements() {
+        return 9;
+    }
+
+    @Override
+    public <T extends Matrix> T copy() {
+        return (T)new FixedMatrix3x3_64F(this);
+    }
+
+    @Override
+    public void print() {
+        MatrixIO.print(System.out, this);
+    }
+}
+
diff --git a/main/core/src/org/ejml/data/FixedMatrix4_64F.java b/main/core/src/org/ejml/data/FixedMatrix4_64F.java
new file mode 100644
index 0000000..023d851
--- /dev/null
+++ b/main/core/src/org/ejml/data/FixedMatrix4_64F.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+package org.ejml.data;
+
+import org.ejml.ops.MatrixIO;
+
+/**
+ * Fixed sized vector with 4 elements.  Can represent a 4 x 1 or 1 x 4 matrix, context dependent.
+ *
+ * @author Peter Abeles
+ */
+public class FixedMatrix4_64F implements FixedMatrix64F {
+    public double a1,a2,a3,a4;
+
+    public FixedMatrix4_64F() {
+    }
+
+    public FixedMatrix4_64F(double a1,double a2,double a3,double a4)
+    {
+        this.a1 = a1;
+        this.a2 = a2;
+        this.a3 = a3;
+        this.a4 = a4;
+    }
+
+    public FixedMatrix4_64F(FixedMatrix4_64F o) {
+        this.a1 = o.a1;
+        this.a2 = o.a2;
+        this.a3 = o.a3;
+        this.a4 = o.a4;
+    }
+
+    @Override
+    public double get(int row, int col) {
+        return unsafe_get(row,col);
+    }
+
+    @Override
+    public double unsafe_get(int row, int col) {
+        if( row != 0 && col != 0 )
+            throw new IllegalArgumentException("Row or column must be zero since this is a vector");
+
+        int w = Math.max(row,col);
+
+        if( w == 0 ) {
+            return a1;
+        } else if( w == 1 ) {
+            return a2;
+        } else if( w == 2 ) {
+            return a3;
+        } else if( w == 3 ) {
+            return a4;
+        } else {
+            throw new IllegalArgumentException("Out of range.  "+w);
+        }
+    }
+
+    @Override
+    public void set(int row, int col, double val) {
+        unsafe_set(row,col,val);
+    }
+
+    @Override
+    public void unsafe_set(int row, int col, double val) {
+        if( row != 0 && col != 0 )
+            throw new IllegalArgumentException("Row or column must be zero since this is a vector");
+
+        int w = Math.max(row,col);
+
+        if( w == 0 ) {
+            a1 = val;
+        } else if( w == 1 ) {
+            a2 = val;
+        } else if( w == 2 ) {
+            a3 = val;
+        } else if( w == 3 ) {
+            a4 = val;
+        } else {
+            throw new IllegalArgumentException("Out of range.  "+w);
+        }
+    }
+
+    @Override
+    public void set(Matrix original) {
+        RealMatrix64F m = (RealMatrix64F)original;
+
+        if( m.getNumCols() == 1 && m.getNumRows() == 4 ) {
+            a1 = m.get(0,0);
+            a2 = m.get(1,0);
+            a3 = m.get(2,0);
+            a4 = m.get(3,0);
+        } else if( m.getNumRows() == 1 && m.getNumCols() == 4 ){
+            a1 = m.get(0,0);
+            a2 = m.get(0,1);
+            a3 = m.get(0,2);
+            a4 = m.get(0,3);
+        } else {
+            throw new IllegalArgumentException("Incompatible shape");
+        }
+    }
+
+    @Override
+    public int getNumRows() {
+        return 4;
+    }
+
+    @Override
+    public int getNumCols() {
+        return 1;
+    }
+
+    @Override
+    public int getNumElements() {
+        return 4;
+    }
+
+    @Override
+    public <T extends Matrix> T copy() {
+        return (T)new FixedMatrix4_64F(this);
+    }
+
+    @Override
+    public void print() {
+        MatrixIO.print(System.out, this);
+    }
+}
+
diff --git a/main/core/src/org/ejml/data/FixedMatrix4x4_64F.java b/main/core/src/org/ejml/data/FixedMatrix4x4_64F.java
new file mode 100644
index 0000000..c3e6387
--- /dev/null
+++ b/main/core/src/org/ejml/data/FixedMatrix4x4_64F.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+package org.ejml.data;
+
+import org.ejml.ops.MatrixIO;
+
+/**
+ * Fixed sized 4 by FixedMatrix4x4_64F matrix.  The matrix is stored as class variables for very fast read/write.  aXY is the
+ * value of row = X and column = Y.
+ *
+ * @author Peter Abeles
+ */
+public class FixedMatrix4x4_64F implements FixedMatrix64F {
+
+    public double a11,a12,a13,a14;
+    public double a21,a22,a23,a24;
+    public double a31,a32,a33,a34;
+    public double a41,a42,a43,a44;
+
+    public FixedMatrix4x4_64F() {
+    }
+
+    public FixedMatrix4x4_64F( double a11,double a12,double a13,double a14,
+                              double a21,double a22,double a23,double a24,
+                              double a31,double a32,double a33,double a34,
+                              double a41,double a42,double a43,double a44)
+    {
+        this.a11 = a11;
+        this.a12 = a12;
+        this.a13 = a13;
+        this.a14 = a14;
+        this.a21 = a21;
+        this.a22 = a22;
+        this.a23 = a23;
+        this.a24 = a24;
+        this.a31 = a31;
+        this.a32 = a32;
+        this.a33 = a33;
+        this.a34 = a34;
+        this.a41 = a41;
+        this.a42 = a42;
+        this.a43 = a43;
+        this.a44 = a44;
+    }
+
+    public FixedMatrix4x4_64F( FixedMatrix4x4_64F o ) {
+        this.a11 = o.a11;
+        this.a12 = o.a12;
+        this.a13 = o.a13;
+        this.a14 = o.a14;
+        this.a21 = o.a21;
+        this.a22 = o.a22;
+        this.a23 = o.a23;
+        this.a24 = o.a24;
+        this.a31 = o.a31;
+        this.a32 = o.a32;
+        this.a33 = o.a33;
+        this.a34 = o.a34;
+        this.a41 = o.a41;
+        this.a42 = o.a42;
+        this.a43 = o.a43;
+        this.a44 = o.a44;
+    }
+
+    @Override
+    public double get(int row, int col) {
+        return unsafe_get(row,col);
+    }
+
+    @Override
+    public double unsafe_get(int row, int col) {
+        if( row == 0 ) {
+            if( col == 0 ) {
+                return a11;
+            } else if( col == 1 ) {
+                return a12;
+            } else if( col == 2 ) {
+                return a13;
+            } else if( col == 3 ) {
+                return a14;
+            }
+        } else if( row == 1 ) {
+            if( col == 0 ) {
+                return a21;
+            } else if( col == 1 ) {
+                return a22;
+            } else if( col == 2 ) {
+                return a23;
+            } else if( col == 3 ) {
+                return a24;
+            }
+        } else if( row == 2 ) {
+            if( col == 0 ) {
+                return a31;
+            } else if( col == 1 ) {
+                return a32;
+            } else if( col == 2 ) {
+                return a33;
+            } else if( col == 3 ) {
+                return a34;
+            }
+        } else if( row == 3 ) {
+            if( col == 0 ) {
+                return a41;
+            } else if( col == 1 ) {
+                return a42;
+            } else if( col == 2 ) {
+                return a43;
+            } else if( col == 3 ) {
+                return a44;
+            }
+        }
+        throw new IllegalArgumentException("Row and/or column out of range. "+row+" "+col);
+    }
+
+    @Override
+    public void set(int row, int col, double val) {
+        unsafe_set(row,col,val);
+    }
+
+    @Override
+    public void unsafe_set(int row, int col, double val) {
+        if( row == 0 ) {
+            if( col == 0 ) {
+                a11 = val; return;
+            } else if( col == 1 ) {
+                a12 = val; return;
+            } else if( col == 2 ) {
+                a13 = val; return;
+            } else if( col == 3 ) {
+                a14 = val; return;
+            }
+        } else if( row == 1 ) {
+            if( col == 0 ) {
+                a21 = val; return;
+            } else if( col == 1 ) {
+                a22 = val; return;
+            } else if( col == 2 ) {
+                a23 = val; return;
+            } else if( col == 3 ) {
+                a24 = val; return;
+            }
+        } else if( row == 2 ) {
+            if( col == 0 ) {
+                a31 = val; return;
+            } else if( col == 1 ) {
+                a32 = val; return;
+            } else if( col == 2 ) {
+                a33 = val; return;
+            } else if( col == 3 ) {
+                a34 = val; return;
+            }
+        } else if( row == 3 ) {
+            if( col == 0 ) {
+                a41 = val; return;
+            } else if( col == 1 ) {
+                a42 = val; return;
+            } else if( col == 2 ) {
+                a43 = val; return;
+            } else if( col == 3 ) {
+                a44 = val; return;
+            }
+        }
+        throw new IllegalArgumentException("Row and/or column out of range. "+row+" "+col);
+    }
+
+    @Override
+    public void set(Matrix original) {
+        if( original.getNumCols() != 4 || original.getNumRows() != 4 )
+            throw new IllegalArgumentException("Rows and/or columns do not match");
+        RealMatrix64F m = (RealMatrix64F)original;
+        
+        a11 = m.get(0,0);
+        a12 = m.get(0,1);
+        a13 = m.get(0,2);
+        a14 = m.get(0,3);
+        a21 = m.get(1,0);
+        a22 = m.get(1,1);
+        a23 = m.get(1,2);
+        a24 = m.get(1,3);
+        a31 = m.get(2,0);
+        a32 = m.get(2,1);
+        a33 = m.get(2,2);
+        a34 = m.get(2,3);
+        a41 = m.get(3,0);
+        a42 = m.get(3,1);
+        a43 = m.get(3,2);
+        a44 = m.get(3,3);
+    }
+
+    @Override
+    public int getNumRows() {
+        return 4;
+    }
+
+    @Override
+    public int getNumCols() {
+        return 4;
+    }
+
+    @Override
+    public int getNumElements() {
+        return 16;
+    }
+
+    @Override
+    public <T extends Matrix> T copy() {
+        return (T)new FixedMatrix4x4_64F(this);
+    }
+
+    @Override
+    public void print() {
+        MatrixIO.print(System.out, this);
+    }
+}
+
diff --git a/main/core/src/org/ejml/data/FixedMatrix5_64F.java b/main/core/src/org/ejml/data/FixedMatrix5_64F.java
new file mode 100644
index 0000000..5d596ed
--- /dev/null
+++ b/main/core/src/org/ejml/data/FixedMatrix5_64F.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+package org.ejml.data;
+
+import org.ejml.ops.MatrixIO;
+
+/**
+ * Fixed sized vector with 5 elements.  Can represent a 5 x 1 or 1 x 5 matrix, context dependent.
+ *
+ * @author Peter Abeles
+ */
+public class FixedMatrix5_64F implements FixedMatrix64F {
+    public double a1,a2,a3,a4,a5;
+
+    public FixedMatrix5_64F() {
+    }
+
+    public FixedMatrix5_64F(double a1,double a2,double a3,double a4,double a5)
+    {
+        this.a1 = a1;
+        this.a2 = a2;
+        this.a3 = a3;
+        this.a4 = a4;
+        this.a5 = a5;
+    }
+
+    public FixedMatrix5_64F(FixedMatrix5_64F o) {
+        this.a1 = o.a1;
+        this.a2 = o.a2;
+        this.a3 = o.a3;
+        this.a4 = o.a4;
+        this.a5 = o.a5;
+    }
+
+    @Override
+    public double get(int row, int col) {
+        return unsafe_get(row,col);
+    }
+
+    @Override
+    public double unsafe_get(int row, int col) {
+        if( row != 0 && col != 0 )
+            throw new IllegalArgumentException("Row or column must be zero since this is a vector");
+
+        int w = Math.max(row,col);
+
+        if( w == 0 ) {
+            return a1;
+        } else if( w == 1 ) {
+            return a2;
+        } else if( w == 2 ) {
+            return a3;
+        } else if( w == 3 ) {
+            return a4;
+        } else if( w == 4 ) {
+            return a5;
+        } else {
+            throw new IllegalArgumentException("Out of range.  "+w);
+        }
+    }
+
+    @Override
+    public void set(int row, int col, double val) {
+        unsafe_set(row,col,val);
+    }
+
+    @Override
+    public void unsafe_set(int row, int col, double val) {
+        if( row != 0 && col != 0 )
+            throw new IllegalArgumentException("Row or column must be zero since this is a vector");
+
+        int w = Math.max(row,col);
+
+        if( w == 0 ) {
+            a1 = val;
+        } else if( w == 1 ) {
+            a2 = val;
+        } else if( w == 2 ) {
+            a3 = val;
+        } else if( w == 3 ) {
+            a4 = val;
+        } else if( w == 4 ) {
+            a5 = val;
+        } else {
+            throw new IllegalArgumentException("Out of range.  "+w);
+        }
+    }
+
+    @Override
+    public void set(Matrix original) {
+        RealMatrix64F m = (RealMatrix64F)original;
+
+        if( m.getNumCols() == 1 && m.getNumRows() == 5 ) {
+            a1 = m.get(0,0);
+            a2 = m.get(1,0);
+            a3 = m.get(2,0);
+            a4 = m.get(3,0);
+            a5 = m.get(4,0);
+        } else if( m.getNumRows() == 1 && m.getNumCols() == 5 ){
+            a1 = m.get(0,0);
+            a2 = m.get(0,1);
+            a3 = m.get(0,2);
+            a4 = m.get(0,3);
+            a5 = m.get(0,4);
+        } else {
+            throw new IllegalArgumentException("Incompatible shape");
+        }
+    }
+
+    @Override
+    public int getNumRows() {
+        return 5;
+    }
+
+    @Override
+    public int getNumCols() {
+        return 1;
+    }
+
+    @Override
+    public int getNumElements() {
+        return 5;
+    }
+
+    @Override
+    public <T extends Matrix> T copy() {
+        return (T)new FixedMatrix5_64F(this);
+    }
+
+    @Override
+    public void print() {
+        MatrixIO.print(System.out, this);
+    }
+}
+
diff --git a/main/core/src/org/ejml/data/FixedMatrix5x5_64F.java b/main/core/src/org/ejml/data/FixedMatrix5x5_64F.java
new file mode 100644
index 0000000..c8f681e
--- /dev/null
+++ b/main/core/src/org/ejml/data/FixedMatrix5x5_64F.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+package org.ejml.data;
+
+import org.ejml.ops.MatrixIO;
+
+/**
+ * Fixed sized 5 by FixedMatrix5x5_64F matrix.  The matrix is stored as class variables for very fast read/write.  aXY is the
+ * value of row = X and column = Y.
+ *
+ * @author Peter Abeles
+ */
+public class FixedMatrix5x5_64F implements FixedMatrix64F {
+
+    public double a11,a12,a13,a14,a15;
+    public double a21,a22,a23,a24,a25;
+    public double a31,a32,a33,a34,a35;
+    public double a41,a42,a43,a44,a45;
+    public double a51,a52,a53,a54,a55;
+
+    public FixedMatrix5x5_64F() {
+    }
+
+    public FixedMatrix5x5_64F( double a11,double a12,double a13,double a14,double a15,
+                              double a21,double a22,double a23,double a24,double a25,
+                              double a31,double a32,double a33,double a34,double a35,
+                              double a41,double a42,double a43,double a44,double a45,
+                              double a51,double a52,double a53,double a54,double a55)
+    {
+        this.a11 = a11;
+        this.a12 = a12;
+        this.a13 = a13;
+        this.a14 = a14;
+        this.a15 = a15;
+        this.a21 = a21;
+        this.a22 = a22;
+        this.a23 = a23;
+        this.a24 = a24;
+        this.a25 = a25;
+        this.a31 = a31;
+        this.a32 = a32;
+        this.a33 = a33;
+        this.a34 = a34;
+        this.a35 = a35;
+        this.a41 = a41;
+        this.a42 = a42;
+        this.a43 = a43;
+        this.a44 = a44;
+        this.a45 = a45;
+        this.a51 = a51;
+        this.a52 = a52;
+        this.a53 = a53;
+        this.a54 = a54;
+        this.a55 = a55;
+    }
+
+    public FixedMatrix5x5_64F( FixedMatrix5x5_64F o ) {
+        this.a11 = o.a11;
+        this.a12 = o.a12;
+        this.a13 = o.a13;
+        this.a14 = o.a14;
+        this.a15 = o.a15;
+        this.a21 = o.a21;
+        this.a22 = o.a22;
+        this.a23 = o.a23;
+        this.a24 = o.a24;
+        this.a25 = o.a25;
+        this.a31 = o.a31;
+        this.a32 = o.a32;
+        this.a33 = o.a33;
+        this.a34 = o.a34;
+        this.a35 = o.a35;
+        this.a41 = o.a41;
+        this.a42 = o.a42;
+        this.a43 = o.a43;
+        this.a44 = o.a44;
+        this.a45 = o.a45;
+        this.a51 = o.a51;
+        this.a52 = o.a52;
+        this.a53 = o.a53;
+        this.a54 = o.a54;
+        this.a55 = o.a55;
+    }
+
+    @Override
+    public double get(int row, int col) {
+        return unsafe_get(row,col);
+    }
+
+    @Override
+    public double unsafe_get(int row, int col) {
+        if( row == 0 ) {
+            if( col == 0 ) {
+                return a11;
+            } else if( col == 1 ) {
+                return a12;
+            } else if( col == 2 ) {
+                return a13;
+            } else if( col == 3 ) {
+                return a14;
+            } else if( col == 4 ) {
+                return a15;
+            }
+        } else if( row == 1 ) {
+            if( col == 0 ) {
+                return a21;
+            } else if( col == 1 ) {
+                return a22;
+            } else if( col == 2 ) {
+                return a23;
+            } else if( col == 3 ) {
+                return a24;
+            } else if( col == 4 ) {
+                return a25;
+            }
+        } else if( row == 2 ) {
+            if( col == 0 ) {
+                return a31;
+            } else if( col == 1 ) {
+                return a32;
+            } else if( col == 2 ) {
+                return a33;
+            } else if( col == 3 ) {
+                return a34;
+            } else if( col == 4 ) {
+                return a35;
+            }
+        } else if( row == 3 ) {
+            if( col == 0 ) {
+                return a41;
+            } else if( col == 1 ) {
+                return a42;
+            } else if( col == 2 ) {
+                return a43;
+            } else if( col == 3 ) {
+                return a44;
+            } else if( col == 4 ) {
+                return a45;
+            }
+        } else if( row == 4 ) {
+            if( col == 0 ) {
+                return a51;
+            } else if( col == 1 ) {
+                return a52;
+            } else if( col == 2 ) {
+                return a53;
+            } else if( col == 3 ) {
+                return a54;
+            } else if( col == 4 ) {
+                return a55;
+            }
+        }
+        throw new IllegalArgumentException("Row and/or column out of range. "+row+" "+col);
+    }
+
+    @Override
+    public void set(int row, int col, double val) {
+        unsafe_set(row,col,val);
+    }
+
+    @Override
+    public void unsafe_set(int row, int col, double val) {
+        if( row == 0 ) {
+            if( col == 0 ) {
+                a11 = val; return;
+            } else if( col == 1 ) {
+                a12 = val; return;
+            } else if( col == 2 ) {
+                a13 = val; return;
+            } else if( col == 3 ) {
+                a14 = val; return;
+            } else if( col == 4 ) {
+                a15 = val; return;
+            }
+        } else if( row == 1 ) {
+            if( col == 0 ) {
+                a21 = val; return;
+            } else if( col == 1 ) {
+                a22 = val; return;
+            } else if( col == 2 ) {
+                a23 = val; return;
+            } else if( col == 3 ) {
+                a24 = val; return;
+            } else if( col == 4 ) {
+                a25 = val; return;
+            }
+        } else if( row == 2 ) {
+            if( col == 0 ) {
+                a31 = val; return;
+            } else if( col == 1 ) {
+                a32 = val; return;
+            } else if( col == 2 ) {
+                a33 = val; return;
+            } else if( col == 3 ) {
+                a34 = val; return;
+            } else if( col == 4 ) {
+                a35 = val; return;
+            }
+        } else if( row == 3 ) {
+            if( col == 0 ) {
+                a41 = val; return;
+            } else if( col == 1 ) {
+                a42 = val; return;
+            } else if( col == 2 ) {
+                a43 = val; return;
+            } else if( col == 3 ) {
+                a44 = val; return;
+            } else if( col == 4 ) {
+                a45 = val; return;
+            }
+        } else if( row == 4 ) {
+            if( col == 0 ) {
+                a51 = val; return;
+            } else if( col == 1 ) {
+                a52 = val; return;
+            } else if( col == 2 ) {
+                a53 = val; return;
+            } else if( col == 3 ) {
+                a54 = val; return;
+            } else if( col == 4 ) {
+                a55 = val; return;
+            }
+        }
+        throw new IllegalArgumentException("Row and/or column out of range. "+row+" "+col);
+    }
+
+    @Override
+    public void set(Matrix original) {
+        if( original.getNumCols() != 5 || original.getNumRows() != 5 )
+            throw new IllegalArgumentException("Rows and/or columns do not match");
+        RealMatrix64F m = (RealMatrix64F)original;
+        
+        a11 = m.get(0,0);
+        a12 = m.get(0,1);
+        a13 = m.get(0,2);
+        a14 = m.get(0,3);
+        a15 = m.get(0,4);
+        a21 = m.get(1,0);
+        a22 = m.get(1,1);
+        a23 = m.get(1,2);
+        a24 = m.get(1,3);
+        a25 = m.get(1,4);
+        a31 = m.get(2,0);
+        a32 = m.get(2,1);
+        a33 = m.get(2,2);
+        a34 = m.get(2,3);
+        a35 = m.get(2,4);
+        a41 = m.get(3,0);
+        a42 = m.get(3,1);
+        a43 = m.get(3,2);
+        a44 = m.get(3,3);
+        a45 = m.get(3,4);
+        a51 = m.get(4,0);
+        a52 = m.get(4,1);
+        a53 = m.get(4,2);
+        a54 = m.get(4,3);
+        a55 = m.get(4,4);
+    }
+
+    @Override
+    public int getNumRows() {
+        return 5;
+    }
+
+    @Override
+    public int getNumCols() {
+        return 5;
+    }
+
+    @Override
+    public int getNumElements() {
+        return 25;
+    }
+
+    @Override
+    public <T extends Matrix> T copy() {
+        return (T)new FixedMatrix5x5_64F(this);
+    }
+
+    @Override
+    public void print() {
+        MatrixIO.print(System.out, this);
+    }
+}
+
diff --git a/main/core/src/org/ejml/data/FixedMatrix64F.java b/main/core/src/org/ejml/data/FixedMatrix64F.java
new file mode 100644
index 0000000..681eb54
--- /dev/null
+++ b/main/core/src/org/ejml/data/FixedMatrix64F.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+/**
+ * Interface which all fixed sized matrices must implement
+ *
+ * @author Peter Abeles
+ */
+public interface FixedMatrix64F extends RealMatrix64F {
+}
diff --git a/main/core/src/org/ejml/data/FixedMatrix6_64F.java b/main/core/src/org/ejml/data/FixedMatrix6_64F.java
new file mode 100644
index 0000000..c0ba35d
--- /dev/null
+++ b/main/core/src/org/ejml/data/FixedMatrix6_64F.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+package org.ejml.data;
+
+import org.ejml.ops.MatrixIO;
+
+/**
+ * Fixed sized vector with 6 elements.  Can represent a 6 x 1 or 1 x 6 matrix, context dependent.
+ *
+ * @author Peter Abeles
+ */
+public class FixedMatrix6_64F implements FixedMatrix64F {
+    public double a1,a2,a3,a4,a5,a6;
+
+    public FixedMatrix6_64F() {
+    }
+
+    public FixedMatrix6_64F(double a1,double a2,double a3,double a4,double a5,double a6)
+    {
+        this.a1 = a1;
+        this.a2 = a2;
+        this.a3 = a3;
+        this.a4 = a4;
+        this.a5 = a5;
+        this.a6 = a6;
+    }
+
+    public FixedMatrix6_64F(FixedMatrix6_64F o) {
+        this.a1 = o.a1;
+        this.a2 = o.a2;
+        this.a3 = o.a3;
+        this.a4 = o.a4;
+        this.a5 = o.a5;
+        this.a6 = o.a6;
+    }
+
+    @Override
+    public double get(int row, int col) {
+        return unsafe_get(row,col);
+    }
+
+    @Override
+    public double unsafe_get(int row, int col) {
+        if( row != 0 && col != 0 )
+            throw new IllegalArgumentException("Row or column must be zero since this is a vector");
+
+        int w = Math.max(row,col);
+
+        if( w == 0 ) {
+            return a1;
+        } else if( w == 1 ) {
+            return a2;
+        } else if( w == 2 ) {
+            return a3;
+        } else if( w == 3 ) {
+            return a4;
+        } else if( w == 4 ) {
+            return a5;
+        } else if( w == 5 ) {
+            return a6;
+        } else {
+            throw new IllegalArgumentException("Out of range.  "+w);
+        }
+    }
+
+    @Override
+    public void set(int row, int col, double val) {
+        unsafe_set(row,col,val);
+    }
+
+    @Override
+    public void unsafe_set(int row, int col, double val) {
+        if( row != 0 && col != 0 )
+            throw new IllegalArgumentException("Row or column must be zero since this is a vector");
+
+        int w = Math.max(row,col);
+
+        if( w == 0 ) {
+            a1 = val;
+        } else if( w == 1 ) {
+            a2 = val;
+        } else if( w == 2 ) {
+            a3 = val;
+        } else if( w == 3 ) {
+            a4 = val;
+        } else if( w == 4 ) {
+            a5 = val;
+        } else if( w == 5 ) {
+            a6 = val;
+        } else {
+            throw new IllegalArgumentException("Out of range.  "+w);
+        }
+    }
+
+    @Override
+    public void set(Matrix original) {
+        RealMatrix64F m = (RealMatrix64F)original;
+
+        if( m.getNumCols() == 1 && m.getNumRows() == 6 ) {
+            a1 = m.get(0,0);
+            a2 = m.get(1,0);
+            a3 = m.get(2,0);
+            a4 = m.get(3,0);
+            a5 = m.get(4,0);
+            a6 = m.get(5,0);
+        } else if( m.getNumRows() == 1 && m.getNumCols() == 6 ){
+            a1 = m.get(0,0);
+            a2 = m.get(0,1);
+            a3 = m.get(0,2);
+            a4 = m.get(0,3);
+            a5 = m.get(0,4);
+            a6 = m.get(0,5);
+        } else {
+            throw new IllegalArgumentException("Incompatible shape");
+        }
+    }
+
+    @Override
+    public int getNumRows() {
+        return 6;
+    }
+
+    @Override
+    public int getNumCols() {
+        return 1;
+    }
+
+    @Override
+    public int getNumElements() {
+        return 6;
+    }
+
+    @Override
+    public <T extends Matrix> T copy() {
+        return (T)new FixedMatrix6_64F(this);
+    }
+
+    @Override
+    public void print() {
+        MatrixIO.print(System.out, this);
+    }
+}
+
diff --git a/main/core/src/org/ejml/data/FixedMatrix6x6_64F.java b/main/core/src/org/ejml/data/FixedMatrix6x6_64F.java
new file mode 100644
index 0000000..7420564
--- /dev/null
+++ b/main/core/src/org/ejml/data/FixedMatrix6x6_64F.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+package org.ejml.data;
+
+import org.ejml.ops.MatrixIO;
+
+/**
+ * Fixed sized 6 by FixedMatrix6x6_64F matrix.  The matrix is stored as class variables for very fast read/write.  aXY is the
+ * value of row = X and column = Y.
+ *
+ * @author Peter Abeles
+ */
+public class FixedMatrix6x6_64F implements FixedMatrix64F {
+
+    public double a11,a12,a13,a14,a15,a16;
+    public double a21,a22,a23,a24,a25,a26;
+    public double a31,a32,a33,a34,a35,a36;
+    public double a41,a42,a43,a44,a45,a46;
+    public double a51,a52,a53,a54,a55,a56;
+    public double a61,a62,a63,a64,a65,a66;
+
+    public FixedMatrix6x6_64F() {
+    }
+
+    public FixedMatrix6x6_64F( double a11,double a12,double a13,double a14,double a15,double a16,
+                              double a21,double a22,double a23,double a24,double a25,double a26,
+                              double a31,double a32,double a33,double a34,double a35,double a36,
+                              double a41,double a42,double a43,double a44,double a45,double a46,
+                              double a51,double a52,double a53,double a54,double a55,double a56,
+                              double a61,double a62,double a63,double a64,double a65,double a66)
+    {
+        this.a11 = a11;
+        this.a12 = a12;
+        this.a13 = a13;
+        this.a14 = a14;
+        this.a15 = a15;
+        this.a16 = a16;
+        this.a21 = a21;
+        this.a22 = a22;
+        this.a23 = a23;
+        this.a24 = a24;
+        this.a25 = a25;
+        this.a26 = a26;
+        this.a31 = a31;
+        this.a32 = a32;
+        this.a33 = a33;
+        this.a34 = a34;
+        this.a35 = a35;
+        this.a36 = a36;
+        this.a41 = a41;
+        this.a42 = a42;
+        this.a43 = a43;
+        this.a44 = a44;
+        this.a45 = a45;
+        this.a46 = a46;
+        this.a51 = a51;
+        this.a52 = a52;
+        this.a53 = a53;
+        this.a54 = a54;
+        this.a55 = a55;
+        this.a56 = a56;
+        this.a61 = a61;
+        this.a62 = a62;
+        this.a63 = a63;
+        this.a64 = a64;
+        this.a65 = a65;
+        this.a66 = a66;
+    }
+
+    public FixedMatrix6x6_64F( FixedMatrix6x6_64F o ) {
+        this.a11 = o.a11;
+        this.a12 = o.a12;
+        this.a13 = o.a13;
+        this.a14 = o.a14;
+        this.a15 = o.a15;
+        this.a16 = o.a16;
+        this.a21 = o.a21;
+        this.a22 = o.a22;
+        this.a23 = o.a23;
+        this.a24 = o.a24;
+        this.a25 = o.a25;
+        this.a26 = o.a26;
+        this.a31 = o.a31;
+        this.a32 = o.a32;
+        this.a33 = o.a33;
+        this.a34 = o.a34;
+        this.a35 = o.a35;
+        this.a36 = o.a36;
+        this.a41 = o.a41;
+        this.a42 = o.a42;
+        this.a43 = o.a43;
+        this.a44 = o.a44;
+        this.a45 = o.a45;
+        this.a46 = o.a46;
+        this.a51 = o.a51;
+        this.a52 = o.a52;
+        this.a53 = o.a53;
+        this.a54 = o.a54;
+        this.a55 = o.a55;
+        this.a56 = o.a56;
+        this.a61 = o.a61;
+        this.a62 = o.a62;
+        this.a63 = o.a63;
+        this.a64 = o.a64;
+        this.a65 = o.a65;
+        this.a66 = o.a66;
+    }
+
+    @Override
+    public double get(int row, int col) {
+        return unsafe_get(row,col);
+    }
+
+    @Override
+    public double unsafe_get(int row, int col) {
+        if( row == 0 ) {
+            if( col == 0 ) {
+                return a11;
+            } else if( col == 1 ) {
+                return a12;
+            } else if( col == 2 ) {
+                return a13;
+            } else if( col == 3 ) {
+                return a14;
+            } else if( col == 4 ) {
+                return a15;
+            } else if( col == 5 ) {
+                return a16;
+            }
+        } else if( row == 1 ) {
+            if( col == 0 ) {
+                return a21;
+            } else if( col == 1 ) {
+                return a22;
+            } else if( col == 2 ) {
+                return a23;
+            } else if( col == 3 ) {
+                return a24;
+            } else if( col == 4 ) {
+                return a25;
+            } else if( col == 5 ) {
+                return a26;
+            }
+        } else if( row == 2 ) {
+            if( col == 0 ) {
+                return a31;
+            } else if( col == 1 ) {
+                return a32;
+            } else if( col == 2 ) {
+                return a33;
+            } else if( col == 3 ) {
+                return a34;
+            } else if( col == 4 ) {
+                return a35;
+            } else if( col == 5 ) {
+                return a36;
+            }
+        } else if( row == 3 ) {
+            if( col == 0 ) {
+                return a41;
+            } else if( col == 1 ) {
+                return a42;
+            } else if( col == 2 ) {
+                return a43;
+            } else if( col == 3 ) {
+                return a44;
+            } else if( col == 4 ) {
+                return a45;
+            } else if( col == 5 ) {
+                return a46;
+            }
+        } else if( row == 4 ) {
+            if( col == 0 ) {
+                return a51;
+            } else if( col == 1 ) {
+                return a52;
+            } else if( col == 2 ) {
+                return a53;
+            } else if( col == 3 ) {
+                return a54;
+            } else if( col == 4 ) {
+                return a55;
+            } else if( col == 5 ) {
+                return a56;
+            }
+        } else if( row == 5 ) {
+            if( col == 0 ) {
+                return a61;
+            } else if( col == 1 ) {
+                return a62;
+            } else if( col == 2 ) {
+                return a63;
+            } else if( col == 3 ) {
+                return a64;
+            } else if( col == 4 ) {
+                return a65;
+            } else if( col == 5 ) {
+                return a66;
+            }
+        }
+        throw new IllegalArgumentException("Row and/or column out of range. "+row+" "+col);
+    }
+
+    @Override
+    public void set(int row, int col, double val) {
+        unsafe_set(row,col,val);
+    }
+
+    @Override
+    public void unsafe_set(int row, int col, double val) {
+        if( row == 0 ) {
+            if( col == 0 ) {
+                a11 = val; return;
+            } else if( col == 1 ) {
+                a12 = val; return;
+            } else if( col == 2 ) {
+                a13 = val; return;
+            } else if( col == 3 ) {
+                a14 = val; return;
+            } else if( col == 4 ) {
+                a15 = val; return;
+            } else if( col == 5 ) {
+                a16 = val; return;
+            }
+        } else if( row == 1 ) {
+            if( col == 0 ) {
+                a21 = val; return;
+            } else if( col == 1 ) {
+                a22 = val; return;
+            } else if( col == 2 ) {
+                a23 = val; return;
+            } else if( col == 3 ) {
+                a24 = val; return;
+            } else if( col == 4 ) {
+                a25 = val; return;
+            } else if( col == 5 ) {
+                a26 = val; return;
+            }
+        } else if( row == 2 ) {
+            if( col == 0 ) {
+                a31 = val; return;
+            } else if( col == 1 ) {
+                a32 = val; return;
+            } else if( col == 2 ) {
+                a33 = val; return;
+            } else if( col == 3 ) {
+                a34 = val; return;
+            } else if( col == 4 ) {
+                a35 = val; return;
+            } else if( col == 5 ) {
+                a36 = val; return;
+            }
+        } else if( row == 3 ) {
+            if( col == 0 ) {
+                a41 = val; return;
+            } else if( col == 1 ) {
+                a42 = val; return;
+            } else if( col == 2 ) {
+                a43 = val; return;
+            } else if( col == 3 ) {
+                a44 = val; return;
+            } else if( col == 4 ) {
+                a45 = val; return;
+            } else if( col == 5 ) {
+                a46 = val; return;
+            }
+        } else if( row == 4 ) {
+            if( col == 0 ) {
+                a51 = val; return;
+            } else if( col == 1 ) {
+                a52 = val; return;
+            } else if( col == 2 ) {
+                a53 = val; return;
+            } else if( col == 3 ) {
+                a54 = val; return;
+            } else if( col == 4 ) {
+                a55 = val; return;
+            } else if( col == 5 ) {
+                a56 = val; return;
+            }
+        } else if( row == 5 ) {
+            if( col == 0 ) {
+                a61 = val; return;
+            } else if( col == 1 ) {
+                a62 = val; return;
+            } else if( col == 2 ) {
+                a63 = val; return;
+            } else if( col == 3 ) {
+                a64 = val; return;
+            } else if( col == 4 ) {
+                a65 = val; return;
+            } else if( col == 5 ) {
+                a66 = val; return;
+            }
+        }
+        throw new IllegalArgumentException("Row and/or column out of range. "+row+" "+col);
+    }
+
+    @Override
+    public void set(Matrix original) {
+        if( original.getNumCols() != 6 || original.getNumRows() != 6 )
+            throw new IllegalArgumentException("Rows and/or columns do not match");
+        RealMatrix64F m = (RealMatrix64F)original;
+        
+        a11 = m.get(0,0);
+        a12 = m.get(0,1);
+        a13 = m.get(0,2);
+        a14 = m.get(0,3);
+        a15 = m.get(0,4);
+        a16 = m.get(0,5);
+        a21 = m.get(1,0);
+        a22 = m.get(1,1);
+        a23 = m.get(1,2);
+        a24 = m.get(1,3);
+        a25 = m.get(1,4);
+        a26 = m.get(1,5);
+        a31 = m.get(2,0);
+        a32 = m.get(2,1);
+        a33 = m.get(2,2);
+        a34 = m.get(2,3);
+        a35 = m.get(2,4);
+        a36 = m.get(2,5);
+        a41 = m.get(3,0);
+        a42 = m.get(3,1);
+        a43 = m.get(3,2);
+        a44 = m.get(3,3);
+        a45 = m.get(3,4);
+        a46 = m.get(3,5);
+        a51 = m.get(4,0);
+        a52 = m.get(4,1);
+        a53 = m.get(4,2);
+        a54 = m.get(4,3);
+        a55 = m.get(4,4);
+        a56 = m.get(4,5);
+        a61 = m.get(5,0);
+        a62 = m.get(5,1);
+        a63 = m.get(5,2);
+        a64 = m.get(5,3);
+        a65 = m.get(5,4);
+        a66 = m.get(5,5);
+    }
+
+    @Override
+    public int getNumRows() {
+        return 6;
+    }
+
+    @Override
+    public int getNumCols() {
+        return 6;
+    }
+
+    @Override
+    public int getNumElements() {
+        return 36;
+    }
+
+    @Override
+    public <T extends Matrix> T copy() {
+        return (T)new FixedMatrix6x6_64F(this);
+    }
+
+    @Override
+    public void print() {
+        MatrixIO.print(System.out, this);
+    }
+}
+
diff --git a/main/core/src/org/ejml/data/Matrix.java b/main/core/src/org/ejml/data/Matrix.java
new file mode 100644
index 0000000..496202c
--- /dev/null
+++ b/main/core/src/org/ejml/data/Matrix.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import java.io.Serializable;
+
+/**
+ * Base interface for all rectangular matrices
+ *
+ * @author Peter Abeles
+ */
+public interface Matrix extends Serializable {
+    /**
+     * Returns the number of rows in this matrix.
+     *
+     * @return Number of rows.
+     */
+    public int getNumRows();
+
+    /**
+     * Returns the number of columns in this matrix.
+     *
+     * @return Number of columns.
+     */
+    public int getNumCols();
+
+    /**
+     * Creates an exact copy of the matrix
+     */
+    public <T extends Matrix> T copy();
+
+    /**
+     * Sets this matrix to be identical to the 'original' matrix passed in.
+     */
+    public void set( Matrix original );
+
+    /**
+     * Prints the matrix to standard out.
+     */
+    public void print();
+}
diff --git a/main/core/src/org/ejml/data/MatrixIterator32F.java b/main/core/src/org/ejml/data/MatrixIterator32F.java
new file mode 100644
index 0000000..c7b938c
--- /dev/null
+++ b/main/core/src/org/ejml/data/MatrixIterator32F.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import java.util.Iterator;
+
+
+/**
+ * This is a matrix iterator for traversing through a submatrix.  For speed it is recommended
+ * that you directly access the elements in the matrix, but there are some situations where this
+ * can be a better design.
+ *
+ * @author Peter Abeles
+ */
+public class MatrixIterator32F implements Iterator<Float> {
+    // the matrix which is being iterated through
+    private D1Matrix32F a;
+
+    // should it iterate through by row or by column
+    private boolean rowMajor;
+
+    // the first row and column it returns
+    private int minCol;
+    private int minRow;
+
+    // where in the iteration it is
+    private int index = 0;
+    // how many elements inside will it return
+    private int size;
+
+    // how wide the submatrix is
+    private int submatrixStride;
+
+    // the current element
+    int subRow,subCol;
+
+    /**
+     * Creates a new iterator for traversing through a submatrix inside this matrix.  It can be traversed
+     * by row or by column.  Range of elements is inclusive, e.g. minRow = 0 and maxRow = 1 will include rows
+     * 0 and 1.  The iteration starts at (minRow,minCol) and ends at (maxRow,maxCol)
+     *
+     * @param a the matrix it is iterating through
+     * @param rowMajor true means it will traverse through the submatrix by row first, false by columns.
+     * @param minRow first row it will start at.
+     * @param minCol first column it will start at.
+     * @param maxRow last row it will stop at.
+     * @param maxCol last column it will stop at.
+     * @return A new MatrixIterator
+     */
+    public MatrixIterator32F(D1Matrix32F a, boolean rowMajor,
+                             int minRow, int minCol, int maxRow, int maxCol
+    ) {
+        if( maxCol < minCol )
+            throw new IllegalArgumentException("maxCol has to be more than or equal to minCol");
+        if( maxRow < minRow )
+            throw new IllegalArgumentException("maxRow has to be more than or equal to minCol");
+        if( maxCol >= a.numCols)
+            throw new IllegalArgumentException("maxCol must be < numCols");
+        if( maxRow >= a.numRows)
+            throw new IllegalArgumentException("maxRow must be < numCRows");
+
+
+
+        this.a = a;
+        this.rowMajor = rowMajor;
+        this.minCol = minCol;
+        this.minRow = minRow;
+
+        size = (maxCol-minCol+1)*(maxRow-minRow+1);
+
+        if( rowMajor )
+            submatrixStride = maxCol-minCol+1;
+        else
+            submatrixStride = maxRow-minRow+1;
+    }
+
+    @Override
+    public boolean hasNext() {
+        return index < size;
+    }
+
+    @Override
+    public Float next() {
+        if( rowMajor ) {
+            subRow = index / submatrixStride;
+            subCol = index % submatrixStride;
+        } else {
+            subRow = index % submatrixStride;
+            subCol = index / submatrixStride;
+        }
+        index++;
+        return a.get(subRow+minRow,subCol+minCol);
+    }
+
+    @Override
+    public void remove() {
+        throw new RuntimeException("Operation not supported");
+    }
+
+    /**
+     * Which element in the submatrix was returned by next()
+     *
+     * @return Submatrix element's index.
+     */
+    public int getIndex() {
+        return index-1;
+    }
+
+    /**
+     * True if it is iterating through the matrix by rows and false if by columns.
+     * @return row major or column major
+     */
+    public boolean isRowMajor() {
+        return rowMajor;
+    }
+
+    /**
+     * Sets the value of the current element.
+     *
+     * @param value The element's new value.
+     */
+    public void set( float value ) {
+        a.set(subRow+minRow,subCol+minCol,value);
+    }
+}
diff --git a/main/core/src/org/ejml/data/MatrixIterator64F.java b/main/core/src/org/ejml/data/MatrixIterator64F.java
new file mode 100644
index 0000000..1f89eb0
--- /dev/null
+++ b/main/core/src/org/ejml/data/MatrixIterator64F.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import java.util.Iterator;
+
+
+/**
+ * This is a matrix iterator for traversing through a submatrix.  For speed it is recommended
+ * that you directly access the elements in the matrix, but there are some situations where this
+ * can be a better design.
+ *
+ * @author Peter Abeles
+ */
+public class MatrixIterator64F implements Iterator<Double> {
+    // the matrix which is being iterated through
+    private D1Matrix64F a;
+
+    // should it iterate through by row or by column
+    private boolean rowMajor;
+
+    // the first row and column it returns
+    private int minCol;
+    private int minRow;
+
+    // where in the iteration it is
+    private int index = 0;
+    // how many elements inside will it return
+    private int size;
+
+    // how wide the submatrix is
+    private int submatrixStride;
+
+    // the current element
+    int subRow,subCol;
+
+    /**
+     * Creates a new iterator for traversing through a submatrix inside this matrix.  It can be traversed
+     * by row or by column.  Range of elements is inclusive, e.g. minRow = 0 and maxRow = 1 will include rows
+     * 0 and 1.  The iteration starts at (minRow,minCol) and ends at (maxRow,maxCol)
+     *
+     * @param a the matrix it is iterating through
+     * @param rowMajor true means it will traverse through the submatrix by row first, false by columns.
+     * @param minRow first row it will start at.
+     * @param minCol first column it will start at.
+     * @param maxRow last row it will stop at.
+     * @param maxCol last column it will stop at.
+     * @return A new MatrixIterator
+     */
+    public MatrixIterator64F(D1Matrix64F a, boolean rowMajor,
+                             int minRow, int minCol, int maxRow, int maxCol
+    ) {
+        if( maxCol < minCol )
+            throw new IllegalArgumentException("maxCol has to be more than or equal to minCol");
+        if( maxRow < minRow )
+            throw new IllegalArgumentException("maxRow has to be more than or equal to minCol");
+        if( maxCol >= a.numCols)
+            throw new IllegalArgumentException("maxCol must be < numCols");
+        if( maxRow >= a.numRows)
+            throw new IllegalArgumentException("maxRow must be < numCRows");
+
+
+
+        this.a = a;
+        this.rowMajor = rowMajor;
+        this.minCol = minCol;
+        this.minRow = minRow;
+
+        size = (maxCol-minCol+1)*(maxRow-minRow+1);
+
+        if( rowMajor )
+            submatrixStride = maxCol-minCol+1;
+        else
+            submatrixStride = maxRow-minRow+1;
+    }
+
+    @Override
+    public boolean hasNext() {
+        return index < size;
+    }
+
+    @Override
+    public Double next() {
+        if( rowMajor ) {
+            subRow = index / submatrixStride;
+            subCol = index % submatrixStride;
+        } else {
+            subRow = index % submatrixStride;
+            subCol = index / submatrixStride;
+        }
+        index++;
+        return a.get(subRow+minRow,subCol+minCol);
+    }
+
+    @Override
+    public void remove() {
+        throw new RuntimeException("Operation not supported");
+    }
+
+    /**
+     * Which element in the submatrix was returned by next()
+     *
+     * @return Submatrix element's index.
+     */
+    public int getIndex() {
+        return index-1;
+    }
+
+    /**
+     * True if it is iterating through the matrix by rows and false if by columns.
+     * @return row major or column major
+     */
+    public boolean isRowMajor() {
+        return rowMajor;
+    }
+
+    /**
+     * Sets the value of the current element.
+     *
+     * @param value The element's new value.
+     */
+    public void set( double value ) {
+        a.set(subRow+minRow,subCol+minCol,value);
+    }
+}
diff --git a/main/core/src/org/ejml/data/RealMatrix32F.java b/main/core/src/org/ejml/data/RealMatrix32F.java
new file mode 100644
index 0000000..727b13d
--- /dev/null
+++ b/main/core/src/org/ejml/data/RealMatrix32F.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+/**
+ * Interface for all 32 bit floating point rectangular matrices.
+ *
+ * @author Peter Abeles
+ */
+public interface RealMatrix32F extends Matrix {
+
+    /**
+     * Returns the value of value of the specified matrix element.
+     *
+     * @param row Matrix element's row index..
+     * @param col Matrix element's column index.
+     * @return The specified element's value.
+     */
+    public float get(int row, int col);
+
+    /**
+     * Same as {@link #get} but does not perform bounds check on input parameters.  This results in about a 25%
+     * speed increase but potentially sacrifices stability and makes it more difficult to track down simple errors.
+     * It is not recommended that this function be used, except in highly optimized code where the bounds are
+     * implicitly being checked.
+     *
+     * @param row Matrix element's row index..
+     * @param col Matrix element's column index.
+     * @return The specified element's value.
+     */
+    public float unsafe_get(int row, int col);
+
+    /**
+     * Sets the value of the specified matrix element.
+     *
+     * @param row Matrix element's row index..
+     * @param col Matrix element's column index.
+     * @param val The element's new value.
+     */
+    public void set(int row, int col, float val);
+
+    /**
+     * Same as {@link #set} but does not perform bounds check on input parameters.  This results in about a 25%
+     * speed increase but potentially sacrifices stability and makes it more difficult to track down simple errors.
+     * It is not recommended that this function be used, except in highly optimized code where the bounds are
+     * implicitly being checked.
+     *
+     * @param row Matrix element's row index..
+     * @param col Matrix element's column index.
+     * @param val The element's new value.
+     */
+    public void unsafe_set(int row, int col, float val);
+
+    /**
+     * Returns the number of elements in this matrix, which is the number of rows
+     * times the number of columns.
+     *
+     * @return Number of elements in this matrix.
+     */
+    public int getNumElements();
+}
diff --git a/main/core/src/org/ejml/data/RealMatrix64F.java b/main/core/src/org/ejml/data/RealMatrix64F.java
new file mode 100644
index 0000000..6794803
--- /dev/null
+++ b/main/core/src/org/ejml/data/RealMatrix64F.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+/**
+ * Interface for all 64 bit floating point rectangular matrices.
+ *
+ * @author Peter Abeles
+ */
+public interface RealMatrix64F extends Matrix {
+
+    /**
+     * Returns the value of value of the specified matrix element.
+     *
+     * @param row Matrix element's row index..
+     * @param col Matrix element's column index.
+     * @return The specified element's value.
+     */
+    public double get(int row, int col);
+
+    /**
+     * Same as {@link #get} but does not perform bounds check on input parameters.  This results in about a 25%
+     * speed increase but potentially sacrifices stability and makes it more difficult to track down simple errors.
+     * It is not recommended that this function be used, except in highly optimized code where the bounds are
+     * implicitly being checked.
+     *
+     * @param row Matrix element's row index..
+     * @param col Matrix element's column index.
+     * @return The specified element's value.
+     */
+    public double unsafe_get(int row, int col);
+
+    /**
+     * Sets the value of the specified matrix element.
+     *
+     * @param row Matrix element's row index..
+     * @param col Matrix element's column index.
+     * @param val The element's new value.
+     */
+    public void set(int row, int col, double val);
+
+    /**
+     * Same as {@link #set} but does not perform bounds check on input parameters.  This results in about a 25%
+     * speed increase but potentially sacrifices stability and makes it more difficult to track down simple errors.
+     * It is not recommended that this function be used, except in highly optimized code where the bounds are
+     * implicitly being checked.
+     *
+     * @param row Matrix element's row index..
+     * @param col Matrix element's column index.
+     * @param val The element's new value.
+     */
+    public void unsafe_set(int row, int col, double val);
+
+    /**
+     * Returns the number of elements in this matrix, which is the number of rows
+     * times the number of columns.
+     *
+     * @return Number of elements in this matrix.
+     */
+    public int getNumElements();
+}
diff --git a/main/core/src/org/ejml/data/ReshapeMatrix.java b/main/core/src/org/ejml/data/ReshapeMatrix.java
new file mode 100644
index 0000000..eb0135b
--- /dev/null
+++ b/main/core/src/org/ejml/data/ReshapeMatrix.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+/**
+ * Matrix which can be reshaped
+ *
+ * @author Peter Abeles
+ */
+public interface ReshapeMatrix extends Matrix{
+    /**
+     * Equivalent to invoking reshape(numRows,numCols,false);
+     *
+     * @param numRows The new number of rows in the matrix.
+     * @param numCols The new number of columns in the matrix.
+     */
+    public void reshape( int numRows , int numCols );
+}
diff --git a/main/core/src/org/ejml/data/RowD1Matrix64F.java b/main/core/src/org/ejml/data/RowD1Matrix64F.java
new file mode 100644
index 0000000..39a9b79
--- /dev/null
+++ b/main/core/src/org/ejml/data/RowD1Matrix64F.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2009-2013, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+/**
+ * Interface for a row-major matrix that uses a single array internally.
+ *
+ * @author Peter Abeles
+ */
+public abstract class RowD1Matrix64F extends D1Matrix64F {
+
+}
diff --git a/main/core/src/org/ejml/interfaces/decomposition/BidiagonalDecomposition.java b/main/core/src/org/ejml/interfaces/decomposition/BidiagonalDecomposition.java
new file mode 100644
index 0000000..45974f0
--- /dev/null
+++ b/main/core/src/org/ejml/interfaces/decomposition/BidiagonalDecomposition.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.interfaces.decomposition;
+
+import org.ejml.data.Matrix;
+
+/**
+ * <p>
+ * Computes a matrix decomposition such that:<br>
+ * <br>
+ * A = U*B*V<sup>T</sup><br>
+ * <br>
+ * where A is m by n, U is orthogonal and m by m, B is an m by n bidiagonal matrix, V is orthogonal and n by n.
+ * This is used as a first step in computing the SVD of a matrix for the QR algorithm approach.
+ * </p>
+ * <p>
+ * A bidiagonal matrix has zeros in every element except for the two diagonals.<br>
+ * <br>
+ * b_ij = 0    if i > j or i < j-1<br>
+ * </p>
+ *
+ *
+ * @author Peter Abeles
+ */
+public interface BidiagonalDecomposition <T extends Matrix>
+        extends DecompositionInterface<T> {
+
+    /**
+     * Returns the bidiagonal matrix.
+     *
+     * @param B If not null the results are stored here, if null a new matrix is created.
+     * @return The bidiagonal matrix.
+     */
+    public T getB( T B , boolean compact );
+
+    /**
+     * Returns the orthogonal U matrix.
+     *
+     * @param U If not null then the results will be stored here.  Otherwise a new matrix will be created.
+     * @return The extracted Q matrix.
+     */
+    public T getU( T U , boolean transpose , boolean compact );
+
+
+    /**
+     * Returns the orthogonal V matrix.
+     *
+     * @param V If not null then the results will be stored here.  Otherwise a new matrix will be created.
+     * @return The extracted Q matrix.
+     */
+    public T getV( T V ,  boolean transpose , boolean compact );
+
+    /**
+     * Extracts the diagonal and off diagonal elements from the decomposition.
+     *
+     * @param diag diagonal elements from B.
+     * @param off off diagonal elements form B.
+     */
+    public void getDiagonal( double diag[], double off[] );
+
+}
\ No newline at end of file
diff --git a/main/core/src/org/ejml/interfaces/decomposition/CholeskyDecomposition.java b/main/core/src/org/ejml/interfaces/decomposition/CholeskyDecomposition.java
new file mode 100644
index 0000000..e71ff61
--- /dev/null
+++ b/main/core/src/org/ejml/interfaces/decomposition/CholeskyDecomposition.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.interfaces.decomposition;
+
+import org.ejml.data.Complex64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.data.Matrix;
+
+
+/**
+ * <p>
+ * Cholesky decomposition for {@link DenseMatrix64F}.  It decomposes positive-definite symmetric matrices (real)
+ * or hermitian-positive definite (complex) into either upper or lower triangles:<br>
+ * <br>
+ * L*L<sup>H</sup>=A<br>
+ * R<sup>H</sup>*R=A<br>
+ * <br>
+ * where L is a lower triangular matrix and R is an upper triangular matrix.  This is typically 
+ * used to invert matrices, such as a covariance matrix.<br>
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public interface CholeskyDecomposition <MatrixType extends Matrix>
+        extends DecompositionInterface<MatrixType> {
+
+    /**
+     * If true the decomposition was for a lower triangular matrix.
+     * If false it was for an upper triangular matrix.
+     *
+     * @return True if lower, false if upper.
+     */
+    public boolean isLower();
+
+    /**
+     * <p>
+     * Returns the triangular matrix from the decomposition.
+     * </p>
+     *
+     * <p>
+     * If an input is provided that matrix is used to write the results to.
+     * Otherwise a new matrix is created and the results written to it.
+     * </p>
+     *
+     * @param T If not null then the decomposed matrix is written here.
+     * @return A lower or upper triangular matrix.
+     */
+    public MatrixType getT( MatrixType T  );
+
+    /**
+     * Computes the matrix's determinant using the decomposition.
+     *
+     * @return The determinant.
+     */
+    public Complex64F computeDeterminant();
+
+}
\ No newline at end of file
diff --git a/main/core/src/org/ejml/interfaces/decomposition/CholeskyLDLDecomposition.java b/main/core/src/org/ejml/interfaces/decomposition/CholeskyLDLDecomposition.java
new file mode 100644
index 0000000..65ff64c
--- /dev/null
+++ b/main/core/src/org/ejml/interfaces/decomposition/CholeskyLDLDecomposition.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.interfaces.decomposition;
+
+import org.ejml.data.Matrix;
+
+
+/**
+ * <p>
+ * Cholesky LDL<sup>T<sup> decomposition for {@link org.ejml.data.DenseMatrix64F}.
+ * <p>
+ * <p>
+ * A Cholesky LDL decomposition decomposes positive-definite symmetric matrices into:<br>
+ * <br>
+ * L*D*L<sup>T</sup>=A<br>
+ * <br>
+ * where L is a lower triangular matrix and D is a diagonal matrix.  The main advantage of LDL versus LL or RR Cholesky is that
+ * it avoid a square root operation.
+ * </p>
+ *
+ * @see org.ejml.alg.dense.decomposition.chol.CholeskyDecompositionLDL_D64
+ *
+ * @author Peter Abeles
+ */
+public interface CholeskyLDLDecomposition<MatrixType extends Matrix>
+        extends DecompositionInterface<MatrixType> {
+
+
+    /**
+     * <p>
+     * Returns the lower triangular matrix from the decomposition.
+     * </p>
+     *
+     * <p>
+     * If an input is provided that matrix is used to write the results to.
+     * Otherwise a new matrix is created and the results written to it.
+     * </p>
+     *
+     * @param L If not null then the decomposed matrix is written here.
+     * @return A lower triangular matrix.
+     */
+    public MatrixType getL(MatrixType L);
+
+    /**
+     * Returns the elements in the diagonal matrix
+     * @return array with diagonal elements. Array might be larger than the number of elements.
+     */
+    public double[] getDiagonal();
+
+    /**
+     * <p>
+     * Returns the diagonal matrixfrom the decomposition.
+     * </p>
+     *
+     * <p>
+     * If an input is provided that matrix is used to write the results to.
+     * Otherwise a new matrix is created and the results written to it.
+     * </p>
+     *
+     * @param D If not null it will be used to store the diagonal matrix
+     * @return D Square diagonal matrix
+     */
+    public MatrixType getD(MatrixType D);
+
+}
\ No newline at end of file
diff --git a/main/core/src/org/ejml/interfaces/decomposition/DecompositionInterface.java b/main/core/src/org/ejml/interfaces/decomposition/DecompositionInterface.java
new file mode 100644
index 0000000..3327025
--- /dev/null
+++ b/main/core/src/org/ejml/interfaces/decomposition/DecompositionInterface.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.interfaces.decomposition;
+
+import org.ejml.data.Matrix;
+
+
+/**
+ * <p>
+ * An interface for performing matrix decompositions on a {@link org.ejml.data.DenseMatrix64F}.
+ * </p>
+ *
+ * <p>
+ * A matrix decomposition is an algorithm which decomposes the input matrix into a set of equivalent
+ * matrices that store the same information as the original.  Decompositions are useful
+ * in that they allow specialized efficient algorithms to be run on generic input
+ * matrices.
+ * </p>
+ *
+ * <p>
+ * By default most decompositions will modify the input matrix.  This is done to save
+ * memory and simply code by reducing the number of cases which need to be tested.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public interface DecompositionInterface <T extends Matrix> {
+
+    /**
+     * Computes the decomposition of the input matrix.  Depending on the implementation
+     * the input matrix might be stored internally or modified.  If it is modified then
+     * the function {@link #inputModified()} will return true and the matrix should not be
+     * modified until the decomposition is no longer needed.
+     *
+     * @param orig The matrix which is being decomposed.  Modification is implementation dependent.
+     * @return Returns if it was able to decompose the matrix.
+     */
+    public boolean decompose( T orig );
+
+    /**
+     * Is the input matrix to {@link #decompose(org.ejml.data.Matrix)} is modified during
+     * the decomposition process.
+     *
+     * @return true if the input matrix to decompose() is modified.
+     */
+    public boolean inputModified();
+}
diff --git a/main/core/src/org/ejml/interfaces/decomposition/EigenDecomposition.java b/main/core/src/org/ejml/interfaces/decomposition/EigenDecomposition.java
new file mode 100644
index 0000000..08c91f0
--- /dev/null
+++ b/main/core/src/org/ejml/interfaces/decomposition/EigenDecomposition.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.interfaces.decomposition;
+
+import org.ejml.data.Complex64F;
+import org.ejml.data.Matrix;
+
+
+/**
+ * <p>
+ * This is a generic interface for computing the eigenvalues and eigenvectors of a matrix.
+ * Eigenvalues and eigenvectors have the following property:<br>
+ * <br>
+ * A*v=λ*v<br>
+ * <br>
+ * where A is a square matrix and v is an eigenvector associated with the eigenvalue λ.
+ * </p>
+ *
+ * <p>
+ * In general, both eigenvalues and eigenvectors can be complex numbers.  For symmetric matrices the
+ * eigenvalues and eigenvectors are always real numbers.  EJML does not support complex matrices but
+ * it does have minimal support for complex numbers.  As a result complex eigenvalues are found, but only
+ * the real eigenvectors are computed.
+ * </p>
+ *
+ * <p>
+ * To create a new instance of {@link EigenDecomposition} use {@link org.ejml.factory.DecompositionFactory}. If the matrix
+ * is known to be symmetric be sure to use the symmetric decomposition, which is much faster and more accurate
+ * than the general purpose one.
+ * </p>
+ * @author Peter Abeles
+ */
+public interface EigenDecomposition<MatrixType extends Matrix>
+        extends DecompositionInterface<MatrixType> {
+
+    /**
+     * Returns the number of eigenvalues/eigenvectors.  This is the matrix's dimension.
+     *
+     * @return number of eigenvalues/eigenvectors.
+     */
+    public int getNumberOfEigenvalues();
+
+    /**
+     * <p>
+     * Returns an eigenvalue as a complex number.  For symmetric matrices the returned eigenvalue will always be a real
+     * number, which means the imaginary component will be equal to zero.
+     * </p>
+     *
+     * <p>
+     * NOTE: The order of the eigenvalues is dependent upon the decomposition algorithm used.  This means that they may
+     * or may not be ordered by magnitude.  For example the QR algorithm will returns results that are partially
+     * ordered by magnitude, but this behavior should not be relied upon.
+     * </p>
+     * 
+     * @param index Index of the eigenvalue eigenvector pair.
+     * @return An eigenvalue.
+     */
+    public Complex64F getEigenvalue( int index );
+
+    /**
+     * <p>
+     * Used to retrieve real valued eigenvectors.  If an eigenvector is associated with a complex eigenvalue
+     * then null is returned instead.
+     * </p>
+     *
+     * @param index Index of the eigenvalue eigenvector pair.
+     * @return If the associated eigenvalue is real then an eigenvector is returned, null otherwise.
+     */
+    public MatrixType getEigenVector( int index );
+}
diff --git a/main/core/src/org/ejml/interfaces/decomposition/LUDecomposition.java b/main/core/src/org/ejml/interfaces/decomposition/LUDecomposition.java
new file mode 100644
index 0000000..5c0703c
--- /dev/null
+++ b/main/core/src/org/ejml/interfaces/decomposition/LUDecomposition.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.interfaces.decomposition;
+
+import org.ejml.data.Complex64F;
+import org.ejml.data.Matrix;
+
+
+/**
+ * <p>
+ * LU Decomposition refactors the original matrix such that:<br>
+ * <div align=center> P<sup>T</sup>*L*U = A</div>
+ * where P is a pivot matrix, L is a lower triangular matrix, U is an upper triangular matrix and A is
+ * the original matrix.
+ * </p>
+ *
+ * <p>
+ * LU Decomposition is useful since once the decomposition has been performed linear
+ * equations can be quickly solved and the original matrix A inverted.  Different algorithms
+ * can be selected to perform the decomposition, all will have the same end result.
+ * </p>
+ * <p>
+ * To use this class first specify the size of the matrix that will be decomposed by it in
+ * the constructor.  Only square m by m matrices can be decomposed.  Then to decompose a matrix
+ * call {@link #decompose}.  If it encounters any problems an exception will be thrown.  After
+ * that all the other functions will be available for solving and inverting matrices.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public interface LUDecomposition <T extends Matrix>
+        extends DecompositionInterface<T> {
+
+    /**
+     * <p>
+     * Returns the L matrix from the decomposition.  Should only
+     * be called after {@link #decompose(org.ejml.data.Matrix)} has
+     * been called.
+     * </p>
+     *
+     * <p>
+     * If parameter 'lower' is not null, then that matrix is used to store the L matrix.  Otherwise
+     * a new matrix is created.
+     * </p>
+     *
+     * @param lower Storage for T matrix. If null then a new matrix is returned.  Modified.
+     * @return The L matrix.
+     */
+    public T getLower( T lower );
+
+    /**
+     * <p>
+     * Returns the U matrix from the decomposition.  Should only
+     * be called after {@link #decompose(org.ejml.data.Matrix)}  has
+     * been called.
+     * </p>
+     *
+     * <p>
+     * If parameter 'upper' is not null, then that matrix is used to store the U matrix.  Otherwise
+     * a new matrix is created.
+     * </p>
+     *
+     * @param upper Storage for U matrix. If null then a new matrix is returned. Modified.
+     * @return The U matrix.
+     */
+    public T getUpper( T upper );
+
+    /**
+     * <p>
+     * For numerical stability there are often row interchanges.  This computes
+     * a pivot matrix that will undo those changes.
+     * </p>
+     *
+     * @param pivot Storage for the pivot matrix. If null then a new matrix is returned. Modified.
+     * @return The pivot matrix.
+     */
+    public T getPivot( T pivot );
+
+    /**
+     * Returns true if the decomposition detected a singular matrix.  This check
+     * will not work 100% of the time due to machine precision issues.
+     *
+     * @return True if the matrix is singular and false if it is not.
+     */
+    // TODO Remove?  If singular decomposition will fail.
+    public boolean isSingular();
+
+    /**
+     * Computes the matrix's determinant using the LU decomposition.
+     *
+     * @return The determinant.
+     */
+    public Complex64F computeDeterminant();
+}
diff --git a/main/core/src/org/ejml/interfaces/decomposition/QRDecomposition.java b/main/core/src/org/ejml/interfaces/decomposition/QRDecomposition.java
new file mode 100644
index 0000000..9bfff06
--- /dev/null
+++ b/main/core/src/org/ejml/interfaces/decomposition/QRDecomposition.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.interfaces.decomposition;
+
+import org.ejml.data.Matrix;
+
+
+/**
+ * <p>
+ * QR decompositions decompose a rectangular matrix 'A' such that 'A=QR'.  Where
+ * A ∈ ℜ <sup>n × m</sup> , n ≥ m, Q ∈ ℜ <sup>n × n</sup> is an orthogonal matrix,
+ * and R ∈ ℜ <sup>n × m</sup> is an upper triangular matrix.  Some implementations
+ * of QR decomposition require that A has full rank.
+ * </p>
+ * <p>
+ * Some features of QR decompositions:
+ * <ul>
+ * <li> Can decompose rectangular matrices. </li>
+ * <li> Numerically stable solutions to least-squares problem, but not as stable as SVD </li>
+ * <li> Can incrementally add and remove columns from the decomposed matrix.  See {@link org.ejml.alg.dense.linsol.qr.AdjLinearSolverQr_D64} </li>
+ * </ul>
+ * </p>
+ * <p>
+ * Orthogonal matrices have the following properties:
+ * <ul>
+ * <li>QQ<sup>T</sup>=I</li>
+ * <li>Q<sup>T</sup>=Q<sup>-1</sup></li>
+ * </ul>
+ * </p>
+
+ * @see org.ejml.alg.dense.decomposition.qr.QRDecompositionHouseholder_D64
+ * @see org.ejml.alg.dense.decomposition.qr.QRDecompositionHouseholderColumn_D64
+ *
+ * @author Peter Abeles
+ */
+public interface QRDecomposition <T extends Matrix>
+        extends DecompositionInterface<T> {
+    /**
+     * <p>
+     * Returns the Q matrix from the decomposition.  Should only
+     * be called after {@link #decompose(org.ejml.data.Matrix)} has
+     * been called.
+     * </p>
+     *
+     * <p>
+     * If parameter Q is not null, then that matrix is used to store the Q matrix.  Otherwise
+     * a new matrix is created.
+     * </p>
+     *
+     * @param Q If not null then the Q matrix is written to it.  Modified.
+     * @param compact If true an m by n matrix is created, otherwise n by n.
+     * @return The Q matrix.
+     */
+    public T getQ( T Q, boolean compact);
+
+    /**
+     * <p>
+     * Returns the R matrix from the decomposition.  Should only be
+     * called after {@link #decompose(org.ejml.data.Matrix)} has been.
+     * </p>
+     * <p>
+     * If setZeros is true then an n × m matrix is required and all the elements are set.
+     * If setZeros is false then the matrix must be at least m × m and only the upper triangular
+     * elements are set.
+     * </p>
+     *
+     * <p>
+     * If parameter R is not null, then that matrix is used to store the R matrix.  Otherwise
+     * a new matrix is created.
+     * </p>
+     *
+     * @param R If not null then the R matrix is written to it. Modified.
+     * @param compact If true only the upper triangular elements are set
+     * @return The R matrix.
+     */
+    public T getR( T R, boolean compact);
+}
diff --git a/main/core/src/org/ejml/interfaces/decomposition/QRPDecomposition.java b/main/core/src/org/ejml/interfaces/decomposition/QRPDecomposition.java
new file mode 100644
index 0000000..42efc5a
--- /dev/null
+++ b/main/core/src/org/ejml/interfaces/decomposition/QRPDecomposition.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.interfaces.decomposition;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.data.Matrix;
+
+/**
+ * <p>
+ * Similar to {@link QRDecomposition} but it can handle the rank deficient case by
+ * performing column pivots during the decomposition. The final decomposition has the
+ * following structure:<br>
+ * A*P=Q*R<br>
+ * where A is the original matrix, P is a pivot matrix, Q is an orthogonal matrix, and R is
+ * upper triangular.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public interface QRPDecomposition <T extends Matrix>
+        extends QRDecomposition<T>
+{
+    /**
+     * <p>
+     * Specifies the threshold used to flag a column as being singular.  The specified threshold is relative
+     * and will very depending on the system.  The default value is UtilEJML.EPS.
+     * </p>
+     *
+     * @param threshold Singular threshold.
+     */
+    public void setSingularThreshold( double threshold );
+
+    /**
+     * Returns the rank as determined by the algorithm.  This is dependent upon a fixed threshold
+     * and might not be appropriate for some applications.
+     *
+     * @return Matrix's rank
+     */
+    public int getRank();
+
+    /**
+     * Ordering of each column after pivoting.   The current column i was original at column pivot[i].
+     *
+     * @return Order of columns.
+     */
+    public int[] getPivots();
+
+    /**
+     * Creates the pivot matrix.
+     *
+     * @param P Optional storage for pivot matrix.  If null a new matrix will be created.
+     * @return The pivot matrix.
+     */
+    public DenseMatrix64F getPivotMatrix( DenseMatrix64F P );
+}
diff --git a/main/core/src/org/ejml/interfaces/decomposition/SingularValueDecomposition.java b/main/core/src/org/ejml/interfaces/decomposition/SingularValueDecomposition.java
new file mode 100644
index 0000000..367504a
--- /dev/null
+++ b/main/core/src/org/ejml/interfaces/decomposition/SingularValueDecomposition.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.interfaces.decomposition;
+
+import org.ejml.data.Matrix;
+
+
+/**
+ * <p>
+ * This is an abstract class for computing the singular value decomposition (SVD) of a matrix, which is defined
+ * as:<br>
+ * <div align=center> A = U * W * V <sup>T</sup> </div><br>
+ * where A is m by n, and U and V are orthogonal matrices, and  W is a diagonal matrix.
+ * </p>
+ *
+ * <p>
+ * The dimension of U,W,V depends if it is a compact SVD or not.  If not compact then U  is m by m, W is  m by n, V is n by n.
+ * If compact then let s be the number of singular values, U is m by s, W is s by s, and V is n by s.
+ * </p>
+ *
+ * <p>
+ * Accessor functions for decomposed matrices can return an internally constructed matrix if null is passed in for the
+ * optional storage parameter.  The exact behavior is implementation specific.  If an internally maintained matrix is
+ * returned then on the next call to decompose the matrix will be modified.  The advantage of this approach is reduced
+ * memory overhead.
+ * </p>
+ *
+ * <p>
+ * To create a new instance of SingularValueDecomposition see {@link org.ejml.factory.DecompositionFactory#svd(int, int, boolean, boolean, boolean)}
+ * and {@link org.ejml.ops.SingularOps} contains additional helpful SVD related functions.
+ * </p>
+ *
+ * <p>
+ * <b>*Note*</b> that the ordering of singular values is not guaranteed, unless done so by a specific implementation.
+ * The singular values can be put into descending order while adjusting U and V using {@link org.ejml.ops.SingularOps#descendingOrder(org.ejml.data.DenseMatrix64F, boolean, org.ejml.data.DenseMatrix64F, org.ejml.data.DenseMatrix64F, boolean)}  SingularOps.descendingOrder()}.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public abstract interface SingularValueDecomposition <T extends Matrix>
+        extends DecompositionInterface<T> {
+
+    /**
+     * Returns the singular values.  This is the diagonal elements of the W matrix in the decomposition.
+     * <b>Ordering of singular values is not guaranteed.</b>.
+     * 
+     * @return Singular values. Note this array can be longer than the number of singular values.
+     * Extra elements have no meaning.
+     */
+    public double [] getSingularValues();
+
+    /**
+     * The number of singular values in the matrix. This is equal to the length of the smallest side.
+     *
+     * @return Number of singular values in the matrix.
+     */
+    public int numberOfSingularValues();
+
+    /**
+     * If true then compact matrices are returned.
+     *
+     * @return true if results use compact notation.
+     */
+    public boolean isCompact();
+
+    /**
+     * <p>
+     * Returns the orthogonal 'U' matrix.
+     * </p>
+     * <p>
+     * Internally the SVD algorithm might compute U transposed or it might not.  To avoid an
+     * unnecessary double transpose the option is provided to select if the transpose is returned.
+     * </p>
+     *
+     * @param U Optional storage for U. If null a new instance or internally maintained matrix is returned.  Modified.
+     * @param transposed If the returned U is transposed.
+     * @return An orthogonal matrix.
+     */
+    public T getU( T U , boolean transposed );
+
+    /**
+     * <p>
+     * Returns the orthogonal 'V' matrix.
+     * </p>
+     *
+     * <p>
+     * Internally the SVD algorithm might compute V transposed or it might not.  To avoid an
+     * unnecessary double transpose the option is provided to select if the transpose is returned.
+     * </p>
+     *
+     * @param V Optional storage for v. If null a new instance or internally maintained matrix is returned.  Modified.
+     * @param transposed If the returned V is transposed.
+     * @return An orthogonal matrix.
+     */
+    public T getV( T V , boolean transposed );
+
+    /**
+     * Returns a diagonal matrix with the singular values.  Order of the singular values
+     * is not guaranteed.
+     *
+     * @param W Optional storage for W. If null a new instance or internally maintained matrix is returned.  Modified.
+     * @return Diagonal matrix with singular values along the diagonal.
+     */
+    public T getW( T W );
+
+    /**
+     * Number of rows in the decomposed matrix.
+     * @return Number of rows in the decomposed matrix.
+     */
+    public int numRows();
+
+    /**
+     * Number of columns in the decomposed matrix.
+     * @return Number of columns in the decomposed matrix.
+     */
+    public int numCols();
+}
diff --git a/main/core/src/org/ejml/interfaces/decomposition/TridiagonalSimilarDecomposition.java b/main/core/src/org/ejml/interfaces/decomposition/TridiagonalSimilarDecomposition.java
new file mode 100644
index 0000000..c0db05c
--- /dev/null
+++ b/main/core/src/org/ejml/interfaces/decomposition/TridiagonalSimilarDecomposition.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.interfaces.decomposition;
+
+import org.ejml.data.Matrix;
+
+
+/**
+ * <p>
+ * Finds the decomposition of a matrix in the form of:<br>
+ * <br>
+ * A = O*T*O<sup>T</sup><br>
+ * <br>
+ * where A is a symmetric m by m matrix, O is an orthogonal matrix, and T is a tridiagonal matrix.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public interface TridiagonalSimilarDecomposition<MatrixType extends Matrix>
+        extends DecompositionInterface<MatrixType> {
+
+    /**
+     * Extracts the tridiagonal matrix found in the decomposition.
+     *
+     * @param T If not null then the results will be stored here.  Otherwise a new matrix will be created.
+     * @return The extracted T matrix.
+     */
+    public MatrixType getT( MatrixType T );
+
+    /**
+     * An orthogonal matrix that has the following property: T = Q<sup>T</sup>AQ
+     *
+     * @param Q If not null then the results will be stored here.  Otherwise a new matrix will be created.
+     * @return The extracted Q matrix.
+     */
+    public MatrixType getQ( MatrixType Q , boolean transposed );
+
+    /**
+     * Extracts the diagonal and off diagonal elements of the decomposed tridiagonal matrix.
+     * Since it is symmetric only one off diagonal array is returned.
+     *
+     * @param diag Diagonal elements. Modified.
+     * @param off off diagonal elements. Modified.
+     */
+    public void getDiagonal( double []diag, double []off );
+}
diff --git a/main/core/src/org/ejml/interfaces/linsol/LinearSolver.java b/main/core/src/org/ejml/interfaces/linsol/LinearSolver.java
new file mode 100644
index 0000000..c2c44a8
--- /dev/null
+++ b/main/core/src/org/ejml/interfaces/linsol/LinearSolver.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.interfaces.linsol;
+
+import org.ejml.data.Matrix;
+import org.ejml.interfaces.decomposition.DecompositionInterface;
+
+
+/**
+ * <p>
+ * An implementation of LinearSolver solves a linear system or inverts a matrix.  It masks more complex
+ * implementation details, while giving the programmer control over memory management and performance.
+ * To quickly detect nearly singular matrices without computing the SVD the {@link #quality()}
+ * function is provided.
+ * </p>
+ *
+ * <p>
+ * A linear system is defined as:
+ * A*X = B.<br>
+ * where A ∈ ℜ <sup>m × n</sup>, X ∈ ℜ <sup>n × p</sup>,
+ * B ∈ ℜ <sup>m × p</sup>.  Different implementations can solve different
+ * types and shapes in input matrices and have different memory and runtime performance.
+ *</p>
+
+ * <p>
+ * To solve a system:<br>
+ * <ol>
+ * <li> Call {@link #setA(org.ejml.data.Matrix)}
+ * <li> Call {@link #solve(org.ejml.data.Matrix, org.ejml.data.Matrix)}.
+ * </ol>
+ * </p>
+ *
+ * <p>
+ * To invert a matrix:<br>
+ * <ol>
+ * <li> Call {@link #setA(org.ejml.data.Matrix)}
+ * <li> Call {@link #invert(org.ejml.data.Matrix)}.
+ * </ol>
+ * A matrix can also be inverted by passing in an identity matrix to solve, but this will be
+ * slower and more memory intensive than the specialized invert() function.
+ * </p>
+ *
+ * <p>
+ * <b>IMPORTANT:</b> Depending upon the implementation, input matrices might be overwritten by
+ * the solver.  This
+ * reduces memory and computational requirements and give more control to the programmer.  If
+ * the input matrices need to be not modified then {@link org.ejml.alg.dense.linsol.LinearSolverSafe} can be used.  The
+ * functions {@link #modifiesA()} and {@link #modifiesB()} specify which input matrices are being
+ * modified.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public interface LinearSolver< T extends Matrix> {
+
+    /**
+     * <p>
+     * Specifies the A matrix in the linear equation.  A reference might be saved
+     * and it might also be modified depending on the implementation.  If it is modified
+     * then {@link #modifiesA()} will return true.
+     * </p>
+     *
+     * <p>
+     * If this value returns true that does not guarantee a valid solution was generated.  This
+     * is because some decompositions don't detect singular matrices.
+     * </p>
+     *
+     * @param A The 'A' matrix in the linear equation. Might be modified or save the reference.
+     * @return true if it can be processed.
+     */
+    public boolean setA( T A );
+
+    /**
+     * <p>
+     * Returns a very quick to compute measure of how singular the system is.  This measure will
+     * be invariant to the scale of the matrix and always be positive, with larger values
+     * indicating it is less singular.  If not supported by the solver then the runtime
+     * exception IllegalArgumentException is thrown.  This is NOT the matrix's condition.
+     * </p>
+     *
+     * <p>
+     * How this function is implemented is not specified.  One possible implementation is the following:
+     * In many decompositions a triangular matrix
+     * is extracted.  The determinant of a triangular matrix is easily computed and once normalized
+     * to be scale invariant and its absolute value taken it will provide functionality described above.
+     * </p>
+     *
+     * @return The quality of the linear system.
+     */
+    public double quality();
+
+    /**
+     * <p>
+     * Solves for X in the linear system, A*X=B.
+     * </p>
+     * <p>
+     * In some implementations 'B' and 'X' can be the same instance of a variable.  Call
+     * {@link #modifiesB()} to determine if 'B' is modified.
+     * </p>
+     *
+     * @param B A matrix ℜ <sup>m × p</sup>.  Might be modified.
+     * @param X A matrix ℜ <sup>n × p</sup>, where the solution is written to.  Modified.
+     */
+    public void solve( T B , T X );
+
+
+    /**
+     * Computes the inverse of of the 'A' matrix passed into {@link #setA(org.ejml.data.Matrix)}
+     * and writes the results to the provided matrix.  If 'A_inv' needs to be different from 'A'
+     * is implementation dependent.
+     *
+     * @param A_inv Where the inverted matrix saved. Modified.
+     */
+    public void invert( T A_inv );
+
+    /**
+     * Returns true if the passed in matrix to {@link #setA(org.ejml.data.Matrix)}
+     * is modified.
+     *
+     * @return true if A is modified in setA().
+     */
+    public boolean modifiesA();
+
+    /**
+     * Returns true if the passed in 'B' matrix to {@link #solve(org.ejml.data.Matrix, org.ejml.data.Matrix)}
+     * is modified.
+     *
+     * @return true if B is modified in solve(B,X).
+     */
+    public boolean modifiesB();
+
+
+    /**
+     * If a decomposition class was used internally then this will return that class.
+     * Most linear solvers decompose the input matrix into a more simplistic form.
+     * However some solutions do not require decomposition, e.g. inverse by minor.
+     * @param <D> Decomposition type
+     * @return Internal decomposition class.  If there is none then null.
+     */
+    public <D extends DecompositionInterface>D getDecomposition();
+}
diff --git a/main/core/src/org/ejml/interfaces/linsol/ReducedRowEchelonForm.java b/main/core/src/org/ejml/interfaces/linsol/ReducedRowEchelonForm.java
new file mode 100644
index 0000000..54d4636
--- /dev/null
+++ b/main/core/src/org/ejml/interfaces/linsol/ReducedRowEchelonForm.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.interfaces.linsol;
+
+import org.ejml.data.RealMatrix64F;
+
+/**
+ * <p>
+ * An augmented system matrix is said to be in reduced row echelon form (RREF) if the following are true:
+ * </p>
+ *
+ * <ol>
+ *     <li>If a row has non-zero entries, then the first non-zero entry is 1.  This is known as the leading one.</li>
+ *     <li>If a column contains a leading one then all other entries in that column are zero.</li>
+ *     <li>If a row contains a leading 1, then each row above contains a leading 1 further to the left.</li>
+ * </ol>
+ *
+ * <p>
+ * [1] Page 19 in, Otter Bretscherm "Linear Algebra with Applications" Prentice-Hall Inc, 1997
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public interface ReducedRowEchelonForm<T extends RealMatrix64F> {
+
+    /**
+     * Puts the augmented matrix into RREF.  The coefficient matrix is stored in
+     * columns less than coefficientColumns.
+     *
+     *
+     * @param A Input: Augmented matrix.  Output: RREF.  Modified.
+     * @param coefficientColumns Number of coefficients in the system matrix.
+     */
+    public void reduce( T A , int coefficientColumns );
+
+    /**
+     * Specifies tolerance for determining if the system is singular and it should stop processing.
+     * A reasonable value is: tol = EPS/max(||tol||).
+     *
+     * @param tol Tolerance for singular matrix. A reasonable value is: tol = EPS/max(||tol||). Or just set to zero.
+     */
+    public void setTolerance(double tol);
+}
diff --git a/main/core/src/org/ejml/ops/ComplexMath64F.java b/main/core/src/org/ejml/ops/ComplexMath64F.java
new file mode 100644
index 0000000..4d2208d
--- /dev/null
+++ b/main/core/src/org/ejml/ops/ComplexMath64F.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.data.Complex64F;
+import org.ejml.data.ComplexPolar64F;
+
+/**
+ * Basic math operations on complex numbers.
+ *
+ * @author Peter Abeles
+ */
+public class ComplexMath64F
+{
+
+    /**
+     * Complex conjugate
+     * @param input Input complex number
+     * @param conj Complex conjugate of the input number
+     */
+    public static void conj( Complex64F input , Complex64F conj ) {
+        conj.real = input.real;
+        conj.imaginary = -input.imaginary;
+    }
+
+    /**
+     * <p>
+     * Addition: result = a + b
+     * </p>
+     *
+     * @param a Complex number. Not modified.
+     * @param b Complex number. Not modified.
+     * @param result Storage for output
+     */
+    public static void plus( Complex64F a , Complex64F b , Complex64F result ) {
+        result.real = a.real + b.real;
+        result.imaginary = a.imaginary + b.imaginary;
+    }
+
+    /**
+     * <p>
+     * Subtraction: result = a - b
+     * </p>
+     *
+     * @param a Complex number. Not modified.
+     * @param b Complex number. Not modified.
+     * @param result Storage for output
+     */
+    public static void minus( Complex64F a , Complex64F b , Complex64F result ) {
+        result.real = a.real - b.real;
+        result.imaginary = a.imaginary - b.imaginary;
+    }
+
+    /**
+     * <p>
+     * Multiplication: result = a * b
+     * </p>
+     *
+     * @param a Complex number. Not modified.
+     * @param b Complex number. Not modified.
+     * @param result Storage for output
+     */
+    public static void multiply(Complex64F a, Complex64F b, Complex64F result) {
+        result.real = a.real * b.real - a.imaginary*b.imaginary;
+        result.imaginary = a.real*b.imaginary + a.imaginary*b.real;
+    }
+
+    /**
+     * <p>
+     * Division: result = a / b
+     * </p>
+     *
+     * @param a Complex number. Not modified.
+     * @param b Complex number. Not modified.
+     * @param result Storage for output
+     */
+    public static void divide(Complex64F a, Complex64F b, Complex64F result) {
+        double norm = b.getMagnitude2();
+        result.real = (a.real * b.real + a.imaginary*b.imaginary)/norm;
+        result.imaginary = (a.imaginary*b.real - a.real*b.imaginary)/norm;
+    }
+
+    /**
+     * <p>
+     * Converts a complex number into polar notation.
+     * </p>
+     *
+     * @param input Standard notation
+     * @param output Polar notation
+     */
+    public static void convert( Complex64F input , ComplexPolar64F output ) {
+        output.r = input.getMagnitude();
+        output.theta = Math.atan2(input.imaginary, input.real);
+    }
+
+    /**
+     * <p>
+     * Converts a complex number in polar notation into standard notation.
+     * </p>
+     *
+     * @param input Standard notation
+     * @param output Polar notation
+     */
+    public static void convert( ComplexPolar64F input , Complex64F output ) {
+        output.real = input.r*Math.cos(input.theta);
+        output.imaginary = input.r*Math.sin(input.theta);
+    }
+
+    /**
+     * Division in polar notation.
+     *
+     * @param a Complex number in polar notation. Not modified.
+     * @param b Complex number in polar notation. Not modified.
+     * @param result Storage for output.
+     */
+    public static void multiply(ComplexPolar64F a, ComplexPolar64F b, ComplexPolar64F result)
+    {
+        result.r = a.r*b.r;
+        result.theta = a.theta + b.theta;
+    }
+
+    /**
+     * Division in polar notation.
+     *
+     * @param a Complex number in polar notation. Not modified.
+     * @param b Complex number in polar notation. Not modified.
+     * @param result Storage for output.
+     */
+    public static void divide(ComplexPolar64F a, ComplexPolar64F b, ComplexPolar64F result)
+    {
+        result.r = a.r/b.r;
+        result.theta = a.theta - b.theta;
+    }
+
+    /**
+     * Computes the power of a complex number in polar notation
+     *
+     * @param a Complex number
+     * @param N Power it is to be multiplied by
+     * @param result Result
+     */
+    public static void pow( ComplexPolar64F a , int N , ComplexPolar64F result )
+    {
+        result.r = Math.pow(a.r,N);
+        result.theta = N*a.theta;
+    }
+
+    /**
+     * Computes the N<sup>th</sup> root of a complex number in polar notation.  There are
+     * N distinct N<sup>th</sup> roots.
+     *
+     * @param a Complex number
+     * @param N The root's magnitude
+     * @param k Specifies which root.  0 ≤ k < N
+     * @param result Computed root
+     */
+    public static void root( ComplexPolar64F a , int N , int k , ComplexPolar64F result )
+    {
+        result.r = Math.pow(a.r,1.0/N);
+        result.theta = (a.theta + 2.0*k*Math.PI)/N;
+    }
+
+    /**
+     * Computes the N<sup>th</sup> root of a complex number.  There are
+     * N distinct N<sup>th</sup> roots.
+     *
+     * @param a Complex number
+     * @param N The root's magnitude
+     * @param k Specifies which root.  0 ≤ k < N
+     * @param result Computed root
+     */
+    public static void root( Complex64F a , int N , int k , Complex64F result )
+    {
+        double r = a.getMagnitude();
+        double theta = Math.atan2(a.imaginary,a.real);
+
+        r = Math.pow(r,1.0/N);
+        theta = (theta + 2.0*k*Math.PI)/N;
+
+        result.real = r*Math.cos(theta);
+        result.imaginary = r*Math.sin(theta);
+    }
+
+    /**
+     * Computes the square root of the complex number.
+     *
+     * @param input Input complex number.
+     * @param root Output. The square root of the input
+     */
+    public static void sqrt(Complex64F input, Complex64F root)
+    {
+        double r = input.getMagnitude();
+        double a = input.real;
+
+        root.real = Math.sqrt((r+a)/2.0);
+        root.imaginary = Math.sqrt((r-a)/2.0);
+        if( input.imaginary < 0 )
+            root.imaginary = -root.imaginary;
+    }
+}
diff --git a/main/core/src/org/ejml/ops/ConvertMatrixType.java b/main/core/src/org/ejml/ops/ConvertMatrixType.java
new file mode 100644
index 0000000..d0ce592
--- /dev/null
+++ b/main/core/src/org/ejml/ops/ConvertMatrixType.java
@@ -0,0 +1,744 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.data.*;
+
+/**
+ * Functions for converting between matrix types.  Both matrices must be the same size and their values will
+ * be copied.
+ *
+ * @author Peter Abeles
+ */
+public class ConvertMatrixType {
+
+    /**
+     * Generic, but slow, conversion function.
+     *
+     * @param input Input matrix.
+     * @param output Output matrix.
+     */
+    public static void convert( RealMatrix64F input , RealMatrix64F output ) {
+        if( input.getNumRows() != output.getNumRows() )
+            throw new IllegalArgumentException("Number of rows do not match");
+        if( input.getNumCols() != output.getNumCols() )
+            throw new IllegalArgumentException("Number of columns do not match");
+
+        for( int i = 0; i < input.getNumRows(); i++  ) {
+            for( int j = 0; j < input.getNumCols(); j++ ) {
+                output.unsafe_set(i,j,input.unsafe_get(i,j));
+            }
+        }
+    }
+
+    /**
+     * Converts {@link FixedMatrix2x2_64F} into {@link DenseMatrix64F}.
+     *
+     * @param input Input matrix.
+     * @param output Output matrix.  If null a new matrix will be declared.
+     * @return Converted matrix.
+     */
+    public static DenseMatrix64F convert( FixedMatrix2x2_64F input , DenseMatrix64F output ) {
+        if( output == null)
+            output = new DenseMatrix64F(2,2);
+
+        if( input.getNumRows() != output.getNumRows() )
+            throw new IllegalArgumentException("Number of rows do not match");
+        if( input.getNumCols() != output.getNumCols() )
+            throw new IllegalArgumentException("Number of columns do not match");
+
+        output.data[0] = input.a11;
+        output.data[1] = input.a12;
+        output.data[2] = input.a21;
+        output.data[3] = input.a22;
+
+        return output;
+    }
+
+    /**
+     * Converts {@link FixedMatrix3x3_64F} into {@link DenseMatrix64F}.
+     *
+     * @param input Input matrix.
+     * @param output Output matrix.  If null a new matrix will be declared.
+     * @return Converted matrix.
+     */
+    public static DenseMatrix64F convert( FixedMatrix3x3_64F input , DenseMatrix64F output ) {
+        if( output == null)
+            output = new DenseMatrix64F(3,3);
+
+        if( input.getNumRows() != output.getNumRows() )
+            throw new IllegalArgumentException("Number of rows do not match");
+        if( input.getNumCols() != output.getNumCols() )
+            throw new IllegalArgumentException("Number of columns do not match");
+
+        output.data[0] = input.a11;
+        output.data[1] = input.a12;
+        output.data[2] = input.a13;
+        output.data[3] = input.a21;
+        output.data[4] = input.a22;
+        output.data[5] = input.a23;
+        output.data[6] = input.a31;
+        output.data[7] = input.a32;
+        output.data[8] = input.a33;
+
+        return output;
+    }
+
+    /**
+     * Converts {@link FixedMatrix4x4_64F} into {@link DenseMatrix64F}.
+     *
+     * @param input Input matrix.
+     * @param output Output matrix.  If null a new matrix will be declared.
+     * @return Converted matrix.
+     */
+    public static DenseMatrix64F convert( FixedMatrix4x4_64F input , DenseMatrix64F output ) {
+        if( output == null)
+            output = new DenseMatrix64F(4,4);
+
+        if( input.getNumRows() != output.getNumRows() )
+            throw new IllegalArgumentException("Number of rows do not match");
+        if( input.getNumCols() != output.getNumCols() )
+            throw new IllegalArgumentException("Number of columns do not match");
+
+        output.data[0] = input.a11;
+        output.data[1] = input.a12;
+        output.data[2] = input.a13;
+        output.data[3] = input.a14;
+        output.data[4] = input.a21;
+        output.data[5] = input.a22;
+        output.data[6] = input.a23;
+        output.data[7] = input.a24;
+        output.data[8] = input.a31;
+        output.data[9] = input.a32;
+        output.data[10] = input.a33;
+        output.data[11] = input.a34;
+        output.data[12] = input.a41;
+        output.data[13] = input.a42;
+        output.data[14] = input.a43;
+        output.data[15] = input.a44;
+
+        return output;
+    }
+
+    /**
+     * Converts {@link FixedMatrix5x5_64F} into {@link DenseMatrix64F}.
+     *
+     * @param input Input matrix.
+     * @param output Output matrix.  If null a new matrix will be declared.
+     * @return Converted matrix.
+     */
+    public static DenseMatrix64F convert( FixedMatrix5x5_64F input , DenseMatrix64F output ) {
+        if( output == null)
+            output = new DenseMatrix64F(5,5);
+
+        if( input.getNumRows() != output.getNumRows() )
+            throw new IllegalArgumentException("Number of rows do not match");
+        if( input.getNumCols() != output.getNumCols() )
+            throw new IllegalArgumentException("Number of columns do not match");
+
+        output.data[0] = input.a11;
+        output.data[1] = input.a12;
+        output.data[2] = input.a13;
+        output.data[3] = input.a14;
+        output.data[4] = input.a15;
+        output.data[5] = input.a21;
+        output.data[6] = input.a22;
+        output.data[7] = input.a23;
+        output.data[8] = input.a24;
+        output.data[9] = input.a25;
+        output.data[10] = input.a31;
+        output.data[11] = input.a32;
+        output.data[12] = input.a33;
+        output.data[13] = input.a34;
+        output.data[14] = input.a35;
+        output.data[15] = input.a41;
+        output.data[16] = input.a42;
+        output.data[17] = input.a43;
+        output.data[18] = input.a44;
+        output.data[19] = input.a45;
+        output.data[20] = input.a51;
+        output.data[21] = input.a52;
+        output.data[22] = input.a53;
+        output.data[23] = input.a54;
+        output.data[24] = input.a55;
+
+        return output;
+    }
+
+    /**
+     * Converts {@link FixedMatrix6x6_64F} into {@link DenseMatrix64F}.
+     *
+     * @param input Input matrix.
+     * @param output Output matrix.  If null a new matrix will be declared.
+     * @return Converted matrix.
+     */
+    public static DenseMatrix64F convert( FixedMatrix6x6_64F input , DenseMatrix64F output ) {
+        if( output == null)
+            output = new DenseMatrix64F(6,6);
+
+        if( input.getNumRows() != output.getNumRows() )
+            throw new IllegalArgumentException("Number of rows do not match");
+        if( input.getNumCols() != output.getNumCols() )
+            throw new IllegalArgumentException("Number of columns do not match");
+
+        output.data[0] = input.a11;
+        output.data[1] = input.a12;
+        output.data[2] = input.a13;
+        output.data[3] = input.a14;
+        output.data[4] = input.a15;
+        output.data[5] = input.a16;
+        output.data[6] = input.a21;
+        output.data[7] = input.a22;
+        output.data[8] = input.a23;
+        output.data[9] = input.a24;
+        output.data[10] = input.a25;
+        output.data[11] = input.a26;
+        output.data[12] = input.a31;
+        output.data[13] = input.a32;
+        output.data[14] = input.a33;
+        output.data[15] = input.a34;
+        output.data[16] = input.a35;
+        output.data[17] = input.a36;
+        output.data[18] = input.a41;
+        output.data[19] = input.a42;
+        output.data[20] = input.a43;
+        output.data[21] = input.a44;
+        output.data[22] = input.a45;
+        output.data[23] = input.a46;
+        output.data[24] = input.a51;
+        output.data[25] = input.a52;
+        output.data[26] = input.a53;
+        output.data[27] = input.a54;
+        output.data[28] = input.a55;
+        output.data[29] = input.a56;
+        output.data[30] = input.a61;
+        output.data[31] = input.a62;
+        output.data[32] = input.a63;
+        output.data[33] = input.a64;
+        output.data[34] = input.a65;
+        output.data[35] = input.a66;
+
+        return output;
+    }
+
+    /**
+     * Converts {@link DenseMatrix64F} into {@link FixedMatrix2x2_64F}
+     *
+     * @param input Input matrix.
+     * @param output Output matrix.  If null a new matrix will be declared.
+     * @return Converted matrix.
+     */
+    public static FixedMatrix2x2_64F convert( DenseMatrix64F input , FixedMatrix2x2_64F output ) {
+        if( output == null)
+            output = new FixedMatrix2x2_64F();
+
+        if( input.getNumRows() != output.getNumRows() )
+            throw new IllegalArgumentException("Number of rows do not match");
+        if( input.getNumCols() != output.getNumCols() )
+            throw new IllegalArgumentException("Number of columns do not match");
+
+        output.a11 = input.data[0];
+        output.a12 = input.data[1];
+        output.a21 = input.data[2];
+        output.a22 = input.data[3];
+
+        return output;
+    }
+
+    /**
+     * Converts {@link DenseMatrix64F} into {@link FixedMatrix3x3_64F}
+     *
+     * @param input Input matrix.
+     * @param output Output matrix.  If null a new matrix will be declared.
+     * @return Converted matrix.
+     */
+    public static FixedMatrix3x3_64F convert( DenseMatrix64F input , FixedMatrix3x3_64F output ) {
+        if( output == null)
+            output = new FixedMatrix3x3_64F();
+
+        if( input.getNumRows() != output.getNumRows() )
+            throw new IllegalArgumentException("Number of rows do not match");
+        if( input.getNumCols() != output.getNumCols() )
+            throw new IllegalArgumentException("Number of columns do not match");
+
+        output.a11 = input.data[0];
+        output.a12 = input.data[1];
+        output.a13 = input.data[2];
+        output.a21 = input.data[3];
+        output.a22 = input.data[4];
+        output.a23 = input.data[5];
+        output.a31 = input.data[6];
+        output.a32 = input.data[7];
+        output.a33 = input.data[8];
+
+        return output;
+    }
+
+    /**
+     * Converts {@link DenseMatrix64F} into {@link FixedMatrix4x4_64F}
+     *
+     * @param input Input matrix.
+     * @param output Output matrix.  If null a new matrix will be declared.
+     * @return Converted matrix.
+     */
+    public static FixedMatrix4x4_64F convert( DenseMatrix64F input , FixedMatrix4x4_64F output ) {
+        if( output == null)
+            output = new FixedMatrix4x4_64F();
+
+        if( input.getNumRows() != output.getNumRows() )
+            throw new IllegalArgumentException("Number of rows do not match");
+        if( input.getNumCols() != output.getNumCols() )
+            throw new IllegalArgumentException("Number of columns do not match");
+
+        output.a11 = input.data[0];
+        output.a12 = input.data[1];
+        output.a13 = input.data[2];
+        output.a14 = input.data[3];
+        output.a21 = input.data[4];
+        output.a22 = input.data[5];
+        output.a23 = input.data[6];
+        output.a24 = input.data[7];
+        output.a31 = input.data[8];
+        output.a32 = input.data[9];
+        output.a33 = input.data[10];
+        output.a34 = input.data[11];
+        output.a41 = input.data[12];
+        output.a42 = input.data[13];
+        output.a43 = input.data[14];
+        output.a44 = input.data[15];
+
+        return output;
+    }
+
+    /**
+     * Converts {@link DenseMatrix64F} into {@link FixedMatrix5x5_64F}
+     *
+     * @param input Input matrix.
+     * @param output Output matrix.  If null a new matrix will be declared.
+     * @return Converted matrix.
+     */
+    public static FixedMatrix5x5_64F convert( DenseMatrix64F input , FixedMatrix5x5_64F output ) {
+        if( output == null)
+            output = new FixedMatrix5x5_64F();
+
+        if( input.getNumRows() != output.getNumRows() )
+            throw new IllegalArgumentException("Number of rows do not match");
+        if( input.getNumCols() != output.getNumCols() )
+            throw new IllegalArgumentException("Number of columns do not match");
+
+        output.a11 = input.data[0];
+        output.a12 = input.data[1];
+        output.a13 = input.data[2];
+        output.a14 = input.data[3];
+        output.a15 = input.data[4];
+        output.a21 = input.data[5];
+        output.a22 = input.data[6];
+        output.a23 = input.data[7];
+        output.a24 = input.data[8];
+        output.a25 = input.data[9];
+        output.a31 = input.data[10];
+        output.a32 = input.data[11];
+        output.a33 = input.data[12];
+        output.a34 = input.data[13];
+        output.a35 = input.data[14];
+        output.a41 = input.data[15];
+        output.a42 = input.data[16];
+        output.a43 = input.data[17];
+        output.a44 = input.data[18];
+        output.a45 = input.data[19];
+        output.a51 = input.data[20];
+        output.a52 = input.data[21];
+        output.a53 = input.data[22];
+        output.a54 = input.data[23];
+        output.a55 = input.data[24];
+
+        return output;
+    }
+
+    /**
+     * Converts {@link DenseMatrix64F} into {@link FixedMatrix6x6_64F}
+     *
+     * @param input Input matrix.
+     * @param output Output matrix.  If null a new matrix will be declared.
+     * @return Converted matrix.
+     */
+    public static FixedMatrix6x6_64F convert( DenseMatrix64F input , FixedMatrix6x6_64F output ) {
+        if( output == null)
+            output = new FixedMatrix6x6_64F();
+
+        if( input.getNumRows() != output.getNumRows() )
+            throw new IllegalArgumentException("Number of rows do not match");
+        if( input.getNumCols() != output.getNumCols() )
+            throw new IllegalArgumentException("Number of columns do not match");
+
+        output.a11 = input.data[0];
+        output.a12 = input.data[1];
+        output.a13 = input.data[2];
+        output.a14 = input.data[3];
+        output.a15 = input.data[4];
+        output.a16 = input.data[5];
+        output.a21 = input.data[6];
+        output.a22 = input.data[7];
+        output.a23 = input.data[8];
+        output.a24 = input.data[9];
+        output.a25 = input.data[10];
+        output.a26 = input.data[11];
+        output.a31 = input.data[12];
+        output.a32 = input.data[13];
+        output.a33 = input.data[14];
+        output.a34 = input.data[15];
+        output.a35 = input.data[16];
+        output.a36 = input.data[17];
+        output.a41 = input.data[18];
+        output.a42 = input.data[19];
+        output.a43 = input.data[20];
+        output.a44 = input.data[21];
+        output.a45 = input.data[22];
+        output.a46 = input.data[23];
+        output.a51 = input.data[24];
+        output.a52 = input.data[25];
+        output.a53 = input.data[26];
+        output.a54 = input.data[27];
+        output.a55 = input.data[28];
+        output.a56 = input.data[29];
+        output.a61 = input.data[30];
+        output.a62 = input.data[31];
+        output.a63 = input.data[32];
+        output.a64 = input.data[33];
+        output.a65 = input.data[34];
+        output.a66 = input.data[35];
+
+        return output;
+    }
+
+    /**
+     * Converts {@link FixedMatrix2_64F} into {@link DenseMatrix64F}.
+     *
+     * @param input Input matrix.
+     * @param output Output matrix.  If null a new matrix will be declared.
+     * @return Converted matrix.
+     */
+    public static DenseMatrix64F convert( FixedMatrix2_64F input , DenseMatrix64F output ) {
+        if( output == null)
+            output = new DenseMatrix64F(2,1);
+
+        if( output.getNumRows() != 1 && output.getNumCols() != 1 )
+            throw new IllegalArgumentException("One row or column must have a length of 1 for it to be a vector");
+        int length = Math.max(output.getNumRows(),output.getNumCols());
+        if( length != 2 )
+            throw new IllegalArgumentException("Length of input vector is not 2.  It is "+length);
+
+        output.data[0] = input.a1;
+        output.data[1] = input.a2;
+
+        return output;
+    }
+
+    /**
+     * Converts {@link FixedMatrix3_64F} into {@link DenseMatrix64F}.
+     *
+     * @param input Input matrix.
+     * @param output Output matrix.  If null a new matrix will be declared.
+     * @return Converted matrix.
+     */
+    public static DenseMatrix64F convert( FixedMatrix3_64F input , DenseMatrix64F output ) {
+        if( output == null)
+            output = new DenseMatrix64F(3,1);
+
+        if( output.getNumRows() != 1 && output.getNumCols() != 1 )
+            throw new IllegalArgumentException("One row or column must have a length of 1 for it to be a vector");
+        int length = Math.max(output.getNumRows(),output.getNumCols());
+        if( length != 3 )
+            throw new IllegalArgumentException("Length of input vector is not 3.  It is "+length);
+
+        output.data[0] = input.a1;
+        output.data[1] = input.a2;
+        output.data[2] = input.a3;
+
+        return output;
+    }
+
+    /**
+     * Converts {@link FixedMatrix4_64F} into {@link DenseMatrix64F}.
+     *
+     * @param input Input matrix.
+     * @param output Output matrix.  If null a new matrix will be declared.
+     * @return Converted matrix.
+     */
+    public static DenseMatrix64F convert( FixedMatrix4_64F input , DenseMatrix64F output ) {
+        if( output == null)
+            output = new DenseMatrix64F(4,1);
+
+        if( output.getNumRows() != 1 && output.getNumCols() != 1 )
+            throw new IllegalArgumentException("One row or column must have a length of 1 for it to be a vector");
+        int length = Math.max(output.getNumRows(),output.getNumCols());
+        if( length != 4 )
+            throw new IllegalArgumentException("Length of input vector is not 4.  It is "+length);
+
+        output.data[0] = input.a1;
+        output.data[1] = input.a2;
+        output.data[2] = input.a3;
+        output.data[3] = input.a4;
+
+        return output;
+    }
+
+    /**
+     * Converts {@link FixedMatrix5_64F} into {@link DenseMatrix64F}.
+     *
+     * @param input Input matrix.
+     * @param output Output matrix.  If null a new matrix will be declared.
+     * @return Converted matrix.
+     */
+    public static DenseMatrix64F convert( FixedMatrix5_64F input , DenseMatrix64F output ) {
+        if( output == null)
+            output = new DenseMatrix64F(5,1);
+
+        if( output.getNumRows() != 1 && output.getNumCols() != 1 )
+            throw new IllegalArgumentException("One row or column must have a length of 1 for it to be a vector");
+        int length = Math.max(output.getNumRows(),output.getNumCols());
+        if( length != 5 )
+            throw new IllegalArgumentException("Length of input vector is not 5.  It is "+length);
+
+        output.data[0] = input.a1;
+        output.data[1] = input.a2;
+        output.data[2] = input.a3;
+        output.data[3] = input.a4;
+        output.data[4] = input.a5;
+
+        return output;
+    }
+
+    /**
+     * Converts {@link FixedMatrix6_64F} into {@link DenseMatrix64F}.
+     *
+     * @param input Input matrix.
+     * @param output Output matrix.  If null a new matrix will be declared.
+     * @return Converted matrix.
+     */
+    public static DenseMatrix64F convert( FixedMatrix6_64F input , DenseMatrix64F output ) {
+        if( output == null)
+            output = new DenseMatrix64F(6,1);
+
+        if( output.getNumRows() != 1 && output.getNumCols() != 1 )
+            throw new IllegalArgumentException("One row or column must have a length of 1 for it to be a vector");
+        int length = Math.max(output.getNumRows(),output.getNumCols());
+        if( length != 6 )
+            throw new IllegalArgumentException("Length of input vector is not 6.  It is "+length);
+
+        output.data[0] = input.a1;
+        output.data[1] = input.a2;
+        output.data[2] = input.a3;
+        output.data[3] = input.a4;
+        output.data[4] = input.a5;
+        output.data[5] = input.a6;
+
+        return output;
+    }
+
+    /**
+     * Converts {@link DenseMatrix64F} into {@link FixedMatrix2_64F}
+     *
+     * @param input Input matrix.
+     * @param output Output matrix.  If null a new matrix will be declared.
+     * @return Converted matrix.
+     */
+    public static FixedMatrix2_64F convert( DenseMatrix64F input , FixedMatrix2_64F output ) {
+        if( output == null)
+            output = new FixedMatrix2_64F();
+
+        if( input.getNumRows() != 1 && input.getNumCols() != 1 )
+            throw new IllegalArgumentException("One row or column must have a length of 1 for it to be a vector");
+        int length = Math.max(input.getNumRows(),input.getNumCols());
+        if( length != 2 )
+            throw new IllegalArgumentException("Length of input vector is not 2.  It is "+length);
+
+        output.a1 = input.data[0];
+        output.a2 = input.data[1];
+
+        return output;
+    }
+
+    /**
+     * Converts {@link DenseMatrix64F} into {@link FixedMatrix3_64F}
+     *
+     * @param input Input matrix.
+     * @param output Output matrix.  If null a new matrix will be declared.
+     * @return Converted matrix.
+     */
+    public static FixedMatrix3_64F convert( DenseMatrix64F input , FixedMatrix3_64F output ) {
+        if( output == null)
+            output = new FixedMatrix3_64F();
+
+        if( input.getNumRows() != 1 && input.getNumCols() != 1 )
+            throw new IllegalArgumentException("One row or column must have a length of 1 for it to be a vector");
+        int length = Math.max(input.getNumRows(),input.getNumCols());
+        if( length != 3 )
+            throw new IllegalArgumentException("Length of input vector is not 3.  It is "+length);
+
+        output.a1 = input.data[0];
+        output.a2 = input.data[1];
+        output.a3 = input.data[2];
+
+        return output;
+    }
+
+    /**
+     * Converts {@link DenseMatrix64F} into {@link FixedMatrix4_64F}
+     *
+     * @param input Input matrix.
+     * @param output Output matrix.  If null a new matrix will be declared.
+     * @return Converted matrix.
+     */
+    public static FixedMatrix4_64F convert( DenseMatrix64F input , FixedMatrix4_64F output ) {
+        if( output == null)
+            output = new FixedMatrix4_64F();
+
+        if( input.getNumRows() != 1 && input.getNumCols() != 1 )
+            throw new IllegalArgumentException("One row or column must have a length of 1 for it to be a vector");
+        int length = Math.max(input.getNumRows(),input.getNumCols());
+        if( length != 4 )
+            throw new IllegalArgumentException("Length of input vector is not 4.  It is "+length);
+
+        output.a1 = input.data[0];
+        output.a2 = input.data[1];
+        output.a3 = input.data[2];
+        output.a4 = input.data[3];
+
+        return output;
+    }
+
+    /**
+     * Converts {@link DenseMatrix64F} into {@link FixedMatrix5_64F}
+     *
+     * @param input Input matrix.
+     * @param output Output matrix.  If null a new matrix will be declared.
+     * @return Converted matrix.
+     */
+    public static FixedMatrix5_64F convert( DenseMatrix64F input , FixedMatrix5_64F output ) {
+        if( output == null)
+            output = new FixedMatrix5_64F();
+
+        if( input.getNumRows() != 1 && input.getNumCols() != 1 )
+            throw new IllegalArgumentException("One row or column must have a length of 1 for it to be a vector");
+        int length = Math.max(input.getNumRows(),input.getNumCols());
+        if( length != 5 )
+            throw new IllegalArgumentException("Length of input vector is not 5.  It is "+length);
+
+        output.a1 = input.data[0];
+        output.a2 = input.data[1];
+        output.a3 = input.data[2];
+        output.a4 = input.data[3];
+        output.a5 = input.data[4];
+
+        return output;
+    }
+
+    /**
+     * Converts {@link DenseMatrix64F} into {@link FixedMatrix6_64F}
+     *
+     * @param input Input matrix.
+     * @param output Output matrix.  If null a new matrix will be declared.
+     * @return Converted matrix.
+     */
+    public static FixedMatrix6_64F convert( DenseMatrix64F input , FixedMatrix6_64F output ) {
+        if( output == null)
+            output = new FixedMatrix6_64F();
+
+        if( input.getNumRows() != 1 && input.getNumCols() != 1 )
+            throw new IllegalArgumentException("One row or column must have a length of 1 for it to be a vector");
+        int length = Math.max(input.getNumRows(),input.getNumCols());
+        if( length != 6 )
+            throw new IllegalArgumentException("Length of input vector is not 6.  It is "+length);
+
+        output.a1 = input.data[0];
+        output.a2 = input.data[1];
+        output.a3 = input.data[2];
+        output.a4 = input.data[3];
+        output.a5 = input.data[4];
+        output.a6 = input.data[5];
+
+        return output;
+    }
+
+    /**
+     * Converts {@link DenseMatrix64F} into {@link BlockMatrix64F}
+     *
+     * Can't handle null output matrix since block size needs to be specified.
+     *
+     * @param src Input matrix.
+     * @param dst Output matrix.
+     */
+    public static void convert( DenseMatrix64F src , BlockMatrix64F dst ) {
+        if( src.numRows != dst.numRows || src.numCols != dst.numCols )
+            throw new IllegalArgumentException("Must be the same size.");
+
+        for( int i = 0; i < dst.numRows; i += dst.blockLength ) {
+            int blockHeight = Math.min( dst.blockLength , dst.numRows - i);
+
+            for( int j = 0; j < dst.numCols; j += dst.blockLength ) {
+                int blockWidth = Math.min( dst.blockLength , dst.numCols - j);
+
+                int indexDst = i*dst.numCols + blockHeight*j;
+                int indexSrcRow = i*dst.numCols + j;
+
+                for( int k = 0; k < blockHeight; k++ ) {
+                    System.arraycopy(src.data,indexSrcRow,dst.data,indexDst,blockWidth);
+                    indexDst += blockWidth;
+                    indexSrcRow += dst.numCols;
+                }
+            }
+        }
+    }
+
+    /**
+     * Converts {@link BlockMatrix64F} into {@link DenseMatrix64F}
+     *
+     * @param src Input matrix.
+     * @param dst Output matrix.  If null a new matrix will be declared.
+     * @return Converted matrix.
+     */
+    public static DenseMatrix64F convert( BlockMatrix64F src , DenseMatrix64F dst ) {
+        if( dst != null ) {
+            if( dst.numRows != src.numRows || dst.numCols != src.numCols )
+                throw new IllegalArgumentException("Must be the same size.");
+        } else {
+            dst = new DenseMatrix64F(src.numRows,src.numCols);
+        }
+
+        for( int i = 0; i < src.numRows; i += src.blockLength ) {
+            int blockHeight = Math.min( src.blockLength , src.numRows - i);
+
+            for( int j = 0; j < src.numCols; j += src.blockLength ) {
+                int blockWidth = Math.min( src.blockLength , src.numCols - j);
+
+                int indexSrc = i*src.numCols + blockHeight*j;
+                int indexDstRow = i*dst.numCols + j;
+
+                for( int k = 0; k < blockHeight; k++ ) {
+                    System.arraycopy(src.data,indexSrc,dst.data,indexDstRow,blockWidth);
+                    indexSrc += blockWidth;
+                    indexDstRow += dst.numCols;
+                }
+            }
+        }
+
+        return dst;
+    }
+}
diff --git a/main/core/src/org/ejml/ops/MatrixDimensionException.java b/main/core/src/org/ejml/ops/MatrixDimensionException.java
new file mode 100644
index 0000000..786ab20
--- /dev/null
+++ b/main/core/src/org/ejml/ops/MatrixDimensionException.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+
+/**
+ * If two matrices did not have compatible dimensions for the operation this exception
+ * is thrown.
+ *
+ * @author Peter Abeles
+ */
+public class MatrixDimensionException 
+    extends RuntimeException
+{
+    public MatrixDimensionException(){}
+
+    public MatrixDimensionException(String message ) {
+        super(message);
+    }
+}
diff --git a/main/core/src/org/ejml/ops/MatrixIO.java b/main/core/src/org/ejml/ops/MatrixIO.java
new file mode 100644
index 0000000..d9caf78
--- /dev/null
+++ b/main/core/src/org/ejml/ops/MatrixIO.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.data.*;
+
+import java.io.*;
+
+
+/**
+ * Provides simple to use routines for reading and writing matrices to and from files.
+ *
+ * @author Peter Abeles
+ */
+public class MatrixIO {
+
+    /**
+     * Saves a matrix to disk using Java binary serialization.
+     *
+     * @param A The matrix being saved.
+     * @param fileName Name of the file its being saved at.
+     * @throws java.io.IOException
+     */
+    public static void saveBin(RealMatrix64F A, String fileName)
+        throws IOException
+    {
+        FileOutputStream fileStream = new FileOutputStream(fileName);
+        ObjectOutputStream stream = new ObjectOutputStream(fileStream);
+
+        try {
+            stream.writeObject(A);
+            stream.flush();
+        } finally {
+            // clean up
+            try {
+                stream.close();
+            } finally {
+                fileStream.close();
+            }
+        }
+
+    }
+
+    /**
+     * Loads a DeneMatrix64F which has been saved to file using Java binary
+     * serialization.
+     *
+     * @param fileName The file being loaded.
+     * @return  DenseMatrix64F
+     * @throws IOException
+     */
+    public static <T extends RealMatrix64F> T loadBin(String fileName)
+        throws IOException
+    {
+        FileInputStream fileStream = new FileInputStream(fileName);
+        ObjectInputStream stream = new ObjectInputStream(fileStream);
+
+        T ret;
+        try {
+            ret = (T)stream.readObject();
+            if( stream.available() !=  0 ) {
+                throw new RuntimeException("File not completely read?");
+            }
+        } catch (ClassNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+
+        stream.close();
+        return (T)ret;
+    }
+
+    /**
+     * Saves a matrix to disk using in a Column Space Value (CSV) format. For a 
+     * description of the format see {@link MatrixIO#loadCSV(String)}.
+     *
+     * @param A The matrix being saved.
+     * @param fileName Name of the file its being saved at.
+     * @throws java.io.IOException
+     */
+    public static void saveCSV( RealMatrix64F A , String fileName )
+        throws IOException
+    {
+        PrintStream fileStream = new PrintStream(fileName);
+
+        fileStream.println(A.getNumRows() + " " + A.getNumCols() + " real");
+        for( int i = 0; i < A.getNumRows(); i++ ) {
+            for( int j = 0; j < A.getNumCols(); j++ ) {
+                fileStream.print(A.get(i,j)+" ");
+            }
+            fileStream.println();
+        }
+        fileStream.close();
+    }
+
+    /**
+     * Reads a matrix in which has been encoded using a Column Space Value (CSV)
+     * file format. The number of rows and columns are read in on the first line. Then
+     * each row is read in the subsequent lines.
+     *
+     * @param fileName The file being loaded.
+     * @return DenseMatrix64F
+     * @throws IOException
+     */
+    public static DenseMatrix64F loadCSV( String fileName )
+        throws IOException
+    {
+        FileInputStream fileStream = new FileInputStream(fileName);
+        ReadMatrixCsv csv = new ReadMatrixCsv(fileStream);
+
+        DenseMatrix64F ret = csv.read();
+
+        fileStream.close();
+
+        return ret;
+    }
+
+    /**
+     * Reads a matrix in which has been encoded using a Column Space Value (CSV)
+     * file format.  For a description of the format see {@link MatrixIO#loadCSV(String)}.
+     *
+     * @param fileName The file being loaded.
+     * @param numRows number of rows in the matrix.
+     * @param numCols number of columns in the matrix.
+     * @return DenseMatrix64F
+     * @throws IOException
+     */
+    public static DenseMatrix64F loadCSV( String fileName , int numRows , int numCols )
+        throws IOException
+    {
+        FileInputStream fileStream = new FileInputStream(fileName);
+        ReadMatrixCsv csv = new ReadMatrixCsv(fileStream);
+
+        DenseMatrix64F ret = csv.readReal(numRows, numCols);
+
+        fileStream.close();
+
+        return ret;
+    }
+
+    public static void print( PrintStream out , RealMatrix64F mat ) {
+        print(out,mat,6,3);
+    }
+
+    public static void print(PrintStream out, RealMatrix64F mat , int numChar , int precision ) {
+        String format = "%"+numChar+"."+precision+"f ";
+
+        print(out, mat,format);
+    }
+
+    public static void print(PrintStream out , RealMatrix64F mat , String format ) {
+
+        String type = ReshapeMatrix.class.isAssignableFrom(mat.getClass()) ? "dense" : "dense fixed";
+
+        out.println("Type = "+type+" real , numRows = "+mat.getNumRows()+" , numCols = "+mat.getNumCols());
+
+        format += " ";
+
+        for( int y = 0; y < mat.getNumRows(); y++ ) {
+            for( int x = 0; x < mat.getNumCols(); x++ ) {
+                out.printf(format,mat.get(y,x));
+            }
+            out.println();
+        }
+    }
+
+    public static void print( PrintStream out , RealMatrix32F mat ) {
+        print(out,mat,6,3);
+    }
+
+    public static void print(PrintStream out, RealMatrix32F mat , int numChar , int precision ) {
+        String format = "%"+numChar+"."+precision+"f ";
+
+        print(out, mat,format);
+    }
+
+    public static void print(PrintStream out , RealMatrix32F mat , String format ) {
+
+        String type = ReshapeMatrix.class.isAssignableFrom(mat.getClass()) ? "dense" : "dense fixed";
+
+        out.println("Type = "+type+" , numRows = "+mat.getNumRows()+" , numCols = "+mat.getNumCols());
+
+        format += " ";
+
+        for( int y = 0; y < mat.getNumRows(); y++ ) {
+            for( int x = 0; x < mat.getNumCols(); x++ ) {
+                out.printf(format,mat.get(y,x));
+            }
+            out.println();
+        }
+    }
+
+    public static void print( PrintStream out , RealMatrix64F mat , String format ,
+                              int row0 , int row1, int col0 , int col1 ) {
+        out.println("Type = submatrix , rows "+row0+" to "+row1+"  columns "+col0+" to "+col1);
+
+        format += " ";
+
+        for( int y = row0; y < row1; y++ ) {
+            for( int x = col0; x < col1; x++ ) {
+                out.printf(format,mat.get(y,x));
+            }
+            out.println();
+        }
+    }
+
+    public static void print( PrintStream out , ComplexMatrix64F mat ) {
+        print(out,mat,6,3);
+    }
+
+    public static void print(PrintStream out, ComplexMatrix64F mat , int numChar , int precision ) {
+        String format = "%"+numChar+"."+precision+"f + %"+numChar+"."+precision+"fi";
+
+        print(out, mat,format);
+    }
+
+    public static void print(PrintStream out , ComplexMatrix64F mat , String format ) {
+
+        String type = "dense";
+
+        out.println("Type = "+type+" complex , numRows = "+mat.getNumRows()+" , numCols = "+mat.getNumCols());
+
+        format += " ";
+
+        Complex64F c = new Complex64F();
+        for( int y = 0; y < mat.getNumRows(); y++ ) {
+            for( int x = 0; x < mat.getNumCols(); x++ ) {
+                mat.get(y,x,c);
+                out.printf(format,c.real,c.imaginary);
+                if( x < mat.getNumCols()-1 ) {
+                    out.print(" , ");
+                }
+            }
+            out.println();
+        }
+    }
+
+//    public static void main( String []args ) {
+//        Random rand = new Random(234234);
+//        DenseMatrix64F A = RandomMatrices.createRandom(50,70,rand);
+//
+//        SingularValueDecomposition decomp = DecompositionFactory.svd();
+//
+//        decomp.decompose(A);
+//
+//        displayMatrix(A,"Original");
+//        displayMatrix(decomp.getU(false),"U");
+//        displayMatrix(decomp.getV(false),"V");
+//        displayMatrix(decomp.getW(null),"W");
+//    }
+}
diff --git a/main/core/src/org/ejml/ops/ReadCsv.java b/main/core/src/org/ejml/ops/ReadCsv.java
new file mode 100644
index 0000000..82aed9d
--- /dev/null
+++ b/main/core/src/org/ejml/ops/ReadCsv.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2009-2013, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <p>
+ * Base class for reading CSV formatted files.  CSV stands for column-space-value where text strings are separated
+ * by a space character.  The values are typically stored in a human readable format.  The encoded text for a single
+ * variable is referred to as a word.
+ * </p>
+ *
+ * <p>
+ * Comments are allowed and identified by starting a line with the comment character.  The comment character is user
+ * configurable.  By default there is no comment character.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class ReadCsv {
+    // if there is a comment character
+    private boolean hasComment = false;
+    // what the comment character is
+    private char comment;
+
+    // reader for the input stream
+    private BufferedReader in;
+
+    // number of lines that have been read
+    private int lineNumber = 0;
+
+    /**
+     * Constructor for ReadCsv
+     *
+     * @param in Where the input comes from.
+     */
+    public ReadCsv(InputStream in) {
+        this.in = new BufferedReader(new InputStreamReader(in));
+    }
+
+    /**
+     * Sets the comment character.  All lines that start with this character will be ignored.
+     *
+     * @param comment The new comment character.
+     */
+    public void setComment(char comment) {
+        hasComment = true;
+        this.comment = comment;
+    }
+
+    /**
+     * Returns how many lines have been read.
+     *
+     * @return Line number
+     */
+    public int getLineNumber() {
+        return lineNumber;
+    }
+
+    /**
+     * Returns the reader that it is using internally.
+     * @return The reader.
+     */
+    public BufferedReader getReader() {
+        return in;
+    }
+
+    /**
+     * Finds the next valid line of words in the stream and extracts them.
+     *
+     * @return List of valid words on the line.  null if the end of the file has been reached.
+     * @throws java.io.IOException
+     */
+    protected List<String> extractWords() throws IOException
+    {
+        while( true ) {
+            lineNumber++;
+            String line = in.readLine();
+            if( line == null ) {
+                return null;
+            }
+
+            // skip comment lines
+            if( hasComment ) {
+                if( line.charAt(0) == comment )
+                    continue;
+            }
+
+            // extract the words, which are the variables encoded
+            return parseWords(line);
+        }
+    }
+
+    /**
+     * Extracts the words from a string.  Words are seperated by a space character.
+     *
+     * @param line The line that is being parsed.
+     * @return A list of words contained on the line.
+     */
+    protected List<String> parseWords(String line) {
+        List<String> words = new ArrayList<String>();
+        boolean insideWord = !isSpace(line.charAt(0));
+        int last = 0;
+        for( int i = 0; i < line.length(); i++) {
+            char c = line.charAt(i);
+
+            if( insideWord ) {
+                // see if its at the end of a word
+                if( isSpace(c)) {
+                    words.add( line.substring(last,i) );
+                    insideWord = false;
+                }
+            } else {
+                if( !isSpace(c)) {
+                    last = i;
+                    insideWord = true;
+                }
+            }
+        }
+
+        // if the line ended add the final word
+        if( insideWord ) {
+            words.add( line.substring(last));
+        }
+        return words;
+    }
+
+    /**
+     * Checks to see if 'c' is a space character or not.
+     *
+     * @param c The character being tested.
+     * @return if it is a space character or not.
+     */
+    private boolean isSpace(char c) {
+        return c == ' ' || c == '\t';
+    }
+}
diff --git a/main/core/src/org/ejml/ops/ReadMatrixCsv.java b/main/core/src/org/ejml/ops/ReadMatrixCsv.java
new file mode 100644
index 0000000..7eaf333
--- /dev/null
+++ b/main/core/src/org/ejml/ops/ReadMatrixCsv.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.data.Matrix;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+
+/**
+ * Reads in a matrix that is in a column-space-value (CSV) format.
+ *
+ * @author Peter Abeles
+ */
+public class ReadMatrixCsv extends ReadCsv {
+
+    /**
+     * Specifies where input comes from.
+     *
+     * @param in Where the input comes from.
+     */
+    public ReadMatrixCsv(InputStream in) {
+        super(in);
+    }
+
+    /**
+     * Reads in a DenseMatrix64F from the IO stream.
+     * @return DenseMatrix64F
+     * @throws IOException If anything goes wrong.
+     */
+    public <M extends Matrix>M read() throws IOException {
+        List<String> words = extractWords();
+        if( words.size() != 3 )
+            throw new IOException("Unexpected number of words on first line.");
+
+        int numRows = Integer.parseInt(words.get(0));
+        int numCols = Integer.parseInt(words.get(1));
+        boolean real = words.get(2).compareToIgnoreCase("real") == 0;
+
+        if( numRows < 0 || numCols < 0)
+            throw new IOException("Invalid number of rows and/or columns: "+numRows+" "+numCols);
+
+        if( real )
+            return (M)readReal(numRows, numCols);
+        else
+            return (M)readComplex(numRows, numCols);
+    }
+
+    /**
+     * Reads in a DenseMatrix64F from the IO stream where the user specifies the matrix dimensions.
+     *
+     * @param numRows Number of rows in the matrix
+     * @param numCols Number of columns in the matrix
+     * @return DenseMatrix64F
+     * @throws IOException
+     */
+    public DenseMatrix64F readReal(int numRows, int numCols) throws IOException {
+
+        DenseMatrix64F A = new DenseMatrix64F(numRows,numCols);
+
+        for( int i = 0; i < numRows; i++ ) {
+            List<String> words = extractWords();
+            if( words == null )
+                throw new IOException("Too few rows found. expected "+numRows+" actual "+i);
+
+            if( words.size() != numCols )
+                throw new IOException("Unexpected number of words in column. Found "+words.size()+" expected "+numCols);
+            for( int j = 0; j < numCols; j++ ) {
+                A.set(i,j,Double.parseDouble(words.get(j)));
+            }
+        }
+
+        return A;
+    }
+
+    /**
+     * Reads in a CDenseMatrix64F from the IO stream where the user specifies the matrix dimensions.
+     *
+     * @param numRows Number of rows in the matrix
+     * @param numCols Number of columns in the matrix
+     * @return DenseMatrix64F
+     * @throws IOException
+     */
+    public CDenseMatrix64F readComplex(int numRows, int numCols) throws IOException {
+
+        CDenseMatrix64F A = new CDenseMatrix64F(numRows,numCols);
+
+        int wordsCol = numCols*2;
+
+        for( int i = 0; i < numRows; i++ ) {
+            List<String> words = extractWords();
+            if( words == null )
+                throw new IOException("Too few rows found. expected "+numRows+" actual "+i);
+
+            if( words.size() != wordsCol )
+                throw new IOException("Unexpected number of words in column. Found "+words.size()+" expected "+wordsCol);
+            for( int j = 0; j < wordsCol; j += 2 ) {
+
+                double real = Double.parseDouble(words.get(j));
+                double imaginary = Double.parseDouble(words.get(j+1));
+
+                A.set(i, j, real, imaginary);
+            }
+        }
+
+        return A;
+    }
+}
diff --git a/main/core/test/org/ejml/TestUtilEjml.java b/main/core/test/org/ejml/TestUtilEjml.java
new file mode 100644
index 0000000..a1b9fdf
--- /dev/null
+++ b/main/core/test/org/ejml/TestUtilEjml.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2009-2013, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml;
+
+import org.ejml.data.DenseMatrix64F;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestUtilEjml {
+
+    Random rand = new Random(23423);
+
+
+    @Test
+    public void max_array() {
+        double a[] = new double[]{-1,2,3,4,5,6,3,4,5,7,8,2,3,-5,-6};
+
+        assertTrue(8==UtilEjml.max(a,0,a.length));
+        assertTrue(5==UtilEjml.max(a,6,3));
+    }
+
+    /**
+     * Provide it a couple of different strings to parse.  Then compare
+     * the results against the expect answer
+     */
+    @Test
+    public void testParseMatrix() {
+        String a = "-0.779094   1.682750\n" +
+                 "   1.304014  -1.880739\n";
+
+        DenseMatrix64F m = UtilEjml.parseMatrix(a,2);
+
+        assertEquals(2,m.numCols);
+        assertEquals(2,m.numRows);
+        assertEquals(-0.779094 , m.get(0,0) , 1e-5);
+        assertEquals(1.682750  , m.get(0,1)  , 1e-5);
+        assertEquals(1.304014  , m.get(1,0)  , 1e-5);
+        assertEquals(-1.880739 , m.get(1,1) , 1e-5);
+
+        // give it a matrix with a space in the first element, see if that screws it up
+        a = " -0.779094   1.682750  5\n" +
+           "   1.304014  -1.880739  8\n";
+
+        m = UtilEjml.parseMatrix(a,3);
+        assertEquals(3,m.numCols);
+        assertEquals(2,m.numRows);
+        assertEquals(-0.779094 , m.get(0,0) , 1e-5);
+    }
+}
diff --git a/main/core/test/org/ejml/alg/dense/linsol/TestLinearSolverSafe.java b/main/core/test/org/ejml/alg/dense/linsol/TestLinearSolverSafe.java
new file mode 100644
index 0000000..d201d37
--- /dev/null
+++ b/main/core/test/org/ejml/alg/dense/linsol/TestLinearSolverSafe.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.DecompositionInterface;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestLinearSolverSafe {
+
+    Random rand = new Random(234);
+
+    DenseMatrix64F Ainput = new DenseMatrix64F(1,1);
+    DenseMatrix64F Binput = new DenseMatrix64F(1,1);
+
+    /**
+     * Checks to see if the input matrix is copied after multiple calls.  This was an actual bug.
+     */
+    @Test
+    public void multipleCalls_setA() {
+        DummySolver dummy = new DummySolver(true,false);
+        dummy.expectedA = 5;
+
+        LinearSolver<DenseMatrix64F> s = new LinearSolverSafe<DenseMatrix64F>(dummy);
+
+        Ainput.set(0,5);
+        s.setA(Ainput);
+        // call it a second time and see if the input matrix has been reset to the
+        // correct value
+        s.setA(Ainput);
+
+        assertTrue(dummy.passedin != Ainput);
+    }
+
+    /**
+     * Checks to see if the input matrix is copied after multiple calls.  This was an actual bug.
+     */
+    @Test
+    public void multipleCalls_setB() {
+        DummySolver dummy = new DummySolver(false,true);
+        dummy.expectedB = 5;
+
+        LinearSolver<DenseMatrix64F> s = new LinearSolverSafe<DenseMatrix64F>(dummy);
+
+        Binput.set(0,5);
+        s.solve(Binput,new DenseMatrix64F(1,1));
+        // call it a second time and see if the input matrix has been reset to the
+        // correct value
+        s.solve(Binput,new DenseMatrix64F(1,1));
+
+        assertTrue(dummy.passedin != Ainput);
+    }
+
+    @Test
+    public void testSetA_notMod() {
+        DummySolver dummy = new DummySolver(false,false);
+
+        LinearSolver<DenseMatrix64F> s = new LinearSolverSafe<DenseMatrix64F>(dummy);
+
+        s.setA(Ainput);
+
+        assertTrue(dummy.passedin == Ainput);
+    }
+
+    @Test
+    public void testSetA_mod() {
+        DummySolver dummy = new DummySolver(true,false);
+
+        LinearSolver<DenseMatrix64F> s = new LinearSolverSafe<DenseMatrix64F>(dummy);
+
+        s.setA(Ainput);
+
+        assertTrue(dummy.passedin != Ainput);
+    }
+
+    @Test
+    public void testSolver_notMod() {
+        DummySolver dummy = new DummySolver(false,false);
+
+        LinearSolver<DenseMatrix64F> s = new LinearSolverSafe<DenseMatrix64F>(dummy);
+
+        s.solve(Binput,new DenseMatrix64F(1,1));
+
+        assertTrue(dummy.passedin == Binput);
+    }
+
+    @Test
+    public void testSolver_mod() {
+        DummySolver dummy = new DummySolver(false,true);
+
+        LinearSolver<DenseMatrix64F> s = new LinearSolverSafe<DenseMatrix64F>(dummy);
+
+        s.solve(Binput,new DenseMatrix64F(1,1));
+
+        assertTrue(dummy.passedin != Binput);
+    }
+
+    @Test
+    public void quality() {
+        DummySolver dummy = new DummySolver(false,false);
+
+        LinearSolver<DenseMatrix64F> s = new LinearSolverSafe<DenseMatrix64F>(dummy);
+
+        assertTrue(s.quality()==dummy.quality());
+    }
+
+    @Test
+    public void modifies() {
+        LinearSolver<DenseMatrix64F> s = new LinearSolverSafe<DenseMatrix64F>(null);
+
+        assertFalse(s.modifiesA());
+        assertFalse(s.modifiesB());
+
+    }
+
+    private class DummySolver implements LinearSolver<DenseMatrix64F>
+    {
+        boolean modifiesA;
+        boolean modifiesB;
+
+        DenseMatrix64F passedin;
+
+        // the expected value of the input matrix
+        double expectedA = Double.NaN;
+        double expectedB = Double.NaN;
+
+        private DummySolver(boolean modifiesA, boolean modifiesB) {
+            this.modifiesA = modifiesA;
+            this.modifiesB = modifiesB;
+        }
+
+        @Override
+        public boolean setA(DenseMatrix64F A) {
+            passedin = A;
+
+            // the input matrix has an expected input value
+            if( !Double.isNaN(expectedA))
+                assertEquals(expectedA,A.get(0),1e-8);
+
+            if( modifiesA ) {
+                A.set(0,0,rand.nextDouble());
+            }
+
+            return true;
+        }
+
+        @Override
+        public double quality() {
+            return 1.1;
+        }
+
+        @Override
+        public void solve(DenseMatrix64F B, DenseMatrix64F X) {
+            passedin = B;
+
+            // the input matrix has an expected input value
+            if( !Double.isNaN(expectedB))
+                assertEquals(expectedB,B.get(0),1e-8);
+
+            if( modifiesB ) {
+                B.set(0,0,rand.nextDouble());
+            }
+        }
+
+        @Override
+        public void invert(DenseMatrix64F A_inv) {
+
+        }
+
+        @Override
+        public boolean modifiesA() {
+            return modifiesA;
+        }
+
+        @Override
+        public boolean modifiesB() {
+            return modifiesB;
+        }
+
+        @Override
+        public <D extends DecompositionInterface> D getDecomposition() {
+            return null;
+        }
+    }
+}
diff --git a/main/core/test/org/ejml/data/GenericTestsD1Matrix32F.java b/main/core/test/org/ejml/data/GenericTestsD1Matrix32F.java
new file mode 100644
index 0000000..4dd9bc8
--- /dev/null
+++ b/main/core/test/org/ejml/data/GenericTestsD1Matrix32F.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public abstract class GenericTestsD1Matrix32F extends GenericTestsMatrix32F {
+
+    protected abstract D1Matrix32F createMatrix( int numRows , int numCols );
+
+    public void allTests() {
+        super.allTests();
+        testReshape();
+        testSetAndGet_1D();
+    }
+
+    public void testReshape() {
+        D1Matrix32F mat = createMatrix(3,2);
+
+        float []origData = mat.getData();
+
+        mat.reshape(6,1, false);
+
+        assertTrue(origData == mat.getData());
+        assertEquals(1,mat.getNumCols());
+        assertEquals(6,mat.getNumRows());
+    }
+
+    public void testSetAndGet_1D() {
+        D1Matrix32F mat = createMatrix(3,4);
+
+        int indexA = mat.getIndex(1,2);
+        int indexB = mat.getIndex(2,1);
+
+        assertTrue(indexA!=indexB);
+
+        mat.set(indexA,2.0f);
+
+        assertEquals(0,mat.get(indexB),1e-6);
+        assertEquals(2,mat.get(indexA),1e-6);
+    }
+}
\ No newline at end of file
diff --git a/main/core/test/org/ejml/data/GenericTestsD1Matrix64F.java b/main/core/test/org/ejml/data/GenericTestsD1Matrix64F.java
new file mode 100644
index 0000000..a0bb052
--- /dev/null
+++ b/main/core/test/org/ejml/data/GenericTestsD1Matrix64F.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2009-2013, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public abstract class GenericTestsD1Matrix64F extends GenericTestsMatrix64F {
+
+    protected abstract D1Matrix64F createMatrix( int numRows , int numCols );
+
+    public void allTests() {
+        super.allTests();
+        testReshape();
+        testSetAndGet_1D();
+    }
+
+    public void testReshape() {
+        D1Matrix64F mat = createMatrix(3,2);
+
+        double []origData = mat.getData();
+
+        mat.reshape(6,1, false);
+
+        assertTrue(origData == mat.getData());
+        assertEquals(1,mat.getNumCols());
+        assertEquals(6,mat.getNumRows());
+    }
+
+    public void testSetAndGet_1D() {
+        D1Matrix64F mat = createMatrix(3,4);
+
+        int indexA = mat.getIndex(1,2);
+        int indexB = mat.getIndex(2,1);
+
+        assertTrue(indexA!=indexB);
+
+        mat.set(indexA,2.0);
+
+        assertEquals(0,mat.get(indexB),1e-6);
+        assertEquals(2,mat.get(indexA),1e-6);
+    }
+}
\ No newline at end of file
diff --git a/main/core/test/org/ejml/data/GenericTestsMatrix32F.java b/main/core/test/org/ejml/data/GenericTestsMatrix32F.java
new file mode 100644
index 0000000..c60c34f
--- /dev/null
+++ b/main/core/test/org/ejml/data/GenericTestsMatrix32F.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import static org.junit.Assert.assertEquals;
+
+
+/**
+ * @author Peter Abeles
+ */
+public abstract class GenericTestsMatrix32F {
+
+    protected abstract RealMatrix32F createMatrix( int numRows , int numCols );
+
+    public void allTests() {
+        testGetNumRows();
+        testGetNumCols();
+        testSetAndGet_2D();
+        testSetAndGet_2D_unsafe();
+    }
+
+    public void testGetNumRows() {
+        RealMatrix32F mat = createMatrix(2,3);
+
+        assertEquals(2,mat.getNumRows());
+    }
+
+    public void testGetNumCols() {
+        RealMatrix32F mat = createMatrix(2,3);
+
+        assertEquals(3,mat.getNumCols());
+    }
+
+    public void testSetAndGet_2D() {
+        // test a variety of different shapes.  Added rigor needed
+        // to properly test block matrix.
+        checkSetAndGet(10, 12);
+        checkSetAndGet(12, 10);
+        checkSetAndGet(10, 10);
+        checkSetAndGet(19, 5);
+        checkSetAndGet(5, 19);
+        checkSetAndGet(19, 19);
+    }
+
+    private void checkSetAndGet(int m, int n) {
+        RealMatrix32F mat = createMatrix(m, n);
+
+        for( int i = 0; i < m; i++ ) {
+            for( int j = 0; j < n; j++ ) {
+                mat.set(i,j, i* m +j);
+            }
+        }
+
+        for( int i = 0; i < m; i++ ) {
+            for( int j = 0; j < n; j++ ) {
+                double found = mat.get(i,j);
+
+                assertEquals(i* m +j,found,1e-8);
+            }
+        }
+    }
+
+    public void testSetAndGet_2D_unsafe() {
+        // test a variety of different shapes.  Added rigor needed
+        // to properly test block matrix.
+        checkSetAndGet_unsafe(10, 12);
+        checkSetAndGet_unsafe(12, 10);
+        checkSetAndGet_unsafe(10, 10);
+        checkSetAndGet_unsafe(19, 5);
+        checkSetAndGet_unsafe(5, 19);
+        checkSetAndGet_unsafe(19, 19);
+    }
+
+    private void checkSetAndGet_unsafe(int m, int n) {
+        RealMatrix32F mat = createMatrix(m, n);
+
+        for( int i = 0; i < m; i++ ) {
+            for( int j = 0; j < n; j++ ) {
+                mat.unsafe_set(i,j, i* m +j);
+            }
+        }
+
+        for( int i = 0; i < m; i++ ) {
+            for( int j = 0; j < n; j++ ) {
+                double found = mat.unsafe_get(i,j);
+
+                assertEquals(i* m +j,found,1e-8);
+            }
+        }
+    }
+
+}
diff --git a/main/core/test/org/ejml/data/GenericTestsMatrix64F.java b/main/core/test/org/ejml/data/GenericTestsMatrix64F.java
new file mode 100644
index 0000000..b05342d
--- /dev/null
+++ b/main/core/test/org/ejml/data/GenericTestsMatrix64F.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import static org.junit.Assert.assertEquals;
+
+
+/**
+ * @author Peter Abeles
+ */
+public abstract class GenericTestsMatrix64F {
+
+    protected abstract RealMatrix64F createMatrix( int numRows , int numCols );
+
+    public void allTests() {
+        testGetNumRows();
+        testGetNumCols();
+        testSetAndGet_2D();
+        testSetAndGet_2D_unsafe();
+    }
+
+    public void testGetNumRows() {
+        RealMatrix64F mat = createMatrix(2,3);
+
+        assertEquals(2,mat.getNumRows());
+    }
+
+    public void testGetNumCols() {
+        RealMatrix64F mat = createMatrix(2,3);
+
+        assertEquals(3,mat.getNumCols());
+    }
+
+    public void testSetAndGet_2D() {
+        // test a variety of different shapes.  Added rigor needed
+        // to properly test block matrix.
+        checkSetAndGet(10, 12);
+        checkSetAndGet(12, 10);
+        checkSetAndGet(10, 10);
+        checkSetAndGet(19, 5);
+        checkSetAndGet(5, 19);
+        checkSetAndGet(19, 19);
+    }
+
+    private void checkSetAndGet(int m, int n) {
+        RealMatrix64F mat = createMatrix(m, n);
+
+        for( int i = 0; i < m; i++ ) {
+            for( int j = 0; j < n; j++ ) {
+                mat.set(i,j, i* m +j);
+            }
+        }
+
+        for( int i = 0; i < m; i++ ) {
+            for( int j = 0; j < n; j++ ) {
+                double found = mat.get(i,j);
+
+                assertEquals(i* m +j,found,1e-8);
+            }
+        }
+    }
+
+    public void testSetAndGet_2D_unsafe() {
+        // test a variety of different shapes.  Added rigor needed
+        // to properly test block matrix.
+        checkSetAndGet_unsafe(10, 12);
+        checkSetAndGet_unsafe(12, 10);
+        checkSetAndGet_unsafe(10, 10);
+        checkSetAndGet_unsafe(19, 5);
+        checkSetAndGet_unsafe(5, 19);
+        checkSetAndGet_unsafe(19, 19);
+    }
+
+    private void checkSetAndGet_unsafe(int m, int n) {
+        RealMatrix64F mat = createMatrix(m, n);
+
+        for( int i = 0; i < m; i++ ) {
+            for( int j = 0; j < n; j++ ) {
+                mat.unsafe_set(i,j, i* m +j);
+            }
+        }
+
+        for( int i = 0; i < m; i++ ) {
+            for( int j = 0; j < n; j++ ) {
+                double found = mat.unsafe_get(i,j);
+
+                assertEquals(i* m +j,found,1e-8);
+            }
+        }
+    }
+
+}
diff --git a/main/core/test/org/ejml/data/TestBlockMatrix64F.java b/main/core/test/org/ejml/data/TestBlockMatrix64F.java
new file mode 100644
index 0000000..6c7edd9
--- /dev/null
+++ b/main/core/test/org/ejml/data/TestBlockMatrix64F.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2009-2013, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import org.junit.Test;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestBlockMatrix64F {
+
+    @Test
+    public void testGeneric() {
+        GenericTestsD1Matrix64F g;
+        g = new GenericTestsD1Matrix64F() {
+            protected D1Matrix64F createMatrix(int numRows, int numCols) {
+                return new BlockMatrix64F(numRows,numCols,10);
+            }
+        };
+
+        g.allTests();
+    }
+
+}
diff --git a/main/core/test/org/ejml/data/TestCD1Matrix64F.java b/main/core/test/org/ejml/data/TestCD1Matrix64F.java
new file mode 100644
index 0000000..d63a4c9
--- /dev/null
+++ b/main/core/test/org/ejml/data/TestCD1Matrix64F.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestCD1Matrix64F {
+    @Test
+    public void set_matrix() {
+        CD1Matrix64F a = new CDenseMatrix64F(3,4);
+        a.set(1,3,9,2);
+
+        CD1Matrix64F b = new CDenseMatrix64F(3,4);
+
+        b.set(a);
+        for (int i = 0; i < a.getDataLength(); i++) {
+            assertEquals(a.data[i],b.data[i],1e-8);
+        }
+        assertEquals(9, b.getReal(1, 3), 1e-8);
+    }
+
+    @Test
+    public void getNumRows() {
+        CD1Matrix64F a = new CDenseMatrix64F(3,4);
+        assertEquals(3,a.getNumRows());
+    }
+
+    @Test
+    public void getNumCols() {
+        CD1Matrix64F a = new CDenseMatrix64F(3,4);
+        assertEquals(4,a.getNumCols());
+    }
+
+    @Test
+    public void setNumRows() {
+        CD1Matrix64F a = new CDenseMatrix64F(3,4);
+        a.setNumRows(6);
+        assertEquals(6, a.getNumRows());
+    }
+
+    @Test
+    public void setNumCols() {
+        CD1Matrix64F a = new CDenseMatrix64F(3,4);
+        a.setNumCols(6);
+        assertEquals(6,a.getNumCols());
+    }
+}
\ No newline at end of file
diff --git a/main/core/test/org/ejml/data/TestCDenseMatrix64F.java b/main/core/test/org/ejml/data/TestCDenseMatrix64F.java
new file mode 100644
index 0000000..36709b7
--- /dev/null
+++ b/main/core/test/org/ejml/data/TestCDenseMatrix64F.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import org.ejml.ops.CCommonOps;
+import org.ejml.ops.CMatrixFeatures;
+import org.ejml.ops.CRandomMatrices;
+import org.ejml.ops.EjmlUnitTests;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestCDenseMatrix64F {
+
+    Random rand = new Random(234);
+
+    @Test
+    public void constructor_darray() {
+        CDenseMatrix64F a = new CDenseMatrix64F(new double[][]{{1,2,3,4},{5,6,7,8},{5,6,7,8}});
+        CDenseMatrix64F b = new CDenseMatrix64F(3,2,true,1,2,3,4,5,6,7,8,5,6,7,8);
+
+        EjmlUnitTests.assertEquals(a, b, 1e-8);
+    }
+
+    @Test
+    public void constructor_cmatrix() {
+        CDenseMatrix64F a = new CDenseMatrix64F(3,4);
+        a.set(1,3,9,2);
+
+        CDenseMatrix64F b = new CDenseMatrix64F(a);
+        for (int i = 0; i < a.getDataLength(); i++) {
+            assertEquals(a.data[i],b.data[i],1e-8);
+        }
+    }
+
+    @Test
+    public void constructor_shape() {
+        CDenseMatrix64F a = new CDenseMatrix64F(5,7);
+        assertEquals(5,a.numRows);
+        assertEquals(7,a.numCols);
+        assertEquals(5*7*2,a.data.length);
+
+    }
+
+    @Test
+    public void getIndex() {
+        CDenseMatrix64F a = new CDenseMatrix64F(5,7);
+
+        assertEquals(2*14+6,a.getIndex(2,3));
+    }
+
+    @Test
+    public void reshape() {
+        CDenseMatrix64F a = new CDenseMatrix64F(5,7);
+
+        assertEquals(5*7*2,a.data.length);
+        assertEquals(5, a.numRows);
+        assertEquals(7,a.numCols);
+
+        // make it larger
+        a.reshape(10,6);
+
+        assertEquals(10*6*2,a.data.length);
+        assertEquals(10,a.numRows);
+        assertEquals(6,a.numCols);
+
+        // make it smaller
+        a.reshape(3,2);
+
+        assertTrue(a.data.length > 3*2*2);
+        assertEquals(3,a.numRows);
+        assertEquals(2,a.numCols);
+    }
+
+    @Test
+    public void get() {
+        CDenseMatrix64F a = new CDenseMatrix64F(3,4);
+
+        a.data[2*4*2 + 2] = 5;
+        a.data[2*4*2 + 3] = 6;
+
+        Complex64F c = new Complex64F();
+        a.get(2,1,c);
+
+        assertEquals(c.real,5,1e-8);
+        assertEquals(c.imaginary, 6, 1e-8);
+    }
+
+    @Test
+    public void set_rowcolumn() {
+        CDenseMatrix64F a = new CDenseMatrix64F(3,4);
+
+        a.set(2, 1, 5, 6);
+
+        assertEquals(5,a.data[2*4*2+2],1e-8);
+        assertEquals(6,a.data[2*4*2+3],1e-8);
+    }
+
+    @Test
+    public void getReal() {
+        CDenseMatrix64F a = new CDenseMatrix64F(3,4);
+
+        a.data[2*4*2 + 2] = 5;
+        a.data[2*4*2 + 3] = 6;
+
+        Complex64F c = new Complex64F();
+        a.get(2,1,c);
+
+        assertEquals(a.getReal(2, 1), 5, 1e-8);
+    }
+
+    @Test
+    public void setReal() {
+
+        CDenseMatrix64F a = new CDenseMatrix64F(3,4);
+
+        a.setReal(2,1,5);
+
+        assertEquals(5, a.data[2 * 4 * 2 + 2], 1e-8);
+    }
+
+    @Test
+    public void getImaginary() {
+        CDenseMatrix64F a = new CDenseMatrix64F(3,4);
+
+        a.data[2*4*2 + 2] = 5;
+        a.data[2*4*2 + 3] = 6;
+
+        Complex64F c = new Complex64F();
+        a.get(2,1,c);
+
+        assertEquals(a.getImaginary(2, 1), 6, 1e-8);
+    }
+
+    @Test
+    public void setImaginary() {
+        CDenseMatrix64F a = new CDenseMatrix64F(3,4);
+
+        a.setImaginary(2, 1, 6);
+
+        assertEquals(6, a.data[2 * 4 * 2 + 3], 1e-8);
+    }
+
+    @Test
+    public void getDataLength() {
+        assertEquals(3*4*2,new CDenseMatrix64F(3,4).getDataLength());
+    }
+
+    @Test
+    public void copy() {
+        CDenseMatrix64F a = new CDenseMatrix64F(3,4);
+        a.set(1, 3, 9, 2);
+
+        CDenseMatrix64F b = a.copy();
+        for (int i = 0; i < a.getDataLength(); i++) {
+            assertEquals(a.data[i],b.data[i],1e-8);
+        }
+    }
+
+    @Test
+    public void getRowStride() {
+        CDenseMatrix64F a = new CDenseMatrix64F(3,4);
+        assertEquals(4*2,a.getRowStride());
+    }
+
+    @Test
+    public void set_array() {
+        CDenseMatrix64F A = CRandomMatrices.createRandom(3,4,rand);
+
+        CDenseMatrix64F B = new CDenseMatrix64F(1,1);
+        B.set(3,4,true,A.data);
+
+        assertTrue(CMatrixFeatures.isEquals(A,B));
+
+        CDenseMatrix64F A_tran = new CDenseMatrix64F(4,3);
+        CCommonOps.transpose(A,A_tran);
+
+        B.set(3,4,false,A_tran.data);
+
+        assertTrue(CMatrixFeatures.isEquals(A,B));
+    }
+}
diff --git a/main/core/test/org/ejml/data/TestD1Submatrix64F.java b/main/core/test/org/ejml/data/TestD1Submatrix64F.java
new file mode 100644
index 0000000..d94dfde
--- /dev/null
+++ b/main/core/test/org/ejml/data/TestD1Submatrix64F.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestD1Submatrix64F {
+
+    Random rand = new Random(234234);
+
+    @Test
+    public void get() {
+        DenseMatrix64F A = RandomMatrices.createRandom(5,10,-1,1,rand);
+
+        D1Submatrix64F S = new D1Submatrix64F(A,2,4,1,10);
+
+        assertEquals(A.get(3,2),S.get(1,1),1e-8);
+    }
+
+    @Test
+    public void set() {
+        DenseMatrix64F A = RandomMatrices.createRandom(5,10,-1,1,rand);
+
+        D1Submatrix64F S = new D1Submatrix64F(A,2,4,1,10);
+
+        S.set(1,1,5);
+
+        assertEquals(A.get(3,2),5,1e-8);
+    }
+
+    @Test
+    public void extract() {
+        DenseMatrix64F A = RandomMatrices.createRandom(5,10,-1,1,rand);
+
+        D1Submatrix64F S = new D1Submatrix64F(A,2,4,1,10);
+
+        DenseMatrix64F M = S.extract();
+
+        DenseMatrix64F E = CommonOps.extract(A,2,4,1,10);
+
+        assertTrue(MatrixFeatures.isEquals(E,M));
+    }
+}
diff --git a/main/core/test/org/ejml/data/TestDenseMatrix32F.java b/main/core/test/org/ejml/data/TestDenseMatrix32F.java
new file mode 100644
index 0000000..35c9a4d
--- /dev/null
+++ b/main/core/test/org/ejml/data/TestDenseMatrix32F.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import org.ejml.EjmlParameters;
+import org.ejml.ops.EjmlUnitTests;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestDenseMatrix32F {
+
+    Random rand = new Random(23432);
+
+    @Test
+    public void testGeneric() {
+        GenericTestsD1Matrix32F g;
+        g = new GenericTestsD1Matrix32F() {
+            protected D1Matrix32F createMatrix(int numRows, int numCols) {
+                return new DenseMatrix32F(numRows,numCols);
+            }
+        };
+
+        g.allTests();
+    }
+
+    /**
+     * Tests the following constructor:
+     *
+     * DenseMatrix32F( float data[] , int numCols , int numRows )
+     */
+    @Test
+    public void testConstructorSingleArray()
+    {
+        float d[] = new float[]{1,2,3,4,5,6};
+
+        DenseMatrix32F mat = new DenseMatrix32F(3,2, true, d);
+
+        assertTrue( mat.data != d );
+
+        assertEquals(1,mat.get(0,0),1e-8);
+        assertEquals(2,mat.get(0,1),1e-8);
+        assertEquals(3,mat.get(1,0),1e-8);
+        assertEquals(4,mat.get(1,1),1e-8);
+        assertEquals(5,mat.get(2,0),1e-8);
+        assertEquals(6,mat.get(2,1),1e-8);
+    }
+
+    /**
+     * Tests the following constructor:
+     *
+     * DenseMatrix32F( float data[][] )
+     */
+    @Test
+    public void testConstruactorfloatArray() {
+        float d[][] = new float[][]{{1,2},{3,4},{5,6}};
+
+        DenseMatrix32F mat = new DenseMatrix32F(d);
+
+        assertEquals(1,mat.get(0,0),1e-8);
+        assertEquals(2,mat.get(0,1),1e-8);
+        assertEquals(3,mat.get(1,0),1e-8);
+        assertEquals(4,mat.get(1,1),1e-8);
+        assertEquals(5,mat.get(2,0),1e-8);
+        assertEquals(6,mat.get(2,1),1e-8);
+    }
+
+    /**
+     * Tests the following constructor:
+     *
+     * DenseMatrix32F( int numCols , int numRows )
+     */
+    @Test
+    public void testConstructorShape() {
+        DenseMatrix32F mat = new DenseMatrix32F(7,5);
+
+        assertEquals(5,mat.getNumCols());
+        assertEquals(7,mat.getNumRows());
+        assertEquals(7*5,mat.data.length);
+    }
+
+    /**
+     * Tests the following constructor:
+     *
+     * DenseMatrix32F( DenseMatrix32F orig )
+     */
+    @Test
+    public void testConstructorCopy() {
+        float d[][] = new float[][]{{1,2},{3,4},{5,6}};
+
+        DenseMatrix32F mat = new DenseMatrix32F(d);
+        DenseMatrix32F copy = new DenseMatrix32F(mat);
+
+        assertTrue( mat.data != copy.data );
+
+        assertEquals(mat.getNumCols(),copy.getNumCols());
+        assertEquals(mat.getNumRows(),copy.getNumRows());
+
+        for( int i = 0; i < mat.getNumElements(); i++ ) {
+            assertEquals(mat.get(i),copy.get(i),EjmlParameters.TOL32);
+        }
+    }
+
+    @Test
+    public void wrap() {
+        float d[] = new float[]{1,2,3,4,5,6};
+
+        DenseMatrix32F mat = DenseMatrix32F.wrap(3,2,d);
+
+        assertTrue(mat.data==d);
+
+        assertEquals(1,mat.get(0,0),1e-8);
+        assertEquals(2,mat.get(0,1),1e-8);
+        assertEquals(3,mat.get(1,0),1e-8);
+        assertEquals(4,mat.get(1,1),1e-8);
+        assertEquals(5,mat.get(2,0),1e-8);
+        assertEquals(6,mat.get(2,1),1e-8);
+    }
+
+    @Test
+    public void testInBounds() {
+        DenseMatrix32F mat = new DenseMatrix32F(2,3);
+
+        assertTrue(mat.isInBounds(0,0));
+        assertTrue(mat.isInBounds(1,2));
+        assertTrue(mat.isInBounds(0,2));
+        assertFalse(mat.isInBounds(2,0));
+        assertFalse(mat.isInBounds(20,30));
+    }
+
+    @Test
+    public void testSet_Matrix() {
+        float d[][] = new float[][]{{1,2},{3,4},{5,6}};
+
+        DenseMatrix32F mat = new DenseMatrix32F(d);
+        DenseMatrix32F mat2 = new DenseMatrix32F(mat.numRows,mat.numCols);
+
+        mat2.set(mat);
+
+        EjmlUnitTests.assertEquals(mat,mat2,EjmlParameters.TOL32);
+    }
+
+    @Test
+    @Ignore
+    public void set_ColumnMajor() {
+        // todo implement later after basic 32bit operations are done
+        fail("implement");
+//        DenseMatrix32F A = UtilTestMatrix.random32(3,5,-1,1,rand);
+//
+//        DenseMatrix32F Atran = A.copy();
+//        CommonOps.transpose(Atran);
+//        DenseMatrix32F Afound = new DenseMatrix32F(3,5);
+//        Afound.set(3,5, false, Atran.data);
+//
+//        assertTrue(MatrixFeatures.isIdentical(Afound,A,EjmlParameters.TOL32));
+    }
+
+    @Test
+    @Ignore
+    public void set_RowMajor() {
+        // todo implement later after basic 32bit operations are done
+        fail("implement");
+//        DenseMatrix32F A = UtilTestMatrix.random32(3, 5, -1, 1, rand);
+//
+//        DenseMatrix32F Afound = new DenseMatrix32F(3,5);
+//        Afound.set(3,5, true, A.data);
+//
+//        assertTrue(MatrixFeatures.isIdentical(Afound,A,EjmlParameters.TOL32));
+//        assertTrue(A.data != Afound.data);
+    }
+
+    @Test
+    public void testset_Matrix() {
+        float d[][] = new float[][]{{1,2},{3,4},{5,6}};
+
+        DenseMatrix32F mat = new DenseMatrix32F(d);
+        DenseMatrix32F mat2 = new DenseMatrix32F(5,5);
+
+        mat2.set(mat);
+
+        assertEquals(mat.getNumCols(),mat2.getNumCols());
+        assertEquals(mat.getNumRows(),mat2.getNumRows());
+
+        EjmlUnitTests.assertEquals(mat,mat2, EjmlParameters.TOL32);
+    }
+
+    @Test
+    public void testReshape() {
+        float d[][] = new float[][]{{1,2},{3,4},{5,6}};
+
+        DenseMatrix32F mat = new DenseMatrix32F(d);
+        DenseMatrix32F orig = mat.copy();
+
+        // first see if reshape saves the data
+        mat.reshape(10,10,true);
+
+        for( int i = 0; i < 6; i++ ) {
+            assertEquals(mat.data[i],orig.data[i],EjmlParameters.TOL32);
+        }
+
+        // now make sure it doesn't
+        mat.reshape(11,10,false);
+
+        for( int i = 0; i < 6; i++ ) {
+            assertTrue(Math.abs(mat.data[i]-orig.data[i])>EjmlParameters.TOL32);
+        }
+    }
+
+}
diff --git a/main/core/test/org/ejml/data/TestDenseMatrix64F.java b/main/core/test/org/ejml/data/TestDenseMatrix64F.java
new file mode 100644
index 0000000..21c5520
--- /dev/null
+++ b/main/core/test/org/ejml/data/TestDenseMatrix64F.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.EjmlUnitTests;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestDenseMatrix64F {
+
+    Random rand = new Random(23432);
+
+    @Test
+    public void testGeneric() {
+        GenericTestsD1Matrix64F g;
+        g = new GenericTestsD1Matrix64F() {
+            protected D1Matrix64F createMatrix(int numRows, int numCols) {
+                return new DenseMatrix64F(numRows,numCols);
+            }
+        };
+
+        g.allTests();
+    }
+
+    /**
+     * Tests the following constructor:
+     *
+     * DenseMatrix64F( double data[] , int numCols , int numRows )
+     */
+    @Test
+    public void testConstructorSingleArray()
+    {
+        double d[] = new double[]{1,2,3,4,5,6};
+
+        DenseMatrix64F mat = new DenseMatrix64F(3,2, true, d);
+
+        assertTrue( mat.data != d );
+
+        assertEquals(1,mat.get(0,0),1e-8);
+        assertEquals(2,mat.get(0,1),1e-8);
+        assertEquals(3,mat.get(1,0),1e-8);
+        assertEquals(4,mat.get(1,1),1e-8);
+        assertEquals(5,mat.get(2,0),1e-8);
+        assertEquals(6,mat.get(2,1),1e-8);
+    }
+
+    /**
+     * Tests the following constructor:
+     *
+     * DenseMatrix64F( double data[][] )
+     */
+    @Test
+    public void testConstruactorDoubleArray() {
+        double d[][] = new double[][]{{1,2},{3,4},{5,6}};
+
+        DenseMatrix64F mat = new DenseMatrix64F(d);
+
+        assertEquals(1,mat.get(0,0),1e-8);
+        assertEquals(2,mat.get(0,1),1e-8);
+        assertEquals(3,mat.get(1,0),1e-8);
+        assertEquals(4,mat.get(1,1),1e-8);
+        assertEquals(5,mat.get(2,0),1e-8);
+        assertEquals(6,mat.get(2,1),1e-8);
+    }
+
+    /**
+     * Tests the following constructor:
+     *
+     * DenseMatrix64F( int numCols , int numRows )
+     */
+    @Test
+    public void testConstructorShape() {
+        DenseMatrix64F mat = new DenseMatrix64F(7,5);
+
+        assertEquals(5,mat.getNumCols());
+        assertEquals(7,mat.getNumRows());
+        assertEquals(7*5,mat.data.length);
+    }
+
+    /**
+     * Tests the following constructor:
+     *
+     * DenseMatrix64F( DenseMatrix64F orig )
+     */
+    @Test
+    public void testConstructorCopy() {
+        double d[][] = new double[][]{{1,2},{3,4},{5,6}};
+
+        DenseMatrix64F mat = new DenseMatrix64F(d);
+        DenseMatrix64F copy = new DenseMatrix64F(mat);
+
+        assertTrue( mat.data != copy.data );
+
+        assertEquals(mat.getNumCols(),copy.getNumCols());
+        assertEquals(mat.getNumRows(),copy.getNumRows());
+
+        for( int i = 0; i < mat.getNumElements(); i++ ) {
+            assertEquals(mat.get(i),copy.get(i),1e-10);
+        }
+    }
+
+    @Test
+    public void wrap() {
+        double d[] = new double[]{1,2,3,4,5,6};
+
+        DenseMatrix64F mat = DenseMatrix64F.wrap(3,2,d);
+
+        assertTrue(mat.data==d);
+
+        assertEquals(1,mat.get(0,0),1e-8);
+        assertEquals(2,mat.get(0,1),1e-8);
+        assertEquals(3,mat.get(1,0),1e-8);
+        assertEquals(4,mat.get(1,1),1e-8);
+        assertEquals(5,mat.get(2,0),1e-8);
+        assertEquals(6,mat.get(2,1),1e-8);
+    }
+
+    @Test
+    public void testInBounds() {
+        DenseMatrix64F mat = new DenseMatrix64F(2,3);
+
+        assertTrue(mat.isInBounds(0,0));
+        assertTrue(mat.isInBounds(1,2));
+        assertTrue(mat.isInBounds(0,2));
+        assertFalse(mat.isInBounds(2,0));
+        assertFalse(mat.isInBounds(20,30));
+    }
+
+    @Test
+    public void testSet_Matrix() {
+        double d[][] = new double[][]{{1,2},{3,4},{5,6}};
+
+        DenseMatrix64F mat = new DenseMatrix64F(d);
+        DenseMatrix64F mat2 = new DenseMatrix64F(mat.numRows,mat.numCols);
+
+        mat2.set(mat);
+
+        EjmlUnitTests.assertEquals(mat,mat2,1e-10);
+    }
+
+    @Test
+    public void set_ColumnMajor() {
+        DenseMatrix64F A = RandomMatrices.createRandom(3,5,rand);
+
+        DenseMatrix64F Atran = A.copy();
+        CommonOps.transpose(Atran);
+        DenseMatrix64F Afound = new DenseMatrix64F(3,5);
+        Afound.set(3,5, false, Atran.data);
+
+        assertTrue(MatrixFeatures.isIdentical(Afound,A,1e-10));
+    }
+
+    @Test
+    public void set_RowMajor() {
+        DenseMatrix64F A = RandomMatrices.createRandom(3,5,rand);
+
+        DenseMatrix64F Afound = new DenseMatrix64F(3,5);
+        Afound.set(3,5, true, A.data);
+
+        assertTrue(MatrixFeatures.isIdentical(Afound,A,1e-10));
+        assertTrue(A.data != Afound.data);
+    }
+
+    @Test
+    public void testset_Matrix() {
+        double d[][] = new double[][]{{1,2},{3,4},{5,6}};
+
+        DenseMatrix64F mat = new DenseMatrix64F(d);
+        DenseMatrix64F mat2 = new DenseMatrix64F(5,5);
+
+        mat2.set(mat);
+
+        assertEquals(mat.getNumCols(),mat2.getNumCols());
+        assertEquals(mat.getNumRows(),mat2.getNumRows());
+
+        EjmlUnitTests.assertEquals(mat,mat2,1e-10);
+    }
+
+    @Test
+    public void testReshape() {
+        double d[][] = new double[][]{{1,2},{3,4},{5,6}};
+
+        DenseMatrix64F mat = new DenseMatrix64F(d);
+        DenseMatrix64F orig = mat.copy();
+
+        // first see if reshape saves the data
+        mat.reshape(10,10,true);
+
+        for( int i = 0; i < 6; i++ ) {
+            assertEquals(mat.data[i],orig.data[i],1e-8);
+        }
+
+        // now make sure it doesn't
+        mat.reshape(11,10,false);
+
+        for( int i = 0; i < 6; i++ ) {
+            assertTrue(Math.abs(mat.data[i]-orig.data[i])>1e-8);
+        }
+    }
+
+}
diff --git a/main/core/test/org/ejml/data/TestMatrixIterator32F.java b/main/core/test/org/ejml/data/TestMatrixIterator32F.java
new file mode 100644
index 0000000..1b343cc
--- /dev/null
+++ b/main/core/test/org/ejml/data/TestMatrixIterator32F.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import org.ejml.EjmlParameters;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestMatrixIterator32F {
+
+    Random rand = new Random(234234);
+
+    @Test
+    public void allRow() {
+        DenseMatrix32F A = UtilTestMatrix.random32(3, 6, -1, 1, rand);
+
+        MatrixIterator32F iter = A.iterator(true,0, 0, A.numRows-1, A.numCols-1);
+
+        for( int i = 0; i < A.numRows; i++ ) {
+            for( int j = 0; j < A.numCols; j++ ) {
+                assertTrue(iter.hasNext());
+                assertEquals(A.get(i,j),iter.next(),EjmlParameters.TOL32);
+            }
+        }
+        assertTrue(!iter.hasNext());
+    }
+
+    @Test
+    public void allCol() {
+        DenseMatrix32F A = UtilTestMatrix.random32(3, 6,-1,1, rand);
+
+        MatrixIterator32F iter = A.iterator(false,0, 0, A.numRows-1, A.numCols-1);
+
+        for( int j = 0; j < A.numCols; j++ ) {
+            for( int i = 0; i < A.numRows; i++ ) {
+                assertTrue(iter.hasNext());
+                assertEquals(A.get(i,j),iter.next(),EjmlParameters.TOL32);
+            }
+        }
+        assertTrue(!iter.hasNext());
+    }
+
+    @Test
+    public void subRow() {
+        DenseMatrix32F A = UtilTestMatrix.random32(3, 6, -1,1,rand);
+
+        MatrixIterator32F iter = A.iterator(true,1, 2 , A.numRows-2, A.numCols-1);
+
+        for( int i = 1; i < A.numRows-1; i++ ) {
+            for( int j = 2; j < A.numCols; j++ ) {
+                assertTrue(iter.hasNext());
+                assertEquals(A.get(i,j),iter.next(),EjmlParameters.TOL32);
+            }
+        }
+        assertTrue(!iter.hasNext());
+
+    }
+
+    @Test
+    public void subCol() {
+        DenseMatrix32F A = UtilTestMatrix.random32(3, 6,-1,1,rand);
+
+        MatrixIterator32F iter = A.iterator(false,1, 2 , A.numRows-2, A.numCols-1);
+
+        for( int j = 2; j < A.numCols; j++ ) {
+            for( int i = 1; i < A.numRows-1; i++ ) {
+                assertTrue(iter.hasNext());
+                assertEquals(A.get(i,j),iter.next(), EjmlParameters.TOL32);
+            }
+        }
+        assertTrue(!iter.hasNext());
+    }
+
+}
diff --git a/main/core/test/org/ejml/data/TestMatrixIterator64F.java b/main/core/test/org/ejml/data/TestMatrixIterator64F.java
new file mode 100644
index 0000000..deaf7f1
--- /dev/null
+++ b/main/core/test/org/ejml/data/TestMatrixIterator64F.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestMatrixIterator64F {
+
+    Random rand = new Random(234234);
+
+    @Test
+    public void allRow() {
+        DenseMatrix64F A = RandomMatrices.createRandom(3,6,rand);
+
+        MatrixIterator64F iter = A.iterator(true,0, 0, A.numRows-1, A.numCols-1);
+
+        for( int i = 0; i < A.numRows; i++ ) {
+            for( int j = 0; j < A.numCols; j++ ) {
+                assertTrue(iter.hasNext());
+                assertEquals(A.get(i,j),iter.next(),1e-8);
+            }
+        }
+        assertTrue(!iter.hasNext());
+    }
+
+    @Test
+    public void allCol() {
+        DenseMatrix64F A = RandomMatrices.createRandom(3,6,rand);
+
+        MatrixIterator64F iter = A.iterator(false,0, 0, A.numRows-1, A.numCols-1);
+
+        for( int j = 0; j < A.numCols; j++ ) {
+            for( int i = 0; i < A.numRows; i++ ) {
+                assertTrue(iter.hasNext());
+                assertEquals(A.get(i,j),iter.next(),1e-8);
+            }
+        }
+        assertTrue(!iter.hasNext());
+    }
+
+    @Test
+    public void subRow() {
+        DenseMatrix64F A = RandomMatrices.createRandom(3,6,rand);
+
+        MatrixIterator64F iter = A.iterator(true,1, 2 , A.numRows-2, A.numCols-1);
+
+        for( int i = 1; i < A.numRows-1; i++ ) {
+            for( int j = 2; j < A.numCols; j++ ) {
+                assertTrue(iter.hasNext());
+                assertEquals(A.get(i,j),iter.next(),1e-8);
+            }
+        }
+        assertTrue(!iter.hasNext());
+
+    }
+
+    @Test
+    public void subCol() {
+        DenseMatrix64F A = RandomMatrices.createRandom(3,6,rand);
+
+        MatrixIterator64F iter = A.iterator(false,1, 2 , A.numRows-2, A.numCols-1);
+
+        for( int j = 2; j < A.numCols; j++ ) {
+            for( int i = 1; i < A.numRows-1; i++ ) {
+                assertTrue(iter.hasNext());
+                assertEquals(A.get(i,j),iter.next(),1e-8);
+            }
+        }
+        assertTrue(!iter.hasNext());
+    }
+
+}
diff --git a/main/core/test/org/ejml/data/UtilTestMatrix.java b/main/core/test/org/ejml/data/UtilTestMatrix.java
new file mode 100644
index 0000000..8c6b376
--- /dev/null
+++ b/main/core/test/org/ejml/data/UtilTestMatrix.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import java.util.Arrays;
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * Contains functions useful for testing the results of matrices
+ *
+ * @author Peter Abeles
+ */
+public class UtilTestMatrix {
+
+    public static void checkMat( DenseMatrix64F mat , double ...d )
+    {
+        double data[] = mat.getData();
+
+        for( int i = 0; i < mat.getNumElements(); i++ ) {
+            assertEquals(d[i],data[i],1e-6);
+        }
+    }
+
+    public static void checkSameElements( double tol, int length , double a[], double b[] )
+    {
+        double aa[] = new double[ length ];
+        double bb[] = new double[ length ];
+
+        System.arraycopy(a,0,aa,0,length);
+        System.arraycopy(b,0,bb,0,length);
+
+        Arrays.sort(aa);
+        Arrays.sort(bb);
+
+        for( int i = 0; i < length; i++ ) {
+            if( Math.abs(aa[i]-bb[i])> tol )
+                fail("Mismatched elements");
+        }
+    }
+
+    public static void checkNumFound( int expected , double tol , double value , double data[] )
+    {
+        int numFound = 0;
+
+        for( int i = 0; i < data.length; i++ ) {
+            if( Math.abs(data[i]-value) <= tol )
+                numFound++;
+        }
+
+        assertEquals(expected,numFound);
+    }
+
+    /**
+     * <p>
+     * Sets each element in the matrix to a value drawn from an uniform distribution from 'min' to 'max' inclusive.
+     * </p>
+     *
+     * @param min The minimum value each element can be.
+     * @param max The maximum value each element can be.
+     * @param rand Random number generator used to fill the matrix.
+     */
+    public static DenseMatrix64F random64( int numRows , int numCols , double min , double max , Random rand )
+    {
+        DenseMatrix64F mat = new DenseMatrix64F(numRows,numCols);
+        double d[] = mat.getData();
+        int size = mat.getNumElements();
+
+        double r = max-min;
+
+        for( int i = 0; i < size; i++ ) {
+            d[i] = r*rand.nextDouble()+min;
+        }
+
+        return mat;
+    }
+
+    /**
+     * <p>
+     * Sets each element in the matrix to a value drawn from an uniform distribution from 'min' to 'max' inclusive.
+     * </p>
+     *
+     * @param min The minimum value each element can be.
+     * @param max The maximum value each element can be.
+     * @param rand Random number generator used to fill the matrix.
+     */
+    public static DenseMatrix32F random32( int numRows , int numCols , float min , float max , Random rand )
+    {
+        DenseMatrix32F mat = new DenseMatrix32F(numRows,numCols);
+        float d[] = mat.getData();
+        int size = mat.getNumElements();
+
+        float r = max-min;
+
+        for( int i = 0; i < size; i++ ) {
+            d[i] = r*rand.nextFloat()+min;
+        }
+
+        return mat;
+    }
+
+    
+}
diff --git a/main/core/test/org/ejml/ops/TestComplexMath64F.java b/main/core/test/org/ejml/ops/TestComplexMath64F.java
new file mode 100644
index 0000000..1a93f6b
--- /dev/null
+++ b/main/core/test/org/ejml/ops/TestComplexMath64F.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.data.Complex64F;
+import org.ejml.data.ComplexPolar64F;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestComplexMath64F {
+    @Test
+    public void conj() {
+        Complex64F a = new Complex64F(2, 3);
+        Complex64F b = new Complex64F(-3, 6);
+
+        ComplexMath64F.conj(a, b);
+
+        assertEquals(a.real, b.real, 1e-8);
+        assertEquals(-a.imaginary, b.imaginary, 1e-8);
+    }
+
+    @Test
+    public void plus() {
+        Complex64F a = new Complex64F(2, 3);
+        Complex64F b = new Complex64F(-3, 6);
+        Complex64F c = new Complex64F();
+
+        ComplexMath64F.plus(a, b, c);
+
+        assertEquals(-1, c.real, 1e-8);
+        assertEquals(9, c.imaginary, 1e-8);
+    }
+
+    @Test
+    public void minus() {
+        Complex64F a = new Complex64F(2, 3);
+        Complex64F b = new Complex64F(-3, 6);
+        Complex64F c = new Complex64F();
+
+        ComplexMath64F.minus(a, b, c);
+
+        assertEquals(5, c.real, 1e-8);
+        assertEquals(-3, c.imaginary, 1e-8);
+    }
+
+    @Test
+    public void multiply() {
+        Complex64F a = new Complex64F(2, 3);
+        Complex64F b = new Complex64F(-3, 6);
+        Complex64F c = new Complex64F();
+
+        ComplexMath64F.multiply(a, b, c);
+
+        assertEquals(-24, c.real, 1e-8);
+        assertEquals(3, c.imaginary, 1e-8);
+    }
+
+    @Test
+    public void divide() {
+        Complex64F a = new Complex64F(2, 3);
+        Complex64F b = new Complex64F(-3, 6);
+        Complex64F c = new Complex64F();
+
+        ComplexMath64F.divide(a, b, c);
+
+        assertEquals(0.26666666666, c.real, 1e-8);
+        assertEquals(-0.466666666666, c.imaginary, 1e-8);
+    }
+
+    /**
+     * Test conversion to and from polar form by doing just that and see if it gets the original answer again
+     */
+    @Test
+    public void convert() {
+        Complex64F a = new Complex64F(2, 3);
+        ComplexPolar64F b = new ComplexPolar64F();
+        Complex64F c = new Complex64F();
+
+        ComplexMath64F.convert(a, b);
+        ComplexMath64F.convert(b, c);
+
+        assertEquals(a.real, c.real, 1e-8);
+        assertEquals(a.imaginary, c.imaginary, 1e-8);
+    }
+
+    @Test
+    public void mult_polar() {
+        Complex64F a = new Complex64F(2, 3);
+        Complex64F b = new Complex64F(-3, 6);
+        Complex64F expected = new Complex64F();
+
+        ComplexMath64F.multiply(a, b, expected);
+
+        ComplexPolar64F pa = new ComplexPolar64F(a);
+        ComplexPolar64F pb = new ComplexPolar64F(b);
+        ComplexPolar64F pc = new ComplexPolar64F();
+
+        ComplexMath64F.multiply(pa, pb, pc);
+
+        Complex64F found = pc.toStandard();
+
+        assertEquals(expected.real, found.real, 1e-8);
+        assertEquals(expected.imaginary, found.imaginary, 1e-8);
+    }
+
+    @Test
+    public void div_polar() {
+        Complex64F a = new Complex64F(2, 3);
+        Complex64F b = new Complex64F(-3, 6);
+        Complex64F expected = new Complex64F();
+
+        ComplexMath64F.divide(a, b, expected);
+
+        ComplexPolar64F pa = new ComplexPolar64F(a);
+        ComplexPolar64F pb = new ComplexPolar64F(b);
+        ComplexPolar64F pc = new ComplexPolar64F();
+
+        ComplexMath64F.divide(pa, pb, pc);
+
+        Complex64F found = pc.toStandard();
+
+        assertEquals(expected.real, found.real, 1e-8);
+        assertEquals(expected.imaginary, found.imaginary, 1e-8);
+    }
+
+    @Test
+    public void pow() {
+        ComplexPolar64F a = new ComplexPolar64F(2, 0.2);
+        ComplexPolar64F expected = new ComplexPolar64F();
+        ComplexPolar64F found = new ComplexPolar64F();
+
+        ComplexMath64F.multiply(a, a, expected);
+        ComplexMath64F.multiply(a, expected, expected);
+
+        ComplexMath64F.pow(a, 3, found);
+
+        assertEquals(expected.r, found.r, 1e-8);
+        assertEquals(expected.theta, found.theta, 1e-8);
+    }
+
+    @Test
+    public void root_polar() {
+        ComplexPolar64F expected = new ComplexPolar64F(2, 0.2);
+        ComplexPolar64F root = new ComplexPolar64F();
+        ComplexPolar64F found = new ComplexPolar64F();
+
+        // compute the square root of a complex number then see if the
+        // roots equal the output
+        for (int i = 0; i < 2; i++) {
+            ComplexMath64F.root(expected, 2, 0, root);
+
+            ComplexMath64F.multiply(root, root, found);
+
+            Complex64F e = expected.toStandard();
+            Complex64F f = found.toStandard();
+
+            assertEquals(e.real, f.real, 1e-8);
+            assertEquals(e.imaginary, f.imaginary, 1e-8);
+        }
+    }
+
+    @Test
+    public void root_standard() {
+        Complex64F expected = new Complex64F(2, 0.2);
+        Complex64F root = new Complex64F();
+        Complex64F found = new Complex64F();
+
+        // compute the square root of a complex number then see if the
+        // roots equal the output
+        for (int i = 0; i < 2; i++) {
+            ComplexMath64F.root(expected, 2, 0, root);
+
+            ComplexMath64F.multiply(root, root, found);
+
+            assertEquals(expected.real, found.real, 1e-8);
+            assertEquals(expected.imaginary, found.imaginary, 1e-8);
+        }
+    }
+
+    @Test
+    public void sqrt_standard() {
+        Complex64F input = new Complex64F(2, 0.2);
+        Complex64F root = new Complex64F();
+        Complex64F found = new Complex64F();
+
+        ComplexMath64F.sqrt(input, root);
+        ComplexMath64F.multiply(root, root, found);
+
+        assertEquals(input.real, found.real, 1e-8);
+        assertEquals(input.imaginary, found.imaginary, 1e-8);
+
+        input = new Complex64F(2, -0.2);
+
+        ComplexMath64F.sqrt(input, root);
+        ComplexMath64F.multiply(root, root, found);
+
+        assertEquals(input.real, found.real, 1e-8);
+        assertEquals(input.imaginary, found.imaginary, 1e-8);
+    }
+}
diff --git a/main/core/test/org/ejml/ops/TestConvertMatrixType.java b/main/core/test/org/ejml/ops/TestConvertMatrixType.java
new file mode 100644
index 0000000..fc20417
--- /dev/null
+++ b/main/core/test/org/ejml/ops/TestConvertMatrixType.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.alg.block.BlockMatrixOps;
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.data.FixedMatrix64F;
+import org.ejml.data.RealMatrix64F;
+import org.junit.Test;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestConvertMatrixType {
+
+    Random rand = new Random(234);
+
+    @Test
+    public void any_to_any() {
+        DenseMatrix64F a = new DenseMatrix64F(2,3,true,1,2,3,4,5,6);
+        DenseMatrix64F b = new DenseMatrix64F(2,3);
+
+        ConvertMatrixType.convert((RealMatrix64F)a,(RealMatrix64F)b);
+
+        assertTrue(MatrixFeatures.isIdentical(a,b,1e-12));
+    }
+
+    @Test
+    public void checkAll_Fixed_to_DM() throws IllegalAccessException, InstantiationException, InvocationTargetException {
+        Method[] methods = ConvertMatrixType.class.getMethods();
+
+        int numFound = 0;
+
+        for( Method m : methods ) {
+            if( !m.getName().equals("convert") )
+                continue;
+            Class[]param = m.getParameterTypes();
+
+            if( !FixedMatrix64F.class.isAssignableFrom(param[0]) ) {
+                continue;
+            }
+
+            FixedMatrix64F a = (FixedMatrix64F)param[0].newInstance();
+            DenseMatrix64F b = new DenseMatrix64F(a.getNumRows(),a.getNumCols());
+
+            for( int i = 0; i < b.numRows; i++ ) {
+                for( int j = 0; j < b.numCols; j++ ) {
+                    a.set(i, j, rand.nextDouble());
+                }
+            }
+
+            Object[] input = new Object[param.length];
+            input[0] = a;
+            input[1] = b;
+
+            m.invoke(null,input);
+
+            checkIdentical(a,b);
+
+            numFound++;
+        }
+
+        assertEquals(5+5,numFound);
+    }
+
+    @Test
+    public void checkAll_DM_to_Fixed() throws IllegalAccessException, InstantiationException, InvocationTargetException {
+        Method[] methods = ConvertMatrixType.class.getMethods();
+
+        int numFound = 0;
+
+        for( Method m : methods ) {
+            if( !m.getName().equals("convert") )
+                continue;
+            Class[]param = m.getParameterTypes();
+
+            if( !FixedMatrix64F.class.isAssignableFrom(param[1]) ) {
+                continue;
+            }
+
+            FixedMatrix64F b = (FixedMatrix64F)param[1].newInstance();
+            DenseMatrix64F a = new DenseMatrix64F(b.getNumRows(),b.getNumCols());
+
+            for( int i = 0; i < a.numRows; i++ ) {
+                for( int j = 0; j < a.numCols; j++ ) {
+                    a.set(i, j, rand.nextDouble());
+                }
+            }
+
+            Object[] input = new Object[param.length];
+            input[0] = a;
+            input[1] = b;
+
+            m.invoke(null,input);
+
+            checkIdentical(a,b);
+
+            numFound++;
+        }
+
+        assertEquals(5+5,numFound);
+    }
+
+    @Test
+    public void BM_to_DM() {
+        for( int rows = 1; rows <= 8; rows++ ) {
+            for( int cols = 1; cols <= 8; cols++ ) {
+                BlockMatrix64F a = BlockMatrixOps.createRandom(rows,cols,-1,2,rand);
+                DenseMatrix64F b = new DenseMatrix64F(rows,cols);
+
+                ConvertMatrixType.convert(a,b);
+
+                checkIdentical(a,b);
+            }
+        }
+    }
+
+    @Test
+    public void DM_to_BM() {
+        for( int rows = 1; rows <= 8; rows++ ) {
+            for( int cols = 1; cols <= 8; cols++ ) {
+                DenseMatrix64F a = RandomMatrices.createRandom(rows,cols,rand);
+                BlockMatrix64F b = new BlockMatrix64F(rows,cols,3);
+
+                ConvertMatrixType.convert(a,b);
+
+                checkIdentical(a,b);
+            }
+        }
+    }
+
+
+    private void checkIdentical( RealMatrix64F a , RealMatrix64F b ) {
+        for( int i = 0; i < a.getNumRows(); i++  ) {
+            for( int j = 0; j < a.getNumCols(); j++ ) {
+                assertEquals(a.get(i,j),b.get(i,j),1e-8);
+            }
+        }
+    }
+
+    private void checkIdenticalV( RealMatrix64F a , RealMatrix64F b ) {
+        boolean columnVectorA = a.getNumRows() > a.getNumCols();
+        boolean columnVectorB = b.getNumRows() > b.getNumCols();
+
+        int length = Math.max(a.getNumRows(),b.getNumRows());
+
+        for( int i = 0; i < length; i++  ) {
+
+            double valueA,valueB;
+
+            if( columnVectorA )
+                valueA = a.get(i,0);
+            else
+                valueA = a.get(0,i);
+
+            if( columnVectorB )
+                valueB = b.get(i,0);
+            else
+                valueB = b.get(0,i);
+
+            assertEquals(valueA,valueB,1e-8);
+        }
+
+
+
+    }
+
+}
diff --git a/main/core/test/org/ejml/ops/TestMatrixIO.java b/main/core/test/org/ejml/ops/TestMatrixIO.java
new file mode 100644
index 0000000..1884b56
--- /dev/null
+++ b/main/core/test/org/ejml/ops/TestMatrixIO.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2009-2013, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.data.DenseMatrix64F;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Random;
+
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestMatrixIO {
+
+    Random rand = new Random(23424);
+
+    @Test
+    public void load_save_binary() throws IOException {
+        DenseMatrix64F A = RandomMatrices.createRandom(6,3,rand);
+
+        MatrixIO.saveBin(A, "temp.mat");
+
+        DenseMatrix64F A_copy = MatrixIO.loadBin("temp.mat");
+
+        assertTrue(A != A_copy);
+        assertTrue(MatrixFeatures.isEquals(A,A_copy));
+
+        // clean up
+        File f = new File("temp.mat");
+        assertTrue(f.exists());
+        assertTrue(f.delete());
+    }
+
+    @Test
+    public void load_save_csv() throws IOException {
+        DenseMatrix64F A = RandomMatrices.createRandom(6,3,rand);
+
+        MatrixIO.saveCSV(A,"temp.csv");
+
+        DenseMatrix64F A_copy = MatrixIO.loadCSV("temp.csv");
+
+        assertTrue(A != A_copy);
+        assertTrue(MatrixFeatures.isEquals(A,A_copy));
+
+        // clean up
+        File f = new File("temp.csv");
+        assertTrue(f.exists());
+        assertTrue(f.delete());
+    }
+}
diff --git a/main/core/test/org/ejml/ops/TestReadMatrixCsv.java b/main/core/test/org/ejml/ops/TestReadMatrixCsv.java
new file mode 100644
index 0000000..a40e7e3
--- /dev/null
+++ b/main/core/test/org/ejml/ops/TestReadMatrixCsv.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.data.CDenseMatrix64F;
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestReadMatrixCsv {
+    /**
+     * Make sure incorrectly formatted data is handled gracefully
+     */
+    @Test(expected=IOException.class)
+    public void bad_matrix_row() throws IOException {
+        String s = "3 2 real\n0 0\n1 1";
+
+        ReadMatrixCsv alg = new ReadMatrixCsv(new ByteArrayInputStream(s.getBytes()));
+
+        alg.read();
+        fail("Should have had an exception");
+    }
+
+    @Test(expected=IOException.class)
+    public void bad_matrix_col() throws IOException {
+        String s = "3 2 real\n0 0\n1\n0 3";
+
+        ReadMatrixCsv alg = new ReadMatrixCsv(new ByteArrayInputStream(s.getBytes()));
+
+        alg.read();
+        fail("Should have had an exception");
+    }
+
+    public void complex() throws IOException {
+        String s = "3 2 complex\n0 2 0 -1\n1 2 -1 -1\n0 2 3 10";
+
+        ReadMatrixCsv alg = new ReadMatrixCsv(new ByteArrayInputStream(s.getBytes()));
+
+        CDenseMatrix64F expected = new CDenseMatrix64F(3,2,true,0,2,0,-1,1,2,-1,-1,0,2,3,10);
+        CDenseMatrix64F m = alg.read();
+
+        assertTrue(CMatrixFeatures.isIdentical(expected,m,1e-8));
+    }
+}
diff --git a/main/dense64/build.gradle b/main/dense64/build.gradle
new file mode 100644
index 0000000..bb96630
--- /dev/null
+++ b/main/dense64/build.gradle
@@ -0,0 +1,11 @@
+dependencies {
+    compile project(':main:core')
+    testCompile project(':main:experimental')
+    testCompile project(':main:core').sourceSets.test.output
+}
+
+idea {
+    module {
+        name = "EJML Dense 64"
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/generate/org/ejml/CodeGeneratorBase.java b/main/dense64/generate/org/ejml/CodeGeneratorBase.java
new file mode 100644
index 0000000..992689b
--- /dev/null
+++ b/main/dense64/generate/org/ejml/CodeGeneratorBase.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.PrintStream;
+
+
+/**
+ * <p>Base class for code generators.</p>
+ *
+ * @author Peter Abeles
+ */
+public abstract class CodeGeneratorBase {
+
+    public static final String copyright =
+            "/*\n" +
+                    " * Copyright (c) 2009-2013, Peter Abeles. All Rights Reserved.\n" +
+                    " *\n" +
+                    " * This file is part of Efficient Java Matrix Library (EJML).\n" +
+                    " *\n" +
+                    " * Licensed under the Apache License, Version 2.0 (the \"License\");\n" +
+                    " * you may not use this file except in compliance with the License.\n" +
+                    " * You may obtain a copy of the License at\n" +
+                    " *\n" +
+                    " *   http://www.apache.org/licenses/LICENSE-2.0\n" +
+                    " *\n" +
+                    " * Unless required by applicable law or agreed to in writing, software\n" +
+                    " * distributed under the License is distributed on an \"AS IS\" BASIS,\n" +
+                    " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" +
+                    " * See the License for the specific language governing permissions and\n" +
+                    " * limitations under the License.\n" +
+                    " */";
+
+    protected PrintStream out;
+    protected String className;
+
+    /**
+     * Creates
+     *
+     * @throws java.io.FileNotFoundException
+     */
+    public abstract void generate() throws FileNotFoundException;
+
+    public void setOutputFile( String className ) throws FileNotFoundException {
+        this.className = className;
+        out = new PrintStream(new FileOutputStream(className + ".java"));
+        out.print(copyright);
+        out.println();
+        out.println("package " + getPackage() + ";");
+        out.println();
+    }
+
+    public String getPackage() {
+        return getClass().getPackage().getName();
+    }
+}
diff --git a/main/dense64/generate/org/ejml/alg/dense/misc/GenerateDeterminantFromMinor.java b/main/dense64/generate/org/ejml/alg/dense/misc/GenerateDeterminantFromMinor.java
new file mode 100644
index 0000000..98b76b8
--- /dev/null
+++ b/main/dense64/generate/org/ejml/alg/dense/misc/GenerateDeterminantFromMinor.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.misc;
+
+import org.ejml.alg.generic.CodeGeneratorMisc;
+
+import java.io.FileNotFoundException;
+import java.io.PrintStream;
+
+
+/**
+ * Generates code for an unrolled determinant by minor.
+ *
+ * NOTE:  There are some repeat calculations of inner determinants.   Maybe it could be speed up by calculating those?
+ *
+ * @author Peter Abeles
+ */
+public class GenerateDeterminantFromMinor {
+
+    protected PrintStream stream;
+    protected int N;
+
+    public GenerateDeterminantFromMinor( String fileName ) throws FileNotFoundException {
+        stream = new PrintStream(fileName);
+    }
+
+    public GenerateDeterminantFromMinor(PrintStream stream) {
+        this.stream = stream;
+    }
+
+    public void createClass(int N) {
+        printTop(N);
+
+        printCalls(N);
+
+        print2();
+        print3();
+        for( int i = 4; i <= N; i++ ) {
+            printFunction(i);
+        }
+
+        stream.print("}\n");
+    }
+
+    private void printTop(int N) {
+        String foo = CodeGeneratorMisc.COPYRIGHT +
+                "\n" +
+                "package org.ejml.alg.dense.misc;\n" +
+                "\n" +
+                "import org.ejml.data.RowD1Matrix64F;\n" +
+                "\n" +
+                "\n" +
+                "/**\n" +
+                " * This code was auto generated by  {@link GenerateDeterminantFromMinor} and should not be modified\n" +
+                " * directly.  \n" +
+                " * \n" +
+                " * @author Peter Abeles\n" +
+                " */\n" +
+                "public class UnrolledDeterminantFromMinor {\n"+
+                "    \n" +
+                "    public static final int MAX = "+N+";\n";
+
+        stream.print(foo);
+    }
+
+    private void print2() {
+        stream.print("    public static double det2( RowD1Matrix64F mat )\n" +
+                "    {\n" +
+                "        return mat.get(0)*mat.get(3) - mat.get(1)*mat.get(2);\n" +
+                "    }\n\n");
+    }
+
+    private void print3() {
+        stream.print("    public static double det3( RowD1Matrix64F mat )\n" +
+                "    {\n" +
+                "        double a11 = mat.get( 0 );\n" +
+                "        double a12 = mat.get( 1 );\n" +
+                "        double a13 = mat.get( 2 );\n" +
+                "        double a21 = mat.get( 3 );\n" +
+                "        double a22 = mat.get( 4 );\n" +
+                "        double a23 = mat.get( 5 );\n" +
+                "        double a31 = mat.get( 6 );\n" +
+                "        double a32 = mat.get( 7 );\n" +
+                "        double a33 = mat.get( 8 );\n" +
+                "\n" +
+                "        double a = a11*(a22*a33 - a23*a32);\n" +
+                "        double b = a12*(a21*a33 - a23*a31);\n" +
+                "        double c = a13*(a21*a32 - a31*a22);\n" +
+                "\n" +
+                "        return a-b+c;\n" +
+                "    }\n" +
+                "\n");
+    }
+
+    private void printCalls( int N )
+    {
+        stream.print(
+                "    \n" +
+                        "    public static double det( RowD1Matrix64F mat ) {\n");
+        stream.print(
+                "        if( mat.numRows == 2 ) {\n" +
+                "            return det2(mat);\n");
+        for( int i = 3; i <= N; i++ ) {
+            stream.print("        } else if( mat.numRows == "+i+" ) {\n" +
+                    "            return det"+i+"(mat);            \n");
+        }
+        stream.print("        }\n" +
+                "        \n" +
+                "        throw new IllegalArgumentException(\"Not supported\");\n" +
+                "    }\n\n");
+    }
+
+    protected String getInputValue( int element ) {
+        return "mat.get( "+element+" )";
+    }
+
+    private void printFunction( int N )
+    {
+        stream.print("    public static double det"+N+"( RowD1Matrix64F mat )\n" +
+                "    {\n");
+
+        printFunctionInner(N);
+
+        stream.print("        return ret;\n");
+        stream.print("    }\n");
+        stream.print("\n");
+    }
+
+    public void printFunctionInner( int N ) {
+        // extracts the first minor
+        int M = N-1;
+        this.N = M;
+        int matrix[] = new int[M*M];
+        int index = 0;
+        for( int i = 1; i <= M; i++ ) {
+            int origIndex = i*N+1;
+            for( int j = 1; j <= M; j++ , origIndex++,index++) {
+                matrix[index] = index;
+                stream.print("        double  "+a(index)+" = "+getInputValue(origIndex)+";\n");
+            }
+        }
+
+        stream.print("\n");
+        stream.print("        double ret = 0;\n");
+        stream.print("        ret += "+getInputValue(0)+" * (");
+        minor(matrix, 0, M);
+        stream.print(");\n");
+
+        for( int minor = 2; minor <= N; minor++ ) {
+            for( int i = 1; i <= M; i++ ) {
+                index = (minor-2)+(i-1)*M;
+                int origIndex = minor-2+i*N;
+                stream.print("        "+a(index)+" = "+getInputValue(origIndex)+";\n");
+            }
+
+            if( minor % 2 == 0 ) {
+                stream.print("        ret -= ");
+            } else {
+                stream.print("        ret += ");
+            }
+            stream.print(getInputValue(minor-1)+" * (");
+            minor(matrix,0,M);
+            stream.print(");\n");
+        }
+    }
+
+    private void minor( int m[] , int row , int N )
+    {
+        if( N == 2 ) {
+            stream.print(a(m[0])+"*"+a(m[3])+" - "+a(m[1])+"*"+a(m[2]));
+        } else {
+            int M = N-1;
+            int d[] = new int[ M*M ];
+
+            for( int i = 0; i < N; i++ ) {
+                int index = 0;
+
+                for( int j = 1; j < N; j++ ) {
+                    for( int k = 0; k < N; k++ ) {
+                       if( k != i ) {
+                           d[index++] = m[j*N+k];
+                       }
+                    }
+                }
+
+                int pow = i;
+
+                if( pow % 2 == 0 )
+                    stream.print(" + "+a(m[i])+"*(");
+                else
+                    stream.print(" - "+a(m[i])+"*(");
+
+                minor(d,row+1,M);
+
+                stream.print(")");
+            }
+
+        }
+    }
+
+    private String a( int index )
+    {
+        int i = index/N+1;
+        int j = index%N+1;
+
+        return "a"+i+""+j;
+    }
+
+    public static void main( String args[] ) throws FileNotFoundException {
+        GenerateDeterminantFromMinor gen = new GenerateDeterminantFromMinor("UnrolledDeterminantFromMinor.java");
+
+        gen.createClass(6);
+    }
+}
diff --git a/main/dense64/generate/org/ejml/alg/dense/misc/GenerateInverseFromMinor.java b/main/dense64/generate/org/ejml/alg/dense/misc/GenerateInverseFromMinor.java
new file mode 100644
index 0000000..6d708d2
--- /dev/null
+++ b/main/dense64/generate/org/ejml/alg/dense/misc/GenerateInverseFromMinor.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.misc;
+
+import org.ejml.alg.generic.CodeGeneratorMisc;
+
+import java.io.FileNotFoundException;
+import java.io.PrintStream;
+
+
+/**
+ * Generates unrolled matrix from minor analytical functions.  these can run much faster than LU but will only
+ * work for small matrices.
+ *
+ * When computing the determinants for each minor there are some repeat calculations going on.  I manually
+ * removed those by storing them in a local variable and only computing it once.  Despite reducing the FLOP count
+ * it didn't seem to noticeably improve performance in a runtime benchmark..
+ *
+ * @author Peter Abeles
+ */
+public class GenerateInverseFromMinor {
+
+    String className = "UnrolledInverseFromMinor";
+    PrintStream stream;
+    int N;
+
+    public GenerateInverseFromMinor( boolean createStream ) throws FileNotFoundException {
+        if( createStream )
+            stream = new PrintStream(className +".java");
+    }
+
+    public void createClass(int N) {
+        printTop(N);
+
+        printCalls(N);
+
+        for( int i = 2; i <= N; i++ ) {
+            printFunction(i);
+        }
+
+        stream.print("}\n");
+    }
+
+    private void printTop(int N) {
+        String foo = CodeGeneratorMisc.COPYRIGHT +
+                "\n" +
+                "package org.ejml.alg.dense.misc;\n" +
+                "\n" +
+                "import org.ejml.data.DenseMatrix64F;\n" +
+                "\n" +
+                "\n" +
+                "/**\n" +
+                " * This code was auto generated by  {@link GenerateInverseFromMinor} and should not be modified\n" +
+                " * directly.  The input matrix is scaled make it much less prone to overflow and underflow issues.\n" +
+                " * \n" +
+                " * @author Peter Abeles\n" +
+                " */\n" +
+                "public class "+className+" {\n"+
+                "    \n" +
+                "    public static final int MAX = "+N+";\n";
+
+        stream.print(foo);
+    }
+
+    private void printCalls( int N )
+    {
+        stream.print(
+                "    \n" +
+                        "    public static void inv( DenseMatrix64F mat , DenseMatrix64F inv ) {\n");
+        stream.print("        double max = Math.abs(mat.data[0]);\n" +
+                "        int N = mat.getNumElements();\n" +
+                "        \n" +
+                "        for( int i = 1; i < N; i++ ) {\n" +
+                "            double a = Math.abs(mat.data[i]);\n" +
+                "            if( a > max ) max = a;\n" +
+                "        }\n\n");
+        stream.print(
+                "        if( mat.numRows == 2 ) {\n" +
+                        "            inv2(mat,inv,1.0/max);\n");
+        for( int i = 3; i <= N; i++ ) {
+            stream.print("        } else if( mat.numRows == "+i+" ) {\n" +
+                    "            inv"+i+"(mat,inv,1.0/max);            \n");
+        }
+        stream.print("        } else {\n" +
+                "            throw new IllegalArgumentException(\"Not supported\");\n" +
+                "        }\n" +
+                "    }\n\n");
+    }
+
+    private void printFunction( int N )
+    {
+        stream.print("    public static void inv"+N+"( DenseMatrix64F mat , DenseMatrix64F inv , double scale )\n" +
+                "    {\n" +
+                "        double []data = mat.data;\n"+
+                "\n");
+
+
+        this.N = N;
+
+        // extracts the first minor
+        int matrix[] = new int[N*N];
+        int index = 0;
+        for( int i = 1; i <= N; i++ ) {
+            for( int j = 1; j <= N; j++ , index++) {
+                matrix[index] = index;
+                stream.print("        double "+a(index)+" = "+"data[ "+index+" ]*scale;\n");
+            }
+        }
+        stream.println();
+
+        printMinors(matrix, N, stream);
+
+        stream.println();
+        stream.print("        data = inv.data;\n");
+
+        index = 0;
+        for( int i = 1; i <= N; i++ ) {
+            for( int j = 1; j <= N; j++ , index++) {
+                stream.print("        "+"data["+index+"] = m"+j+""+i+" / det;\n");
+            }
+        }
+
+        stream.println();
+        stream.print("    }\n");
+        stream.print("\n");
+    }
+
+    /**
+     * Put the core auto-code algorithm here so an external class can call it
+     */
+    public void printMinors(int matrix[], int N, PrintStream stream) {
+        this.N = N;
+        this.stream = stream;
+
+        // compute all the minors
+        int index = 0;
+        for( int i = 1; i <= N; i++ ) {
+            for( int j = 1; j <= N; j++ , index++) {
+                stream.print("        double m"+i+""+j+" = ");
+                if( (i+j) % 2 == 1 )
+                    stream.print("-( ");
+                printTopMinor(matrix,i-1,j-1,N);
+                if( (i+j) % 2 == 1 )
+                    stream.print(")");
+                stream.print(";\n");
+            }
+        }
+
+        stream.println();
+        // compute the determinant
+        stream.print("        double det = (a11*m11");
+        for( int i = 2; i <= N; i++ ) {
+            stream.print(" + "+a(i-1)+"*m"+1+""+i);
+        }
+        stream.println(")/scale;");
+
+    }
+
+    private void printTopMinor( int m[] , int row , int col , int N )
+    {
+        int d[] = createMinor(m,row,col,N);
+
+        det(d,0,N-1);
+    }
+
+    private int[] createMinor( int m[] , int row , int col , int N )
+    {
+        int M = N-1;
+
+        int[] ret = new int[ M*M ];
+
+        int index = 0;
+        for( int i = 0; i < N; i++ ) {
+            if( i == row ) continue;
+            for( int j = 0; j < N; j++ ) {
+                if( j == col ) continue;
+
+                ret[index++] = m[i*N+j];
+            }
+        }
+
+        return ret;
+    }
+    private void det( int m[] , int row , int N )
+    {
+        if( N == 1 ) {
+            stream.print(a(m[0]));
+        } else if( N == 2 ) {
+            stream.print(a(m[0])+"*"+a(m[3])+" - "+a(m[1])+"*"+a(m[2]));
+        } else {
+            int M = N-1;
+
+            for( int i = 0; i < N; i++ ) {
+                int d[] = createMinor(m,0,i,N);
+
+                int pow = i;
+
+                if( pow % 2 == 0 )
+                    stream.print(" + "+a(m[i])+"*(");
+                else
+                    stream.print(" - "+a(m[i])+"*(");
+
+                det(d,row+1,M);
+
+                stream.print(")");
+            }
+
+        }
+    }
+
+    private String a( int index )
+    {
+        int i = index/N+1;
+        int j = index%N+1;
+
+        return "a"+i+""+j;
+    }
+
+    public static void main( String args[] ) throws FileNotFoundException {
+        GenerateInverseFromMinor gen = new GenerateInverseFromMinor(true);
+
+        gen.createClass(5);
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/generate/org/ejml/alg/dense/mult/GeneratorMatrixMatrixMult.java b/main/dense64/generate/org/ejml/alg/dense/mult/GeneratorMatrixMatrixMult.java
new file mode 100644
index 0000000..569eb8c
--- /dev/null
+++ b/main/dense64/generate/org/ejml/alg/dense/mult/GeneratorMatrixMatrixMult.java
@@ -0,0 +1,523 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.mult;
+
+import org.ejml.alg.generic.CodeGeneratorMisc;
+
+import java.io.FileNotFoundException;
+import java.io.PrintStream;
+
+
+/**
+ * <p>
+ * This class generates code for various matrix matrix multiplication operations.  The code associated
+ * with these operators is often only slightly different from each other.  So to remove some
+ * of the tediousness of writing and maintaining it is autogenerated.
+ * <p>
+ * <p>
+ * To create {@link MatrixMatrixMult} simply run this application and copy it to the appropriate location.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class GeneratorMatrixMatrixMult {
+
+    PrintStream stream;
+
+    public GeneratorMatrixMatrixMult( String fileName ) throws FileNotFoundException {
+        stream = new PrintStream(fileName);
+    }
+
+    public void createClass() {
+        String preamble = CodeGeneratorMisc.COPYRIGHT +
+                "\n" +
+                "package org.ejml.alg.dense.mult;\n" +
+                "\n" +
+                "import org.ejml.data.RowD1Matrix64F;\n"+
+                "import org.ejml.ops.CommonOps;\n" +
+                "\n" +
+                "/**\n" +
+                " * <p>\n" +
+                " * This class contains various types of matrix matrix multiplication operations for {@link RowD1Matrix64F}.\n" +
+                " * </p>\n" +
+                " * <p>\n" +
+                " * Two algorithms that are equivalent can often have very different runtime performance.\n" +
+                " * This is because of how modern computers uses fast memory caches to speed up reading/writing to data.\n" +
+                " * Depending on the order in which variables are processed different algorithms can run much faster than others,\n" +
+                " * even if the number of operations is the same.\n" +
+                " * </p>\n" +
+                " *\n" +
+                " * <p>\n" +
+                " * Algorithms that are labeled as 'reorder' are designed to avoid caching jumping issues, some times at the cost\n" +
+                " * of increasing the number of operations.  This is important for large matrices.  The straight forward \n" +
+                " * implementation seems to be faster for small matrices.\n" +
+                " * </p>\n" +
+                " * \n" +
+                " * <p>\n" +
+                " * Algorithms that are labeled as 'aux' use an auxiliary array of length n.  This array is used to create\n" +
+                " * a copy of an out of sequence column vector that is referenced several times.  This reduces the number\n" +
+                " * of cache misses.  If the 'aux' parameter passed in is null then the array is declared internally.\n" +
+                " * </p>\n" +
+                " *\n" +
+                " * <p>\n" +
+                " * Typically the straight forward implementation runs about 30% faster on smaller matrices and\n" +
+                " * about 5 times slower on larger matrices.  This is all computer architecture and matrix shape/size specific.\n" +
+                " * </p>\n" +
+                " * \n" +
+                " * <p>\n" +
+                " * <center>******** IMPORTANT **********</center>\n" +
+                " * This class was auto generated using {@link "+getClass().getName()+"}\n" +
+                " * </p>\n" +
+                " * \n" +
+                " * @author Peter Abeles\n" +
+                " */\n"+
+                "public class MatrixMatrixMult {\n";
+
+        stream.print(preamble);
+
+        for( int i = 0; i < 2; i++ ) {
+            boolean alpha = i == 1;
+            for( int j = 0; j < 2; j++ ) {
+                boolean add = j == 1;
+                printMult_reroder(alpha,add);
+                stream.print("\n");
+                printMult_small(alpha,add);
+                stream.print("\n");
+                printMult_aux(alpha,add);
+                stream.print("\n");
+                printMultTransA_reorder(alpha,add);
+                stream.print("\n");
+                printMultTransA_small(alpha,add);
+                stream.print("\n");
+                printMultTransAB(alpha,add);
+                stream.print("\n");
+                printMultTransAB_aux(alpha,add);
+                stream.print("\n");
+                printMultTransB(alpha,add);
+                stream.print("\n");
+            }
+        }
+        stream.print("}\n");
+    }
+
+    private String makeBoundsCheck(boolean tranA, boolean tranB, String auxLength)
+    {
+        String a_numCols = tranA ? "a.numRows" : "a.numCols";
+        String a_numRows = tranA ? "a.numCols" : "a.numRows";
+        String b_numCols = tranB ? "b.numRows" : "b.numCols";
+        String b_numRows = tranB ? "b.numCols" : "b.numRows";
+
+        String ret =
+                        "        if( a == c || b == c )\n" +
+                        "            throw new IllegalArgumentException(\"Neither 'a' or 'b' can be the same matrix as 'c'\");\n"+
+                        "        else if( "+a_numCols+" != "+b_numRows+" ) {\n" +
+                        "            throw new MatrixDimensionException(\"The 'a' and 'b' matrices do not have compatible dimensions\");\n" +
+                        "        } else if( "+a_numRows+" != c.numRows || "+b_numCols+" != c.numCols ) {\n" +
+                        "            throw new MatrixDimensionException(\"The results matrix does not have the desired dimensions\");\n" +
+                        "        }\n" +
+                        "\n";
+
+        if( auxLength != null ) {
+            ret += "        if( aux == null ) aux = new double[ "+auxLength+" ];\n\n";
+        }
+
+        return ret;
+    }
+
+    private String handleZeros( boolean add ) {
+
+        String fill = add ? "" : "            CommonOps.fill(c,0);\n";
+
+        String ret =
+                "        if( a.numCols == 0 || a.numRows == 0 ) {\n" +
+                fill +
+                "            return;\n" +
+                "        }\n";
+        return ret;
+    }
+
+    private String makeComment( String nameOp , boolean hasAlpha )
+    {
+        String a = hasAlpha ? "double, " : "";
+        String inputs = "("+a+" org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)";
+
+
+        String ret =
+                "    /**\n" +
+                "     * @see org.ejml.ops.CommonOps#"+nameOp+inputs+"\n" +
+                "     */\n";
+        return ret;
+    }
+
+    private String makeHeader(String nameOp, String variant,
+                              boolean add, boolean hasAlpha, boolean hasAux,
+                              boolean tranA, boolean tranB)
+    {
+        if( add ) nameOp += "Add";
+
+        // make the op name
+        if( tranA && tranB ) {
+            nameOp += "TransAB";
+        } else if( tranA ) {
+            nameOp += "TransA";
+        } else if( tranB ) {
+            nameOp += "TransB";
+        }
+
+        String ret = makeComment(nameOp,hasAlpha)+
+                     "    public static void "+nameOp;
+
+        if( variant != null ) ret += "_"+variant+"( ";
+        else ret += "( ";
+
+        if( hasAlpha ) ret += "double alpha , ";
+
+        if( hasAux ) {
+            ret += "RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c , double []aux )\n";
+        } else {
+            ret += "RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )\n";
+        }
+
+        ret += "    {\n";
+
+        return ret;
+    }
+
+    public void printMult_reroder( boolean alpha , boolean add ) {
+        String header,valLine;
+
+        header = makeHeader("mult","reorder",add,alpha, false, false,false);
+
+        if( alpha ) {
+            valLine = "valA = alpha*a.get(indexA++);\n";
+        } else {
+            valLine = "valA = a.get(indexA++);\n";
+        }
+
+        String assignment = add ? "plus" : "set";
+
+        String foo =
+                header + makeBoundsCheck(false,false, null)+handleZeros(add) +
+                        "        double valA;\n"+
+                        "        int indexCbase= 0;\n" +
+                        "        int endOfKLoop = b.numRows*b.numCols;\n"+
+                        "\n" +
+                        "        for( int i = 0; i < a.numRows; i++ ) {\n" +
+                        "            int indexA = i*a.numCols;\n" +
+                        "\n"+
+                        "            // need to assign c.data to a value initially\n" +
+                        "            int indexB = 0;\n" +
+                        "            int indexC = indexCbase;\n" +
+                        "            int end = indexB + b.numCols;\n" +
+                        "\n" +
+                        "            "+valLine +
+                        "\n" +
+                        "            while( indexB < end ) {\n" +
+                        "                c."+assignment+"(indexC++ , valA*b.get(indexB++));\n" +
+                        "            }\n" +
+                        "\n" +
+                        "            // now add to it\n"+
+                        "            while( indexB != endOfKLoop ) { // k loop\n"+
+                        "                indexC = indexCbase;\n" +
+                        "                end = indexB + b.numCols;\n" +
+                        "\n" +
+                        "                "+valLine+
+                        "\n" +
+                        "                while( indexB < end ) { // j loop\n" +
+                        "                    c.plus(indexC++ , valA*b.get(indexB++));\n" +
+                        "                }\n" +
+                        "            }\n" +
+                        "            indexCbase += c.numCols;\n" +
+                        "        }\n" +
+                        "    }\n";
+
+        stream.print(foo);
+    }
+
+    public void printMult_small( boolean alpha , boolean add ) {
+        String header,valLine;
+
+        header = makeHeader("mult","small",add,alpha, false, false,false);
+
+        String assignment = add ? "plus" : "set";
+
+        if( alpha ) {
+            valLine = "                c."+assignment+"( cIndex++ , alpha*total );\n";
+        } else {
+            valLine = "                c."+assignment+"( cIndex++ , total );\n";
+        }
+
+        String foo =
+                header + makeBoundsCheck(false,false, null)+
+                        "        int aIndexStart = 0;\n" +
+                        "        int cIndex = 0;\n" +
+                        "\n" +
+                        "        for( int i = 0; i < a.numRows; i++ ) {\n" +
+                        "            for( int j = 0; j < b.numCols; j++ ) {\n" +
+                        "                double total = 0;\n" +
+                        "\n" +
+                        "                int indexA = aIndexStart;\n" +
+                        "                int indexB = j;\n" +
+                        "                int end = indexA + b.numRows;\n" +
+                        "                while( indexA < end ) {\n" +
+                        "                    total += a.get(indexA++) * b.get(indexB);\n" +
+                        "                    indexB += b.numCols;\n" +
+                        "                }\n" +
+                        "\n" +
+                        valLine +
+                        "            }\n" +
+                        "            aIndexStart += a.numCols;\n" +
+                        "        }\n" +
+                        "    }\n";
+        stream.print(foo);
+    }
+
+    public void printMult_aux( boolean alpha , boolean add ) {
+        String header,valLine;
+
+        header = makeHeader("mult","aux",add,alpha, true, false,false);
+
+        String assignment = add ? "plus" : "set";
+
+        if( alpha ) {
+            valLine = "                c."+assignment+"( i*c.numCols+j , alpha*total );\n";
+        } else {
+            valLine = "                c."+assignment+"( i*c.numCols+j , total );\n";
+        }
+
+        String foo =
+                header + makeBoundsCheck(false,false, "b.numRows")+
+                        "        for( int j = 0; j < b.numCols; j++ ) {\n" +
+                        "            // create a copy of the column in B to avoid cache issues\n" +
+                        "            for( int k = 0; k < b.numRows; k++ ) {\n" +
+                        "                aux[k] = b.unsafe_get(k,j);\n" +
+                        "            }\n" +
+                        "\n" +
+                        "            int indexA = 0;\n" +
+                        "            for( int i = 0; i < a.numRows; i++ ) {\n" +
+                        "                double total = 0;\n" +
+                        "                for( int k = 0; k < b.numRows; ) {\n" +
+                        "                    total += a.get(indexA++)*aux[k++];\n" +
+                        "                }\n" +
+                        valLine +
+                        "            }\n" +
+                        "        }\n" +
+                        "    }\n";
+        stream.print(foo);
+    }
+
+    public void printMultTransA_reorder( boolean alpha , boolean add ) {
+        String header,valLine1,valLine2;
+
+        header = makeHeader("mult","reorder",add,alpha, false, true,false);
+
+        String assignment = add ? "plus" : "set";
+
+        if( alpha ) {
+            valLine1 = "valA = alpha*a.get(i);\n";
+            valLine2 = "valA = alpha*a.unsafe_get(k,i);\n";
+        } else {
+            valLine1 = "valA = a.get(i);\n";
+            valLine2 = "valA = a.unsafe_get(k,i);\n";
+        }
+
+        String foo =
+                header + makeBoundsCheck(true,false, null)+handleZeros(add)+
+                        "        double valA;\n" +
+                        "\n" +
+                        "        for( int i = 0; i < a.numCols; i++ ) {\n" +
+                        "            int indexC_start = i*c.numCols;\n" +
+                        "\n" +
+                        "            // first assign R\n" +
+                        "            " +valLine1+
+                        "            int indexB = 0;\n" +
+                        "            int end = indexB+b.numCols;\n" +
+                        "            int indexC = indexC_start;\n" +
+                        "            while( indexB<end ) {\n" +
+                        "                c."+assignment+"( indexC++ , valA*b.get(indexB++));\n" +
+                        "            }\n" +
+                        "            // now increment it\n" +
+                        "            for( int k = 1; k < a.numRows; k++ ) {\n" +
+                        "                " +valLine2+
+                        "                end = indexB+b.numCols;\n" +
+                        "                indexC = indexC_start;\n" +
+                        "                // this is the loop for j\n" +
+                        "                while( indexB<end ) {\n" +
+                        "                    c.plus( indexC++ , valA*b.get(indexB++));\n" +
+                        "                }\n" +
+                        "            }\n" +
+                        "        }\n" +
+                        "    }\n";
+        stream.print(foo);
+    }
+
+    public void printMultTransA_small( boolean alpha , boolean add ) {
+        String header,valLine;
+
+        header = makeHeader("mult","small",add,alpha, false, true,false);
+
+        String assignment = add ? "plus" : "set";
+
+        if( alpha ) {
+            valLine = "c."+assignment+"( cIndex++ , alpha*total );\n";
+        } else {
+            valLine = "c."+assignment+"( cIndex++ , total );\n";
+        }
+
+        String foo =
+                header + makeBoundsCheck(true,false, null)+
+                        "        int cIndex = 0;\n" +
+                        "\n" +
+                        "        for( int i = 0; i < a.numCols; i++ ) {\n" +
+                        "            for( int j = 0; j < b.numCols; j++ ) {\n" +
+                        "                int indexA = i;\n" +
+                        "                int indexB = j;\n" +
+                        "                int end = indexB + b.numRows*b.numCols;\n" +
+                        "\n" +
+                        "                double total = 0;\n" +
+                        "\n" +
+                        "                // loop for k\n" +
+                        "                for(; indexB < end; indexB += b.numCols ) {\n" +
+                        "                    total += a.get(indexA) * b.get(indexB);\n" +
+                        "                    indexA += a.numCols;\n" +
+                        "                }\n" +
+                        "\n" +
+                        "                "+valLine +
+                        "            }\n" +
+                        "        }\n" +
+                        "    }\n";
+
+         stream.print(foo);
+    }
+
+    public void printMultTransB( boolean alpha , boolean add ) {
+        String header,valLine;
+
+        header = makeHeader("mult",null,add,alpha, false, false,true);
+
+        String assignment = add ? "plus" : "set";
+
+        if( alpha ) {
+            valLine = "c."+assignment+"( cIndex++ , alpha*total );\n";
+        } else {
+            valLine = "c."+assignment+"( cIndex++ , total );\n";
+        }
+
+        String foo =
+                header + makeBoundsCheck(false,true, null)+
+                        "        int cIndex = 0;\n" +
+                        "        int aIndexStart = 0;\n" +
+                        "\n" +
+                        "        for( int xA = 0; xA < a.numRows; xA++ ) {\n" +
+                        "            int end = aIndexStart + b.numCols;\n" +
+                        "            int indexB = 0;\n"+
+                        "            for( int xB = 0; xB < b.numRows; xB++ ) {\n" +
+                        "                int indexA = aIndexStart;\n" +
+                        "\n" +
+                        "                double total = 0;\n" +
+                        "\n" +
+                        "                while( indexA<end ) {\n" +
+                        "                    total += a.get(indexA++) * b.get(indexB++);\n" +
+                        "                }\n" +
+                        "\n" +
+                        "                "+valLine +
+                        "            }\n" +
+                        "            aIndexStart += a.numCols;\n" +
+                        "        }\n" +
+                        "    }\n";
+        stream.print(foo);
+    }
+
+    public void printMultTransAB( boolean alpha , boolean add ) {
+        String header,valLine;
+
+        header = makeHeader("mult",null,add,alpha, false, true,true);
+
+        String assignment = add ? "plus" : "set";
+
+        if( alpha ) {
+            valLine = "c."+assignment+"( cIndex++ , alpha*total );\n";
+        } else {
+            valLine = "c."+assignment+"( cIndex++ , total );\n";
+        }
+
+        String foo =
+                header + makeBoundsCheck(true,true, null)+
+                        "        int cIndex = 0;\n" +
+                        "\n" +
+                        "        for( int i = 0; i < a.numCols; i++ ) {\n" +
+                        "            int indexB = 0;\n"+
+                        "            for( int j = 0; j < b.numRows; j++ ) {\n" +
+                        "                int indexA = i;\n" +
+                        "                int end = indexB + b.numCols;\n" +
+                        "\n" +
+                        "                double total = 0;\n" +
+                        "\n" +
+                        "                for( ;indexB<end; ) {\n" +
+                        "                    total += a.get(indexA) * b.get(indexB++);\n" +
+                        "                    indexA += a.numCols;\n" +
+                        "                }\n" +
+                        "\n" +
+                        "                "+valLine+
+                        "            }\n" +
+                        "        }\n"+
+                        "    }\n";
+        stream.print(foo);
+    }
+
+    public void printMultTransAB_aux( boolean alpha , boolean add ) {
+        String header,valLine;
+
+        header = makeHeader("mult","aux",add,alpha, true, true,true);
+
+        String assignment = add ? "plus" : "set";
+
+        if( alpha ) {
+            valLine = "c."+assignment+"( indexC++ , alpha*total );\n";
+        } else {
+            valLine = "c."+assignment+"( indexC++ , total );\n";
+        }
+
+        String foo =
+                header + makeBoundsCheck(true,true, "a.numRows")+handleZeros(add)+
+                        "        int indexC = 0;\n" +
+                        "        for( int i = 0; i < a.numCols; i++ ) {\n" +
+                        "            for( int k = 0; k < b.numCols; k++ ) {\n" +
+                        "                aux[k] = a.unsafe_get(k,i);\n" +
+                        "            }\n" +
+                        "\n" +
+                        "            for( int j = 0; j < b.numRows; j++ ) {\n" +
+                        "                double total = 0;\n" +
+                        "\n" +
+                        "                for( int k = 0; k < b.numCols; k++ ) {\n" +
+                        "                    total += aux[k] * b.unsafe_get(j,k);\n" +
+                        "                }\n" +
+                        "                "+valLine +
+                        "            }\n" +
+                        "        }\n"+
+                        "    }\n";
+        stream.print(foo);
+    }
+
+    public static void main( String args[] ) throws FileNotFoundException {
+        GeneratorMatrixMatrixMult gen = new GeneratorMatrixMatrixMult("MatrixMatrixMult.java");
+
+        gen.createClass();
+    }
+}
diff --git a/main/dense64/generate/org/ejml/alg/fixed/GenerateFixedOps.java b/main/dense64/generate/org/ejml/alg/fixed/GenerateFixedOps.java
new file mode 100644
index 0000000..74ab946
--- /dev/null
+++ b/main/dense64/generate/org/ejml/alg/fixed/GenerateFixedOps.java
@@ -0,0 +1,1395 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.fixed;
+
+import org.ejml.CodeGeneratorBase;
+import org.ejml.alg.dense.misc.GenerateDeterminantFromMinor;
+import org.ejml.alg.dense.misc.GenerateInverseFromMinor;
+
+import java.io.FileNotFoundException;
+
+/**
+ * Automatic code generator for FixedOps
+ *
+ * @author Peter Abeles
+ */
+public class GenerateFixedOps extends CodeGeneratorBase {
+
+    String classPreamble = "FixedOps";
+
+    String nameMatrix;
+    String nameVector;
+
+    // The maximize size it will do inverse on
+    public static int maxInverseSize = 5;
+
+    @Override
+    public void generate() throws FileNotFoundException {
+        for( int dimension = 2; dimension <= 6; dimension++ ){
+            printPreable(dimension);
+
+            add(dimension);
+            vector_add(dimension);
+            addEquals(dimension);
+            vector_addEquals(dimension);
+            subtract(dimension);
+            vector_subtract(dimension);
+            subtractEquals(dimension);
+            vector_subtractEquals(dimension);
+            transpose_one(dimension);
+            transpose_two(dimension);
+            for( int i = 0; i < 2; i ++ ) {
+                boolean add = i == 1;
+                mult(dimension,add);
+                multTransA(dimension,add);
+                multTransAB(dimension,add);
+                multTransB(dimension,add);
+            }
+            mult_m_v_v(dimension);
+            mult_v_m_v(dimension);
+            dot(dimension);
+            setIdentity(dimension);
+            if( dimension <= maxInverseSize ) {
+                invert(dimension);
+                det(dimension);
+            }
+            trace(dimension);
+            diag(dimension);
+            elementMax(dimension);
+            elementMax_vector(dimension);
+            elementMaxAbs(dimension);
+            elementMaxAbs_vector(dimension);
+            elementMin(dimension);
+            elementMin_vector(dimension);
+            elementMinAbs(dimension);
+            elementMinAbs_vector(dimension);
+            elementMult_two(dimension);
+            elementMult_vector_two(dimension);
+            elementMult_three(dimension);
+            elementMult_vector_three(dimension);
+            elementDiv_two(dimension);
+            elementDiv_vector_two(dimension);
+            elementDiv_three(dimension);
+            elementDiv_vector_three(dimension);
+            scale_two(dimension);
+            scale_vector_two(dimension);
+            scale_three(dimension);
+            scale_vector_three(dimension);
+            divide_two(dimension);
+            divide_vector_two(dimension);
+            divide_three(dimension);
+            divide_vector_three(dimension);
+            changeSign(dimension);
+            changeSign_vector(dimension);
+            fill(dimension);
+            fill_vector(dimension);
+            extract(dimension);
+
+            out.println("}\n");
+        }
+    }
+
+    public void printPreable( int dimen ) throws FileNotFoundException {
+
+        String className = classPreamble+dimen;
+
+        nameMatrix = "FixedMatrix"+dimen+"x"+dimen+"_64F";
+        nameVector = "FixedMatrix"+dimen+"_64F";
+
+        setOutputFile(className);
+
+        out.print("import org.ejml.data."+nameVector+";\n" +
+                "import org.ejml.data."+nameMatrix+";\n" +
+                "\n" +
+                "/**\n" +
+                " * <p>Common matrix operations for fixed sized matrices which are "+dimen+" x "+dimen+" or "+dimen+" element vectors.</p>\n" +
+                " * <p>DO NOT MODIFY.  Automatically generated code created by "+getClass().getSimpleName()+"</p>\n" +
+                " *\n" +
+                " * @author Peter Abeles\n" +
+                " */\n" +
+                "public class "+className+" {\n");
+    }
+
+    private void add(int dimen){
+        out.print("    /**\n" +
+                "     * <p>Performs the following operation:<br>\n" +
+                "     * <br>\n" +
+                "     * c = a + b <br>\n" +
+                "     * c<sub>ij</sub> = a<sub>ij</sub> + b<sub>ij</sub> <br>\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * <p>\n" +
+                "     * Matrix C can be the same instance as Matrix A and/or B.\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a A Matrix. Not modified.\n" +
+                "     * @param b A Matrix. Not modified.\n" +
+                "     * @param c A Matrix where the results are stored. Modified.\n" +
+                "     */\n" +
+                "    public static void add( " + nameMatrix + " a , " + nameMatrix + " b , " + nameMatrix + " c ) {\n");
+        for( int y = 1; y <= dimen; y++ ) {
+            for( int x = 1; x <= dimen; x++ ) {
+                String n = y+""+x;
+                out.print("        c.a"+n+" = a.a"+n+" + b.a"+n+";\n");
+            }
+        }
+        out.print("    }\n\n");
+    }
+
+    private void vector_add( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>Performs the following operation:<br>\n" +
+                "     * <br>\n" +
+                "     * c = a + b <br>\n" +
+                "     * c<sub>i</sub> = a<sub>i</sub> + b<sub>i</sub> <br>\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * <p>\n" +
+                "     * Vector C can be the same instance as Vector A and/or B.\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a A Vector. Not modified.\n" +
+                "     * @param b A Vector. Not modified.\n" +
+                "     * @param c A Vector where the results are stored. Modified.\n" +
+                "     */\n" +
+                "    public static void add( " + nameVector + " a , " + nameVector + " b , " + nameVector + " c ) {\n");
+        for( int y = 1; y <= dimen; y++ ) {
+            out.print("        c.a"+y+" = a.a"+y+" + b.a"+y+";\n");
+        }
+        out.print("    }\n\n");
+    }
+
+    private void addEquals( int dimen ){
+        out.print("    /**\n" +
+                "     * <p>Performs the following operation:<br>\n" +
+                "     * <br>\n" +
+                "     * a = a + b <br>\n" +
+                "     * a<sub>ij</sub> = a<sub>ij</sub> + b<sub>ij</sub> <br>\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a A Matrix. Modified.\n" +
+                "     * @param b A Matrix. Not modified.\n" +
+                "     */\n" +
+                "    public static void addEquals( " + nameMatrix + " a , " + nameMatrix + " b ) {\n");
+        for( int y = 1; y <= dimen; y++ ) {
+            for( int x = 1; x <= dimen; x++ ) {
+                String n = y+""+x;
+                out.print("        a.a"+n+" += b.a"+n+";\n");
+            }
+        }
+        out.print("    }\n\n");
+    }
+
+    private void vector_addEquals( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>Performs the following operation:<br>\n" +
+                "     * <br>\n" +
+                "     * a = a + b <br>\n" +
+                "     * a<sub>i</sub> = a<sub>i</sub> + b<sub>i</sub> <br>\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a A Vector. Modified.\n" +
+                "     * @param b A Vector. Not modified.\n" +
+                "     */\n" +
+                "    public static void addEquals( " + nameVector + " a , " + nameVector + " b ) {\n");
+        for( int y = 1; y <= dimen; y++ ) {
+            out.print("        a.a"+y+" += b.a"+y+";\n");
+        }
+        out.print("    }\n\n");
+    }
+
+    private void subtract(int dimen){
+        out.print("    /**\n" +
+                "     * <p>Performs the following operation:<br>\n" +
+                "     * <br>\n" +
+                "     * c = a - b <br>\n" +
+                "     * c<sub>ij</sub> = a<sub>ij</sub> - b<sub>ij</sub> <br>\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * <p>\n" +
+                "     * Matrix C can be the same instance as Matrix A and/or B.\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a A Matrix. Not modified.\n" +
+                "     * @param b A Matrix. Not modified.\n" +
+                "     * @param c A Matrix where the results are stored. Modified.\n" +
+                "     */\n" +
+                "    public static void subtract( " + nameMatrix + " a , " + nameMatrix + " b , " + nameMatrix + " c ) {\n");
+        for( int y = 1; y <= dimen; y++ ) {
+            for( int x = 1; x <= dimen; x++ ) {
+                String n = y+""+x;
+                out.print("        c.a"+n+" = a.a"+n+" - b.a"+n+";\n");
+            }
+        }
+        out.print("    }\n\n");
+    }
+
+    private void vector_subtract( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>Performs the following operation:<br>\n" +
+                "     * <br>\n" +
+                "     * c = a - b <br>\n" +
+                "     * c<sub>i</sub> = a<sub>i</sub> - b<sub>i</sub> <br>\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * <p>\n" +
+                "     * Vector C can be the same instance as Vector A and/or B.\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a A Vector. Not modified.\n" +
+                "     * @param b A Vector. Not modified.\n" +
+                "     * @param c A Vector where the results are stored. Modified.\n" +
+                "     */\n" +
+                "    public static void subtract( "+nameVector+" a , "+nameVector+" b , "+nameVector+" c ) {\n");
+        for( int y = 1; y <= dimen; y++) {
+            out.print("        c.a" + y + " = a.a" + y + " - b.a" + y + ";\n");
+        }
+        out.print("    }\n\n");
+    }
+
+    private void subtractEquals(int dimen) {
+        out.print("    /**\n" +
+                "     * <p>Performs the following operation:<br>\n" +
+                "     * <br>\n" +
+                "     * a = a - b <br>\n" +
+                "     * a<sub>ij</sub> = a<sub>ij</sub> - b<sub>ij</sub> <br>\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a A Matrix. Modified.\n" +
+                "     * @param b A Matrix. Not modified.\n" +
+                "     */\n" +
+                "    public static void subtractEquals( "+nameMatrix+" a , "+nameMatrix+" b ) {\n");
+        for( int y = 1; y <= dimen; y++ ) {
+            for (int x = 1; x <= dimen; x++) {
+                String n = y + "" + x;
+                out.print("        a.a" + n + " -= b.a" + n + ";\n");
+            }
+        }
+        out.print("    }\n\n");
+    }
+
+    private void vector_subtractEquals( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>Performs the following operation:<br>\n" +
+                "     * <br>\n" +
+                "     * a = a - b <br>\n" +
+                "     * a<sub>i</sub> = a<sub>i</sub> - b<sub>i</sub> <br>\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a A Vector. Modified.\n" +
+                "     * @param b A Vector. Not modified.\n" +
+                "     */\n" +
+                "    public static void subtractEquals( "+nameVector+" a , "+nameVector+" b ) {\n");
+        for( int y = 1; y <= dimen; y++ ) {
+            String n = ""+y;
+            out.print("        a.a"+n+" -= b.a"+n+";\n");
+        }
+        out.print("    }\n\n");
+    }
+
+    private void transpose_one( int dimen ){
+        out.print("    /**\n" +
+                "     * Performs an in-place transpose.  This algorithm is only efficient for square\n" +
+                "     * matrices.\n" +
+                "     *\n" +
+                "     * @param m The matrix that is to be transposed. Modified.\n" +
+                "     */\n" +
+                "    public static void transpose( " + nameMatrix + " m ) {\n" +
+                "        double tmp;\n");
+        for (int y = 1; y <= dimen; y++) {
+            for (int x = y + 1; x <= dimen; x++) {
+                String f = +y + "" + x;
+                String t = +x + "" + y;
+
+                out.print("        tmp = m.a"+f+"; m.a"+f+" = m.a"+t+"; m.a"+t+" = tmp;\n");
+            }
+        }
+        out.print("    }\n\n");
+    }
+
+    private void transpose_two( int dimen ){
+        out.print("    /**\n" +
+                "     * <p>\n" +
+                "     * Transposes matrix 'a' and stores the results in 'b':<br>\n" +
+                "     * <br>\n" +
+                "     * b<sub>ij</sub> = a<sub>ji</sub><br>\n" +
+                "     * where 'b' is the transpose of 'a'.\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param input The original matrix.  Not modified.\n" +
+                "     * @param output Where the transpose is stored. If null a new matrix is created. Modified.\n" +
+                "     * @return The transposed matrix.\n" +
+                "     */\n" +
+                "    public static " + nameMatrix + " transpose( " + nameMatrix + " input , " + nameMatrix + " output ) {\n" +
+                "        if( input == null )\n" +
+                "            input = new " + nameMatrix + "();\n\n");
+        for (int y = 1; y <= dimen; y++) {
+            for (int x = 1; x <= dimen; x++) {
+                String f = +y + "" + x;
+                String t = +x+""+y;
+
+                out.print("        output.a"+f+" = input.a"+t+";\n");
+            }
+        }
+
+        out.print("\n        return output;\n" +
+                "    }\n\n");
+    }
+
+    private void mult( int dimen , boolean add ){
+        String plus = add ? "+" : "";
+        String name = add ? "multAdd" : "mult";
+
+        out.print("    /**\n" +
+                "     * <p>Performs the following operation:<br>\n" +
+                "     * <br>\n" +
+                "     * c "+plus+"= a * b <br>\n" +
+                "     * <br>\n" +
+                "     * c<sub>ij</sub> "+plus+"= ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>kj</sub>}\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a The left matrix in the multiplication operation. Not modified.\n" +
+                "     * @param b The right matrix in the multiplication operation. Not modified.\n" +
+                "     * @param c Where the results of the operation are stored. Modified.\n" +
+                "     */\n" +
+                "    public static void "+name+"( "+nameMatrix+" a , "+nameMatrix+" b , "+nameMatrix+" c) {\n");
+
+        for( int y = 1; y <= dimen; y++ ) {
+            for( int x = 1; x <= dimen; x++ ) {
+                out.print("        c.a"+y+""+x+" "+plus+"= ");
+                for( int k = 1; k <= dimen; k++ ) {
+                    out.print("a.a"+y+""+k+"*b.a"+k+""+x);
+                    if( k < dimen )
+                        out.print(" + ");
+                    else
+                        out.print(";\n");
+                }
+            }
+        }
+        out.print("    }\n\n");
+    }
+
+    private void multTransA( int dimen , boolean add ){
+        String plus = add ? "+" : "";
+        String name = add ? "multAddTransA" : "multTransA";
+
+        out.print("    /**\n" +
+                "     * <p>Performs the following operation:<br>\n" +
+                "     * <br>\n" +
+                "     * c " + plus + "= a<sup>T</sup> * b <br>\n" +
+                "     * <br>\n" +
+                "     * c<sub>ij</sub> " + plus + "= ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>kj</sub>}\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a The left matrix in the multiplication operation. Not modified.\n" +
+                "     * @param b The right matrix in the multiplication operation. Not modified.\n" +
+                "     * @param c Where the results of the operation are stored. Modified.\n" +
+                "     */\n" +
+                "    public static void " + name + "( " + nameMatrix + " a , " + nameMatrix + " b , " + nameMatrix + " c) {\n");
+        for (int y = 1; y <= dimen; y++) {
+            for (int x = 1; x <= dimen; x++) {
+                out.print("        c.a" + y + "" + x + " " + plus + "= ");
+                for (int k = 1; k <= dimen; k++) {
+                    out.print("a.a" + k + "" + y + "*b.a" + k + "" + x);
+                    if (k < dimen)
+                        out.print(" + ");
+                    else
+                        out.print(";\n");
+                }
+            }
+        }
+        out.printf("    }\n\n");
+    }
+
+    private void multTransAB( int dimen , boolean add ){
+        String plus = add ? "+" : "";
+        String name = add ? "multAddTransAB" : "multTransAB";
+
+        out.printf("    /**\n" +
+                "     * <p>\n" +
+                "     * Performs the following operation:<br>\n" +
+                "     * <br>\n" +
+                "     * c " + plus + "= a<sup>T</sup> * b<sup>T</sup><br>\n" +
+                "     * c<sub>ij</sub> " + plus + "= ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>jk</sub>}\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a The left matrix in the multiplication operation. Not modified.\n" +
+                "     * @param b The right matrix in the multiplication operation. Not modified.\n" +
+                "     * @param c Where the results of the operation are stored. Modified.\n" +
+                "     */\n" +
+                "    public static void " + name + "( " + nameMatrix + " a , " + nameMatrix + " b , " + nameMatrix + " c) {\n");
+        for (int y = 1; y <= dimen; y++) {
+            for (int x = 1; x <= dimen; x++) {
+                out.print("        c.a" + y + "" + x + " " + plus + "= ");
+                for (int k = 1; k <= dimen; k++) {
+                    out.print("a.a" + k + "" + y + "*b.a" + x + "" + k);
+                    if (k < dimen)
+                        out.print(" + ");
+                    else
+                        out.print(";\n");
+                }
+            }
+        }
+        out.print("    }\n\n");
+    }
+
+    private void multTransB( int dimen , boolean add ){
+        String plus = add ? "+" : "";
+        String name = add ? "multAddTransB" : "multTransB";
+
+        out.print("    /**\n" +
+                "     * <p>\n" +
+                "     * Performs the following operation:<br>\n" +
+                "     * <br>\n" +
+                "     * c "+plus+"= a * b<sup>T</sup> <br>\n" +
+                "     * c<sub>ij</sub> "+plus+"= ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>jk</sub>}\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a The left matrix in the multiplication operation. Not modified.\n" +
+                "     * @param b The right matrix in the multiplication operation. Not modified.\n" +
+                "     * @param c Where the results of the operation are stored. Modified.\n" +
+                "     */\n" +
+                "    public static void " + name + "( " + nameMatrix + " a , " + nameMatrix + " b , " + nameMatrix + " c) {\n");
+        for (int y = 1; y <= dimen; y++) {
+            for (int x = 1; x <= dimen; x++) {
+                out.print("        c.a" + y + "" + x + " " + plus + "= ");
+                for (int k = 1; k <= dimen; k++) {
+                    out.print("a.a" + y + "" + k + "*b.a" + x + "" + k);
+                    if (k < dimen )
+                        out.print(" + ");
+                    else
+                        out.print(";\n");
+                }
+            }
+        }
+        out.print("    }\n\n");
+    }
+
+    private void mult_m_v_v( int dimen ){
+        out.print("    /**\n" +
+                "     * <p>Performs matrix to vector multiplication:<br>\n" +
+                "     * <br>\n" +
+                "     * c = a * b <br>\n" +
+                "     * <br>\n" +
+                "     * c<sub>i</sub> = ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>k</sub>}\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a The left matrix in the multiplication operation. Not modified.\n" +
+                "     * @param b The right vector in the multiplication operation. Not modified.\n" +
+                "     * @param c Where the results of the operation are stored. Modified.\n" +
+                "     */\n" +
+                "    public static void mult( " + nameMatrix + " a , " + nameVector + " b , " + nameVector + " c) {\n");
+        for (int y = 1; y <= dimen; y++) {
+            out.print("        c.a" + y + " = ");
+            for (int x = 1; x <= dimen; x++) {
+                out.print("a.a" + y + "" + x + "*b.a"+x);
+                if( x < dimen )
+                    out.print(" + ");
+                else
+                    out.print(";\n");
+            }
+        }
+        out.printf("    }\n\n");
+
+    }
+
+    private void mult_v_m_v( int dimen ){
+        out.print("    /**\n" +
+                "     * <p>Performs vector to matrix multiplication:<br>\n" +
+                "     * <br>\n" +
+                "     * c = a * b <br>\n" +
+                "     * <br>\n" +
+                "     * c<sub>j</sub> = ∑<sub>k=1:n</sub> { b<sub>k</sub> * a<sub>kj</sub> }\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a The left vector in the multiplication operation. Not modified.\n" +
+                "     * @param b The right matrix in the multiplication operation. Not modified.\n" +
+                "     * @param c Where the results of the operation are stored. Modified.\n" +
+                "     */\n" +
+                "    public static void mult( "+nameVector+" a , "+nameMatrix+" b , "+nameVector+" c) {\n");
+
+        for( int y = 1; y <= dimen; y++ ) {
+            out.print("        c.a"+y+" = ");
+            for( int x = 1; x <= dimen; x++ ) {
+                out.print("a.a"+x+"*b.a"+x+""+y);
+                if( x < dimen )
+                    out.print(" + ");
+                else
+                    out.print(";\n");
+            }
+        }
+        out.print("    }\n\n");
+
+    }
+
+    private void dot( int dimen ){
+        out.print("    /**\n" +
+                "     * <p>Performs the vector dot product:<br>\n" +
+                "     * <br>\n" +
+                "     * c = a * b <br>\n" +
+                "     * <br>\n" +
+                "     * c> = ∑<sub>k=1:n</sub> { b<sub>k</sub> * a<sub>k</sub> }\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a The left vector in the multiplication operation. Not modified.\n" +
+                "     * @param b The right matrix in the multiplication operation. Not modified.\n" +
+                "     * @return The dot product\n" +
+                "     */\n" +
+                "    public static double dot( "+nameVector+" a , "+nameVector+" b ) {\n");
+        out.print("        return ");
+        for( int i = 1; i <= dimen; i++) {
+            out.print("a.a"+i+"*b.a"+i);
+            if( i < dimen )
+                out.print(" + ");
+            else
+                out.print(";\n");
+        }
+        out.print("    }\n\n");
+    }
+
+    private void setIdentity( int dimen ){
+        out.print("    /**\n" +
+                "     * Sets all the diagonal elements equal to one and everything else equal to zero.\n" +
+                "     * If this is a square matrix then it will be an identity matrix.\n" +
+                "     *\n" +
+                "     * @param a A matrix.\n" +
+                "     */\n" +
+                "    public static void setIdentity( "+nameMatrix+" a ) {\n");
+        for( int y = 1; y <= dimen; y++ ) {
+            out.print("        ");
+            for( int x = 1; x <= dimen; x++ ) {
+                int val = x==y?1:0;
+                out.print("a.a"+x+""+y+" = "+val+";");
+                if( x < dimen )
+                    out.print(" ");
+                else
+                    out.print("\n");
+            }
+        }
+        out.print("    }\n\n");
+    }
+
+    private void invert( int dimen ){
+        out.print("    /**\n" +
+                "     * Inverts matrix 'a' using minor matrices and stores the results in 'inv'.  Scaling is applied to improve\n" +
+                "     * stability against overflow and underflow.\n" +
+                "     *\n" +
+                "     * WARNING: Potentially less stable than using LU decomposition.\n" +
+                "     *\n" +
+                "     * @param a Input matrix. Not modified.\n" +
+                "     * @param inv Inverted output matrix.  Modified.\n" +
+                "     * @return true if it was successful or false if it failed.  Not always reliable.\n" +
+                "     */\n" +
+                "    public static boolean invert( "+nameMatrix+" a , "+nameMatrix+" inv ) {\n" +
+                "\n" +
+                "        double scale = 1.0/elementMaxAbs(a);\n" +
+                "\n");
+
+        int matrix[] = new int[dimen*dimen];
+        int index = 0;
+        for (int y = 1; y <= dimen; y++) {
+            for (int x = 1; x <= dimen; x++, index++) {
+                matrix[index] = index;
+                String coor = y + "" + x;
+                out.print("        double a" + coor + " = a.a" + coor + "*scale;\n");
+            }
+        }
+        out.println();
+
+        try {
+            GenerateInverseFromMinor gen = new GenerateInverseFromMinor(false);
+            gen.printMinors(matrix, dimen, out);
+        } catch (FileNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+        out.println();
+
+        for (int y = 1; y <= dimen; y++) {
+            for( int x = 1; x <= dimen; x++ ) {
+                String coor0 = y+""+x;
+                String coor1 = x+""+y;
+                out.print("        inv.a"+coor0+" = m"+coor1+"/det;\n");
+            }
+        }
+        out.println();
+        out.print("        return !Double.isNaN(det) && !Double.isInfinite(det);\n");
+        out.print("    }\n\n");
+
+    }
+
+    private void trace(int dimen) {
+        out.print("    /**\n" +
+                "     * <p>\n" +
+                "     * This computes the trace of the matrix:<br>\n" +
+                "     * <br>\n" +
+                "     * trace = ∑<sub>i=1:n</sub> { a<sub>ii</sub> }\n" +
+                "     * </p>\n" +
+                "     * <p>\n" +
+                "     * The trace is only defined for square matrices.\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a A square matrix.  Not modified.\n" +
+                "     */\n" +
+                "    public static double trace( "+nameMatrix+" a ) {\n");
+        out.print("        return ");
+        for( int i = 1; i <= dimen; i++ ) {
+            out.print("a.a"+i+""+1);
+            if( i < dimen )
+                out.print(" + ");
+            else
+                out.println(";");
+        }
+        out.print("    }\n\n");
+    }
+
+    private void det( int dimen ){
+        out.print("    /**\n" +
+                "     * Computes the determinant using minor matrices.\n" +
+                "     * <p></p>\n" +
+                "     * WARNING: Potentially less stable than using LU decomposition.\n" +
+                "     *\n" +
+                "     * @param mat Input matrix.  Not modified.\n" +
+                "     * @return The determinant.\n" +
+                "     */\n" +
+                "    public static double det( "+nameMatrix+" mat ) {\n" +
+                "\n");
+        if( dimen == 2 ) {
+            out.print("        return mat.a11*mat.a22 - mat.a12*mat.a21;\n");
+        } else if( dimen == 3 ) {
+            out.print( "        double a = mat.a11*(mat.a22*mat.a33 - mat.a23*mat.a32);\n" +
+                    "        double b = mat.a12*(mat.a21*mat.a33 - mat.a23*mat.a31);\n" +
+                    "        double c = mat.a13*(mat.a21*mat.a32 - mat.a31*mat.a22);\n" +
+                    "\n" +
+                    "        return a-b+c;\n");
+        } else {
+            GenerateDeterminantFromMinor helper = new GenerateDeterminantFromMinor(out) {
+                @Override
+                protected String getInputValue(int element) {
+                    int row = element/(N+1) + 1;
+                    int col = element%(N+1) + 1;
+                    return "mat.a"+row+""+col;
+                }
+            };
+            helper.printFunctionInner(dimen);
+            out.print("\n        return ret;\n");
+        }
+
+        out.print("    }\n\n");
+    }
+
+    private void diag( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>\n" +
+                "     * Extracts all diagonal elements from 'input' and places them inside the 'out' vector. Elements\n" +
+                "     * are in sequential order.\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     *\n" +
+                "     * @param input Matrix.  Not modified.\n" +
+                "     * @param out Vector containing diagonal elements.  Modified.\n" +
+                "     */\n" +
+                "    public static void diag( "+nameMatrix+" input , "+nameVector+" out ) {\n");
+        for( int i = 1; i <= dimen; i++ ) {
+            out.print("        out.a" + i + " = input.a" + i + "" + i + ";\n");
+        }
+        out.print("    }\n\n");
+    }
+
+    private void elementMax( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>\n" +
+                "     * Returns the value of the element in the matrix that has the largest value.<br>\n" +
+                "     * <br>\n" +
+                "     * Max{ a<sub>ij</sub> } for all i and j<br>\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a A matrix. Not modified.\n" +
+                "     * @return The max element value of the matrix.\n" +
+                "     */\n" +
+                "    public static double elementMax( "+nameMatrix+" a ) {\n");
+
+        out.print("        double max = a.a11;\n");
+        for( int y = 1; y <= dimen; y++ ) {
+            for( int x = 1; x <= dimen; x++ ) {
+                if( y == 1 && x == 1 )
+                    continue;
+                out.print("        max = Math.max(max,a.a"+y+""+x+");\n");
+            }
+        }
+        out.print("\n" +
+                "        return max;\n" +
+                "    }\n\n");
+    }
+
+    private void elementMax_vector( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>\n" +
+                "     * Returns the value of the element in the vector that has the largest value.<br>\n" +
+                "     * <br>\n" +
+                "     * Max{ a<sub>i</sub> } for all i<br>\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a A vector. Not modified.\n" +
+                "     * @return The max element value of the matrix.\n" +
+                "     */\n" +
+                "    public static double elementMax( "+nameVector+" a ) {\n");
+
+        out.print("        double max = a.a1;\n");
+        for( int y = 2; y <= dimen; y++ ) {
+            out.print("        max = Math.max(max,a.a"+y+");\n");
+        }
+        out.print("\n" +
+                "        return max;\n" +
+                "    }\n\n");
+    }
+
+    private void elementMaxAbs( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>\n" +
+                "     * Returns the absolute value of the element in the matrix that has the largest absolute value.<br>\n" +
+                "     * <br>\n" +
+                "     * Max{ |a<sub>ij</sub>| } for all i and j<br>\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a A matrix. Not modified.\n" +
+                "     * @return The max abs element value of the matrix.\n" +
+                "     */\n" +
+                "    public static double elementMaxAbs( "+nameMatrix+" a ) {\n");
+
+        out.print("        double max = a.a11;\n");
+        for (int y = 1; y <= dimen; y++ ) {
+            for( int x = 1; x <= dimen; x++ ) {
+                if( y == 1 && x == 1 )
+                    continue;
+                out.print("        max = Math.max(max,Math.abs(a.a" + y + "" + x + "));\n");
+            }
+        }
+        out.print("\n" +
+                "        return max;\n" +
+                "    }\n\n");
+    }
+
+    private void elementMaxAbs_vector( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>\n" +
+                "     * Returns the absolute value of the element in the vector that has the largest absolute value.<br>\n" +
+                "     * <br>\n" +
+                "     * Max{ |a<sub>i</sub>| } for all i<br>\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a A matrix. Not modified.\n" +
+                "     * @return The max abs element value of the vector.\n" +
+                "     */\n" +
+                "    public static double elementMaxAbs( "+nameVector+" a ) {\n");
+
+        out.print("        double max = a.a1;\n");
+        for( int y = 2; y <= dimen; y++ ) {
+            out.print("        max = Math.max(max,Math.abs(a.a"+y+"));\n");
+        }
+        out.print("\n" +
+                "        return max;\n" +
+                "    }\n\n");
+    }
+
+    private void elementMin( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>\n" +
+                "     * Returns the value of the element in the matrix that has the minimum value.<br>\n" +
+                "     * <br>\n" +
+                "     * Min{ a<sub>ij</sub> } for all i and j<br>\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a A matrix. Not modified.\n" +
+                "     * @return The value of element in the matrix with the minimum value.\n" +
+                "     */\n" +
+                "    public static double elementMin( "+nameMatrix+" a ) {\n");
+
+        out.print("        double min = a.a11;\n");
+        for( int y = 1; y <= dimen; y++ ) {
+            for( int x = 1; x <= dimen; x++ ) {
+                if( y == 1 && x == 1 )
+                    continue;
+                out.print("        min = Math.min(min, a.a"+y+""+x+");\n");
+            }
+        }
+        out.print("\n" +
+                "        return min;\n" +
+                "    }\n\n");
+    }
+
+    private void elementMin_vector( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>\n" +
+                "     * Returns the value of the element in the vector that has the minimum value.<br>\n" +
+                "     * <br>\n" +
+                "     * Min{ a<sub>i</sub> } for all<br>\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a A matrix. Not modified.\n" +
+                "     * @return The value of element in the vector with the minimum value.\n" +
+                "     */\n" +
+                "    public static double elementMin( "+nameVector+" a ) {\n");
+
+        out.print("        double min = a.a1;\n");
+        for( int y = 2; y <= dimen; y++ ) {
+            out.print("        min = Math.min(min, a.a"+y+");\n");
+        }
+        out.print("\n" +
+                "        return min;\n" +
+                "    }\n\n");
+    }
+
+    private void elementMinAbs( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>\n" +
+                "     * Returns the absolute value of the element in the matrix that has the smallest absolute value.<br>\n" +
+                "     * <br>\n" +
+                "     * Min{ |a<sub>ij</sub>| } for all i and j<br>\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a A matrix. Not modified.\n" +
+                "     * @return The max element value of the matrix.\n" +
+                "     */\n" +
+                "    public static double elementMinAbs( "+nameMatrix+" a ) {\n");
+
+        out.print("        double min = a.a11;\n");
+        for (int y = 1; y <= dimen; y++ ) {
+            for( int x = 1; x <= dimen; x++ ) {
+                if( y == 1 && x == 1 )
+                    continue;
+                out.print("        min = Math.min(min,Math.abs(a.a"+y+""+x+"));\n");
+            }
+        }
+        out.print("\n" +
+                "        return min;\n" +
+                "    }\n\n");
+    }
+
+
+    private void elementMinAbs_vector(int dimen) {
+        out.print("    /**\n" +
+                "     * <p>\n" +
+                "     * Returns the absolute value of the element in the vector that has the smallest absolute value.<br>\n" +
+                "     * <br>\n" +
+                "     * Min{ |a<sub>i</sub>| } for all i<br>\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a A matrix. Not modified.\n" +
+                "     * @return The max element value of the vector.\n" +
+                "     */\n" +
+                "    public static double elementMinAbs( "+nameVector+" a ) {\n");
+
+        out.print("        double min = a.a1;\n");
+        for( int y = 2; y <= dimen; y++ ) {
+            out.print("        min = Math.min(min,Math.abs(a.a"+y+"));\n");
+        }
+        out.print("\n" +
+                "        return min;\n" +
+                "    }\n\n");
+    }
+
+    private void elementMult_two( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>Performs an element by element multiplication operation:<br>\n" +
+                "     * <br>\n" +
+                "     * a<sub>ij</sub> = a<sub>ij</sub> * b<sub>ij</sub> <br>\n" +
+                "     * </p>\n" +
+                "     * @param a The left matrix in the multiplication operation. Modified.\n" +
+                "     * @param b The right matrix in the multiplication operation. Not modified.\n" +
+                "     */\n" +
+                "    public static void elementMult( " + nameMatrix + " a , " + nameMatrix + " b) {\n");
+        for( int y = 1; y <= dimen; y++ ) {
+            out.print("        ");
+            for( int x = 1; x <= dimen; x++ ) {
+                String w = "a"+y+""+x;
+                out.print("a."+w+" *= b."+w+ ";");
+                if (x < dimen)
+                    out.print(" ");
+                else
+                    out.println();
+            }
+        }
+        out.print("    }\n\n");
+    }
+
+    private void elementMult_vector_two(int dimen) {
+        out.print("    /**\n" +
+                "     * <p>Performs an element by element multiplication operation:<br>\n" +
+                "     * <br>\n" +
+                "     * a<sub>i</sub> = a<sub>i</sub> * b<sub>i</sub> <br>\n" +
+                "     * </p>\n" +
+                "     * @param a The left vector in the multiplication operation. Modified.\n" +
+                "     * @param b The right vector in the multiplication operation. Not modified.\n" +
+                "     */\n" +
+                "    public static void elementMult( "+nameVector+" a , "+nameVector+" b) {\n");
+        for( int y = 1; y <= dimen; y++ ) {
+            out.print("        a.a"+y+" *= b.a"+y+";\n");
+        }
+        out.print("    }\n\n");
+    }
+
+    private void elementMult_three( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>Performs an element by element multiplication operation:<br>\n" +
+                "     * <br>\n" +
+                "     * c<sub>ij</sub> = a<sub>ij</sub> * b<sub>ij</sub> <br>\n" +
+                "     * </p>\n" +
+                "     * @param a The left matrix in the multiplication operation. Not modified.\n" +
+                "     * @param b The right matrix in the multiplication operation. Not modified.\n" +
+                "     * @param c Where the results of the operation are stored. Modified.\n" +
+                "     */\n" +
+                "    public static void elementMult( "+nameMatrix+" a , "+nameMatrix+" b , "+nameMatrix+" c ) {\n");
+        for( int y = 1; y <= dimen; y++ ) {
+            out.print("        ");
+            for( int x = 1; x <= dimen; x++ ) {
+                String w = "a"+y+""+x;
+                out.print("c."+w+" = a."+w+"*b."+w+";");
+                if( x < dimen )
+                    out.print(" ");
+                else
+                    out.println();
+            }
+        }
+        out.print("    }\n\n");
+    }
+
+    private void elementMult_vector_three( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>Performs an element by element multiplication operation:<br>\n" +
+                "     * <br>\n" +
+                "     * c<sub>i</sub> = a<sub>i</sub> * b<sub>j</sub> <br>\n" +
+                "     * </p>\n" +
+                "     * @param a The left vector in the multiplication operation. Not modified.\n" +
+                "     * @param b The right vector in the multiplication operation. Not modified.\n" +
+                "     * @param c Where the results of the operation are stored. Modified.\n" +
+                "     */\n" +
+                "    public static void elementMult( "+nameVector+" a , "+nameVector+" b , "+nameVector+" c ) {\n");
+        for( int y = 1; y <= dimen; y++ ) {
+            out.print("        c.a"+y+" = a.a"+y+"*b.a"+y+";\n");
+        }
+        out.print("    }\n\n");
+    }
+
+    private void elementDiv_two( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>Performs an element by element division operation:<br>\n" +
+                "     * <br>\n" +
+                "     * a<sub>ij</sub> = a<sub>ij</sub> / b<sub>ij</sub> <br>\n" +
+                "     * </p>\n" +
+                "     * @param a The left matrix in the division operation. Modified.\n" +
+                "     * @param b The right matrix in the division operation. Not modified.\n" +
+                "     */\n" +
+                "    public static void elementDiv( "+nameMatrix+" a , "+nameMatrix+" b) {\n");
+        for( int y = 1; y <= dimen; y++ ) {
+            out.print("        ");
+            for( int x = 1; x <= dimen; x++ ) {
+                String w = "a"+y+""+x;
+                out.print("a."+w+" /= b."+w+";");
+                if( x < dimen )
+                    out.print(" ");
+                else
+                    out.println();
+            }
+        }
+        out.print("    }\n\n");
+    }
+
+    private void elementDiv_vector_two( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>Performs an element by element division operation:<br>\n" +
+                "     * <br>\n" +
+                "     * a<sub>i</sub> = a<sub>i</sub> / b<sub>i</sub> <br>\n" +
+                "     * </p>\n" +
+                "     * @param a The left vector in the division operation. Modified.\n" +
+                "     * @param b The right vector in the division operation. Not modified.\n" +
+                "     */\n" +
+                "    public static void elementDiv( "+nameVector+" a , "+nameVector+" b) {\n");
+        for( int y = 1; y <= dimen; y++ ) {
+            out.print("        a.a"+y+" /= b.a"+y+";\n");
+        }
+        out.print("    }\n\n");
+    }
+
+    private void elementDiv_three( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>Performs an element by element division operation:<br>\n" +
+                "     * <br>\n" +
+                "     * c<sub>ij</sub> = a<sub>ij</sub> / b<sub>ij</sub> <br>\n" +
+                "     * </p>\n" +
+                "     * @param a The left matrix in the division operation. Not modified.\n" +
+                "     * @param b The right matrix in the division operation. Not modified.\n" +
+                "     * @param c Where the results of the operation are stored. Modified.\n" +
+                "     */\n" +
+                "    public static void elementDiv( "+nameMatrix+" a , "+nameMatrix+" b , "+nameMatrix+" c ) {\n");
+        for( int y = 1; y <= dimen; y++ ) {
+            out.print("        ");
+            for( int x = 1; x <= dimen; x++ ) {
+                String w = "a"+y+""+x;
+                out.print("c."+w+" = a."+w+"/b."+w+";");
+                if( x < dimen )
+                    out.print(" ");
+                else
+                    out.println();
+            }
+        }
+        out.print("    }\n\n");
+    }
+
+    private void elementDiv_vector_three( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>Performs an element by element division operation:<br>\n" +
+                "     * <br>\n" +
+                "     * c<sub>i</sub> = a<sub>i</sub> / b<sub>i</sub> <br>\n" +
+                "     * </p>\n" +
+                "     * @param a The left vector in the division operation. Not modified.\n" +
+                "     * @param b The right vector in the division operation. Not modified.\n" +
+                "     * @param c Where the results of the operation are stored. Modified.\n" +
+                "     */\n" +
+                "    public static void elementDiv( "+nameVector+" a , "+nameVector+" b , "+nameVector+" c ) {\n");
+        for( int y = 1; y <= dimen; y++ ) {
+            out.print("        c.a"+y+" = a.a"+y+"/b.a"+y+";\n");
+        }
+        out.print("    }\n\n");
+    }
+
+    private void scale_two( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>\n" +
+                "     * Performs an in-place element by element scalar multiplication.<br>\n" +
+                "     * <br>\n" +
+                "     * a<sub>ij</sub> = α*a<sub>ij</sub>\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a The matrix that is to be scaled.  Modified.\n" +
+                "     * @param alpha the amount each element is multiplied by.\n" +
+                "     */\n" +
+                "    public static void scale( double alpha , "+nameMatrix+" a ) {\n");
+        for( int y = 1; y <= dimen; y++ ) {
+            out.print("        ");
+            for( int x = 1; x <= dimen; x++ ) {
+                String w = "a"+y+""+x;
+                out.print("a."+w+" *= alpha;");
+                if( x < dimen )
+                    out.print(" ");
+                else
+                    out.println();
+            }
+        }
+        out.print("    }\n\n");
+    }
+
+    private void scale_vector_two( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>\n" +
+                "     * Performs an in-place element by element scalar multiplication.<br>\n" +
+                "     * <br>\n" +
+                "     * a<sub>ij</sub> = α*a<sub>ij</sub>\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a The vector that is to be scaled.  Modified.\n" +
+                "     * @param alpha the amount each element is multiplied by.\n" +
+                "     */\n" +
+                "    public static void scale( double alpha , "+nameVector+" a ) {\n");
+        for( int y = 1; y <= dimen; y++ ) {
+            out.print("        a.a"+y+" *= alpha;\n");
+        }
+        out.print("    }\n\n");
+    }
+
+    private void scale_three( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>\n" +
+                "     * Performs an element by element scalar multiplication.<br>\n" +
+                "     * <br>\n" +
+                "     * b<sub>ij</sub> = α*a<sub>ij</sub>\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param alpha the amount each element is multiplied by.\n" +
+                "     * @param a The matrix that is to be scaled.  Not modified.\n" +
+                "     * @param b Where the scaled matrix is stored. Modified.\n" +
+                "     */\n" +
+                "    public static void scale( double alpha , "+nameMatrix+" a , "+nameMatrix+" b ) {\n");
+        for( int y = 1; y <= dimen; y++ ) {
+            out.print("        ");
+            for( int x = 1; x <= dimen; x++ ) {
+                String w = "a"+y+""+x;
+                out.print("b."+w+" = a."+w+"*alpha;");
+                if( x < dimen )
+                    out.print(" ");
+                else
+                    out.println();
+            }
+        }
+        out.print("    }\n\n");
+    }
+
+    private void scale_vector_three( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>\n" +
+                "     * Performs an element by element scalar multiplication.<br>\n" +
+                "     * <br>\n" +
+                "     * b<sub>i</sub> = α*a<sub>i</sub>\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param alpha the amount each element is multiplied by.\n" +
+                "     * @param a The vector that is to be scaled.  Not modified.\n" +
+                "     * @param b Where the scaled matrix is stored. Modified.\n" +
+                "     */\n" +
+                "    public static void scale( double alpha , "+nameVector+" a , "+nameVector+" b ) {\n");
+        for( int y = 1; y <= dimen; y++ ) {
+            out.print("        b.a"+y+" = a.a"+y+"*alpha;\n");
+        }
+        out.print("    }\n\n");
+    }
+
+    private void divide_two( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>\n" +
+                "     * Performs an in-place element by element scalar division. Scalar denominator.<br>\n" +
+                "     * <br>\n" +
+                "     * a<sub>ij</sub> = a<sub>ij</sub>/α\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a The matrix whose elements are to be divided.  Modified.\n" +
+                "     * @param alpha the amount each element is divided by.\n" +
+                "     */\n" +
+                "    public static void divide( "+nameMatrix+" a , double alpha ) {\n");
+        for( int y = 1; y <= dimen; y++ ) {
+            out.print("        ");
+            for( int x = 1; x <= dimen; x++ ) {
+                String w = "a"+y+""+x;
+                out.print("a."+w+" /= alpha;");
+                if( x < dimen )
+                    out.print(" ");
+                else
+                    out.println();
+            }
+        }
+        out.print("    }\n\n");
+    }
+
+    private void divide_vector_two( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>\n" +
+                "     * Performs an in-place element by element scalar division. Scalar denominator.<br>\n" +
+                "     * <br>\n" +
+                "     * a<sub>i</sub> = a<sub>i</sub>/α\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a The vector whose elements are to be divided.  Modified.\n" +
+                "     * @param alpha the amount each element is divided by.\n" +
+                "     */\n" +
+                "    public static void divide( "+nameVector+" a , double alpha ) {\n");
+        for( int y = 1; y <= dimen; y++ ) {
+            out.print("        a.a"+y+" /= alpha;\n");
+        }
+        out.print("    }\n\n");
+    }
+
+    private void divide_three( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>\n" +
+                "     * Performs an element by element scalar division.  Scalar denominator.<br>\n" +
+                "     * <br>\n" +
+                "     * b<sub>ij</sub> = a<sub>ij</sub> /α\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param alpha the amount each element is divided by.\n" +
+                "     * @param a The matrix whose elements are to be divided.  Not modified.\n" +
+                "     * @param b Where the results are stored. Modified.\n" +
+                "     */\n" +
+                "    public static void divide( "+nameMatrix+" a , double alpha , "+nameMatrix+" b ) {\n");
+        for( int y = 1; y <= dimen; y++ ) {
+            out.print("        ");
+            for( int x = 1; x <= dimen; x++ ) {
+                String w = "a"+y+""+x;
+                out.print("b."+w+" = a."+w+"/alpha;");
+                if( x < dimen )
+                    out.print(" ");
+                else
+                    out.println();
+            }
+        }
+        out.print("    }\n\n");
+    }
+
+    private void divide_vector_three( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>\n" +
+                "     * Performs an element by element scalar division.  Scalar denominator.<br>\n" +
+                "     * <br>\n" +
+                "     * b<sub>i</sub> = a<sub>i</sub> /α\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param alpha the amount each element is divided by.\n" +
+                "     * @param a The vector whose elements are to be divided.  Not modified.\n" +
+                "     * @param b Where the results are stored. Modified.\n" +
+                "     */\n" +
+                "    public static void divide( "+nameVector+" a , double alpha , "+nameVector+" b ) {\n");
+        for( int y = 1; y <= dimen; y++ ) {
+                out.print("        b.a"+y+" = a.a"+y+"/alpha;\n");
+        }
+        out.print("    }\n\n");
+    }
+
+    private void changeSign( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>\n" +
+                "     * Changes the sign of every element in the matrix.<br>\n" +
+                "     * <br>\n" +
+                "     * a<sub>ij</sub> = -a<sub>ij</sub>\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a A matrix. Modified.\n" +
+                "     */\n" +
+                "    public static void changeSign( "+nameMatrix+" a )\n" +
+                "    {\n");
+        for( int y = 1; y <= dimen; y++ ) {
+            out.print("        ");
+            for( int x = 1; x <= dimen; x++ ) {
+                String w = "a"+y+""+x;
+                out.print("a."+w+" = -a."+w+";");
+                if( x < dimen )
+                    out.print(" ");
+                else
+                    out.println();
+            }
+        }
+        out.print("    }\n\n");
+    }
+
+    private void changeSign_vector( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>\n" +
+                "     * Changes the sign of every element in the vector.<br>\n" +
+                "     * <br>\n" +
+                "     * a<sub>i</sub> = -a<sub>i</sub>\n" +
+                "     * </p>\n" +
+                "     *\n" +
+                "     * @param a A vector. Modified.\n" +
+                "     */\n" +
+                "    public static void changeSign( "+nameVector+" a )\n" +
+                "    {\n");
+        for( int y = 1; y <= dimen; y++ ) {
+            out.print("        a.a"+y+" = -a.a"+y+";\n");
+        }
+        out.print("    }\n\n");
+    }
+
+    private void fill( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>\n" +
+                "     * Sets every element in the matrix to the specified value.<br>\n" +
+                "     * <br>\n" +
+                "     * a<sub>ij</sub> = value\n" +
+                "     * <p>\n" +
+                "     *\n" +
+                "     * @param a A matrix whose elements are about to be set. Modified.\n" +
+                "     * @param v The value each element will have.\n" +
+                "     */\n" +
+                "    public static void fill( "+nameMatrix+" a , double v  ) {\n");
+        for( int y = 1; y <= dimen; y++ ) {
+            out.print("        ");
+            for( int x = 1; x <= dimen; x++ ) {
+                String w = "a"+y+""+x;
+                out.print("a."+w+" = v;");
+                if( x < dimen )
+                    out.print(" ");
+                else
+                    out.println();
+            }
+        }
+                out.print("    }\n\n");
+    }
+
+    private void fill_vector( int dimen ) {
+        out.print("    /**\n" +
+                "     * <p>\n" +
+                "     * Sets every element in the vector to the specified value.<br>\n" +
+                "     * <br>\n" +
+                "     * a<sub>i</sub> = value\n" +
+                "     * <p>\n" +
+                "     *\n" +
+                "     * @param a A vector whose elements are about to be set. Modified.\n" +
+                "     * @param v The value each element will have.\n" +
+                "     */\n" +
+                "    public static void fill( "+nameVector+" a , double v  ) {\n");
+        for( int y = 1; y <= dimen; y++ ) {
+            out.print("        a.a"+y+" = v;\n");
+        }
+        out.print("    }\n\n");
+    }
+
+    private void extract( int dimen ) {
+        out.print("    /**\n" +
+                "     * Extracts the row from the matrix a.\n" +
+                "     * @param a Input matrix\n" +
+                "     * @param row Which row is to be extracted\n" +
+                "     * @param out output. Storage for the extracted row. If null then a new vector will be returned.\n" +
+                "     * @return The extracted row.\n" +
+                "     */\n" +
+                "    public static "+nameVector+" extractRow( "+nameMatrix+" a , int row , "+nameVector+" out ) {\n" +
+                "        if( out == null) out = new "+nameVector+"();\n" +
+                "        switch( row ) {\n");
+        for (int i = 0; i < dimen; i++) {
+            out.print("            case "+i+":\n");
+            for (int j = 0; j < dimen; j++) {
+                int n = j+1;
+                out.print("                out.a"+n+" = a.a"+(i+1)+""+n+";\n");
+            }
+            out.print("            break;\n");
+        }
+        out.print("            default:\n" +
+                "                throw new IllegalArgumentException(\"Out of bounds row.  row = \"+row);\n" +
+                "        }\n" +
+                "        return out;\n" +
+                "    }\n" +
+                "\n" +
+                "    /**\n" +
+                "     * Extracts the column from the matrix a.\n" +
+                "     * @param a Input matrix\n" +
+                "     * @param column Which column is to be extracted\n" +
+                "     * @param out output. Storage for the extracted column. If null then a new vector will be returned.\n" +
+                "     * @return The extracted column.\n" +
+                "     */\n" +
+                "    public static "+nameVector+" extractColumn( "+nameMatrix+" a , int column , "+nameVector+" out ) {\n" +
+                "        if( out == null) out = new "+nameVector+"();\n" +
+                "        switch( column ) {\n");
+        for (int i = 0; i < dimen; i++) {
+            out.print("            case "+i+":\n");
+            for (int j = 0; j < dimen; j++) {
+                int n = j+1;
+                out.print("                out.a"+n+" = a.a"+n+""+(i+1)+";\n");
+            }
+            out.print("            break;\n");
+        }
+        out.print("            default:\n" +
+                "                throw new IllegalArgumentException(\"Out of bounds column.  column = \"+column);\n" +
+                "        }\n" +
+                "        return out;\n" +
+                "    }\n\n");
+    }
+
+    public static void main( String args[] ) throws FileNotFoundException {
+        GenerateFixedOps app = new GenerateFixedOps();
+
+        app.generate();
+    }
+
+}
\ No newline at end of file
diff --git a/main/dense64/generate/org/ejml/data/GenerateFixedMatrixN.java b/main/dense64/generate/org/ejml/data/GenerateFixedMatrixN.java
new file mode 100644
index 0000000..ac8f0f3
--- /dev/null
+++ b/main/dense64/generate/org/ejml/data/GenerateFixedMatrixN.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import org.ejml.CodeGeneratorBase;
+
+import java.io.FileNotFoundException;
+
+/**
+ * @author Peter Abeles
+ */
+public class GenerateFixedMatrixN extends CodeGeneratorBase{
+
+    String classPreamble = "FixedMatrix";
+
+    @Override
+    public void generate() throws FileNotFoundException {
+        for( int dimension = 2; dimension <= 6; dimension++ ){
+            print(dimension);
+        }
+    }
+
+    public void print( int dimen ) throws FileNotFoundException {
+        String className = classPreamble +dimen+"_64F";
+
+        setOutputFile(className);
+
+        out.print("import org.ejml.ops.MatrixIO;\n" +
+                "\n" +
+                "/**\n" +
+                " * Fixed sized vector with "+dimen+" elements.  Can represent a "+dimen+" x 1 or 1 x "+dimen+" matrix, context dependent.\n" +
+                " * <p>DO NOT MODIFY.  Automatically generated code created by "+getClass().getSimpleName()+"</p>\n" +
+                " *\n" +
+                " * @author Peter Abeles\n" +
+                " */\n" +
+                "public class "+className+" implements FixedMatrix64F {\n");
+        printClassParam(dimen);
+        out.print("\n" +
+                "    public "+className+"() {\n" +
+                "    }\n" +
+                "\n" +
+                "    public "+className+"(");
+        printFunctionParam(dimen);
+        out.print(")\n" +
+                "    {\n");
+        printSetFromParam(dimen,"");
+        out.print("    }\n" +
+                "\n" +
+                "    public "+className+"("+className+" o) {\n");
+        printSetFromParam(dimen,"o.");
+        out.print("    }\n" +
+                "\n" +
+                "    @Override\n" +
+                "    public double get(int row, int col) {\n" +
+                "        return unsafe_get(row,col);\n" +
+                "    }\n" +
+                "\n" +
+                "    @Override\n" +
+                "    public double unsafe_get(int row, int col) {\n" +
+                "        if( row != 0 && col != 0 )\n" +
+                "            throw new IllegalArgumentException(\"Row or column must be zero since this is a vector\");\n" +
+                "\n" +
+                "        int w = Math.max(row,col);\n" +
+                "\n");
+        setGetter(dimen);
+        out.print("        } else {\n" +
+                "            throw new IllegalArgumentException(\"Out of range.  \"+w);\n" +
+                "        }\n" +
+                "    }\n" +
+                "\n" +
+                "    @Override\n" +
+                "    public void set(int row, int col, double val) {\n" +
+                "        unsafe_set(row,col,val);\n" +
+                "    }\n" +
+                "\n" +
+                "    @Override\n" +
+                "    public void unsafe_set(int row, int col, double val) {\n" +
+                "        if( row != 0 && col != 0 )\n" +
+                "            throw new IllegalArgumentException(\"Row or column must be zero since this is a vector\");\n" +
+                "\n" +
+                "        int w = Math.max(row,col);\n" +
+                "\n");
+        setSetter(dimen);
+        out.print("        } else {\n" +
+                "            throw new IllegalArgumentException(\"Out of range.  \"+w);\n" +
+                "        }\n" +
+                "    }\n" +
+                "\n");
+        printSetMatrix(dimen);
+        out.print("    @Override\n" +
+                "    public int getNumRows() {\n" +
+                "        return "+dimen+";\n" +
+                "    }\n" +
+                "\n" +
+                "    @Override\n" +
+                "    public int getNumCols() {\n" +
+                "        return 1;\n" +
+                "    }\n" +
+                "\n" +
+                "    @Override\n" +
+                "    public int getNumElements() {\n" +
+                "        return "+dimen+";\n" +
+                "    }\n" +
+                "\n" +
+                "    @Override\n" +
+                "    public <T extends Matrix> T copy() {\n" +
+                "        return (T)new "+className+"(this);\n" +
+                "    }\n" +
+                "\n" +
+                "    @Override\n" +
+                "    public void print() {\n" +
+                "        MatrixIO.print(System.out, this);\n" +
+                "    }\n" +
+                "}\n\n");
+    }
+
+    private void printClassParam( int dimen ) {
+        out.print("    public double ");
+        for( int i = 1; i <= dimen; i++ ) {
+           out.print("a"+i);
+            if( i < dimen )
+                out.print(",");
+            else
+                out.print(";\n");
+        }
+    }
+
+    private void printFunctionParam( int dimen ) {
+        for( int i = 1; i <= dimen; i++ ) {
+            out.print("double a"+i);
+            if( i < dimen )
+                out.print(",");
+        }
+    }
+
+    private void printSetFromParam(int dimen, String prefix) {
+        for( int i = 1; i <= dimen; i++ ) {
+            out.println("        this.a"+i+" = "+prefix+"a"+i+";");
+        }
+    }
+
+    private void printSetMatrix(int dimen) {
+        out.print("    @Override\n" +
+                "    public void set(Matrix original) {\n" +
+                "        RealMatrix64F m = (RealMatrix64F)original;\n" +
+                "\n" +
+                "        if( m.getNumCols() == 1 && m.getNumRows() == "+dimen+" ) {\n");
+        for (int i = 0; i < dimen; i++) {
+            out.print("            a"+(i+1)+" = m.get("+i+",0);\n");
+        }
+        out.print("        } else if( m.getNumRows() == 1 && m.getNumCols() == "+dimen+" ){\n");
+        for (int i = 0; i < dimen; i++) {
+            out.print("            a"+(i+1)+" = m.get(0,"+i+");\n");
+        }
+        out.print("        } else {\n" +
+                "            throw new IllegalArgumentException(\"Incompatible shape\");\n" +
+                "        }\n" +
+                "    }\n\n");
+    }
+
+    private void setGetter(int dimen) {
+        for( int i = 0; i < dimen; i++ ) {
+            if( i == 0 )
+                out.print("        if( w == 0 ) {\n");
+            else
+                out.print("        } else if( w == "+i+" ) {\n");
+            out.print("            return a"+(i+1)+";\n");
+        }
+    }
+
+    private void setSetter(int dimen ) {
+        for( int i = 0; i < dimen; i++ ) {
+            if( i == 0 )
+                out.print("        if( w == 0 ) {\n");
+            else
+                out.print("        } else if( w == "+i+" ) {\n");
+            out.print("            a"+(i+1)+" = val;\n");
+        }
+    }
+
+    public static void main( String args[] ) throws FileNotFoundException {
+        GenerateFixedMatrixN app = new GenerateFixedMatrixN();
+
+        app.generate();
+    }
+}
diff --git a/main/dense64/generate/org/ejml/data/GenerateFixedMatrixNxN.java b/main/dense64/generate/org/ejml/data/GenerateFixedMatrixNxN.java
new file mode 100644
index 0000000..de37790
--- /dev/null
+++ b/main/dense64/generate/org/ejml/data/GenerateFixedMatrixNxN.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import org.ejml.CodeGeneratorBase;
+
+import java.io.FileNotFoundException;
+
+/**
+ * @author Peter Abeles
+ */
+public class GenerateFixedMatrixNxN extends CodeGeneratorBase{
+
+    String classPreamble = "FixedMatrix";
+
+    @Override
+    public void generate() throws FileNotFoundException {
+        for( int dimension = 2; dimension <= 6; dimension++ ){
+            print(dimension);
+        }
+    }
+
+    public void print( int dimen ) throws FileNotFoundException {
+        String className = classPreamble +dimen+"x"+dimen+"_64F";
+
+        setOutputFile(className);
+
+        out.println("import org.ejml.ops.MatrixIO;\n\n"+
+                "/**\n" +
+                " * Fixed sized "+dimen+" by "+className+" matrix.  The matrix is stored as class variables for very fast read/write.  aXY is the\n" +
+                " * value of row = X and column = Y.\n" +
+                " * <p>DO NOT MODIFY.  Automatically generated code created by "+getClass().getSimpleName()+"</p>\n" +
+                " *\n" +
+                " * @author Peter Abeles\n" +
+                " */\n" +
+                "public class "+className+" implements FixedMatrix64F {\n");
+        printClassParam(dimen);
+                out.print("\n" +
+                "    public "+className+"() {\n" +
+                "    }\n" +
+                "\n" +
+                "    public "+className);
+        printFunctionParam(dimen);
+        out.print("    {\n");
+        printSetFromParam(dimen, "");
+        out.print("    }\n" +
+                "\n" +
+                "    public " + className + "( " + className + " o ) {\n");
+        printSetFromParam(dimen, "o.");
+        out.print("    }\n" +
+                "\n" +
+                "    @Override\n" +
+                "    public double get(int row, int col) {\n" +
+                "        return unsafe_get(row,col);\n" +
+                "    }\n" +
+                "\n" +
+                "    @Override\n" +
+                "    public double unsafe_get(int row, int col) {\n");
+        setGetter(dimen);
+        out.print("        throw new IllegalArgumentException(\"Row and/or column out of range. \"+row+\" \"+col);\n" +
+                "    }\n" +
+                "\n" +
+                "    @Override\n" +
+                "    public void set(int row, int col, double val) {\n" +
+                "        unsafe_set(row,col,val);\n" +
+                "    }\n" +
+                "\n" +
+                "    @Override\n" +
+                "    public void unsafe_set(int row, int col, double val) {\n");
+        setSetter(dimen);
+        out.print("        throw new IllegalArgumentException(\"Row and/or column out of range. \"+row+\" \"+col);\n" +
+                "    }\n" +
+                "\n");
+        printSetMatrix(dimen);
+        out.print("    @Override\n" +
+                "    public int getNumRows() {\n" +
+                "        return "+dimen+";\n" +
+                "    }\n" +
+                "\n" +
+                "    @Override\n" +
+                "    public int getNumCols() {\n" +
+                "        return "+dimen+";\n" +
+                "    }\n" +
+                "\n" +
+                "    @Override\n" +
+                "    public int getNumElements() {\n" +
+                "        return "+(dimen*dimen)+";\n" +
+                "    }\n" +
+                "\n" +
+                "    @Override\n" +
+                "    public <T extends Matrix> T copy() {\n" +
+                "        return (T)new "+className+"(this);\n" +
+                "    }\n" +
+                "\n" +
+                "    @Override\n" +
+                "    public void print() {\n" +
+                "        MatrixIO.print(System.out, this);\n" +
+                "    }\n" +
+                "}\n\n");
+    }
+
+    private void printClassParam( int dimen ) {
+        for( int y = 1; y <= dimen; y++ ) {
+            out.print("    public double ");
+            for( int x = 1; x <= dimen; x++ ) {
+                out.print("a"+y+""+x);
+                if( x != dimen )
+                    out.print(",");
+                else
+                    out.println(";");
+            }
+        }
+    }
+
+    private void printFunctionParam( int dimen ) {
+        for( int y = 1; y <= dimen; y++ ) {
+            if( y == 1 )
+                out.print("( ");
+            else
+                out.print("                              ");
+            for( int x = 1; x <= dimen; x++ ) {
+                out.print("double a"+y+""+x);
+                if( x != dimen )
+                    out.print(",");
+                else if( y != dimen )
+                    out.println(",");
+                else
+                    out.println(")");
+            }
+        }
+    }
+
+    private void printSetFromParam(int dimen, String prefix) {
+        for( int y = 1; y <= dimen; y++ ) {
+            for( int x = 1; x <= dimen; x++ ) {
+                out.println("        this.a"+y+""+x+" = "+prefix+"a"+y+""+x+";");
+            }
+        }
+    }
+
+    private void setGetter(int dimen) {
+        for( int y = 1; y <= dimen; y++ ) {
+            if( y == 1 )
+                out.print("        if( row == 0 ) {\n");
+            else
+                out.print("        } else if( row == "+(y-1)+" ) {\n");
+            for( int x = 1; x <= dimen; x++ ) {
+                if( x == 1 )
+                    out.print("            if( col == 0 ) {\n");
+                else
+                    out.print("            } else if( col == "+(x-1)+" ) {\n");
+                out.print("                return a"+y+""+x+";\n");
+            }
+            out.print("            }\n");
+        }
+        out.print("        }\n");
+    }
+
+    private void setSetter(int dimen ) {
+        for( int y = 1; y <= dimen; y++ ) {
+            if( y == 1 )
+                out.print("        if( row == 0 ) {\n");
+            else
+                out.print("        } else if( row == "+(y-1)+" ) {\n");
+            for( int x = 1; x <= dimen; x++ ) {
+                if( x == 1 )
+                    out.print("            if( col == 0 ) {\n");
+                else
+                    out.print("            } else if( col == "+(x-1)+" ) {\n");
+                out.print("                a"+y+""+x+" = val; return;\n");
+            }
+            out.print("            }\n");
+        }
+        out.print("        }\n");
+    }
+
+    private void printSetMatrix( int dimen ) {
+        out.print("    @Override\n" +
+                "    public void set(Matrix original) {\n" +
+                "        if( original.getNumCols() != "+dimen+" || original.getNumRows() != "+dimen+" )\n" +
+                "            throw new IllegalArgumentException(\"Rows and/or columns do not match\");\n" +
+                "        RealMatrix64F m = (RealMatrix64F)original;\n" +
+                "        \n");
+        for( int y = 1; y <= dimen; y++ ) {
+            for( int x = 1; x <= dimen; x++ ) {
+                out.print("        a"+y+""+x+" = m.get("+(y-1)+","+(x-1)+");\n");
+            }
+        }
+        out.print("    }\n\n");
+    }
+
+    public static void main( String args[] ) throws FileNotFoundException {
+        GenerateFixedMatrixNxN app = new GenerateFixedMatrixNxN();
+
+        app.generate();
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/block/BlockInnerMultiplication.java b/main/dense64/src/org/ejml/alg/block/BlockInnerMultiplication.java
new file mode 100644
index 0000000..b84926d
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/block/BlockInnerMultiplication.java
@@ -0,0 +1,562 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block;
+
+/**
+ * <p>
+ * Matrix multiplication for the inner row major blocks, typically inside of a {@link org.ejml.data.BlockMatrix64F}.
+ * </p>
+ *
+ * <p>
+ * This code was auto generated by {@link GeneratorBlockInnerMultiplication} and should not be modified directly.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class BlockInnerMultiplication {
+
+    /**
+     * <p>
+     * Performs the follow operation on individual inner blocks:<br>
+     * <br>
+     * C = C + A * B
+     * </p>
+     */
+    public static void blockMultPlus( final double[] dataA, final double []dataB, final double []dataC,
+                                     int indexA, int indexB, int indexC,
+                                     final int heightA, final int widthA, final int widthC) {
+//        for( int i = 0; i < heightA; i++ ) {
+//            for( int k = 0; k < widthA; k++ ) {
+//                for( int j = 0; j < widthC; j++ ) {
+//                    dataC[ i*widthC + j + indexC ] += dataA[i*widthA + k + indexA] * dataB[k*widthC + j + indexB];
+//                }
+//            }
+//        }
+
+        int a = indexA;
+        int rowC = indexC;
+        for( int i = 0; i < heightA; i++ , rowC += widthC ) {
+            int b = indexB;
+
+            final int endC = rowC + widthC;
+            final int endA = a + widthA;
+            while( a != endA ) {//for( int k = 0; k < widthA; k++ ) {
+                double valA = dataA[a++];
+
+                int c = rowC;
+
+                while( c != endC  ) {//for( int j = 0; j < widthC; j++ ) {
+                    dataC[ c++ ] += valA * dataB[ b++ ];
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs the follow operation on individual inner blocks:<br>
+     * <br>
+     * C = C + A<sup>T</sup> * B
+     * </p>
+     */
+    public static void blockMultPlusTransA( final double[] dataA, final double []dataB, final double []dataC,
+                                     int indexA, int indexB, int indexC,
+                                     final int heightA, final int widthA, final int widthC) {
+//        for( int i = 0; i < widthA; i++ ) {
+//            for( int k = 0; k < heightA; k++ ) {
+//                double valA = dataA[k*widthA + i + indexA];
+//                for( int j = 0; j < widthC; j++ ) {
+//                    dataC[ i*widthC + j + indexC ] += valA * dataB[k*widthC + j + indexB];
+//                }
+//            }
+//        }
+
+        int rowC = indexC;
+        for( int i = 0; i < widthA; i++ , rowC += widthC) {
+            int colA = i + indexA;
+            int endA = colA + widthA*heightA;
+            int b = indexB;
+
+            // for( int k = 0; k < heightA; k++ ) {
+            while(colA != endA ) {
+                double valA = dataA[colA];
+
+                int c = rowC;
+                final int endB = b + widthC;
+
+                //for( int j = 0; j < widthC; j++ ) {
+                while( b != endB ) {
+                    dataC[ c++ ] += valA * dataB[b++];
+                }
+                colA += widthA;
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs the follow operation on individual inner blocks:<br>
+     * <br>
+     * C = C + A * B<sup>T</sup>
+     * </p>
+     */
+    public static void blockMultPlusTransB( final double[] dataA, final double []dataB, final double []dataC,
+                                     int indexA, int indexB, int indexC,
+                                     final int heightA, final int widthA, final int widthC) {
+        for( int i = 0; i < heightA; i++ ) {
+            for( int j = 0; j < widthC; j++ ) {
+                double val = 0;
+
+                for( int k = 0; k < widthA; k++ ) {
+                    val += dataA[i*widthA + k + indexA] * dataB[j*widthA + k + indexB];
+                }
+
+                dataC[ i*widthC + j + indexC ] += val;
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs the follow operation on individual inner blocks:<br>
+     * <br>
+     * C = C - A * B
+     * </p>
+     */
+    public static void blockMultMinus( final double[] dataA, final double []dataB, final double []dataC,
+                                     int indexA, int indexB, int indexC,
+                                     final int heightA, final int widthA, final int widthC) {
+//        for( int i = 0; i < heightA; i++ ) {
+//            for( int k = 0; k < widthA; k++ ) {
+//                for( int j = 0; j < widthC; j++ ) {
+//                    dataC[ i*widthC + j + indexC ] += dataA[i*widthA + k + indexA] * dataB[k*widthC + j + indexB];
+//                }
+//            }
+//        }
+
+        int a = indexA;
+        int rowC = indexC;
+        for( int i = 0; i < heightA; i++ , rowC += widthC ) {
+            int b = indexB;
+
+            final int endC = rowC + widthC;
+            final int endA = a + widthA;
+            while( a != endA ) {//for( int k = 0; k < widthA; k++ ) {
+                double valA = dataA[a++];
+
+                int c = rowC;
+
+                while( c != endC  ) {//for( int j = 0; j < widthC; j++ ) {
+                    dataC[ c++ ] -= valA * dataB[ b++ ];
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs the follow operation on individual inner blocks:<br>
+     * <br>
+     * C = C - A<sup>T</sup> * B
+     * </p>
+     */
+    public static void blockMultMinusTransA( final double[] dataA, final double []dataB, final double []dataC,
+                                     int indexA, int indexB, int indexC,
+                                     final int heightA, final int widthA, final int widthC) {
+//        for( int i = 0; i < widthA; i++ ) {
+//            for( int k = 0; k < heightA; k++ ) {
+//                double valA = dataA[k*widthA + i + indexA];
+//                for( int j = 0; j < widthC; j++ ) {
+//                    dataC[ i*widthC + j + indexC ] += valA * dataB[k*widthC + j + indexB];
+//                }
+//            }
+//        }
+
+        int rowC = indexC;
+        for( int i = 0; i < widthA; i++ , rowC += widthC) {
+            int colA = i + indexA;
+            int endA = colA + widthA*heightA;
+            int b = indexB;
+
+            // for( int k = 0; k < heightA; k++ ) {
+            while(colA != endA ) {
+                double valA = dataA[colA];
+
+                int c = rowC;
+                final int endB = b + widthC;
+
+                //for( int j = 0; j < widthC; j++ ) {
+                while( b != endB ) {
+                    dataC[ c++ ] -= valA * dataB[b++];
+                }
+                colA += widthA;
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs the follow operation on individual inner blocks:<br>
+     * <br>
+     * C = C - A * B<sup>T</sup>
+     * </p>
+     */
+    public static void blockMultMinusTransB( final double[] dataA, final double []dataB, final double []dataC,
+                                     int indexA, int indexB, int indexC,
+                                     final int heightA, final int widthA, final int widthC) {
+        for( int i = 0; i < heightA; i++ ) {
+            for( int j = 0; j < widthC; j++ ) {
+                double val = 0;
+
+                for( int k = 0; k < widthA; k++ ) {
+                    val += dataA[i*widthA + k + indexA] * dataB[j*widthA + k + indexB];
+                }
+
+                dataC[ i*widthC + j + indexC ] -= val;
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs the follow operation on individual inner blocks:<br>
+     * <br>
+     * C = A * B
+     * </p>
+     */
+    public static void blockMultSet( final double[] dataA, final double []dataB, final double []dataC,
+                                     int indexA, int indexB, int indexC,
+                                     final int heightA, final int widthA, final int widthC) {
+//        for( int i = 0; i < heightA; i++ ) {
+//            for( int k = 0; k < widthA; k++ ) {
+//                for( int j = 0; j < widthC; j++ ) {
+//                    dataC[ i*widthC + j + indexC ] += dataA[i*widthA + k + indexA] * dataB[k*widthC + j + indexB];
+//                }
+//            }
+//        }
+
+        int a = indexA;
+        int rowC = indexC;
+        for( int i = 0; i < heightA; i++ , rowC += widthC ) {
+            int b = indexB;
+
+            final int endC = rowC + widthC;
+            final int endA = a + widthA;
+            while( a != endA ) {//for( int k = 0; k < widthA; k++ ) {
+                double valA = dataA[a++];
+
+                int c = rowC;
+
+                if( b == indexB ) {
+                    while( c != endC  ) {//for( int j = 0; j < widthC; j++ ) {
+                        dataC[ c++ ] = valA * dataB[ b++ ];
+                    }
+                } else {
+                    while( c != endC  ) {//for( int j = 0; j < widthC; j++ ) {
+                        dataC[ c++ ] += valA * dataB[ b++ ];
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs the follow operation on individual inner blocks:<br>
+     * <br>
+     * C = A<sup>T</sup> * B
+     * </p>
+     */
+    public static void blockMultSetTransA( final double[] dataA, final double []dataB, final double []dataC,
+                                     int indexA, int indexB, int indexC,
+                                     final int heightA, final int widthA, final int widthC) {
+//        for( int i = 0; i < widthA; i++ ) {
+//            for( int k = 0; k < heightA; k++ ) {
+//                double valA = dataA[k*widthA + i + indexA];
+//                for( int j = 0; j < widthC; j++ ) {
+//                    dataC[ i*widthC + j + indexC ] += valA * dataB[k*widthC + j + indexB];
+//                }
+//            }
+//        }
+
+        int rowC = indexC;
+        for( int i = 0; i < widthA; i++ , rowC += widthC) {
+            int colA = i + indexA;
+            int endA = colA + widthA*heightA;
+            int b = indexB;
+
+            // for( int k = 0; k < heightA; k++ ) {
+            while(colA != endA ) {
+                double valA = dataA[colA];
+
+                int c = rowC;
+                final int endB = b + widthC;
+
+                //for( int j = 0; j < widthC; j++ ) {
+                if( b == indexB ) {
+                    while( b != endB ) {
+                        dataC[ c++ ] = valA * dataB[b++];
+                    } 
+                } else {
+                    while( b != endB ) {
+                        dataC[ c++ ] += valA * dataB[b++];
+                    }
+                }
+                colA += widthA;
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs the follow operation on individual inner blocks:<br>
+     * <br>
+     * C = A * B<sup>T</sup>
+     * </p>
+     */
+    public static void blockMultSetTransB( final double[] dataA, final double []dataB, final double []dataC,
+                                     int indexA, int indexB, int indexC,
+                                     final int heightA, final int widthA, final int widthC) {
+        for( int i = 0; i < heightA; i++ ) {
+            for( int j = 0; j < widthC; j++ ) {
+                double val = 0;
+
+                for( int k = 0; k < widthA; k++ ) {
+                    val += dataA[i*widthA + k + indexA] * dataB[j*widthA + k + indexB];
+                }
+
+                dataC[ i*widthC + j + indexC ] = val;
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs the follow operation on individual inner blocks:<br>
+     * <br>
+     * C = C +  α A * B
+     * </p>
+     */
+    public static void blockMultPlus( double alpha , final double[] dataA, final double []dataB, final double []dataC,
+                                     int indexA, int indexB, int indexC,
+                                     final int heightA, final int widthA, final int widthC) {
+//        for( int i = 0; i < heightA; i++ ) {
+//            for( int k = 0; k < widthA; k++ ) {
+//                for( int j = 0; j < widthC; j++ ) {
+//                    dataC[ i*widthC + j + indexC ] += dataA[i*widthA + k + indexA] * dataB[k*widthC + j + indexB];
+//                }
+//            }
+//        }
+
+        int a = indexA;
+        int rowC = indexC;
+        for( int i = 0; i < heightA; i++ , rowC += widthC ) {
+            int b = indexB;
+
+            final int endC = rowC + widthC;
+            final int endA = a + widthA;
+            while( a != endA ) {//for( int k = 0; k < widthA; k++ ) {
+                double valA = alpha*dataA[a++];
+
+                int c = rowC;
+
+                while( c != endC  ) {//for( int j = 0; j < widthC; j++ ) {
+                    dataC[ c++ ] += valA * dataB[ b++ ];
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs the follow operation on individual inner blocks:<br>
+     * <br>
+     * C = C +  α A<sup>T</sup> * B
+     * </p>
+     */
+    public static void blockMultPlusTransA( double alpha , final double[] dataA, final double []dataB, final double []dataC,
+                                     int indexA, int indexB, int indexC,
+                                     final int heightA, final int widthA, final int widthC) {
+//        for( int i = 0; i < widthA; i++ ) {
+//            for( int k = 0; k < heightA; k++ ) {
+//                double valA = dataA[k*widthA + i + indexA];
+//                for( int j = 0; j < widthC; j++ ) {
+//                    dataC[ i*widthC + j + indexC ] += valA * dataB[k*widthC + j + indexB];
+//                }
+//            }
+//        }
+
+        int rowC = indexC;
+        for( int i = 0; i < widthA; i++ , rowC += widthC) {
+            int colA = i + indexA;
+            int endA = colA + widthA*heightA;
+            int b = indexB;
+
+            // for( int k = 0; k < heightA; k++ ) {
+            while(colA != endA ) {
+                double valA = alpha*dataA[colA];
+
+                int c = rowC;
+                final int endB = b + widthC;
+
+                //for( int j = 0; j < widthC; j++ ) {
+                while( b != endB ) {
+                    dataC[ c++ ] += valA * dataB[b++];
+                }
+                colA += widthA;
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs the follow operation on individual inner blocks:<br>
+     * <br>
+     * C = C +  α A * B<sup>T</sup>
+     * </p>
+     */
+    public static void blockMultPlusTransB( double alpha , final double[] dataA, final double []dataB, final double []dataC,
+                                     int indexA, int indexB, int indexC,
+                                     final int heightA, final int widthA, final int widthC) {
+        for( int i = 0; i < heightA; i++ ) {
+            for( int j = 0; j < widthC; j++ ) {
+                double val = 0;
+
+                for( int k = 0; k < widthA; k++ ) {
+                    val += dataA[i*widthA + k + indexA] * dataB[j*widthA + k + indexB];
+                }
+
+                dataC[ i*widthC + j + indexC ] += alpha *  val;
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs the follow operation on individual inner blocks:<br>
+     * <br>
+     * C =  α A * B
+     * </p>
+     */
+    public static void blockMultSet( double alpha , final double[] dataA, final double []dataB, final double []dataC,
+                                     int indexA, int indexB, int indexC,
+                                     final int heightA, final int widthA, final int widthC) {
+//        for( int i = 0; i < heightA; i++ ) {
+//            for( int k = 0; k < widthA; k++ ) {
+//                for( int j = 0; j < widthC; j++ ) {
+//                    dataC[ i*widthC + j + indexC ] += dataA[i*widthA + k + indexA] * dataB[k*widthC + j + indexB];
+//                }
+//            }
+//        }
+
+        int a = indexA;
+        int rowC = indexC;
+        for( int i = 0; i < heightA; i++ , rowC += widthC ) {
+            int b = indexB;
+
+            final int endC = rowC + widthC;
+            final int endA = a + widthA;
+            while( a != endA ) {//for( int k = 0; k < widthA; k++ ) {
+                double valA = alpha*dataA[a++];
+
+                int c = rowC;
+
+                if( b == indexB ) {
+                    while( c != endC  ) {//for( int j = 0; j < widthC; j++ ) {
+                        dataC[ c++ ] = valA * dataB[ b++ ];
+                    }
+                } else {
+                    while( c != endC  ) {//for( int j = 0; j < widthC; j++ ) {
+                        dataC[ c++ ] += valA * dataB[ b++ ];
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs the follow operation on individual inner blocks:<br>
+     * <br>
+     * C =  α A<sup>T</sup> * B
+     * </p>
+     */
+    public static void blockMultSetTransA( double alpha , final double[] dataA, final double []dataB, final double []dataC,
+                                     int indexA, int indexB, int indexC,
+                                     final int heightA, final int widthA, final int widthC) {
+//        for( int i = 0; i < widthA; i++ ) {
+//            for( int k = 0; k < heightA; k++ ) {
+//                double valA = dataA[k*widthA + i + indexA];
+//                for( int j = 0; j < widthC; j++ ) {
+//                    dataC[ i*widthC + j + indexC ] += valA * dataB[k*widthC + j + indexB];
+//                }
+//            }
+//        }
+
+        int rowC = indexC;
+        for( int i = 0; i < widthA; i++ , rowC += widthC) {
+            int colA = i + indexA;
+            int endA = colA + widthA*heightA;
+            int b = indexB;
+
+            // for( int k = 0; k < heightA; k++ ) {
+            while(colA != endA ) {
+                double valA = alpha*dataA[colA];
+
+                int c = rowC;
+                final int endB = b + widthC;
+
+                //for( int j = 0; j < widthC; j++ ) {
+                if( b == indexB ) {
+                    while( b != endB ) {
+                        dataC[ c++ ] = valA * dataB[b++];
+                    } 
+                } else {
+                    while( b != endB ) {
+                        dataC[ c++ ] += valA * dataB[b++];
+                    }
+                }
+                colA += widthA;
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs the follow operation on individual inner blocks:<br>
+     * <br>
+     * C =  α A * B<sup>T</sup>
+     * </p>
+     */
+    public static void blockMultSetTransB( double alpha , final double[] dataA, final double []dataB, final double []dataC,
+                                     int indexA, int indexB, int indexC,
+                                     final int heightA, final int widthA, final int widthC) {
+        for( int i = 0; i < heightA; i++ ) {
+            for( int j = 0; j < widthC; j++ ) {
+                double val = 0;
+
+                for( int k = 0; k < widthA; k++ ) {
+                    val += dataA[i*widthA + k + indexA] * dataB[j*widthA + k + indexB];
+                }
+
+                dataC[ i*widthC + j + indexC ] = alpha *  val;
+            }
+        }
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/block/BlockInnerRankUpdate.java b/main/dense64/src/org/ejml/alg/block/BlockInnerRankUpdate.java
new file mode 100644
index 0000000..0795dab
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/block/BlockInnerRankUpdate.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block;
+
+import org.ejml.data.D1Submatrix64F;
+
+
+/**
+ * Performs rank-n update operations on the inner blocks of a {@link org.ejml.data.BlockMatrix64F}
+ *
+ * It is assumed and not checked that the submatrices are aligned along the matrix's blocks.
+ *
+ * @author Peter Abeles
+ */
+public class BlockInnerRankUpdate {
+
+    /**
+     * <p>
+     * Performs:<br>
+     * <br>
+     * A = A + α B <sup>T</sup>B
+     * </p>
+     * 
+     * @param blockLength Size of the block in the block matrix.
+     * @param alpha scaling factor for right hand side.
+     * @param A Block aligned submatrix.
+     * @param B Block aligned submatrix.
+     */
+    public static void rankNUpdate( int blockLength , double alpha ,
+                                    D1Submatrix64F A , D1Submatrix64F B )
+    {
+
+        int heightB = B.row1-B.row0;
+        if( heightB > blockLength )
+            throw new IllegalArgumentException("Height of B cannot be greater than the block length");
+
+        int N = B.col1-B.col0;
+
+        if( A.col1-A.col0 != N )
+            throw new IllegalArgumentException("A does not have the expected number of columns based on B's width");
+        if( A.row1-A.row0 != N )
+            throw new IllegalArgumentException("A does not have the expected number of rows based on B's width");
+
+        for( int i = B.col0; i < B.col1; i += blockLength ) {
+
+            int indexB_i = B.row0*B.original.numCols + i*heightB;
+            int widthB_i = Math.min(blockLength,B.col1-i);
+
+            int rowA = i-B.col0+A.row0;
+            int heightA = Math.min( blockLength , A.row1 - rowA);
+
+            for( int j = B.col0; j < B.col1; j += blockLength ) {
+
+                int widthB_j = Math.min(blockLength,B.col1-j);
+
+                int indexA = rowA * A.original.numCols + (j-B.col0+A.col0)*heightA;
+                int indexB_j = B.row0*B.original.numCols + j*heightB;
+
+                BlockInnerMultiplication.blockMultPlusTransA(alpha,
+                        B.original.data,B.original.data,A.original.data,
+                        indexB_i,indexB_j,indexA,heightB,widthB_i,widthB_j);
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Rank N update function for a symmetric inner submatrix and only operates on the upper
+     * triangular portion of the submatrix.<br>
+     * <br>
+     * A = A - B <sup>T</sup>B
+     * </p>
+     */
+    public static void symmRankNMinus_U( int blockLength ,
+                                          D1Submatrix64F A , D1Submatrix64F B )
+    {
+
+        int heightB = B.row1-B.row0;
+        if( heightB > blockLength )
+            throw new IllegalArgumentException("Height of B cannot be greater than the block length");
+
+        int N = B.col1-B.col0;
+
+        if( A.col1-A.col0 != N )
+            throw new IllegalArgumentException("A does not have the expected number of columns based on B's width");
+        if( A.row1-A.row0 != N )
+            throw new IllegalArgumentException("A does not have the expected number of rows based on B's width");
+
+
+        for( int i = B.col0; i < B.col1; i += blockLength ) {
+
+            int indexB_i = B.row0*B.original.numCols + i*heightB;
+            int widthB_i = Math.min(blockLength,B.col1-i);
+
+            int rowA = i-B.col0+A.row0;
+            int heightA = Math.min( blockLength , A.row1 - rowA);
+
+            for( int j = i; j < B.col1; j += blockLength ) {
+
+                int widthB_j = Math.min(blockLength,B.col1-j);
+
+                int indexA = rowA * A.original.numCols + (j-B.col0+A.col0)*heightA;
+                int indexB_j = B.row0*B.original.numCols + j*heightB;
+
+                if( i == j ) {
+                    // only the upper portion of this block needs to be modified since it is along a diagonal
+                    multTransABlockMinus_U( B.original.data,A.original.data,
+                            indexB_i,indexB_j,indexA,heightB,widthB_i,widthB_j);
+                } else {
+                    multTransABlockMinus( B.original.data,A.original.data,
+                            indexB_i,indexB_j,indexA,heightB,widthB_i,widthB_j);
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Rank N update function for a symmetric inner submatrix and only operates on the lower
+     * triangular portion of the submatrix.<br>
+     * <br>
+     * A = A - B*B<sup>T</sup><br>
+     * </p>
+     */
+    public static void symmRankNMinus_L( int blockLength ,
+                                         D1Submatrix64F A , D1Submatrix64F B )
+    {
+        int widthB = B.col1-B.col0;
+        if( widthB > blockLength )
+            throw new IllegalArgumentException("Width of B cannot be greater than the block length");
+
+        int N = B.row1-B.row0;
+
+        if( A.col1-A.col0 != N )
+            throw new IllegalArgumentException("A does not have the expected number of columns based on B's height");
+        if( A.row1-A.row0 != N )
+            throw new IllegalArgumentException("A does not have the expected number of rows based on B's height");
+
+        for( int i = B.row0; i < B.row1; i += blockLength ) {
+
+
+            int heightB_i = Math.min(blockLength,B.row1-i);
+            int indexB_i = i*B.original.numCols + heightB_i*B.col0;
+
+            int rowA = i-B.row0+A.row0;
+            int heightA = Math.min( blockLength , A.row1 - rowA);
+
+            for( int j = B.row0; j <= i; j += blockLength ) {
+                
+                int widthB_j = Math.min(blockLength,B.row1-j);
+
+                int indexA = rowA * A.original.numCols + (j-B.row0+A.col0)*heightA;
+                int indexB_j = j*B.original.numCols + widthB_j*B.col0;
+
+                if( i == j ) {
+                    multTransBBlockMinus_L( B.original.data,A.original.data,
+                            indexB_i,indexB_j,indexA,widthB,heightB_i,widthB_j);
+                } else {
+                    multTransBBlockMinus( B.original.data,A.original.data,
+                            indexB_i,indexB_j,indexA,widthB,heightB_i,widthB_j);
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs the following operation on a block:<br>
+     * <br>
+     * c = c - a<sup>T</sup>a<br>
+     * </p>
+     */
+    protected static void multTransABlockMinus( double[] dataA, double []dataC,
+                                                int indexA, int indexB, int indexC,
+                                                final int heightA, final int widthA, final int widthC ) {
+//        for( int i = 0; i < widthA; i++ ) {
+//            for( int k = 0; k < heightA; k++ ) {
+//
+//                double valA = dataA[k*widthA + i + indexA];
+//                for( int j = 0; j < widthC; j++ ) {
+//                    dataC[ i*widthC + j + indexC ] -= valA * dataA[k*widthC + j + indexB];
+//                }
+//            }
+//        }
+
+        int rowB = indexB;
+        int endLoopK = rowB + heightA*widthC;
+        int startA = indexA;
+
+        //for( int k = 0; k < heightA; k++ ) {
+        for( ; rowB != endLoopK; rowB += widthC , startA += widthA ) {
+            int a = startA;
+            int c = indexC;
+
+            int endA = a + widthA;
+            int endB = rowB + widthC;
+
+            while( a != endA ) {
+                double valA = dataA[a++];
+
+                int b = rowB;
+                while( b != endB ) {
+                    dataC[ c++ ] -= valA * dataA[b++];
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs the following operation on the upper triangular portion of a block:<br>
+     * <br>
+     * c = c - a<sup>T</sup>a<br>
+     * </p>
+     */
+    protected static void multTransABlockMinus_U( double[] dataA, double []dataC,
+                                                  int indexA, int indexB, int indexC,
+                                                  final int heightA, final int widthA, final int widthC ) {
+//        for( int i = 0; i < widthA; i++ ) {
+//            for( int k = 0; k < heightA; k++ ) {
+//
+//                double valA = dataA[k*widthA + i + indexA];
+//                for( int j = i; j < widthC; j++ ) {
+//                    dataC[ i*widthC + j + indexC ] -= valA * dataA[k*widthC + j + indexB];
+//                }
+//            }
+//        }
+
+        for( int i = 0; i < widthA; i++ ) {
+            for( int k = 0; k < heightA; k++ ) {
+
+                double valA = dataA[k*widthA + i + indexA];
+                int b = k*widthC + indexB + i;
+                int c = i*widthC + indexC + i;
+
+                int endC = (c-i)+widthC;
+
+                while( c != endC ) {
+//                for( int j = i; j < widthC; j++ ) {
+                    dataC[ c++ ] -= valA * dataA[b++];
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs the following operation on a block:<br>
+     * <br>
+     * c = c - a*a<sup>T</sup><br>
+     * </p>
+     */
+    protected static void multTransBBlockMinus( final double[] dataA, final double []dataC,
+                                                final int indexA, final int indexB, final int indexC,
+                                                final int widthA, final int heightA, final int widthC ) {
+//        for( int i = 0; i < heightA; i++ ) {
+//            for( int j = 0; j < widthC; j++ ) {
+//                double sum = 0;
+//                for( int k = 0; k < widthA; k++ ) {
+//                    sum += dataA[i*widthA + k + indexA] * dataA[j*widthA + k + indexB];
+//                }
+//                dataC[ i*widthC + j + indexC ] -= sum;
+//            }
+//        }
+        
+        int rowA = indexA;
+        int c = indexC;
+        for( int i = 0; i < heightA; i++ , rowA += widthA ) {
+            final int endA = rowA + widthA;
+            int rowB = indexB;
+            final int endLoopJ = c + widthC;
+
+            // for( int j = 0; j < widthC; j++  ) {
+            while( c != endLoopJ ) {
+                int a = rowA;
+                int b = rowB;
+
+                double sum = 0;
+                while( a != endA ) {
+                    sum += dataA[a++] * dataA[b++];
+                }
+                dataC[ c++ ] -= sum;
+                rowB += widthA;
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs the following operation on the lower triangular portion of a block:<br>
+     * <br>
+     * c = c - a*a<sup>T</sup><br>
+     * </p>
+     */
+    protected static void multTransBBlockMinus_L( double[] dataA, double []dataC,
+                                                  int indexA, int indexB, int indexC,
+                                                  final int widthA, final int heightA, final int widthC ) {
+//        for( int i = 0; i < heightA; i++ ) {
+//            for( int j = 0; j <= i; j++ ) {
+//                double sum = 0;
+//                for( int k = 0; k < widthA; k++ ) {
+//                    sum += dataA[i*widthA + k + indexA] * dataA[j*widthA + k + indexB];
+//                }
+//                dataC[ i*widthC + j + indexC ] -= sum;
+//            }
+//        }
+
+        for( int i = 0; i < heightA; i++ ) {
+            int rowA = i*widthA+indexA;
+            int endA = rowA + widthA;
+            int rowB = indexB;
+            int rowC = i*widthC + indexC;
+            for( int j = 0; j <= i; j++ , rowB += widthA) {
+                double sum = 0;
+
+                int a = rowA;
+                int b = rowB;
+
+                while( a != endA ) {
+                    sum += dataA[a++] * dataA[b++];
+                }
+                dataC[ rowC + j ] -= sum;
+            }
+        }
+
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/block/BlockInnerTriangularSolver.java b/main/dense64/src/org/ejml/alg/block/BlockInnerTriangularSolver.java
new file mode 100644
index 0000000..aa3c011
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/block/BlockInnerTriangularSolver.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block;
+
+/**
+ * <p>
+ * Contains triangular solvers for inner blocks of a {@link org.ejml.data.BlockMatrix64F}.
+ * </p>
+ *
+ * <p>
+ * Algorithm for lower triangular inverse:<br>
+ *
+ * <pre>
+ * for i=1:m
+ *     for j=1:i-1
+ *         val = 0
+ *         for k=j:i-1
+ *             val = val - L(i,k) * X(k,j)
+ *         end
+ *         x(i,j) = val / L(i,i)
+ *     end
+ *     x(i,i) = 1 / L(i,i)
+ * end
+ * </pre> 
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class BlockInnerTriangularSolver {
+
+    /**
+     * <p>
+     * Inverts a square lower triangular matrix:  L = L<sup>-1</sup>
+     * </p>
+     *
+     * @param L Lower triangular matrix being inverted. Not modified.
+     * @oaran K_inv Where the inverse is stored.  Can be the same as L.  Modified.
+     * @param m The number of rows and columns.
+     * @param offsetL which index does the L matrix start at.
+     * @param offsetL_inv which index does the L_inv matrix start at.
+     *
+     */
+    public static void invertLower( double L[] ,
+                                    double L_inv[] ,
+                                    int m ,
+                                    int offsetL ,
+                                    int offsetL_inv )
+    {
+        for( int i = 0; i < m; i++ ) {
+            double L_ii = L[ offsetL + i*m + i ];
+            for( int j = 0; j < i; j++ ) {
+                double val = 0;
+                for( int k = j; k < i; k++ ) {
+                    val += L[offsetL + i*m + k] * L_inv[ offsetL_inv + k*m + j ];
+                }
+                L_inv[ offsetL_inv + i*m + j ] = -val / L_ii;
+            }
+            L_inv[ offsetL_inv + i*m + i ] =  1.0 / L_ii;
+        }
+    }
+
+    /**
+     * <p>
+     * Inverts a square lower triangular matrix:  L = L<sup>-1</sup>
+     * </p>
+     *
+     * @param L Lower triangular matrix being inverted. Over written with inverted matrix.  Modified.
+     * @param m The number of rows and columns.
+     * @param offsetL which index does the L matrix start at.
+     *
+     */
+    public static void invertLower( double L[] ,
+                                    int m ,
+                                    int offsetL )
+    {
+        for( int i = 0; i < m; i++ ) {
+            double L_ii = L[ offsetL + i*m + i ];
+            for( int j = 0; j < i; j++ ) {
+                double val = 0;
+                for( int k = j; k < i; k++ ) {
+                    val += L[offsetL + i*m + k] * L[ offsetL + k*m + j ];
+                }
+                L[ offsetL + i*m + j ] = -val / L_ii;
+            }
+            L[ offsetL + i*m + i ] =  1.0 / L_ii;
+        }
+    }
+
+    /**
+     * <p>
+     * Solves for non-singular lower triangular matrices using forward substitution.
+     * <br>
+     * B = L<sup>-1</sup>B<br>
+     * <br>
+     * where B is a (m by n) matrix, L is a lower triangular (m by m) matrix.
+     * </p>
+     *
+     * @param L An m by m non-singular lower triangular matrix. Not modified.
+     * @param b An m by n matrix. Modified.
+     * @param m size of the L matrix
+     * @param n number of columns in the B matrix.
+     * @param strideL number of elements that need to be added to go to the next row in L
+     * @param offsetL initial index in L where the matrix starts
+     * @param offsetB initial index in B where the matrix starts
+     */
+    public static void solveL( double L[] , double []b ,
+                               int m , int n ,
+                               int strideL , int offsetL , int offsetB )
+    {
+        for( int j = 0; j < n; j++ ) {
+            for( int i = 0; i < m; i++ ) {
+                double sum = b[offsetB + i*n+j];
+                for( int k=0; k<i; k++ ) {
+                    sum -= L[offsetL + i*strideL+k]* b[offsetB + k*n+j];
+                }
+                b[offsetB + i*n+j] = sum / L[offsetL + i*strideL+i];
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Solves for non-singular transposed lower triangular matrices using backwards substitution:
+     * <br>
+     * B = L<sup>-T</sup>B<br>
+     * <br>
+     * where B is a (m by n) matrix, L is a lower triangular (m by m) matrix.
+     * </p>
+     *
+     * @param L An m by m non-singular lower triangular matrix. Not modified.
+     * @param b An m by n matrix. Modified.
+     * @param m size of the L matrix
+     * @param n number of columns in the B matrix.
+     * @param strideL number of elements that need to be added to go to the next row in L
+     * @param offsetL initial index in L where the matrix starts
+     * @param offsetB initial index in B where the matrix starts
+     */
+    public static void solveTransL( double L[] , double []b ,
+                                    int m , int n ,
+                                    int strideL , int offsetL , int offsetB )
+    {
+        for( int j = 0; j < n; j++ ) {
+            for( int i = m-1; i >= 0; i-- ) {
+                double sum = b[offsetB + i*n+j];
+                for( int k=i+1; k<m; k++ ) {
+                    sum -= L[offsetL + k*strideL+i]* b[offsetB + k*n+j];
+                }
+                b[offsetB + i*n+j] = sum / L[offsetL + i*strideL+i];
+            }
+        }
+    }
+
+     /**
+     * <p>
+     * Solves for non-singular lower triangular matrices using forward substitution.
+     * <br>
+     * B<sup>T</sup> = L<sup>-1</sup>B<sup>T</sup><br>
+     * <br>
+     * where B is a (n by m) matrix, L is a lower triangular (m by m) matrix.
+     * </p>
+     *
+     * @param L An m by m non-singular lower triangular matrix. Not modified.
+     * @param b An n by m matrix. Modified.
+     * @param m size of the L matrix
+     * @param n number of columns in the B matrix.
+     * @param offsetL initial index in L where the matrix starts
+     * @param offsetB initial index in B where the matrix starts
+     */
+    public static void solveLTransB( double L[] , double []b ,
+                                     int m , int n ,
+                                     int strideL , int offsetL , int offsetB )
+    {
+//        for( int j = 0; j < n; j++ ) {
+//            for( int i = 0; i < m; i++ ) {
+//                double sum = b[offsetB + j*m+i];
+//                for( int k=0; k<i; k++ ) {
+//                    sum -= L[offsetL + i*m+k]* b[offsetB + j*m+k];
+//                }
+//                b[offsetB + j*m+i] = sum / L[offsetL + i*m+i];
+//            }
+//        }
+        for( int j = 0; j < n; j++ ) {
+            for( int i = 0; i < m; i++ ) {
+                double sum = b[offsetB + j*m+i];
+                int l = offsetL+i*strideL;
+                int bb = offsetB +j*m;
+                int endL = l+i;
+                while( l != endL ) {
+//                for( int k=0; k<i; k++ ) {
+                    sum -= L[l++]* b[bb++];
+                }
+                b[offsetB + j*m+i] = sum / L[offsetL + i*strideL+i];
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Solves for non-singular upper triangular matrices using backwards substitution.
+     * <br>
+     * B = U<sup>-1</sup>B<br>
+     * <br>
+     * where B (m by n) is a matrix, U is a (m by m ) upper triangular matrix.<br>
+     * </p>
+     *
+     * @param U An m by m non-singular upper triangular matrix. Not modified.
+     * @param b An m by n matrix. Modified.
+     * @param m size of the L matrix
+     * @paramUn number of columns in the B matrix.
+     * @param offsetU initial index in L where the matrix starts
+     * @param offsetB initial index in B where the matrix starts
+     */
+    public static void solveU( double U[] , double []b ,
+                               int m , int n ,
+                               int strideU , int offsetU , int offsetB )
+    {
+        for( int j = 0; j < n; j++ ) {
+            for( int i = m-1; i >= 0; i-- ) {
+                double sum = b[offsetB + i*n+j];
+                for( int k=i+1; k<m; k++ ) {
+                    sum -= U[offsetU + i*strideU+k]* b[offsetB + k*n+j];
+                }
+                b[offsetB + i*n+j] = sum / U[offsetU + i*strideU+i];
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Solves for non-singular upper triangular matrices using forward substitution.
+     * <br>
+     * B = U<sup>-T</sup>B<br>
+     * <br>
+     * where B (m by n) is a matrix, U is a (m by m ) upper triangular matrix.<br>
+     * </p>
+     *
+     * @param U An m by m non-singular upper triangular matrix. Not modified.
+     * @param b An m by n matrix. Modified.
+     * @param m size of the L matrix
+     * @paramUn number of columns in the B matrix.
+     * @param offsetU initial index in L where the matrix starts
+     * @param offsetB initial index in B where the matrix starts
+     */
+    public static void solveTransU( double U[] , double []b ,
+                                    int m , int n ,
+                                    int strideU , int offsetU , int offsetB )
+    {
+        for( int j = 0; j < n; j++ ) {
+            for( int i = 0; i < m; i++ ) {
+                double sum = b[offsetB + i*n+j];
+                for( int k=0; k<i; k++ ) {
+                    sum -= U[offsetU + k*strideU+i]* b[offsetB + k*n+j];
+                }
+                b[offsetB + i*n+j] = sum / U[offsetU + i*strideU+i];
+            }
+        }
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/block/BlockMatrixOps.java b/main/dense64/src/org/ejml/alg/block/BlockMatrixOps.java
new file mode 100644
index 0000000..d197ba0
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/block/BlockMatrixOps.java
@@ -0,0 +1,620 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block;
+
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.D1Submatrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.ConvertMatrixType;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * Various operations on {@link BlockMatrix64F}.
+ *
+ * @author Peter Abeles
+ */
+public class BlockMatrixOps {
+
+    /**
+     * Converts a row major matrix into a row major block matrix.
+     *
+     * @param src Original DenseMatrix64F.  Not modified.
+     * @param dst Equivalent BlockMatrix64F. Modified.
+     */
+    public static void convert( DenseMatrix64F src , BlockMatrix64F dst )
+    {
+        ConvertMatrixType.convert(src,dst);
+    }
+
+    /**
+     * <p>
+     * Converts matrix data stored is a row major format into a block row major format in place.
+     * </p>
+     * 
+     * @param numRows number of rows in the matrix.
+     * @param numCols number of columns in the matrix.
+     * @param blockLength Block size in the converted matrix.
+     * @param data Matrix data in a row-major format. Modified.
+     * @param tmp Temporary data structure that is to be the size of a block row.
+     */
+    public static void convertRowToBlock( int numRows , int numCols , int blockLength ,
+                                          double[] data, double[] tmp )
+    {
+        int minLength = Math.min( blockLength , numRows ) * numCols;
+        if( tmp.length < minLength ) {
+            throw new IllegalArgumentException("tmp must be at least "+minLength+" long ");
+        }
+
+        for( int i = 0; i < numRows; i += blockLength ) {
+            int blockHeight = Math.min( blockLength , numRows - i);
+
+            System.arraycopy(data,i*numCols,tmp,0,blockHeight*numCols);
+
+
+            for( int j = 0; j < numCols; j += blockLength ) {
+                int blockWidth = Math.min( blockLength , numCols - j);
+
+                int indexDst = i*numCols + blockHeight*j;
+                int indexSrcRow = j;
+
+                for( int k = 0; k < blockHeight; k++ ) {
+                    System.arraycopy(tmp,indexSrcRow,data,indexDst,blockWidth);
+                    indexDst += blockWidth;
+                    indexSrcRow += numCols;
+                }
+            }
+        }
+    }
+
+    /**
+     * Converts a row major block matrix into a row major matrix.
+     *
+     * @param src Original BlockMatrix64F..  Not modified.
+     * @param dst Equivalent DenseMatrix64F.  Modified.
+     */
+    public static DenseMatrix64F convert( BlockMatrix64F src , DenseMatrix64F dst )
+    {
+        return ConvertMatrixType.convert(src,dst);
+    }
+
+    /**
+     * <p>
+     * Converts matrix data stored is a block row major format into a row major format in place.
+     * </p>
+     *
+     * @param numRows number of rows in the matrix.
+     * @param numCols number of columns in the matrix.
+     * @param blockLength Block size in the converted matrix.
+     * @param data Matrix data in a block row-major format. Modified.
+     * @param tmp Temporary data structure that is to be the size of a block row.
+     */
+    public static void convertBlockToRow( int numRows , int numCols , int blockLength ,
+                                          double[] data, double[] tmp )
+    {
+        int minLength = Math.min( blockLength , numRows ) * numCols;
+        if( tmp.length < minLength ) {
+            throw new IllegalArgumentException("tmp must be at least "+minLength+" long and not "+tmp.length);
+        }
+
+        for( int i = 0; i < numRows; i += blockLength ) {
+            int blockHeight = Math.min( blockLength , numRows - i);
+
+            System.arraycopy(data,i*numCols,tmp,0,blockHeight*numCols);
+
+            for( int j = 0; j < numCols; j += blockLength ) {
+                int blockWidth = Math.min( blockLength , numCols - j);
+
+                int indexSrc = blockHeight*j;
+                int indexDstRow = i*numCols + j;
+
+                for( int k = 0; k < blockHeight; k++ ) {
+                    System.arraycopy(tmp,indexSrc,data,indexDstRow,blockWidth);
+                    indexSrc += blockWidth;
+                    indexDstRow += numCols;
+                }
+            }
+        }
+    }
+
+    /**
+     * Converts the transpose of a row major matrix into a row major block matrix.
+     *
+     * @param src Original DenseMatrix64F.  Not modified.
+     * @param dst Equivalent BlockMatrix64F. Modified.
+     */
+    public static void convertTranSrc( DenseMatrix64F src , BlockMatrix64F dst )
+    {
+        if( src.numRows != dst.numCols || src.numCols != dst.numRows )
+            throw new IllegalArgumentException("Incompatible matrix shapes.");
+
+        for( int i = 0; i < dst.numRows; i += dst.blockLength ) {
+            int blockHeight = Math.min( dst.blockLength , dst.numRows - i);
+
+            for( int j = 0; j < dst.numCols; j += dst.blockLength ) {
+                int blockWidth = Math.min( dst.blockLength , dst.numCols - j);
+
+                int indexDst = i*dst.numCols + blockHeight*j;
+                int indexSrc = j*src.numCols + i;
+
+                for( int l = 0; l < blockWidth; l++ ) {
+                    int rowSrc = indexSrc + l*src.numCols;
+                    int rowDst = indexDst + l;
+                    for( int k = 0; k < blockHeight; k++ , rowDst += blockWidth ) {
+                        dst.data[ rowDst ] = src.data[rowSrc++];
+                    }
+                }
+            }
+        }
+    }
+
+    // This can be speed up by inlining the multBlock* calls, reducing number of multiplications
+    // and other stuff.  doesn't seem to have any speed advantage over mult_reorder()
+    public static void mult( BlockMatrix64F A , BlockMatrix64F B , BlockMatrix64F C )
+    {
+        if( A.numCols != B.numRows )
+            throw new IllegalArgumentException("Columns in A are incompatible with rows in B");
+        if( A.numRows != C.numRows )
+            throw new IllegalArgumentException("Rows in A are incompatible with rows in C");
+        if( B.numCols != C.numCols )
+            throw new IllegalArgumentException("Columns in B are incompatible with columns in C");
+        if( A.blockLength != B.blockLength || A.blockLength != C.blockLength )
+            throw new IllegalArgumentException("Block lengths are not all the same.");
+
+        final int blockLength = A.blockLength;
+
+        D1Submatrix64F Asub = new D1Submatrix64F(A,0, A.numRows, 0, A.numCols);
+        D1Submatrix64F Bsub = new D1Submatrix64F(B,0, B.numRows, 0, B.numCols);
+        D1Submatrix64F Csub = new D1Submatrix64F(C,0, C.numRows, 0, C.numCols);
+
+        BlockMultiplication.mult(blockLength,Asub,Bsub,Csub);
+    }
+
+    public static void multTransA( BlockMatrix64F A , BlockMatrix64F B , BlockMatrix64F C )
+    {
+        if( A.numRows != B.numRows )
+            throw new IllegalArgumentException("Rows in A are incompatible with rows in B");
+        if( A.numCols != C.numRows )
+            throw new IllegalArgumentException("Columns in A are incompatible with rows in C");
+        if( B.numCols != C.numCols )
+            throw new IllegalArgumentException("Columns in B are incompatible with columns in C");
+        if( A.blockLength != B.blockLength || A.blockLength != C.blockLength )
+            throw new IllegalArgumentException("Block lengths are not all the same.");
+
+        final int blockLength = A.blockLength;
+
+        D1Submatrix64F Asub = new D1Submatrix64F(A,0, A.numRows, 0, A.numCols);
+        D1Submatrix64F Bsub = new D1Submatrix64F(B,0, B.numRows, 0, B.numCols);
+        D1Submatrix64F Csub = new D1Submatrix64F(C,0, C.numRows, 0, C.numCols);
+
+        BlockMultiplication.multTransA(blockLength,Asub,Bsub,Csub);
+    }
+
+    public static void multTransB( BlockMatrix64F A , BlockMatrix64F B , BlockMatrix64F C )
+    {
+        if( A.numCols != B.numCols )
+            throw new IllegalArgumentException("Columns in A are incompatible with columns in B");
+        if( A.numRows != C.numRows )
+            throw new IllegalArgumentException("Rows in A are incompatible with rows in C");
+        if( B.numRows != C.numCols )
+            throw new IllegalArgumentException("Rows in B are incompatible with columns in C");
+        if( A.blockLength != B.blockLength || A.blockLength != C.blockLength )
+            throw new IllegalArgumentException("Block lengths are not all the same.");
+
+        final int blockLength = A.blockLength;
+
+        D1Submatrix64F Asub = new D1Submatrix64F(A,0, A.numRows, 0, A.numCols);
+        D1Submatrix64F Bsub = new D1Submatrix64F(B,0, B.numRows, 0, B.numCols);
+        D1Submatrix64F Csub = new D1Submatrix64F(C,0, C.numRows, 0, C.numCols);
+
+        BlockMultiplication.multTransB(blockLength,Asub,Bsub,Csub);
+    }
+
+    /**
+     * Transposes a block matrix.
+     *
+     * @param A Original matrix.  Not modified.
+     * @param A_tran Transposed matrix.  Modified.
+     */
+    public static BlockMatrix64F transpose( BlockMatrix64F A , BlockMatrix64F A_tran )
+    {
+        if( A_tran != null ) {
+            if( A.numRows != A_tran.numCols || A.numCols != A_tran.numRows )
+                throw new IllegalArgumentException("Incompatible dimensions.");
+            if( A.blockLength != A_tran.blockLength )
+                throw new IllegalArgumentException("Incompatible block size.");
+        } else {
+            A_tran = new BlockMatrix64F(A.numCols,A.numRows,A.blockLength);
+
+        }
+
+        for( int i = 0; i < A.numRows; i += A.blockLength ) {
+            int blockHeight = Math.min( A.blockLength , A.numRows - i);
+
+            for( int j = 0; j < A.numCols; j += A.blockLength ) {
+                int blockWidth = Math.min( A.blockLength , A.numCols - j);
+
+                int indexA = i*A.numCols + blockHeight*j;
+                int indexC = j*A_tran.numCols + blockWidth*i;
+
+                transposeBlock( A , A_tran , indexA , indexC , blockWidth , blockHeight );
+            }
+        }
+
+        return A_tran;
+    }
+
+    /**
+     * Transposes an individual block inside a block matrix.
+     */
+    private static void transposeBlock( BlockMatrix64F A , BlockMatrix64F A_tran,
+                                        int indexA , int indexC ,
+                                        int width , int height )
+    {
+        for( int i = 0; i < height; i++ ) {
+            int rowIndexC = indexC + i;
+            int rowIndexA = indexA + width*i;
+            int end = rowIndexA + width;
+            for( ; rowIndexA < end; rowIndexC += height, rowIndexA++ ) {
+                A_tran.data[ rowIndexC ] = A.data[ rowIndexA ];
+            }
+        }
+    }
+
+    public static BlockMatrix64F createRandom( int numRows , int numCols ,
+                                               double min , double max , Random rand )
+    {
+        BlockMatrix64F ret = new BlockMatrix64F(numRows,numCols);
+
+        RandomMatrices.setRandom(ret,min,max,rand);
+
+        return ret;
+    }
+
+    public static BlockMatrix64F createRandom( int numRows , int numCols ,
+                                               double min , double max , Random rand ,
+                                               int blockLength )
+    {
+        BlockMatrix64F ret = new BlockMatrix64F(numRows,numCols,blockLength);
+
+        RandomMatrices.setRandom(ret,min,max,rand);
+
+        return ret;
+    }
+
+
+    public static BlockMatrix64F convert(DenseMatrix64F A , int blockLength ) {
+        BlockMatrix64F ret = new BlockMatrix64F(A.numRows,A.numCols,blockLength);
+        convert(A,ret);
+        return ret;
+    }
+
+    public static BlockMatrix64F convert(DenseMatrix64F A ) {
+        BlockMatrix64F ret = new BlockMatrix64F(A.numRows,A.numCols);
+        convert(A,ret);
+        return ret;
+    }
+
+    public static boolean isEquals( BlockMatrix64F A , BlockMatrix64F B )
+    {
+        if( A.blockLength != B.blockLength )
+            return false;
+
+        return MatrixFeatures.isEquals(A,B);
+    }
+
+    public static boolean isEquals( BlockMatrix64F A , BlockMatrix64F B , double tol )
+    {
+        if( A.blockLength != B.blockLength )
+            return false;
+
+        return MatrixFeatures.isEquals(A,B,tol);
+    }
+
+    /**
+     * Sets either the upper or low triangle of a matrix to zero
+     */
+    public static void zeroTriangle( boolean upper , BlockMatrix64F A )
+    {
+        int blockLength = A.blockLength;
+
+        if( upper ) {
+            for( int i = 0; i < A.numRows; i += blockLength ) {
+                int h = Math.min(blockLength,A.numRows-i);
+
+                for( int j = i; j < A.numCols; j += blockLength ) {
+                    int w = Math.min(blockLength,A.numCols-j);
+
+                    int index = i*A.numCols + h*j;
+
+                    if( j == i ) {
+                        for( int k = 0; k < h; k++ ) {
+                            for( int l = k+1; l < w; l++ ) {
+                                A.data[index + w*k+l ] = 0;
+                            }
+                        }
+                    } else {
+                        for( int k = 0; k < h; k++ ) {
+                            for( int l = 0; l < w; l++ ) {
+                                A.data[index + w*k+l ] = 0;
+                            }
+                        }
+                    }
+                }
+            }
+        } else {
+            for( int i = 0; i < A.numRows; i += blockLength ) {
+                int h = Math.min(blockLength,A.numRows-i);
+
+                for( int j = 0; j <= i; j += blockLength ) {
+                    int w = Math.min(blockLength,A.numCols-j);
+
+                    int index = i*A.numCols + h*j;
+
+                    if( j == i ) {
+                        for( int k = 0; k < h; k++ ) {
+                            int z = Math.min(k,w);
+                            for( int l = 0; l < z; l++ ) {
+                                A.data[index + w*k+l ] = 0;
+                            }
+                        }
+                    } else {
+                        for( int k = 0; k < h; k++ ) {
+                            for( int l = 0; l < w; l++ ) {
+                                A.data[index + w*k+l ] = 0;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Copies either the upper or lower triangular portion of src into dst.  Dst can be smaller
+     * than src.
+     *
+     * @param upper If the upper or lower triangle is copied.
+     * @param src The source matrix. Not modified.
+     * @param dst The destination matrix. Modified.
+     */
+    public static void copyTriangle( boolean upper , BlockMatrix64F src , BlockMatrix64F dst )
+    {
+        if( src.blockLength != dst.blockLength )
+            throw new IllegalArgumentException("Block size is different");
+        if( src.numRows < dst.numRows )
+            throw new IllegalArgumentException("The src has fewer rows than dst");
+        if( src.numCols < dst.numCols )
+            throw new IllegalArgumentException("The src has fewer columns than dst");
+
+        int blockLength = src.blockLength;
+
+        int numRows = Math.min(src.numRows,dst.numRows);
+        int numCols = Math.min(src.numCols,dst.numCols);
+
+        if( upper ) {
+            for( int i = 0; i < numRows; i += blockLength ) {
+                int heightSrc = Math.min(blockLength,src.numRows-i);
+                int heightDst = Math.min(blockLength,dst.numRows-i);
+
+                for( int j = i; j < numCols; j += blockLength ) {
+                    int widthSrc = Math.min(blockLength,src.numCols-j);
+                    int widthDst = Math.min(blockLength,dst.numCols-j);
+
+                    int indexSrc = i*src.numCols + heightSrc*j;
+                    int indexDst = i*dst.numCols + heightDst*j;
+
+                    if( j == i ) {
+                        for( int k = 0; k < heightDst; k++ ) {
+                            for( int l = k; l < widthDst; l++ ) {
+                                dst.data[indexDst + widthDst*k+l ] = src.data[indexSrc + widthSrc*k+l ];
+                            }
+                        }
+                    } else {
+                        for( int k = 0; k < heightDst; k++ ) {
+                            System.arraycopy(src.data, indexSrc + widthSrc * k, dst.data, indexDst + widthDst * k, widthDst);
+                        }
+                    }
+                }
+            }
+        } else {
+            for( int i = 0; i < numRows; i += blockLength ) {
+                int heightSrc = Math.min(blockLength,src.numRows-i);
+                int heightDst = Math.min(blockLength,dst.numRows-i);
+
+                for( int j = 0; j <= i; j += blockLength ) {
+                    int widthSrc = Math.min(blockLength,src.numCols-j);
+                    int widthDst = Math.min(blockLength,dst.numCols-j);
+
+                    int indexSrc = i*src.numCols + heightSrc*j;
+                    int indexDst = i*dst.numCols + heightDst*j;
+
+                    if( j == i ) {
+                        for( int k = 0; k < heightDst; k++ ) {
+                            int z = Math.min(k+1,widthDst);
+                            for( int l = 0; l < z; l++ ) {
+                                dst.data[indexDst + widthDst*k+l ] = src.data[indexSrc + widthSrc*k+l ];
+                            }
+                        }
+                    } else {
+                        for( int k = 0; k < heightDst; k++ ) {
+                            System.arraycopy(src.data, indexSrc + widthSrc * k, dst.data, indexDst + widthDst * k, widthDst);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Sets every element in the matrix to the specified value.<br>
+     * <br>
+     * a<sub>ij</sub> = value
+     * <p>
+     *
+     * @param A A matrix whose elements are about to be set. Modified.
+     * @param value The value each element will have.
+     */
+    public static void set( BlockMatrix64F A , double value ) {
+        CommonOps.fill(A, value);
+    }
+
+    /**
+     * <p>Sets the value of A to all zeros except along the diagonal.</p>
+     *
+     * @param A Block matrix.
+     */
+    public static void setIdentity( BlockMatrix64F A )
+    {
+        int minLength = Math.min(A.numRows,A.numCols);
+
+        CommonOps.fill(A, 0);
+
+        int blockLength = A.blockLength;
+
+        for( int i = 0; i < minLength; i += blockLength ) {
+            int h = Math.min(blockLength,A.numRows-i);
+            int w = Math.min(blockLength,A.numCols-i);
+
+            int index = i*A.numCols + h*i;
+
+            int m = Math.min(h,w);
+            for( int k = 0; k < m; k++ ) {
+                A.data[index + k*w + k ] = 1;
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Returns a new matrix with ones along the diagonal and zeros everywhere else.
+     * </p>
+     *
+     * @param numRows Number of rows.
+     * @param numCols NUmber of columns.
+     * @param blockLength Block length.
+     * @return An identify matrix.
+     */
+    public static BlockMatrix64F identity(int numRows, int numCols, int blockLength ) {
+        BlockMatrix64F A = new BlockMatrix64F(numRows,numCols,blockLength);
+
+        int minLength = Math.min(numRows,numCols);
+
+        for( int i = 0; i < minLength; i += blockLength ) {
+            int h = Math.min(blockLength,A.numRows-i);
+            int w = Math.min(blockLength,A.numCols-i);
+
+            int index = i*A.numCols + h*i;
+
+            int m = Math.min(h,w);
+            for( int k = 0; k < m; k++ ) {
+                A.data[index + k*w + k ] = 1;
+            }
+        }
+
+        return A;
+    }
+
+    /**
+     * <p>
+     * Checks to see if the two matrices have an identical shape an block size.
+     * </p>
+     *
+     * @param A Matrix.
+     * @param B Matrix.
+     */
+    public static void checkIdenticalShape( BlockMatrix64F A , BlockMatrix64F B ) {
+        if( A.blockLength != B.blockLength )
+            throw new IllegalArgumentException("Block size is different");
+        if( A.numRows != B.numRows )
+            throw new IllegalArgumentException("Number of rows is different");
+        if( A.numCols != B.numCols )
+            throw new IllegalArgumentException("NUmber of columns is different");
+    }
+
+    /**
+     * <p>
+     * Extracts a matrix from src into dst.  The submatrix which is copied has its initial coordinate
+     * at (0,0) and ends at (dst.numRows,dst.numCols). The end rows/columns must be aligned along blocks
+     * or else it will silently screw things up.
+     * </p>
+     *
+     * @param src Matrix which a submatrix is being extracted from. Not modified.
+     * @param dst Where the submatrix is written to.  Its rows and columns be less than or equal to 'src'.  Modified.
+     */
+    public static void extractAligned(BlockMatrix64F src, BlockMatrix64F dst) {
+        if( src.blockLength != dst.blockLength )
+            throw new IllegalArgumentException("Block size is different");
+        if( src.numRows < dst.numRows )
+            throw new IllegalArgumentException("The src has fewer rows than dst");
+        if( src.numCols < dst.numCols )
+            throw new IllegalArgumentException("The src has fewer columns than dst");
+
+        int blockLength = src.blockLength;
+
+        int numRows = Math.min(src.numRows,dst.numRows);
+        int numCols = Math.min(src.numCols,dst.numCols);
+
+        for( int i = 0; i < numRows; i += blockLength ) {
+            int heightSrc = Math.min(blockLength,src.numRows-i);
+            int heightDst = Math.min(blockLength,dst.numRows-i);
+
+            for( int j = 0; j < numCols; j += blockLength ) {
+                int widthSrc = Math.min(blockLength,src.numCols-j);
+                int widthDst = Math.min(blockLength,dst.numCols-j);
+
+                int indexSrc = i*src.numCols + heightSrc*j;
+                int indexDst = i*dst.numCols + heightDst*j;
+
+                for( int k = 0; k < heightDst; k++ ) {
+                    System.arraycopy(src.data, indexSrc + widthSrc * k, dst.data, indexDst + widthDst * k, widthDst);
+                }
+            }
+        }
+    }
+
+    /**
+     * Checks to see if the submatrix has its boundaries along inner blocks.
+     *
+     * @param blockLength Size of an inner block.
+     * @param A Submatrix.
+     * @return If it is block aligned or not.
+     */
+    public static boolean blockAligned( int blockLength , D1Submatrix64F A ) {
+        if( A.col0 % blockLength != 0 )
+            return false;
+        if( A.row0 % blockLength != 0 )
+            return false;
+
+        if( A.col1 % blockLength != 0 && A.col1 != A.original.numCols ) {
+            return false;
+        }
+
+        if( A.row1 % blockLength != 0 && A.row1 != A.original.numRows) {
+            return false;
+        }
+
+        return true;
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/block/BlockMultiplication.java b/main/dense64/src/org/ejml/alg/block/BlockMultiplication.java
new file mode 100644
index 0000000..da95df1
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/block/BlockMultiplication.java
@@ -0,0 +1,337 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block;
+
+import org.ejml.data.D1Submatrix64F;
+
+import static org.ejml.alg.block.BlockInnerMultiplication.*;
+
+/**
+ * <p>
+ * Matrix multiplication for {@link org.ejml.data.BlockMatrix64F}.  All sub-matrices must be
+ * block aligned.
+ * </p>
+ * 
+ * @author Peter Abeles
+ */
+public class BlockMultiplication {
+
+    /**
+     * <p>
+     * Performs a matrix multiplication on {@link org.ejml.data.BlockMatrix64F} submatrices.<br>
+     * <br>
+     * c = a * b <br>
+     * <br>
+     * </p>
+     *
+     * <p>
+     * It is assumed that all submatrices start at the beginning of a block and end at the end of a block.
+     * </p>
+     *
+     * @param blockLength Size of the blocks in the submatrix.
+     * @param A A submatrix.  Not modified.
+     * @param B A submatrix.  Not modified.
+     * @param C Result of the operation.  Modified,
+     */
+    public static void mult( int blockLength ,
+                             D1Submatrix64F A , D1Submatrix64F B ,
+                             D1Submatrix64F C )
+    {
+        for( int i = A.row0; i < A.row1; i += blockLength ) {
+            int heightA = Math.min( blockLength , A.row1 - i );
+
+            for( int j = B.col0; j < B.col1; j += blockLength ) {
+                int widthB = Math.min( blockLength , B.col1 - j );
+
+                int indexC = (i-A.row0+C.row0)*C.original.numCols + (j-B.col0+C.col0)*heightA;
+
+                for( int k = A.col0; k < A.col1; k += blockLength ) {
+                    int widthA = Math.min( blockLength , A.col1 - k );
+
+                    int indexA = i*A.original.numCols + k*heightA;
+                    int indexB = (k-A.col0+B.row0)*B.original.numCols + j*widthA;
+
+                    if( k == A.col0 )
+                        blockMultSet(A.original.data,B.original.data,C.original.data,
+                                indexA,indexB,indexC,heightA,widthA,widthB);
+                    else
+                        blockMultPlus(A.original.data,B.original.data,C.original.data,
+                                indexA,indexB,indexC,heightA,widthA,widthB);
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs a matrix multiplication on {@link org.ejml.data.BlockMatrix64F} submatrices.<br>
+     * <br>
+     * c = c + a * b <br>
+     * <br>
+     * </p>
+     *
+     * <p>
+     * It is assumed that all submatrices start at the beginning of a block and end at the end of a block.
+     * </p>
+     *
+     * @param blockLength Size of the blocks in the submatrix.
+     * @param A A submatrix.  Not modified.
+     * @param B A submatrix.  Not modified.
+     * @param C Result of the operation.  Modified,
+     */
+    public static void multPlus( int blockLength ,
+                                D1Submatrix64F A , D1Submatrix64F B ,
+                                D1Submatrix64F C )
+    {
+//        checkInput( blockLength,A,B,C);
+
+        for( int i = A.row0; i < A.row1; i += blockLength ) {
+            int heightA = Math.min( blockLength , A.row1 - i );
+
+            for( int j = B.col0; j < B.col1; j += blockLength ) {
+                int widthB = Math.min( blockLength , B.col1 - j );
+
+                int indexC = (i-A.row0+C.row0)*C.original.numCols + (j-B.col0+C.col0)*heightA;
+
+                for( int k = A.col0; k < A.col1; k += blockLength ) {
+                    int widthA = Math.min( blockLength , A.col1 - k );
+
+                    int indexA = i*A.original.numCols + k*heightA;
+                    int indexB = (k-A.col0+B.row0)*B.original.numCols + j*widthA;
+
+                    blockMultPlus(A.original.data,B.original.data,C.original.data,
+                            indexA,indexB,indexC,heightA,widthA,widthB);
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs a matrix multiplication on {@link org.ejml.data.BlockMatrix64F} submatrices.<br>
+     * <br>
+     * c = c - a * b <br>
+     * <br>
+     * </p>
+     *
+     * <p>
+     * It is assumed that all submatrices start at the beginning of a block and end at the end of a block.
+     * </p>
+     *
+     * @param blockLength Size of the blocks in the submatrix.
+     * @param A A submatrix.  Not modified.
+     * @param B A submatrix.  Not modified.
+     * @param C Result of the operation.  Modified,
+     */
+    public static void multMinus( int blockLength ,
+                                  D1Submatrix64F A , D1Submatrix64F B ,
+                                  D1Submatrix64F C )
+    {
+        checkInput( blockLength,A,B,C);
+
+        for( int i = A.row0; i < A.row1; i += blockLength ) {
+            int heightA = Math.min( blockLength , A.row1 - i );
+
+            for( int j = B.col0; j < B.col1; j += blockLength ) {
+                int widthB = Math.min( blockLength , B.col1 - j );
+
+                int indexC = (i-A.row0+C.row0)*C.original.numCols + (j-B.col0+C.col0)*heightA;
+
+                for( int k = A.col0; k < A.col1; k += blockLength ) {
+                    int widthA = Math.min( blockLength , A.col1 - k );
+
+                    int indexA = i*A.original.numCols + k*heightA;
+                    int indexB = (k-A.col0+B.row0)*B.original.numCols + j*widthA;
+
+                    blockMultMinus(A.original.data,B.original.data,C.original.data,
+                            indexA,indexB,indexC,heightA,widthA,widthB);
+                }
+            }
+        }
+    }
+
+    private static void checkInput( int blockLength ,
+                                    D1Submatrix64F A , D1Submatrix64F B ,
+                                    D1Submatrix64F C )
+    {
+        int Arow = A.getRows();int Acol = A.getCols();
+        int Brow = B.getRows();int Bcol = B.getCols();
+        int Crow = C.getRows();int Ccol = C.getCols();
+
+        if( Arow != Crow )
+            throw new RuntimeException("Mismatch A and C rows");
+        if( Bcol != Ccol )
+            throw new RuntimeException("Mismatch B and C columns");
+        if( Acol != Brow )
+            throw new RuntimeException("Mismatch A columns and B rows");
+
+        if( !BlockMatrixOps.blockAligned(blockLength,A))
+            throw new RuntimeException("Sub-Matrix A is not block aligned");
+
+        if( !BlockMatrixOps.blockAligned(blockLength,B))
+            throw new RuntimeException("Sub-Matrix B is not block aligned");
+
+        if( !BlockMatrixOps.blockAligned(blockLength,C))
+            throw new RuntimeException("Sub-Matrix C is not block aligned");
+    }
+
+    /**
+     * <p>
+     * Performs a matrix multiplication with a transpose on {@link org.ejml.data.BlockMatrix64F} submatrices.<br>
+     * <br>
+     * c = a<sup>T</sup> * b <br>
+     * <br>
+     * </p>
+     *
+     * <p>
+     * It is assumed that all submatrices start at the beginning of a block and end at the end of a block.
+     * </p>
+     *
+     * @param blockLength Size of the blocks in the submatrix.
+     * @param A A submatrix.  Not modified.
+     * @param B A submatrix.  Not modified.
+     * @param C Result of the operation.  Modified,
+     */
+    public static void multTransA( int blockLength ,
+                                   D1Submatrix64F A , D1Submatrix64F B ,
+                                   D1Submatrix64F C )
+    {
+        for( int i = A.col0; i < A.col1; i += blockLength ) {
+            int widthA = Math.min( blockLength , A.col1 - i );
+
+            for( int j = B.col0; j < B.col1; j += blockLength ) {
+                int widthB = Math.min( blockLength , B.col1 - j );
+
+                int indexC = (i-A.col0+C.row0)*C.original.numCols + (j-B.col0+C.col0)*widthA;
+
+                for( int k = A.row0; k < A.row1; k += blockLength ) {
+                    int heightA = Math.min( blockLength , A.row1 - k );
+
+                    int indexA = k*A.original.numCols + i*heightA;
+                    int indexB = (k-A.row0+B.row0)*B.original.numCols + j*heightA;
+
+                    if( k == A.row0 )
+                        blockMultSetTransA(A.original.data,B.original.data,C.original.data,
+                                indexA,indexB,indexC,heightA,widthA,widthB);
+                    else
+                        blockMultPlusTransA(A.original.data,B.original.data,C.original.data,
+                                indexA,indexB,indexC,heightA,widthA,widthB);
+                }
+            }
+        }
+    }
+
+    public static void multPlusTransA( int blockLength ,
+                                       D1Submatrix64F A , D1Submatrix64F B ,
+                                       D1Submatrix64F C )
+    {
+        for( int i = A.col0; i < A.col1; i += blockLength ) {
+            int widthA = Math.min( blockLength , A.col1 - i );
+
+            for( int j = B.col0; j < B.col1; j += blockLength ) {
+                int widthB = Math.min( blockLength , B.col1 - j );
+
+                int indexC = (i-A.col0+C.row0)*C.original.numCols + (j-B.col0+C.col0)*widthA;
+
+                for( int k = A.row0; k < A.row1; k += blockLength ) {
+                    int heightA = Math.min( blockLength , A.row1 - k );
+
+                    int indexA = k*A.original.numCols + i*heightA;
+                    int indexB = (k-A.row0+B.row0)*B.original.numCols + j*heightA;
+
+                    blockMultPlusTransA(A.original.data,B.original.data,C.original.data,
+                            indexA,indexB,indexC,heightA,widthA,widthB);
+                }
+            }
+        }
+    }
+
+    public static void multMinusTransA( int blockLength ,
+                                        D1Submatrix64F A , D1Submatrix64F B ,
+                                        D1Submatrix64F C )
+    {
+        for( int i = A.col0; i < A.col1; i += blockLength ) {
+            int widthA = Math.min( blockLength , A.col1 - i );
+
+            for( int j = B.col0; j < B.col1; j += blockLength ) {
+                int widthB = Math.min( blockLength , B.col1 - j );
+
+                int indexC = (i-A.col0+C.row0)*C.original.numCols + (j-B.col0+C.col0)*widthA;
+
+                for( int k = A.row0; k < A.row1; k += blockLength ) {
+                    int heightA = Math.min( blockLength , A.row1 - k );
+
+                    int indexA = k*A.original.numCols + i*heightA;
+                    int indexB = (k-A.row0+B.row0)*B.original.numCols + j*heightA;
+
+                    blockMultMinusTransA(A.original.data,B.original.data,C.original.data,
+                            indexA,indexB,indexC,heightA,widthA,widthB);
+
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs a matrix multiplication with a transpose on {@link org.ejml.data.BlockMatrix64F} submatrices.<br>
+     * <br>
+     * c = a * b <sup>T</sup> <br>
+     * <br>
+     * </p>
+     *
+     * <p>
+     * It is assumed that all submatrices start at the beginning of a block and end at the end of a block.
+     * </p>
+     *
+     * @param blockLength Length of the blocks in the submatrix.
+     * @param A A submatrix.  Not modified.
+     * @param B A submatrix.  Not modified.
+     * @param C Result of the operation.  Modified,
+     */
+    public static void multTransB( int blockLength ,
+                                   D1Submatrix64F A , D1Submatrix64F B ,
+                                   D1Submatrix64F C )
+    {
+        for( int i = A.row0; i < A.row1; i += blockLength ) {
+            int heightA = Math.min( blockLength , A.row1 - i );
+
+            for( int j = B.row0; j < B.row1; j += blockLength ) {
+                int widthC = Math.min( blockLength , B.row1 - j );
+
+                int indexC = (i-A.row0+C.row0)*C.original.numCols + (j-B.row0+C.col0)*heightA;
+
+                for( int k = A.col0; k < A.col1; k += blockLength ) {
+                    int widthA = Math.min( blockLength , A.col1 - k );
+
+                    int indexA = i*A.original.numCols + k*heightA;
+                    int indexB = j*B.original.numCols + (k-A.col0+B.col0)*widthC;
+
+                    if( k == A.col0 )
+                        blockMultSetTransB(A.original.data,B.original.data,C.original.data,
+                                indexA,indexB,indexC,heightA,widthA,widthC);
+                    else
+                        blockMultPlusTransB(A.original.data,B.original.data,C.original.data,
+                                indexA,indexB,indexC,heightA,widthA,widthC);
+                }
+            }
+        }
+    }
+
+}
diff --git a/main/dense64/src/org/ejml/alg/block/BlockTriangularSolver.java b/main/dense64/src/org/ejml/alg/block/BlockTriangularSolver.java
new file mode 100644
index 0000000..8eaa8b9
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/block/BlockTriangularSolver.java
@@ -0,0 +1,516 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block;
+
+import org.ejml.data.D1Submatrix64F;
+
+import static org.ejml.alg.block.BlockInnerMultiplication.blockMultMinus;
+
+
+/**
+ * <p>
+ * Contains triangular solvers for {@link org.ejml.data.BlockMatrix64F} block aligned sub-matrices.
+ * </p>
+ *
+ * <p>
+ * For a more detailed description of a similar algorithm see:
+ * Page 30 in "Fundamentals of Matrix Computations" 2nd Ed. by David S. Watkins
+ * or any description of a block triangular solver in any other computational linear algebra book.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class BlockTriangularSolver {
+
+    /**
+     * Inverts an upper or lower triangular block submatrix.
+     *
+     * @param blockLength
+     * @param upper Is it upper or lower triangular.
+     * @param T Triangular matrix that is to be inverted.  Must be block aligned.  Not Modified.
+     * @param T_inv Where the inverse is stored.  This can be the same as T.  Modified.
+     * @param temp Work space variable that is size blockLength*blockLength.  
+     */
+    public static void invert( final int blockLength ,
+                               final boolean upper ,
+                               final D1Submatrix64F T ,
+                               final D1Submatrix64F T_inv ,
+                               final double temp[] )
+    {
+        if( upper )
+            throw new IllegalArgumentException("Upper triangular matrices not supported yet");
+
+        if( temp.length < blockLength*blockLength )
+            throw new IllegalArgumentException("Temp must be at least blockLength*blockLength long.");
+
+        if( T.row0 != T_inv.row0 || T.row1 != T_inv.row1 || T.col0 != T_inv.col0 || T.col1 != T_inv.col1)
+            throw new IllegalArgumentException("T and T_inv must be at the same elements in the matrix");
+
+        final int M = T.row1-T.row0;
+
+        final double dataT[] = T.original.data;
+        final double dataX[] = T_inv.original.data;
+
+        final int offsetT = T.row0*T.original.numCols+M*T.col0;
+
+        for( int i = 0; i < M; i += blockLength ) {
+            int heightT = Math.min(T.row1-(i+T.row0),blockLength);
+
+            int indexII = offsetT + T.original.numCols*(i+T.row0) + heightT*(i+T.col0);
+
+            for( int j = 0; j < i; j += blockLength ) {
+                int widthX = Math.min(T.col1-(j+T.col0),blockLength);
+
+                for( int w = 0; w < temp.length; w++ ) {
+                    temp[w] = 0;
+                }
+
+                for( int k = j; k < i; k += blockLength ) {
+                    int widthT = Math.min(T.col1-(k+T.col0),blockLength);
+
+                    int indexL = offsetT + T.original.numCols*(i+T.row0) + heightT*(k+T.col0);
+                    int indexX = offsetT + T.original.numCols*(k+T.row0) + widthT*(j+T.col0);
+
+                    blockMultMinus(dataT,dataX,temp,indexL,indexX,0,heightT,widthT,widthX);
+                }
+
+                int indexX = offsetT + T.original.numCols*(i+T.row0) + heightT*(j+T.col0);
+
+                BlockInnerTriangularSolver.solveL(dataT,temp,heightT,widthX,heightT,indexII,0);
+                System.arraycopy(temp,0,dataX,indexX,widthX*heightT);
+            }
+            BlockInnerTriangularSolver.invertLower(dataT,dataX,heightT,indexII,indexII);
+        }
+    }
+
+    /**
+     * Inverts an upper or lower triangular block submatrix.
+     *
+     * @param blockLength
+     * @param upper Is it upper or lower triangular.
+     * @param T Triangular matrix that is to be inverted.  Overwritten with solution.  Modified.
+     * @param temp Work space variable that is size blockLength*blockLength.
+     */
+    public static void invert( final int blockLength ,
+                               final boolean upper ,
+                               final D1Submatrix64F T ,
+                               final double temp[] )
+    {
+        if( upper )
+            throw new IllegalArgumentException("Upper triangular matrices not supported yet");
+
+        if( temp.length < blockLength*blockLength )
+            throw new IllegalArgumentException("Temp must be at least blockLength*blockLength long.");
+
+        final int M = T.row1-T.row0;
+
+        final double dataT[] = T.original.data;
+        final int offsetT = T.row0*T.original.numCols+M*T.col0;
+
+        for( int i = 0; i < M; i += blockLength ) {
+            int heightT = Math.min(T.row1-(i+T.row0),blockLength);
+
+            int indexII = offsetT + T.original.numCols*(i+T.row0) + heightT*(i+T.col0);
+
+            for( int j = 0; j < i; j += blockLength ) {
+                int widthX = Math.min(T.col1-(j+T.col0),blockLength);
+
+                for( int w = 0; w < temp.length; w++ ) {
+                    temp[w] = 0;
+                }
+
+                for( int k = j; k < i; k += blockLength ) {
+                    int widthT = Math.min(T.col1-(k+T.col0),blockLength);
+
+                    int indexL = offsetT + T.original.numCols*(i+T.row0) + heightT*(k+T.col0);
+                    int indexX = offsetT + T.original.numCols*(k+T.row0) + widthT*(j+T.col0);
+
+                    blockMultMinus(dataT,dataT,temp,indexL,indexX,0,heightT,widthT,widthX);
+                }
+
+                int indexX = offsetT + T.original.numCols*(i+T.row0) + heightT*(j+T.col0);
+
+                BlockInnerTriangularSolver.solveL(dataT,temp,heightT,widthX,heightT,indexII,0);
+                System.arraycopy(temp,0,dataT,indexX,widthX*heightT);
+            }
+            BlockInnerTriangularSolver.invertLower(dataT,heightT,indexII);
+        }
+    }
+
+
+    /**
+     * <p>
+     * Performs an in-place solve operation on the provided block aligned sub-matrices.<br>
+     * <br>
+     * B = T<sup>-1</sup> B<br>
+     * <br>
+     * where T is a triangular matrix. T or B can be transposed.  T is a square matrix of arbitrary
+     * size and B has the same number of rows as T and an arbitrary number of columns.
+     * </p>
+     *
+     * @param blockLength Size of the inner blocks.
+     * @param upper If T is upper or lower triangular.
+     * @param T An upper or lower triangular matrix. Not modified.
+     * @param B A matrix whose height is the same as T's width. Solution is written here. Modified.
+     */
+    public static void solve( final int blockLength ,
+                              final boolean upper ,
+                              final D1Submatrix64F T ,
+                              final D1Submatrix64F B ,
+                              final boolean transT ) {
+
+        if( upper ) {
+            solveR(blockLength,T,B,transT);
+        } else {
+            solveL(blockLength,T,B,transT);
+        }
+    }
+
+
+    /**
+     * <p>
+     * Performs an in-place solve operation where T is contained in a single block.<br>
+     * <br>
+     * B = T<sup>-1</sup> B<br>
+     * <br>
+     * where T is a triangular matrix contained in an inner block. T or B can be transposed.  T must be a single complete inner block
+     * and B is either a column block vector or row block vector.
+     * </p>
+     *
+     * @param blockLength Size of the inner blocks in the block matrix.
+     * @param upper If T is upper or lower triangular.
+     * @param T An upper or lower triangular matrix that is contained in an inner block. Not modified.
+     * @param B A block aligned row or column submatrix. Modified.
+     * @param transT If T is transposed or not.
+     * @param transB If B is transposed or not.
+     */
+    public static void solveBlock( final int blockLength ,
+                                   final boolean upper , final D1Submatrix64F T ,
+                                   final D1Submatrix64F B ,
+                                   final boolean transT ,final boolean transB )
+    {
+        int Trows = T.row1-T.row0;
+        if( Trows > blockLength )
+            throw new IllegalArgumentException("T can be at most the size of a block");
+        // number of rows in a block.  The submatrix can be smaller than a block
+        final int blockT_rows = Math.min(blockLength,T.original.numRows-T.row0);
+        final int blockT_cols = Math.min(blockLength,T.original.numCols-T.col0);
+
+        int offsetT = T.row0*T.original.numCols+blockT_rows*T.col0;
+
+        final double dataT[] = T.original.data;
+        final double dataB[] = B.original.data;
+
+        if( transB ) {
+            if( upper ) {
+                if ( transT ) {
+                    throw new IllegalArgumentException("Operation not yet supported");
+                } else {
+                    throw new IllegalArgumentException("Operation not yet supported");
+                }
+            } else {
+                if ( transT ) {
+                    throw new IllegalArgumentException("Operation not yet supported");
+                } else {
+                    for( int i = B.row0; i < B.row1; i += blockLength ) {
+                        int N = Math.min(B.row1 , i + blockLength ) - i;
+
+                        int offsetB = i*B.original.numCols + N*B.col0;
+
+                        BlockInnerTriangularSolver.solveLTransB(dataT,dataB,blockT_rows,N,blockT_rows,offsetT,offsetB);
+                    }
+                }
+            }
+        } else {
+            if( Trows != B.row1-B.row0 )
+                throw new IllegalArgumentException("T and B must have the same number of rows.");
+
+            if( upper ) {
+                if ( transT ) {
+                    for( int i = B.col0; i < B.col1; i += blockLength ) {
+                        int offsetB = B.row0*B.original.numCols + Trows*i;
+
+                        int N = Math.min(B.col1 , i + blockLength ) - i;
+                        BlockInnerTriangularSolver.solveTransU(dataT,dataB,Trows,N,Trows,offsetT,offsetB);
+                    }
+                } else {
+                    for( int i = B.col0; i < B.col1; i += blockLength ) {
+                        int offsetB = B.row0*B.original.numCols + Trows*i;
+
+                        int N = Math.min(B.col1 , i + blockLength ) - i;
+                        BlockInnerTriangularSolver.solveU(dataT,dataB,Trows,N,Trows,offsetT,offsetB);
+                    }
+                }
+            } else {
+                if ( transT ) {
+                    for( int i = B.col0; i < B.col1; i += blockLength ) {
+                        int offsetB = B.row0*B.original.numCols + Trows*i;
+
+                        int N = Math.min(B.col1 , i + blockLength ) - i;
+                        BlockInnerTriangularSolver.solveTransL(dataT,dataB,Trows,N,blockT_cols,offsetT,offsetB);
+                    }
+                } else {
+                    for( int i = B.col0; i < B.col1; i += blockLength ) {
+                        int offsetB = B.row0*B.original.numCols + Trows*i;
+
+                        int N = Math.min(B.col1 , i + blockLength ) - i;
+                        BlockInnerTriangularSolver.solveL(dataT,dataB,Trows,N,blockT_cols,offsetT,offsetB);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Solves lower triangular systems:<br>
+     * <br>
+     * B = L<sup>-1</sup> B<br>
+     * <br>
+     * </p>
+     *
+     * <p> Reverse or forward substitution is used depending upon L being transposed or not. </p>
+     *
+     * @param blockLength
+     * @param L Lower triangular with dimensions m by m.  Not modified.
+     * @param B A matrix with dimensions m by n.  Solution is written into here. Modified.
+     * @param transL Is the triangular matrix transposed?
+     */
+    public static void solveL( final int blockLength ,
+                               final D1Submatrix64F L,
+                               final D1Submatrix64F B ,
+                               boolean transL ) {
+
+        D1Submatrix64F Y = new D1Submatrix64F(B.original);
+
+        D1Submatrix64F Linner = new D1Submatrix64F(L.original);
+        D1Submatrix64F Binner = new D1Submatrix64F(B.original);
+
+        int lengthL = B.row1 - B.row0;
+
+        int startI,stepI;
+
+        if( transL ) {
+            startI = lengthL - lengthL % blockLength;
+            if( startI == lengthL && lengthL >= blockLength )
+                startI -= blockLength;
+
+            stepI = -blockLength;
+        } else {
+            startI = 0;
+            stepI = blockLength;
+        }
+
+        for( int i = startI; ; i += stepI ) {
+            if( transL ) {
+                if( i < 0 ) break;
+            } else {
+                if( i >= lengthL ) break;
+            }
+
+            // width and height of the inner T(i,i) block
+            int widthT = Math.min(blockLength, lengthL-i);
+
+            Linner.col0 = L.col0 + i;    Linner.col1 = Linner.col0 + widthT;
+            Linner.row0 = L.row0 + i;    Linner.row1 = Linner.row0 + widthT;
+
+            Binner.col0 = B.col0;       Binner.col1 = B.col1;
+            Binner.row0 = B.row0 + i;   Binner.row1 = Binner.row0 + widthT;
+
+            // solve the top row block
+            // B(i,:) = T(i,i)^-1 Y(i,:)
+            solveBlock(blockLength,false, Linner,Binner,transL,false);
+
+            boolean updateY;
+            if( transL ) {
+                updateY = Linner.row0 > 0;
+            } else {
+                updateY = Linner.row1 < L.row1;
+            }
+            if( updateY ) {
+                // Y[i,:] = Y[i,:] - sum j=1:i-1 { T[i,j] B[j,i] }
+                // where i is the next block down
+                // The summation is a block inner product
+                if( transL ) {
+                    Linner.col1 = Linner.col0;
+                    Linner.col0 = Linner.col1 - blockLength;
+                    Linner.row1 = L.row1;
+                    //Tinner.col1 = Tinner.col1;
+
+//                    Binner.row0 = Binner.row0;
+                    Binner.row1 = B.row1;
+
+                    Y.row0 = Binner.row0-blockLength;
+                    Y.row1 = Binner.row0;
+                } else {
+                    Linner.row0 = Linner.row1;
+                    Linner.row1 = Math.min(Linner.row0+blockLength, L.row1);
+                    Linner.col0 = L.col0;
+                    //Tinner.col1 = Tinner.col1;
+
+                    Binner.row0 = B.row0;
+                    //Binner.row1 = Binner.row1;
+
+                    Y.row0 = Binner.row1;
+                    Y.row1 = Math.min(Y.row0+blockLength,B.row1);
+                }
+
+                // step through each block column
+                for( int k = B.col0; k < B.col1; k += blockLength ) {
+
+                    Binner.col0 = k;
+                    Binner.col1 = Math.min(k+blockLength,B.col1);
+
+                    Y.col0 = Binner.col0;
+                    Y.col1 = Binner.col1;
+
+                    if( transL ) {
+                        // Y = Y - T^T * B
+                        BlockMultiplication.multMinusTransA(blockLength, Linner,Binner,Y);
+                    } else {
+
+                        // Y = Y - T * B
+                        BlockMultiplication.multMinus(blockLength, Linner,Binner,Y);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Solves upper triangular systems:<br>
+     * <br>
+     * B = R<sup>-1</sup> B<br>
+     * <br>
+     * </p>
+     *
+     * <p>Only the first B.numRows rows in R will be processed.  Lower triangular elements are ignored.<p>
+     *
+     * <p> Reverse or forward substitution is used depending upon L being transposed or not. </p>
+     *
+     * @param blockLength
+     * @param R Upper triangular with dimensions m by m.  Not modified.
+     * @param B A matrix with dimensions m by n.  Solution is written into here. Modified.
+     * @param transR Is the triangular matrix transposed?
+     */
+    public static void solveR( final int blockLength ,
+                               final D1Submatrix64F R,
+                               final D1Submatrix64F B ,
+                               boolean transR ) {
+
+        int lengthR = B.row1 - B.row0;
+        if( R.getCols() != lengthR ) {
+            throw new IllegalArgumentException("Number of columns in R must be equal to the number of rows in B");
+        } else if( R.getRows() != lengthR ) {
+            throw new IllegalArgumentException("Number of rows in R must be equal to the number of rows in B");
+        }
+
+        D1Submatrix64F Y = new D1Submatrix64F(B.original);
+
+        D1Submatrix64F Rinner = new D1Submatrix64F(R.original);
+        D1Submatrix64F Binner = new D1Submatrix64F(B.original);
+
+        int startI,stepI;
+
+        if( transR ) {
+            startI = 0;
+            stepI = blockLength;
+        } else {
+            startI = lengthR - lengthR % blockLength;
+            if( startI == lengthR && lengthR >= blockLength )
+                startI -= blockLength;
+
+            stepI = -blockLength;
+        }
+
+        for( int i = startI; ; i += stepI ) {
+            if( transR ) {
+                if( i >= lengthR ) break;
+            } else {
+                if( i < 0 ) break;
+            }
+
+            // width and height of the inner T(i,i) block
+            int widthT = Math.min(blockLength, lengthR-i);
+
+            Rinner.col0 = R.col0 + i;    Rinner.col1 = Rinner.col0 + widthT;
+            Rinner.row0 = R.row0 + i;    Rinner.row1 = Rinner.row0 + widthT;
+
+            Binner.col0 = B.col0;       Binner.col1 = B.col1;
+            Binner.row0 = B.row0 + i;   Binner.row1 = Binner.row0 + widthT;
+
+            // solve the top row block
+            // B(i,:) = T(i,i)^-1 Y(i,:)
+            solveBlock(blockLength,true, Rinner,Binner,transR,false);
+
+            boolean updateY;
+            if( transR ) {
+                updateY = Rinner.row1 < R.row1;
+            } else {
+                updateY = Rinner.row0 > 0;
+            }
+            if( updateY ) {
+                // Y[i,:] = Y[i,:] - sum j=1:i-1 { T[i,j] B[j,i] }
+                // where i is the next block down
+                // The summation is a block inner product
+                if( transR ) {
+                    Rinner.col0 = Rinner.col1;
+                    Rinner.col1 = Math.min(Rinner.col0+blockLength, R.col1);
+                    Rinner.row0 = R.row0;
+                    //Rinner.row1 = Rinner.row1;
+
+                    Binner.row0 = B.row0;
+                    //Binner.row1 = Binner.row1;
+
+                    Y.row0 = Binner.row1;
+                    Y.row1 = Math.min(Y.row0+blockLength,B.row1);
+                } else {
+                    Rinner.row1 = Rinner.row0;
+                    Rinner.row0 = Rinner.row1 - blockLength;
+                    Rinner.col1 = R.col1;
+
+//                    Binner.row0 = Binner.row0;
+                    Binner.row1 = B.row1;
+
+                    Y.row0 = Binner.row0-blockLength;
+                    Y.row1 = Binner.row0;
+                }
+
+                // step through each block column
+                for( int k = B.col0; k < B.col1; k += blockLength ) {
+
+                    Binner.col0 = k;
+                    Binner.col1 = Math.min(k+blockLength,B.col1);
+
+                    Y.col0 = Binner.col0;
+                    Y.col1 = Binner.col1;
+
+                    if( transR ) {
+                        // Y = Y - T^T * B
+                        BlockMultiplication.multMinusTransA(blockLength, Rinner,Binner,Y);
+                    } else {
+                        // Y = Y - T * B
+                        BlockMultiplication.multMinus(blockLength, Rinner,Binner,Y);
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/block/BlockVectorOps.java b/main/dense64/src/org/ejml/alg/block/BlockVectorOps.java
new file mode 100644
index 0000000..593765a
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/block/BlockVectorOps.java
@@ -0,0 +1,359 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block;
+
+import org.ejml.data.D1Submatrix64F;
+
+
+/**
+ * <p>
+ * Math operations for inner vectors (row and column) inside of block matrices:<br>
+ * <br>
+ * scale: b<sub>i</sub> = α*a<sub>i</sub><br>
+ * div:  <sub>i</sub> = a<sub>i</sub>/α<br>
+ * add: c<sub>i</sub> = α*a<sub>i</sub> + βB<sub>i</sub><br>
+ * dot: c = sum a<sub>i</sub>*b<sub>i</sub><br>
+ * </p>
+ *
+ * <p>
+ * All submatrices must be block aligned.  All offsets and end indexes are relative to the beginning of each
+ * submatrix.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class BlockVectorOps {
+
+    /**
+     * <p>
+     * Row vector scale:<br>
+     * scale: b<sub>i</sub> = α*a<sub>i</sub><br>
+     * where 'a' and 'b' are row vectors within the row block vector A and B.
+     * </p>
+     *
+     * @param A submatrix. Not modified.
+     * @param rowA which row in A the vector is contained in.
+     * @param alpha scale factor.
+     * @param B submatrix that the results are written to.  Modified.
+     * @param offset Index at which the vectors start at.
+     * @param end Index at which the vectors end at.
+     */
+    public static void scale_row(final int blockLength,
+                                 D1Submatrix64F A, int rowA,
+                                 double alpha, D1Submatrix64F B, int rowB,
+                                 int offset, int end)
+    {
+        final double dataA[] = A.original.data;
+        final double dataB[] = B.original.data;
+
+        // handle the case where offset is more than a block
+        int startI = offset - offset % blockLength;
+        offset = offset % blockLength;
+
+        // handle rows in any block
+        int rowBlockA = A.row0 + rowA - rowA % blockLength;
+        rowA = rowA % blockLength;
+        int rowBlockB = B.row0 + rowB - rowB % blockLength;
+        rowB = rowB % blockLength;
+
+        final int heightA = Math.min(blockLength,A.row1-rowBlockA);
+        final int heightB = Math.min(blockLength,B.row1-rowBlockB);
+
+        for( int i = startI; i < end; i += blockLength ) {
+            int segment = Math.min(blockLength,end-i);
+
+            int widthA = Math.min(blockLength,A.col1-A.col0-i);
+            int widthB = Math.min(blockLength,B.col1-B.col0-i);
+
+            int indexA = rowBlockA*A.original.numCols + (A.col0+i)*heightA + rowA*widthA;
+            int indexB = rowBlockB*B.original.numCols + (B.col0+i)*heightB + rowB*widthB;
+
+            if( i == startI ) {
+                indexA += offset;
+                indexB += offset;
+
+                for( int j = offset; j < segment; j++ ) {
+                    dataB[indexB++] = alpha*dataA[indexA++];
+                }
+            } else {
+                for( int j = 0; j < segment; j++ ) {
+                    dataB[indexB++] = alpha*dataA[indexA++];
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Row vector divide:<br>
+     * div: b<sub>i</sub> = a<sub>i</sub>/α<br>
+     * where 'a' and 'b' are row vectors within the row block vector A and B.
+     * </p>
+     *
+     * @param A submatrix. Not modified.
+     * @param rowA which row in A the vector is contained in.
+     * @param alpha scale factor.
+     * @param B submatrix that the results are written to.  Modified.
+     * @param offset Index at which the vectors start at.
+     * @param end Index at which the vectors end at.
+     */
+    public static void div_row(final int blockLength,
+                               D1Submatrix64F A, int rowA,
+                               double alpha, D1Submatrix64F B, int rowB,
+                               int offset, int end)
+    {
+        final double dataA[] = A.original.data;
+        final double dataB[] = B.original.data;
+
+        // handle the case where offset is more than a block
+        int startI = offset - offset % blockLength;
+        offset = offset % blockLength;
+
+        // handle rows in any block
+        int rowBlockA = A.row0 + rowA - rowA % blockLength;
+        rowA = rowA % blockLength;
+        int rowBlockB = B.row0 + rowB - rowB % blockLength;
+        rowB = rowB % blockLength;
+
+        final int heightA = Math.min(blockLength,A.row1-rowBlockA);
+        final int heightB = Math.min(blockLength,B.row1-rowBlockB);
+
+        for( int i = startI; i < end; i += blockLength ) {
+            int segment = Math.min(blockLength,end-i);
+
+            int widthA = Math.min(blockLength,A.col1-A.col0-i);
+            int widthB = Math.min(blockLength,B.col1-B.col0-i);
+
+            int indexA = rowBlockA*A.original.numCols + (A.col0+i)*heightA + rowA*widthA;
+            int indexB = rowBlockB*B.original.numCols + (B.col0+i)*heightB + rowB*widthB;
+
+            if( i == startI ) {
+                indexA += offset;
+                indexB += offset;
+
+                for( int j = offset; j < segment; j++ ) {
+                    dataB[indexB++] = dataA[indexA++]/alpha;
+                }
+            } else {
+                for( int j = 0; j < segment; j++ ) {
+                    dataB[indexB++] = dataA[indexA++]/alpha;
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Row vector add:<br>
+     * add: c<sub>i</sub> = α*a<sub>i</sub> + βB<sub>i</sub><br>
+     * where 'a', 'b', and 'c' are row vectors within the row block vectors of A, B, and C respectively.
+     * </p>
+     *
+     * @param blockLength Length of each inner matrix block.
+     * @param A submatrix. Not modified.
+     * @param rowA which row in A the vector is contained in.
+     * @param alpha scale factor of A
+     * @param B submatrix. Not modified.
+     * @param rowB which row in B the vector is contained in.
+     * @param beta scale factor of B
+     * @param C submatrix where the results are written to. Modified.
+     * @param rowC which row in C is the vector contained.
+     * @param offset Index at which the vectors start at.
+     * @param end Index at which the vectors end at.
+     */
+    public static void add_row( final int blockLength ,
+                                D1Submatrix64F A , int rowA , double alpha ,
+                                D1Submatrix64F B , int rowB , double beta ,
+                                D1Submatrix64F C , int rowC ,
+                                int offset , int end ) {
+        final int heightA = Math.min(blockLength,A.row1-A.row0);
+        final int heightB = Math.min(blockLength,B.row1-B.row0);
+        final int heightC = Math.min(blockLength,C.row1-C.row0);
+
+        // handle the case where offset is more than a block
+        int startI = offset - offset % blockLength;
+        offset = offset % blockLength;
+
+        final double dataA[] = A.original.data;
+        final double dataB[] = B.original.data;
+        final double dataC[] = C.original.data;
+
+        for( int i = startI; i < end; i += blockLength ) {
+            int segment = Math.min(blockLength,end-i);
+
+            int widthA = Math.min(blockLength,A.col1-A.col0-i);
+            int widthB = Math.min(blockLength,B.col1-B.col0-i);
+            int widthC = Math.min(blockLength,C.col1-C.col0-i);
+
+            int indexA = A.row0*A.original.numCols + (A.col0+i)*heightA + rowA*widthA;
+            int indexB = B.row0*B.original.numCols + (B.col0+i)*heightB + rowB*widthB;
+            int indexC = C.row0*C.original.numCols + (C.col0+i)*heightC + rowC*widthC;
+
+            if( i == startI ) {
+                indexA += offset;
+                indexB += offset;
+                indexC += offset;
+
+                for( int j = offset; j < segment; j++ ) {
+                    dataC[indexC++] = alpha*dataA[indexA++] + beta*dataB[indexB++];
+                }
+            } else {
+                for( int j = 0; j < segment; j++ ) {
+                    dataC[indexC++] = alpha*dataA[indexA++] + beta*dataB[indexB++];
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Row vector dot/inner product:<br>
+     * dot: c = sum a<sub>i</sub>*b<sub>i</sub><br>
+     * where 'a' and 'b' are row vectors within the row block vector A and B, and 'c' is a scalar.
+     * </p>
+     *
+     * @param A submatrix. Not modified.
+     * @param rowA which row in A the vector is contained in.
+     * @param B submatrix. Not modified.
+     * @param rowB which row in B the vector is contained in.
+     * @param offset Index at which the vectors start at.
+     * @param end Index at which the vectors end at.
+     * @return Results of the dot product.
+     */
+    public static double dot_row(final int blockLength,
+                                 D1Submatrix64F A, int rowA,
+                                 D1Submatrix64F B, int rowB,
+                                 int offset, int end) {
+
+
+        // handle the case where offset is more than a block
+        int startI = offset - offset % blockLength;
+        offset = offset % blockLength;
+
+        final double dataA[] = A.original.data;
+        final double dataB[] = B.original.data;
+
+        double total = 0;
+
+        // handle rows in any block
+        int rowBlockA = A.row0 + rowA - rowA % blockLength;
+        rowA = rowA % blockLength;
+        int rowBlockB = B.row0 + rowB - rowB % blockLength;
+        rowB = rowB % blockLength;
+
+        final int heightA = Math.min(blockLength,A.row1-rowBlockA);
+        final int heightB = Math.min(blockLength,B.row1-rowBlockB);
+
+        if( A.col1 - A.col0 != B.col1 - B.col0 )
+            throw new RuntimeException();
+
+        for( int i = startI; i < end; i += blockLength ) {
+            int segment = Math.min(blockLength,end-i);
+
+            int widthA = Math.min(blockLength,A.col1-A.col0-i);
+            int widthB = Math.min(blockLength,B.col1-B.col0-i);
+
+            int indexA = rowBlockA*A.original.numCols + (A.col0+i)*heightA + rowA*widthA;
+            int indexB = rowBlockB*B.original.numCols + (B.col0+i)*heightB + rowB*widthB;
+
+            if( i == startI ) {
+                indexA += offset;
+                indexB += offset;
+
+                for( int j = offset; j < segment; j++ ) {
+                    total += dataB[indexB++]*dataA[indexA++];
+                }
+            } else {
+                for( int j = 0; j < segment; j++ ) {
+                    total += dataB[indexB++]*dataA[indexA++];
+                }
+            }
+        }
+
+        return total;
+    }
+
+    /**
+     * <p>
+     * vector dot/inner product from one row vector and one column vector:<br>
+     * dot: c = sum a<sub>i</sub>*b<sub>i</sub><br>
+     * where 'a' is a row vector 'b' is a column vectors within the row block vector A and B, and 'c' is a scalar.
+     * </p>
+     *
+     * @param A block row vector. Not modified.
+     * @param rowA which row in A the vector is contained in.
+     * @param B block column vector. Not modified.
+     * @param colB which column in B is the vector contained in.
+     * @param offset Index at which the vectors start at.
+     * @param end Index at which the vectors end at.
+     * @return Results of the dot product.
+     */
+    public static double dot_row_col(final int blockLength,
+                                     D1Submatrix64F A, int rowA,
+                                     D1Submatrix64F B, int colB,
+                                     int offset, int end) {
+
+
+        // handle the case where offset is more than a block
+        int startI = offset - offset % blockLength;
+        offset = offset % blockLength;
+
+        final double dataA[] = A.original.data;
+        final double dataB[] = B.original.data;
+
+        double total = 0;
+
+        // handle rows in any block
+        int rowBlockA = A.row0 + rowA - rowA % blockLength;
+        rowA = rowA % blockLength;
+        int colBlockB = B.col0 + colB - colB % blockLength;
+        colB = colB % blockLength;
+
+        final int heightA = Math.min(blockLength,A.row1-rowBlockA);
+        final int widthB = Math.min(blockLength,B.col1-colBlockB);
+
+        if( A.col1 - A.col0 != B.col1 - B.col0 )
+            throw new RuntimeException();
+
+        for( int i = startI; i < end; i += blockLength ) {
+            int segment = Math.min(blockLength,end-i);
+
+            int widthA = Math.min(blockLength,A.col1-A.col0-i);
+            int heightB = Math.min(blockLength,B.row1-B.row0-i);
+
+            int indexA = rowBlockA*A.original.numCols + (A.col0+i)*heightA + rowA*widthA;
+            int indexB = (B.row0+i)*B.original.numCols + colBlockB*heightB + colB;
+
+            if( i == startI ) {
+                indexA += offset;
+                indexB += offset*widthB;
+
+                for( int j = offset; j < segment; j++ , indexB += widthB) {
+                    total += dataB[indexB]*dataA[indexA++];
+                }
+            } else {
+                for( int j = 0; j < segment; j++ , indexB += widthB) {
+                    total += dataB[indexB]*dataA[indexA++];
+                }
+            }
+        }
+
+        return total;
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/block/GeneratorBlockInnerMultiplication.java b/main/dense64/src/org/ejml/alg/block/GeneratorBlockInnerMultiplication.java
new file mode 100644
index 0000000..1354d39
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/block/GeneratorBlockInnerMultiplication.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block;
+
+import org.ejml.alg.generic.CodeGeneratorMisc;
+
+import java.io.FileNotFoundException;
+import java.io.PrintStream;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class GeneratorBlockInnerMultiplication {
+
+    String className;
+    PrintStream stream;
+
+    public GeneratorBlockInnerMultiplication( String className ) throws FileNotFoundException {
+        this.className = className;
+        stream = new PrintStream(className+".java");
+    }
+
+    public void createClass() {
+        printTop();
+
+        for( int i = 0; i < 2; i++ ) {
+            boolean hasAlpha = i==1;
+            for( Operation o : Operation.values()) {
+                if( hasAlpha && o == Operation.MINUS )
+                    continue;
+                print_mult(hasAlpha,o);
+                print_multTransA(hasAlpha,o);
+                print_multTransB(hasAlpha,o);
+            }
+        }
+
+
+        stream.print("}\n");
+    }
+
+    private void printTop() {
+        String foo = CodeGeneratorMisc.COPYRIGHT +
+                "\n" +
+                "package org.ejml.alg.block;\n" +
+                "\n" +
+                "/**\n" +
+                " * <p>\n" +
+                " * Matrix multiplication for the inner row major blocks, typically inside of a {@link org.ejml.data.BlockMatrix64F}.\n" +
+                " * </p>\n" +
+                " *\n" +
+                " * <p>\n" +
+                " * This code was auto generated by {@link GeneratorBlockInnerMultiplication} and should not be modified directly.\n" +
+                " * </p>\n" +
+                " *\n" +
+                " * @author Peter Abeles\n" +
+                " */\n" +
+                "public class "+className+" {\n";
+
+        stream.print(foo);
+    }
+
+    private void print_mult( boolean hasAlpha , Operation opType ) {
+
+        createHeader(hasAlpha,opType,false,false);
+
+        stream.print(
+                "//        for( int i = 0; i < heightA; i++ ) {\n" +
+                "//            for( int k = 0; k < widthA; k++ ) {\n" +
+                "//                for( int j = 0; j < widthC; j++ ) {\n" +
+                "//                    dataC[ i*widthC + j + indexC ] += dataA[i*widthA + k + indexA] * dataB[k*widthC + j + indexB];\n" +
+                "//                }\n" +
+                "//            }\n" +
+                "//        }\n");
+
+        stream.println();
+
+        String o = ( opType == Operation.MINUS ) ? "-=" : "+=";
+        String m = hasAlpha ? "alpha*" : "";
+
+        stream.print(
+                "        int a = indexA;\n"+
+                "        int rowC = indexC;\n"+
+                "        for( int i = 0; i < heightA; i++ , rowC += widthC ) {\n" +
+                "            int b = indexB;\n" +
+                "\n" +
+                "            final int endC = rowC + widthC;\n" +
+                "            final int endA = a + widthA;"+
+                "\n"+
+                "            while( a != endA ) {//for( int k = 0; k < widthA; k++ ) {\n" +
+                "                double valA = "+m+"dataA[a++];\n" +
+                "\n" +
+                "                int c = rowC;\n" +
+                "\n");
+
+        if( opType == Operation.SET ) {
+             stream.print(
+                "                if( b == indexB ) {\n" +
+                "                    while( c != endC  ) {//for( int j = 0; j < widthC; j++ ) {\n" +
+                "                        dataC[ c++ ] = valA * dataB[ b++ ];\n" +
+                "                    }\n" +
+                "                } else {\n" +
+                "                    while( c != endC  ) {//for( int j = 0; j < widthC; j++ ) {\n" +
+                "                        dataC[ c++ ] "+o+" valA * dataB[ b++ ];\n" +
+                "                    }\n" +
+                "                }\n");
+        } else {
+             stream.print(
+                "                while( c != endC  ) {//for( int j = 0; j < widthC; j++ ) {\n" +
+                "                    dataC[ c++ ] "+o+" valA * dataB[ b++ ];\n" +
+                "                }\n");
+        }
+        stream.println(
+                "            }\n" +
+                "        }");
+
+        stream.println("    }");
+
+    }
+    
+
+    private String createOpString(boolean hasAlpha, Operation opType) {
+        String o = opString(opType);
+        if( hasAlpha ) o += " alpha * ";
+        return o;
+    }
+
+    private void print_multTransA( boolean hasAlpha , Operation opType ) {
+
+        createHeader(hasAlpha,opType,true,false);
+
+        String o = ( opType == Operation.MINUS ) ? "-=" : "+=";
+        String m = hasAlpha ? "alpha*" : "";
+
+        stream.print(
+                "//        for( int i = 0; i < widthA; i++ ) {\n" +
+                "//            for( int k = 0; k < heightA; k++ ) {\n" +
+                "//                double valA = dataA[k*widthA + i + indexA];\n" +
+                "//                for( int j = 0; j < widthC; j++ ) {\n" +
+                "//                    dataC[ i*widthC + j + indexC ] += valA * dataB[k*widthC + j + indexB];\n" +
+                "//                }\n" +
+                "//            }\n" +
+                "//        }\n");
+        stream.println();
+
+        stream.print(
+        "        int rowC = indexC;\n"+
+        "        for( int i = 0; i < widthA; i++ , rowC += widthC) {\n" +
+        "            int colA = i + indexA;\n" +
+        "            int endA = colA + widthA*heightA;\n" +
+        "            int b = indexB;\n" +
+        "\n" +
+        "            // for( int k = 0; k < heightA; k++ ) {\n" +
+        "            while(colA != endA ) {\n" +
+        "                double valA = "+m+"dataA[colA];\n" +
+        "\n" +
+        "                int c = rowC;\n" +
+        "                final int endB = b + widthC;\n" +
+        "\n" +
+        "                //for( int j = 0; j < widthC; j++ ) {\n");
+        if( opType == Operation.SET ) {
+            stream.print(
+                    "                if( b == indexB ) {\n" +
+                    "                    while( b != endB ) {\n" +
+                    "                        dataC[ c++ ] = valA * dataB[b++];\n" +
+                    "                    } \n" +
+                    "                } else {\n" +
+                    "                    while( b != endB ) {\n" +
+                    "                        dataC[ c++ ] "+o+" valA * dataB[b++];\n" +
+                    "                    }\n" +
+                    "                }\n");
+        } else {
+            stream.print(
+                    "                while( b != endB ) {\n" +
+                    "                    dataC[ c++ ] "+o+" valA * dataB[b++];\n" +
+                    "                }\n");
+        }
+        stream.print(
+        "                colA += widthA;\n"+
+        "            }\n" +
+        "        }\n");
+
+
+        stream.println("    }");
+    }
+
+    private void print_multTransB( boolean hasAlpha , Operation opType ) {
+
+        createHeader(hasAlpha,opType,false,true);
+
+        String o = createOpString(hasAlpha, opType);
+
+        stream.println(
+                "        for( int i = 0; i < heightA; i++ ) {\n" +
+                "            for( int j = 0; j < widthC; j++ ) {\n" +
+                "                double val = 0;\n" +
+                "\n" +
+                "                for( int k = 0; k < widthA; k++ ) {\n" +
+                "                    val += dataA[i*widthA + k + indexA] * dataB[j*widthA + k + indexB];\n" +
+                "                }\n" +
+                "\n" +
+                "                dataC[ i*widthC + j + indexC ] "+o+" val;\n" +
+                "            }\n" +
+                "        }");
+
+        stream.println("    }");
+    }
+
+    private void createHeader( boolean hasAlpha , Operation opType , boolean transA , boolean transB )
+    {
+        String alphaString = hasAlpha ? " α " : "";
+        String alphaParam = hasAlpha ? " double alpha ," : "";
+        String transAString = transA ? "<sup>T</sup>" : "";
+        String transBString = transB ? "<sup>T</sup>" : "";
+        String opTypeString;
+
+        switch( opType ) {
+            case MINUS: opTypeString = "C - "; break;
+            case PLUS: opTypeString = "C + "; break;
+            case SET: opTypeString = ""; break;
+            default: throw new RuntimeException("Unknown optype");
+        }
+
+
+        String funcName = "blockMult"+opName(opType);
+        if( transA && transB ) funcName += "TransAB";
+        else if( transA ) funcName += "TransA";
+        else if( transB ) funcName += "TransB";
+
+        stream.println();
+        stream.print(
+                "    /**\n" +
+                "     * <p>\n" +
+                "     * Performs the follow operation on individual inner blocks:<br>\n" +
+                "     * <br>\n");
+
+        stream.print(
+                "     * C = "+opTypeString+alphaString+"A"+transAString+" * B"+transBString+"\n");
+        stream.print(
+                "     * </p>\n" +
+                "     */\n" +
+                "    public static void "+funcName+"("+alphaParam+" final double[] dataA, final double []dataB, final double []dataC,\n" +
+                "                                     int indexA, int indexB, int indexC,\n" +
+                "                                     final int heightA, final int widthA, final int widthC) {\n");
+    }
+
+    private String opString( Operation opType ) {
+        switch( opType ) {
+            case MINUS:
+                return "-=";
+
+            case PLUS:
+                return "+=";
+
+            case SET:
+                return "=";
+
+            default:
+                throw new RuntimeException("Unknown opType "+opType);
+        }
+    }
+
+    private String opName( Operation opType ) {
+        switch( opType ) {
+            case MINUS:
+                return "Minus";
+
+            case PLUS:
+                return "Plus";
+
+            case SET:
+                return "Set";
+
+            default:
+                throw new RuntimeException("Unknown opType "+opType);
+        }
+    }
+
+    private static enum Operation
+    {
+
+        /** Add results to output matrix */
+        PLUS,
+        /** Subtract results from output matrix */
+        MINUS,
+        /** set output matrix to results */
+        SET
+    }
+
+    public static void main( String args[] ) throws FileNotFoundException {
+        GeneratorBlockInnerMultiplication app = new GeneratorBlockInnerMultiplication("BlockInnerMultiplication");
+
+        app.createClass();
+
+        System.out.println("Done generating class");
+
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/block/decomposition/bidiagonal/BidiagonalHelper.java b/main/dense64/src/org/ejml/alg/block/decomposition/bidiagonal/BidiagonalHelper.java
new file mode 100644
index 0000000..72ef130
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/block/decomposition/bidiagonal/BidiagonalHelper.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block.decomposition.bidiagonal;
+
+import org.ejml.data.D1Submatrix64F;
+
+import static org.ejml.alg.block.decomposition.qr.BlockHouseHolder.*;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class BidiagonalHelper {
+
+    /**
+     * Performs a standard bidiagonal decomposition just on the outer blocks of the provided matrix
+     *
+     * @param blockLength
+     * @param A
+     * @param gammasU
+     */
+
+    public static boolean bidiagOuterBlocks( final int blockLength ,
+                                             final D1Submatrix64F A ,
+                                             final double gammasU[],
+                                             final double gammasV[])
+    {
+//        System.out.println("---------- Orig");
+//        A.original.print();
+
+        int width = Math.min(blockLength,A.col1-A.col0);
+        int height = Math.min(blockLength,A.row1-A.row0);
+
+        int min = Math.min(width,height);
+
+        for( int i = 0; i < min; i++ ) {
+            //--- Apply reflector to the column
+
+            // compute the householder vector
+            if (!computeHouseHolderCol(blockLength, A, gammasU, i))
+                return false;
+
+            // apply to rest of the columns in the column block
+            rank1UpdateMultR_Col(blockLength,A,i,gammasU[A.col0+i]);
+
+            // apply to the top row block
+            rank1UpdateMultR_TopRow(blockLength,A,i,gammasU[A.col0+i]);
+
+            System.out.println("After column stuff");
+            A.original.print();
+
+            //-- Apply reflector to the row
+            if(!computeHouseHolderRow(blockLength,A,gammasV,i))
+                return false;
+            
+            // apply to rest of the rows in the row block
+            rank1UpdateMultL_Row(blockLength,A,i,i+1,gammasV[A.row0+i]);
+
+            System.out.println("After update row");
+            A.original.print();
+
+            // apply to the left column block
+            // TODO THIS WON'T WORK!!!!!!!!!!!!!
+            // Needs the whole matrix to have been updated by the left reflector to compute the correct solution
+//            rank1UpdateMultL_LeftCol(blockLength,A,i,i+1,gammasV[A.row0+i]);
+
+            System.out.println("After row stuff");
+            A.original.print();
+        }
+
+        return true;
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/block/decomposition/chol/CholeskyOuterForm_B64.java b/main/dense64/src/org/ejml/alg/block/decomposition/chol/CholeskyOuterForm_B64.java
new file mode 100644
index 0000000..3ebc75e
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/block/decomposition/chol/CholeskyOuterForm_B64.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block.decomposition.chol;
+
+import org.ejml.alg.block.BlockInnerRankUpdate;
+import org.ejml.alg.block.BlockMatrixOps;
+import org.ejml.alg.block.BlockTriangularSolver;
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.Complex64F;
+import org.ejml.data.D1Submatrix64F;
+import org.ejml.interfaces.decomposition.CholeskyDecomposition;
+
+
+/**
+ * <p>
+ * Block Cholesky using outer product form.  The original matrix is stored and modified.
+ * </p>
+ *
+ * <p>
+ * Based on the description provided in "Fundamentals of Matrix Computations" 2nd Ed. by David S. Watkins.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class CholeskyOuterForm_B64 implements CholeskyDecomposition<BlockMatrix64F> {
+
+    // if it should compute an upper or lower triangular matrix
+    private boolean lower = false;
+    // The decomposed matrix.
+    private BlockMatrix64F T;
+
+    // predeclare local work space
+    private D1Submatrix64F subA = new D1Submatrix64F();
+    private D1Submatrix64F subB = new D1Submatrix64F();
+    private D1Submatrix64F subC = new D1Submatrix64F();
+
+    // storage for the determinant
+    private Complex64F det = new Complex64F();
+
+    /**
+     * Creates a new BlockCholeskyOuterForm
+     *
+     * @param lower Should it decompose it into a lower triangular matrix or not.
+     */
+    public CholeskyOuterForm_B64(boolean lower) {
+        this.lower = lower;
+    }
+
+    /**
+     * Decomposes the provided matrix and stores the result in the same matrix.
+     *
+     * @param A Matrix that is to be decomposed.  Modified.
+     * @return If it succeeded or not.
+     */
+    @Override
+    public boolean decompose(BlockMatrix64F A) {
+        if( A.numCols != A.numRows )
+            throw new IllegalArgumentException("A must be square");
+
+        this.T = A;
+
+        if( lower )
+            return decomposeLower();
+        else
+            return decomposeUpper();
+    }
+
+    private boolean decomposeLower() {
+        int blockLength = T.blockLength;
+
+        subA.set(T);
+        subB.set(T);
+        subC.set(T);
+
+        for( int i = 0; i < T.numCols; i += blockLength ) {
+            int widthA = Math.min(blockLength, T.numCols-i);
+
+            subA.col0 = i;           subA.col1 = i+widthA;
+            subA.row0 = subA.col0;   subA.row1 = subA.col1;
+
+            subB.col0 = i;           subB.col1 = i+widthA;
+            subB.row0 = i+widthA;    subB.row1 = T.numRows;
+
+            subC.col0 = i+widthA;    subC.col1 = T.numRows;
+            subC.row0 = i+widthA;    subC.row1 = T.numRows;
+            
+            // cholesky on inner block A
+            if( !InnerCholesky_B64.lower(subA))
+                return false;
+
+            // on the last block these operations are not needed.
+            if( widthA == blockLength ) {
+                // B = L^-1 B
+                BlockTriangularSolver.solveBlock(blockLength,false,subA,subB,false,true);
+
+                // C = C - B * B^T
+                BlockInnerRankUpdate.symmRankNMinus_L(blockLength,subC,subB);
+            }
+        }
+
+        BlockMatrixOps.zeroTriangle(true,T);
+
+        return true;
+    }
+
+
+    private boolean decomposeUpper() {
+        int blockLength = T.blockLength;
+
+        subA.set(T);
+        subB.set(T);
+        subC.set(T);
+
+        for( int i = 0; i < T.numCols; i += blockLength ) {
+            int widthA = Math.min(blockLength, T.numCols-i);
+
+            subA.col0 = i;          subA.col1 = i+widthA;
+            subA.row0 = subA.col0;  subA.row1 = subA.col1;
+
+            subB.col0 = i+widthA;   subB.col1 = T.numCols;
+            subB.row0 = i;          subB.row1 = i+widthA;
+
+            subC.col0 = i+widthA;   subC.col1 = T.numCols;
+            subC.row0 = i+widthA;   subC.row1 = T.numCols;
+
+            // cholesky on inner block A
+            if( !InnerCholesky_B64.upper(subA))
+                return false;
+
+            // on the last block these operations are not needed.
+            if( widthA == blockLength ) {
+                // B = U^-1 B
+                BlockTriangularSolver.solveBlock(blockLength,true,subA,subB,true,false);
+
+                // C = C - B^T * B
+                BlockInnerRankUpdate.symmRankNMinus_U(blockLength,subC,subB);
+            }
+        }
+
+        BlockMatrixOps.zeroTriangle(false,T);
+
+        return true;
+    }
+
+    @Override
+    public boolean isLower() {
+        return lower;
+    }
+
+    @Override
+    public BlockMatrix64F getT(BlockMatrix64F T) {
+        if( T == null )
+            return this.T;
+        T.set(this.T);
+
+        return T;
+    }
+
+    @Override
+    public Complex64F computeDeterminant() {
+        double prod = 1.0;
+
+        int blockLength = T.blockLength;
+        for( int i = 0; i < T.numCols; i += blockLength ) {
+            // width of the submatrix
+            int widthA = Math.min(blockLength, T.numCols-i);
+
+            // index of the first element in the block
+            int indexT = i*T.numCols + i*widthA;
+
+            // product along the diagonal
+            for (int j = 0; j < widthA; j++) {
+                prod *= T.data[indexT];
+                indexT += widthA+1;
+            }
+        }
+
+        det.real = prod*prod;
+        det.imaginary = 0;
+
+        return det;
+    }
+
+    @Override
+    public boolean inputModified() {
+        return true;
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/block/decomposition/chol/InnerCholesky_B64.java b/main/dense64/src/org/ejml/alg/block/decomposition/chol/InnerCholesky_B64.java
new file mode 100644
index 0000000..ca76f95
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/block/decomposition/chol/InnerCholesky_B64.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block.decomposition.chol;
+
+import org.ejml.data.D1Submatrix64F;
+
+
+/**
+ * Performs a cholesky decomposition on an individual inner block.
+ *
+ *  @author Peter Abeles
+ */
+// TODO merge with CholeskyBlockHelper
+public class InnerCholesky_B64 {
+
+    public static boolean upper( D1Submatrix64F T )
+    {
+        int n = T.row1-T.row0;
+        int indexT = T.row0* T.original.numCols + T.col0*n;
+
+        return upper(T.original.data,indexT,n);
+    }
+
+    public static boolean lower( D1Submatrix64F T )
+    {
+        int n = T.row1-T.row0;
+        int indexT = T.row0* T.original.numCols + T.col0*n;
+
+        return lower(T.original.data,indexT,n);
+    }
+
+    /**
+     * Performs an inline upper Cholesky decomposition on an inner row-major matrix.  Only
+     * the upper triangular portion of the matrix is read or written to.
+     *
+     * @param T  Array containing an inner row-major matrix.  Modified.
+     * @param indexT First index of the inner row-major matrix.
+     * @param n Number of rows and columns of the matrix.
+     * @return If the decomposition succeeded.
+     */
+    public static boolean upper( double[]T , int indexT , int n ) {
+        double el_ii;
+        double div_el_ii=0;
+
+        for( int i = 0; i < n; i++ ) {
+            for( int j = i; j < n; j++ ) {
+                double sum = T[ indexT + i*n+j];
+
+                // todo optimize
+                for( int k = 0; k < i; k++ ) {
+                    sum -= T[ indexT + k*n+i] * T[ indexT + k*n+j];
+                }
+
+                if( i == j ) {
+                    // is it positive-definite?
+                    if( sum <= 0.0 )
+                        return false;
+
+                    el_ii = Math.sqrt(sum);
+                    T[ indexT + i*n+i] = el_ii;
+                    div_el_ii = 1.0/el_ii;
+                } else {
+                    T[ indexT + i*n+j] = sum*div_el_ii;
+                }
+            }
+        }
+
+        return true;
+    }
+
+
+    /**
+     * Performs an inline lower Cholesky decomposition on an inner row-major matrix.  Only
+     * the lower triangular portion of the matrix is read or written to.
+     *
+     * @param T  Array containing an inner row-major matrix.  Modified.
+     * @param indexT First index of the inner row-major matrix.
+     * @param n Number of rows and columns of the matrix.
+     * @return If the decomposition succeeded.
+     */
+    public static boolean lower( double[]T , int indexT , int n ) {
+        double el_ii;
+        double div_el_ii=0;
+
+        for( int i = 0; i < n; i++ ) {
+            for( int j = i; j < n; j++ ) {
+                double sum = T[ indexT + j*n+i];
+
+                // todo optimize
+                for( int k = 0; k < i; k++ ) {
+                    sum -= T[ indexT + i*n+k] * T[ indexT + j*n+k];
+                }
+
+                if( i == j ) {
+                    // is it positive-definite?
+                    if( sum <= 0.0 )
+                        return false;
+
+                    el_ii = Math.sqrt(sum);
+                    T[ indexT + i*n+i] = el_ii;
+                    div_el_ii = 1.0/el_ii;
+                } else {
+                    T[ indexT + j*n+i] = sum*div_el_ii;
+                }
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/block/decomposition/hessenberg/TridiagonalDecompositionHouseholder_B64.java b/main/dense64/src/org/ejml/alg/block/decomposition/hessenberg/TridiagonalDecompositionHouseholder_B64.java
new file mode 100644
index 0000000..2e65656
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/block/decomposition/hessenberg/TridiagonalDecompositionHouseholder_B64.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block.decomposition.hessenberg;
+
+import org.ejml.alg.block.BlockMultiplication;
+import org.ejml.alg.block.decomposition.qr.QRDecompositionHouseholder_B64;
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.D1Submatrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.TridiagonalSimilarDecomposition;
+import org.ejml.ops.CommonOps;
+
+import static org.ejml.alg.block.BlockInnerMultiplication.blockMultPlusTransA;
+
+
+/**
+ * <p>
+ * Tridiagonal similar decomposition for block matrices.  Orthogonal matrices are computed using
+ * householder vectors.
+ * </p>
+ *
+ * <p>
+ * Based off algorithm in section 2 of J. J. Dongarra, D. C. Sorensen, S. J. Hammarling,
+ * "Block Reduction of Matrices to Condensed Forms for Eigenvalue Computations" Journal of
+ * Computations and Applied Mathematics 27 (1989) 215-227<b>
+ * <br>
+ * Computations of Householder reflectors has been modified from what is presented in that paper to how 
+ * it is performed in "Fundamentals of Matrix Computations" 2nd ed. by David S. Watkins.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class TridiagonalDecompositionHouseholder_B64
+        implements TridiagonalSimilarDecomposition<BlockMatrix64F> {
+
+    // matrix which is being decomposed
+    // householder vectors are stored along the upper triangle rows
+    protected BlockMatrix64F A;
+    // temporary storage for block computations
+    protected BlockMatrix64F V = new BlockMatrix64F(1,1);
+    // stores intermediate results in matrix multiplication
+    protected BlockMatrix64F tmp = new BlockMatrix64F(1,1);
+    protected double gammas[] = new double[1];
+
+    // temporary storage for zeros and ones in U
+    protected DenseMatrix64F zerosM = new DenseMatrix64F(1,1);
+
+    @Override
+    public BlockMatrix64F getT(BlockMatrix64F T) {
+        if( T == null ) {
+            T = new BlockMatrix64F(A.numRows,A.numCols,A.blockLength);
+        } else {
+            if( T.numRows != A.numRows || T.numCols != A.numCols )
+                throw new IllegalArgumentException("T must have the same dimensions as the input matrix");
+
+            CommonOps.fill(T, 0);
+        }
+
+        T.set(0,0,A.data[0]);
+        for( int i = 1; i < A.numRows; i++ ) {
+            double d = A.get(i-1,i);
+            T.set(i,i,A.get(i,i));
+            T.set(i-1,i,d);
+            T.set(i,i-1,d);
+        }
+
+        return T;
+    }
+
+    @Override
+    public BlockMatrix64F getQ(BlockMatrix64F Q, boolean transposed) {
+        Q = QRDecompositionHouseholder_B64.initializeQ(Q, A.numRows, A.numCols, A.blockLength, false);
+
+        int height = Math.min(A.blockLength,A.numRows);
+        V.reshape(height,A.numCols,false);
+        tmp.reshape(height,A.numCols,false);
+
+        D1Submatrix64F subQ = new D1Submatrix64F(Q);
+        D1Submatrix64F subU = new D1Submatrix64F(A);
+        D1Submatrix64F subW = new D1Submatrix64F(V);
+        D1Submatrix64F tmp = new D1Submatrix64F(this.tmp);
+
+
+        int N = A.numRows;
+
+        int start = N - N % A.blockLength;
+        if( start == N )
+            start -= A.blockLength;
+        if( start < 0 )
+            start = 0;
+
+        // (Q1^T * (Q2^T * (Q3^t * A)))
+        for( int i = start; i >= 0; i -= A.blockLength ) {
+            int blockSize = Math.min(A.blockLength,N-i);
+
+            subW.col0 = i;
+            subW.row1 = blockSize;
+            subW.original.reshape(subW.row1,subW.col1,false);
+
+            if( transposed ) {
+                tmp.row0 = i;
+                tmp.row1 = A.numCols;
+                tmp.col0 = 0;
+                tmp.col1 = blockSize;
+            } else {
+                tmp.col0 = i;
+                tmp.row1 = blockSize;
+            }
+            tmp.original.reshape(tmp.row1,tmp.col1,false);
+
+            subU.col0 = i;
+            subU.row0 = i;
+            subU.row1 = subU.row0+blockSize;
+
+            // zeros and ones are saved and overwritten in U so that standard matrix multiplication can be used
+            copyZeros(subU);
+
+            // compute W for Q(i) = ( I + W*Y^T)
+            TridiagonalHelper_B64.computeW_row(A.blockLength, subU, subW, gammas, i);
+
+            subQ.col0 = i;
+            subQ.row0 = i;
+
+            // Apply the Qi to Q
+            // Qi = I + W*U^T
+
+            // Note that U and V are really row vectors.  but standard notation assumed they are column vectors.
+            // which is why the functions called don't match the math above
+
+            // (I + W*U^T)*Q
+            // F=U^T*Q(i)
+            if( transposed )
+                BlockMultiplication.multTransB(A.blockLength,subQ,subU,tmp);
+            else
+                BlockMultiplication.mult(A.blockLength,subU,subQ,tmp);
+            // Q(i+1) = Q(i) + W*F
+            if( transposed )
+                BlockMultiplication.multPlus(A.blockLength,tmp,subW,subQ);
+            else
+                BlockMultiplication.multPlusTransA(A.blockLength,subW,tmp,subQ);
+
+            replaceZeros(subU);
+        }
+
+        return Q;
+    }
+
+    private void copyZeros( D1Submatrix64F subU ) {
+        int N = Math.min(A.blockLength,subU.col1-subU.col0);
+        for( int i = 0; i < N; i++ ) {
+            // save the zeros
+            for( int j = 0; j <= i; j++ ) {
+                zerosM.unsafe_set(i,j,subU.get(i,j));
+                subU.set(i,j,0);
+            }
+            // save the one
+            if( subU.col0 + i + 1 < subU.original.numCols ) {
+                zerosM.unsafe_set(i,i+1,subU.get(i,i+1));
+                subU.set(i,i+1,1);
+            }
+        }
+    }
+
+    private void replaceZeros( D1Submatrix64F subU ) {
+        int N = Math.min(A.blockLength,subU.col1-subU.col0);
+        for( int i = 0; i < N; i++ ) {
+            // save the zeros
+            for( int j = 0; j <= i; j++ ) {
+                subU.set(i,j,zerosM.get(i,j));
+            }
+            // save the one
+            if( subU.col0 + i + 1 < subU.original.numCols ) {
+                subU.set(i,i+1,zerosM.get(i,i+1));
+            }
+        }
+    }
+
+    @Override
+    public void getDiagonal(double[] diag, double[] off) {
+        diag[0] = A.data[0];
+        for( int i = 1; i < A.numRows; i++ ) {
+            diag[i] = A.get(i,i);
+            off[i-1] = A.get(i-1,i);
+        }
+    }
+
+    @Override
+    public boolean decompose(BlockMatrix64F orig) {
+        if( orig.numCols != orig.numRows )
+            throw new IllegalArgumentException("Input matrix must be square.");
+
+        init(orig);
+
+        D1Submatrix64F subA = new D1Submatrix64F(A);
+        D1Submatrix64F subV = new D1Submatrix64F(V);
+        D1Submatrix64F subU = new D1Submatrix64F(A);
+
+        int N = orig.numCols;
+
+        for( int i = 0; i < N; i += A.blockLength ) {
+//            System.out.println("-------- triag i "+i);
+            int height = Math.min(A.blockLength,A.numRows-i);
+
+            subA.col0 = subU.col0 = i;
+            subA.row0 = subU.row0 = i;
+
+            subU.row1 = subU.row0 + height;
+
+            subV.col0 = i;
+            subV.row1 = height;
+            subV.original.reshape(subV.row1,subV.col1,false);
+
+            // bidiagonalize the top row
+            TridiagonalHelper_B64.tridiagUpperRow(A.blockLength, subA, gammas, subV);
+
+            // apply Householder reflectors to the lower portion using block multiplication
+
+            if( subU.row1 < orig.numCols) {
+                // take in account the 1 in the last row.  The others are skipped over.
+                double before = subU.get(A.blockLength-1,A.blockLength);
+                subU.set(A.blockLength-1,A.blockLength,1);
+
+                // A = A + U*V^T + V*U^T
+                multPlusTransA(A.blockLength,subU,subV,subA);
+                multPlusTransA(A.blockLength,subV,subU,subA);
+
+                subU.set(A.blockLength-1,A.blockLength,before);
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * C = C + A^T*B
+     *
+     * @param blockLength
+     * @param A row block vector
+     * @param B row block vector
+     * @param C
+     */
+    public static void multPlusTransA( int blockLength ,
+                                       D1Submatrix64F A , D1Submatrix64F B ,
+                                       D1Submatrix64F C )
+    {
+        int heightA = Math.min( blockLength , A.row1 - A.row0 );
+
+        for( int i = C.row0+blockLength; i < C.row1; i += blockLength ) {
+            int heightC = Math.min( blockLength , C.row1 - i );
+
+            int indexA = A.row0*A.original.numCols + (i-C.row0+A.col0)*heightA;
+
+            for( int j = i; j < C.col1; j += blockLength ) {
+                int widthC = Math.min( blockLength , C.col1 - j );
+
+                int indexC = i*C.original.numCols + j*heightC;
+                int indexB = B.row0*B.original.numCols + (j-C.col0+B.col0)*heightA;
+
+                blockMultPlusTransA(A.original.data,B.original.data,C.original.data,
+                            indexA,indexB,indexC,heightA,heightC,widthC);
+            }
+        }
+    }
+
+    private void init( BlockMatrix64F orig ) {
+        this.A = orig;
+
+        int height = Math.min(A.blockLength,A.numRows);
+        V.reshape(height,A.numCols,A.blockLength,false);
+        tmp.reshape(height,A.numCols,A.blockLength,false);
+
+        if( gammas.length < A.numCols )
+            gammas = new double[ A.numCols ];
+
+        zerosM.reshape(A.blockLength,A.blockLength+1,false);
+    }
+
+    @Override
+    public boolean inputModified() {
+        return true;
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/block/decomposition/hessenberg/TridiagonalHelper_B64.java b/main/dense64/src/org/ejml/alg/block/decomposition/hessenberg/TridiagonalHelper_B64.java
new file mode 100644
index 0000000..d9de607
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/block/decomposition/hessenberg/TridiagonalHelper_B64.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block.decomposition.hessenberg;
+
+import org.ejml.alg.block.BlockVectorOps;
+import org.ejml.alg.block.decomposition.qr.BlockHouseHolder;
+import org.ejml.data.D1Submatrix64F;
+import org.ejml.ops.CommonOps;
+
+import static org.ejml.alg.block.decomposition.qr.BlockHouseHolder.computeHouseHolderRow;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TridiagonalHelper_B64 {
+
+    /**
+     * <p>
+     * Performs a tridiagonal decomposition on the upper row only.
+     * </p>
+     *
+     * <p>
+     * For each row 'a' in 'A':
+     * Compute 'u' the householder reflector.
+     * y(:) = A*u
+     * v(i) = y - (1/2)*(y^T*u)*u
+     * a(i+1) = a(i) - u*γ*v^T - v*u^t
+     * </p>
+     *
+     * @param blockLength Size of a block
+     * @param A is the row block being decomposed.  Modified.
+     * @param gammas Householder gammas.
+     * @param V Where computed 'v' are stored in a row block.  Modified.
+     */
+    public static void tridiagUpperRow( final int blockLength ,
+                                        final D1Submatrix64F A ,
+                                        final double gammas[] ,
+                                        final D1Submatrix64F V )
+    {
+        int blockHeight = Math.min(blockLength,A.row1-A.row0);
+        if( blockHeight <= 1 )
+            return;
+        int width = A.col1-A.col0;
+        int num = Math.min(width-1,blockHeight);
+        int applyIndex = Math.min(width,blockHeight);
+
+        // step through rows in the block
+        for( int i = 0; i < num; i++ ) {
+            // compute the new reflector and save it in a row in 'A'
+            computeHouseHolderRow(blockLength,A,gammas,i);
+            double gamma = gammas[A.row0+i];
+
+            // compute y
+            computeY(blockLength,A,V,i,gamma);
+
+            // compute v from y
+            computeRowOfV(blockLength,A,V,i,gamma);
+
+            // Apply the reflectors to the next row in 'A' only
+            if( i+1 < applyIndex ) {
+                applyReflectorsToRow( blockLength , A , V , i+1 );
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Computes W from the householder reflectors stored in the columns of the row block
+     * submatrix Y.
+     * </p>
+     *
+     * <p>
+     * Y = v<sup>(1)</sup><br>
+     * W = -β<sub>1</sub>v<sup>(1)</sup><br>
+     * for j=2:r<br>
+     *   z = -β(I +WY<sup>T</sup>)v<sup>(j)</sup> <br>
+     *   W = [W z]<br>
+     *   Y = [Y v<sup>(j)</sup>]<br>
+     * end<br>
+     * <br>
+     * where v<sup>(.)</sup> are the house holder vectors, and r is the block length.  Note that
+     * Y already contains the householder vectors so it does not need to be modified.
+     * </p>
+     *
+     * <p>
+     * Y and W are assumed to have the same number of rows and columns.
+     * </p>
+     */
+    public static void computeW_row( final int blockLength ,
+                                     final D1Submatrix64F Y , final D1Submatrix64F W ,
+                                     final double beta[] , int betaIndex ) {
+
+        final int heightY = Y.row1-Y.row0;
+        CommonOps.fill(W.original, 0);
+
+        // W = -beta*v(1)
+        BlockHouseHolder.scale_row(blockLength,Y,W,0,1,-beta[betaIndex++]);
+
+        final int min = Math.min(heightY,W.col1-W.col0);
+
+        // set up rest of the rows
+        for( int i = 1; i < min; i++ ) {
+            // w=-beta*(I + W*Y^T)*u
+            double b = -beta[betaIndex++];
+
+            // w = w -beta*W*(Y^T*u)
+            for( int j = 0; j < i; j++ ) {
+                double yv = BlockHouseHolder.innerProdRow(blockLength,Y,i,Y,j,1);
+                BlockVectorOps.add_row(blockLength,W,i,1,W,j,b*yv,W,i,1,Y.col1-Y.col0);
+            }
+
+            //w=w -beta*u + stuff above
+            BlockHouseHolder.add_row(blockLength,Y,i,b,W,i,1,W,i,1,Y.col1-Y.col0);
+        }
+    }
+
+
+    /**
+     * <p>
+     * Given an already computed tridiagonal decomposition, compute the V row block vector.<br>
+     * <br>
+     * y(:) = A*u<br>
+     * v(i) = y - (1/2)*γ*(y^T*u)*u
+     * </p>
+     */
+    public static void computeV_blockVector( final int blockLength ,
+                                             final D1Submatrix64F A ,
+                                             final double gammas[] ,
+                                             final D1Submatrix64F V )
+    {
+        int blockHeight = Math.min(blockLength,A.row1-A.row0);
+        if( blockHeight <= 1 )
+            return;
+        int width = A.col1-A.col0;
+        int num = Math.min(width-1,blockHeight);
+
+        for( int i = 0; i < num; i++ ) {
+            double gamma = gammas[A.row0+i];
+
+            // compute y
+            computeY(blockLength,A,V,i,gamma);
+
+            // compute v from y
+            computeRowOfV(blockLength,A,V,i,gamma);
+        }
+    }
+
+    /**
+     * <p>
+     * Applies the reflectors that have been computed previously to the specified row.
+     * <br>
+     * A = A + u*v^T + v*u^T only along the specified row in A.
+     * </p>
+     *
+     * @param blockLength
+     * @param A Contains the reflectors and the row being updated.
+     * @param V Contains previously computed 'v' vectors.
+     * @param row The row of 'A' that is to be updated.
+     */
+    public static void applyReflectorsToRow( final int blockLength ,
+                                             final D1Submatrix64F A ,
+                                             final D1Submatrix64F V ,
+                                             int row )
+    {
+        int height = Math.min(blockLength, A.row1 - A.row0);
+
+        double dataA[] = A.original.data;
+        double dataV[] = V.original.data;
+
+        int indexU,indexV;
+
+        // for each previously computed reflector
+        for( int i = 0; i < row; i++ ) {
+            int width = Math.min(blockLength,A.col1 - A.col0);
+
+            indexU = A.original.numCols*A.row0 + height*A.col0 + i*width + row;
+            indexV = V.original.numCols*V.row0 + height*V.col0 + i*width + row;
+
+            double u_row = (i+1 == row) ? 1.0 : dataA[ indexU ];
+            double v_row = dataV[ indexV ];
+
+            // take in account the leading one
+            double before = A.get(i,i+1);
+            A.set(i,i+1,1);
+
+            // grab only the relevant row from A = A + u*v^T + v*u^T
+            BlockVectorOps.add_row(blockLength,A,row,1,V,i,u_row,A,row,row,A.col1-A.col0);
+            BlockVectorOps.add_row(blockLength,A,row,1,A,i,v_row,A,row,row,A.col1-A.col0);
+
+            A.set(i,i+1,before);
+        }
+    }
+
+    /**
+     * <p>
+     * Computes the 'y' vector and stores the result in 'v'<br>
+     * <br>
+     * y = -γ(A + U*V^T + V*U^T)u
+     * </p>
+     *
+     * @param blockLength
+     * @param A Contains the reflectors and the row being updated.
+     * @param V Contains previously computed 'v' vectors.
+     * @param row The row of 'A' that is to be updated.
+     */
+    public static void computeY( final int blockLength ,
+                                 final D1Submatrix64F A ,
+                                 final D1Submatrix64F V ,
+                                 int row ,
+                                 double gamma )
+    {
+        // Elements in 'y' before 'row' are known to be zero and the element at 'row'
+        // is not used. Thus only elements after row and after are computed.
+        // y = A*u
+        multA_u(blockLength,A,V,row);
+
+        for( int i = 0; i < row; i++ ) {
+            // y = y + u_i*v_i^t*u + v_i*u_i^t*u
+
+            // v_i^t*u
+            double dot_v_u = BlockHouseHolder.innerProdRow(blockLength,A,row,V,i,1);
+
+            // u_i^t*u
+            double dot_u_u = BlockHouseHolder.innerProdRow(blockLength,A,row,A,i,1);
+
+            // y = y + u_i*(v_i^t*u)
+            // the ones in these 'u' are skipped over since the next submatrix of A
+            // is only updated
+            BlockVectorOps.add_row(blockLength,V,row,1,A,i,dot_v_u,V,row,row+1,A.col1-A.col0);
+
+            // y = y + v_i*(u_i^t*u)
+            // the 1 in U is taken account above
+            BlockVectorOps.add_row(blockLength,V,row,1,V,i,dot_u_u,V,row,row+1,A.col1-A.col0);
+        }
+
+        // y = -gamma*y
+        BlockVectorOps.scale_row(blockLength,V,row,-gamma,V,row,row+1,V.col1-V.col0);
+    }
+
+    /**
+     * <p>
+     * Multiples the appropriate submatrix of A by the specified reflector and stores
+     * the result ('y') in V.<br>
+     * <br>
+     * y = A*u<br>
+     * </p>
+     *
+     * @param blockLength
+     * @param A Contains the 'A' matrix and 'u' vector.
+     * @param V Where resulting 'y' row vectors are stored.
+     * @param row row in matrix 'A' that 'u' vector and the row in 'V' that 'y' is stored in.
+     */
+    public static void multA_u( final int blockLength ,
+                                final D1Submatrix64F A ,
+                                final D1Submatrix64F V ,
+                                int row )
+    {
+        int heightMatA = A.row1-A.row0;
+
+        for( int i = row+1; i < heightMatA; i++ ) {
+
+            double val = innerProdRowSymm(blockLength,A,row,A,i,1);
+
+            V.set(row,i,val);
+        }
+    }
+
+    public static double innerProdRowSymm(int blockLength,
+                                          D1Submatrix64F A,
+                                          int rowA,
+                                          D1Submatrix64F B,
+                                          int rowB, int zeroOffset ) {
+        int offset = rowA + zeroOffset;
+        if( offset + B.col0 >= B.col1 )
+            return 0;
+
+        if( offset < rowB ) {
+            // take in account the one in 'A'
+            double total = B.get(offset,rowB);
+
+            total += BlockVectorOps.dot_row_col(blockLength,A,rowA,B,rowB,offset+1,rowB);
+            total += BlockVectorOps.dot_row(blockLength,A,rowA,B,rowB,rowB,A.col1-A.col0);
+
+            return total;
+        } else {
+            // take in account the one in 'A'
+            double total = B.get(rowB,offset);
+
+            total += BlockVectorOps.dot_row(blockLength,A,rowA,B,rowB,offset+1,A.col1-A.col0);
+
+            return total;
+        }
+    }
+
+    /**
+     * <p>
+     * Final computation for a single row of 'v':<br>
+     * <br>
+     * v = y -(1/2)γ(y^T*u)*u
+     * </p>
+     *
+     * @param blockLength
+     * @param A
+     * @param V
+     * @param row
+     * @param gamma
+     */
+    public static void computeRowOfV( final int blockLength ,
+                                      final D1Submatrix64F A ,
+                                      final D1Submatrix64F V ,
+                                      int row ,
+                                      double gamma )
+    {
+        // val=(y^T*u)
+        double val = BlockHouseHolder.innerProdRow(blockLength,A,row,V,row,1);
+
+        // take in account the one
+        double before = A.get(row,row+1);
+        A.set(row,row+1,1);
+
+        // v = y - (1/2)gamma*val * u
+        BlockVectorOps.add_row(blockLength,V,row,1,A,row,-0.5*gamma*val,V,row,row+1,A.col1-A.col0);
+
+        A.set(row,row+1,before);
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/block/decomposition/qr/BlockHouseHolder.java b/main/dense64/src/org/ejml/alg/block/decomposition/qr/BlockHouseHolder.java
new file mode 100644
index 0000000..7e897fd
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/block/decomposition/qr/BlockHouseHolder.java
@@ -0,0 +1,1034 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block.decomposition.qr;
+
+import org.ejml.alg.block.BlockInnerMultiplication;
+import org.ejml.alg.block.BlockVectorOps;
+import org.ejml.data.D1Submatrix64F;
+
+/**
+ *
+ * <p>
+ * Contains various helper functions for performing a block matrix QR decomposition.
+ * </p>
+ *
+ * <p>
+ * Assumptions:
+ * <ul>
+ *  <le> All submatrices are aligned along the inner blocks of the {@link org.ejml.data.BlockMatrix64F}.
+ *  <le> Some times vectors are assumed to have leading zeros and a one.
+ * </ul>
+ *
+ * @author Peter Abeles
+ */
+public class BlockHouseHolder {
+
+    /**
+     * Performs a standard QR decomposition on the specified submatrix that is one block wide.
+     *
+     * @param blockLength
+     * @param Y
+     * @param gamma
+     */
+    public static boolean decomposeQR_block_col( final int blockLength ,
+                                                 final D1Submatrix64F Y ,
+                                                 final double gamma[] )
+    {
+        int width = Y.col1-Y.col0;
+        int height = Y.row1-Y.row0;
+        int min = Math.min(width,height);
+        for( int i = 0; i < min; i++ ) {
+            // compute the householder vector
+            if (!computeHouseHolderCol(blockLength, Y, gamma, i))
+                return false;
+
+            // apply to rest of the columns in the block
+            rank1UpdateMultR_Col(blockLength,Y,i,gamma[Y.col0+i]);
+        }
+
+        return true;
+    }
+
+    /**
+     * <p>
+     * Computes the householder vector that is used to create reflector for the column.
+     * The results are stored in the original matrix.
+     * </p>
+     *
+     * <p>
+     * The householder vector 'u' is computed as follows:<br>
+     * <br>
+     * u(1) = 1 <br>
+     * u(i) = x(i)/(τ + x(1))<br>
+     * </p>
+     *
+     * The first element is implicitly assumed to be one and not written.
+     *
+     * @return If there was any problems or not. true = no problem.
+     */
+    public static boolean computeHouseHolderCol( final int blockLength, final D1Submatrix64F Y,
+                                                 final double[] gamma, final int i) {
+        double max = BlockHouseHolder.findMaxCol(blockLength,Y,i);
+
+        if( max == 0.0 ) {
+            return false;
+        } else {
+            // computes tau and normalizes u by max
+            double tau = computeTauAndDivideCol(blockLength, Y, i, max);
+
+            // divide u by u_0
+            double u_0 = Y.get(i,i) + tau;
+            divideElementsCol(blockLength,Y,i, u_0 );
+
+            gamma[Y.col0+i] = u_0/tau;
+            tau *= max;
+
+            // after the reflector is applied the column would be all zeros but be -tau in the first element
+            Y.set(i,i,-tau);
+        }
+        return true;
+    }
+
+    /**
+     * <p>
+     * Computes the householder vector from the specified row
+     * </p>
+     *
+     * <p>
+     * The householder vector 'u' is computed as follows:<br>
+     * <br>
+     * u(1) = 1 <br>
+     * u(i) = x(i)/(τ + x(1))<br>
+     * </p>
+     *
+     * The first element is implicitly assumed to be one and not written.
+     *
+     * @return If there was any problems or not. true = no problem.
+     */
+    public static boolean computeHouseHolderRow( final int blockLength, final D1Submatrix64F Y,
+                                                 final double[] gamma, final int i) {
+        double max = BlockHouseHolder.findMaxRow(blockLength,Y,i,i+1);
+
+        if( max == 0.0 ) {
+            return false;
+        } else {
+            // computes tau and normalizes u by max
+            double tau = computeTauAndDivideRow(blockLength, Y, i,i+1, max);
+
+            // divide u by u_0
+            double u_0 = Y.get(i,i+1) + tau;
+            BlockVectorOps.div_row(blockLength,Y,i,u_0,Y,i,i+1,Y.col1-Y.col0);   
+
+            gamma[Y.row0+i] = u_0/tau;
+
+            // after the reflector is applied the column would be all zeros but be -tau in the first element
+            Y.set(i,i+1,-tau*max);
+        }
+        return true;
+    }
+
+    /**
+     * <p>
+     * Applies a householder reflector stored in column 'col' to the remainder of the columns
+     * in the block after it.  Takes in account leading zeros and one.<br>
+     * <br>
+     * A = (I - γ*u*u<sup>T</sup>)*A<br>
+     * </p>
+     *
+     * @param A submatrix that is at most one block wide and aligned along inner blocks
+     * @param col The column in A containing 'u'
+     *
+     */
+    public static void rank1UpdateMultR_Col( final int blockLength ,
+                                            final D1Submatrix64F A , final int col , final double gamma )
+    {
+        final int width = Math.min(blockLength,A.col1 - A.col0);
+
+        final double dataA[] = A.original.data;
+
+        for( int j = col+1; j < width; j++ ) {
+
+            // total = U^T * A(:,j)
+            double total = innerProdCol(blockLength, A, col, width, j, width);
+
+            total *= gamma;
+            // A(:,j) - gamma*U*total
+
+            for( int i = A.row0; i < A.row1; i += blockLength ) {
+                int height = Math.min( blockLength , A.row1 - i );
+
+                int indexU = i*A.original.numCols + height*A.col0 + col;
+                int indexA = i*A.original.numCols + height*A.col0 + j;
+
+                if( i == A.row0 ) {
+                    indexU += width*(col+1);
+                    indexA += width*col;
+
+                    dataA[ indexA ] -= total;
+
+                    indexA += width;
+
+                    for( int k = col+1; k < height; k++ , indexU += width, indexA += width ) {
+                        dataA[ indexA ] -= total*dataA[ indexU ];
+                    }
+                } else {
+                    int endU = indexU + width*height;
+                    // for( int k = 0; k < height; k++
+                    for( ; indexU != endU; indexU += width, indexA += width ) {
+                        dataA[ indexA ] -= total*dataA[ indexU ];
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Applies a householder reflector stored in column 'col' to the top block row (excluding
+     * the first column) of A.  Takes in account leading zeros and one.<br>
+     * <br>
+     * A = (I - γ*u*u<sup>T</sup>)*A<br>
+     * </p>
+     *
+     * @param A submatrix that is at most one block wide and aligned along inner blocks
+     * @param col The column in A containing 'u'
+     *
+     */
+    public static void rank1UpdateMultR_TopRow( final int blockLength ,
+                                                final D1Submatrix64F A , final int col , final double gamma )
+    {
+        final double dataA[] = A.original.data;
+
+        final int widthCol = Math.min( blockLength , A.col1 - col );
+
+        // step through columns in top block, skipping over the first block
+        for( int colStartJ = A.col0 + blockLength; colStartJ < A.col1; colStartJ += blockLength ) {
+            final int widthJ = Math.min( blockLength , A.col1 - colStartJ);
+
+            for( int j = 0; j < widthJ; j++ ) {
+                // total = U^T * A(:,j) * gamma
+                double total = innerProdCol(blockLength, A, col, widthCol, (colStartJ-A.col0)+j, widthJ)*gamma;
+
+                // A(:,j) - gamma*U*total
+                // just update the top most block
+                int i = A.row0;
+                int height = Math.min( blockLength , A.row1 - i );
+
+                int indexU = i*A.original.numCols + height*A.col0 + col;
+                int indexA = i*A.original.numCols + height*colStartJ + j;
+
+                // take in account zeros and one
+                indexU += widthCol*(col+1);
+                indexA += widthJ*col;
+
+                dataA[ indexA ] -= total;
+
+                indexA += widthJ;
+
+                for( int k = col+1; k < height; k++ , indexU += widthCol, indexA += widthJ ) {
+                    dataA[ indexA ] -= total*dataA[ indexU ];
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Applies a householder reflector stored in row 'row' to the remainder of the row
+     * in the block after it.  Takes in account leading zeros and one.<br>
+     * <br>
+     * A = A*(I - γ*u*u<sup>T</sup>)<br>
+     * </p>
+     *
+     * @param A submatrix that is block aligned
+     * @param row The row in A containing 'u'
+     * @param colStart First index in 'u' that the reflector starts at
+     *
+     */
+    public static void rank1UpdateMultL_Row( final int blockLength ,
+                                             final D1Submatrix64F A ,
+                                             final int row , final int colStart , final double gamma )
+    {
+        final int height = Math.min(blockLength,A.row1 - A.row0);
+
+        final double dataA[] = A.original.data;
+
+        int zeroOffset = colStart-row;
+
+        for( int i = row+1; i < height; i++ ) {
+            // total = U^T * A(i,:)
+            double total = innerProdRow(blockLength, A, row, A , i, zeroOffset );
+
+            total *= gamma;
+            // A(i,:) - gamma*U*total
+
+            for( int j = A.col0; j < A.col1; j += blockLength ) {
+                int width = Math.min( blockLength , A.col1 - j );
+
+                int indexU = A.row0*A.original.numCols + height*j + row*width;
+                int indexA = A.row0*A.original.numCols + height*j + i*width;
+
+                if( j == A.col0 ) {
+                    indexU += colStart+1;
+                    indexA += colStart;
+
+                    dataA[indexA++] -= total;
+
+                    for( int k = colStart+1; k < width; k++ ) {
+                        dataA[ indexA++ ] -= total*dataA[ indexU++ ];
+                    }
+                } else {
+                    for( int k = 0; k < width; k++ ) {
+                        dataA[ indexA++ ] -= total*dataA[ indexU++ ];
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Applies a householder reflector stored in row 'row' to the left column block.
+     * Takes in account leading zeros and one.<br>
+     * <br>
+     * A = A*(I - γ*u*u<sup>T</sup>)<br>
+     * </p>
+     *
+     * @param A submatrix that is block aligned
+     * @param row The row in A containing 'u'
+     * @param zeroOffset How far off the diagonal is the first element in 'u'
+     *
+     */
+    public static void rank1UpdateMultL_LeftCol( final int blockLength ,
+                                                 final D1Submatrix64F A ,
+                                                 final int row , final double gamma , int zeroOffset )
+    {
+        final int heightU = Math.min(blockLength,A.row1 - A.row0);
+        final int width = Math.min(blockLength,A.col1-A.col0);
+
+        final double data[] = A.original.data;
+
+        for( int blockStart = A.row0+blockLength; blockStart < A.row1; blockStart += blockLength) {
+            final int heightA = Math.min(blockLength,A.row1 - blockStart);
+
+            for( int i = 0; i < heightA; i++ ) {
+
+                // total = U^T * A(i,:)
+                double total = innerProdRow(blockLength, A, row, A, i+(blockStart-A.row0), zeroOffset);
+
+                total *= gamma;
+
+                // A(i,:) - gamma*U*total
+//                plusScale_row(blockLength,);
+
+                int indexU = A.row0*A.original.numCols + heightU*A.col0 + row*width;
+                int indexA = blockStart*A.original.numCols + heightA*A.col0 + i*width;
+
+                // skip over zeros and assume first element in U is 1
+                indexU += zeroOffset+1;
+                indexA += zeroOffset;
+
+                data[indexA++] -= total;
+
+                for( int k = zeroOffset+1; k < width; k++ ) {
+                    data[ indexA++ ] -= total*data[ indexU++ ];
+                }
+
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Computes the inner product of column vector 'colA' against column vector 'colB' while taking account leading zeros and one.<br>
+     * <br>
+     * ret = a<sup>T*b
+     * </p>
+     *
+     * <p>
+     * Column A is assumed to be a householder vector.  Element at 'colA' is one and previous ones are zero.
+     * </p>
+     *
+     * @param blockLength
+     * @param A block aligned submatrix.
+     * @param colA Column inside the block of first column vector.
+     * @param widthA how wide the column block that colA is inside of.
+     * @param colB Column inside the block of second column vector.
+     * @param widthB how wide the column block that colB is inside of.
+     * @return dot product of the two vectors.
+     */
+    public static double innerProdCol( int blockLength, D1Submatrix64F A,
+                                          int colA, int widthA,
+                                          int colB, int widthB ) {
+        double total = 0;
+
+        final double data[] = A.original.data;
+        // first column in the blocks
+        final int colBlockA = A.col0 + colA - colA % blockLength;
+        final int colBlockB = A.col0 + colB - colB % blockLength;
+        colA = colA % blockLength;
+        colB = colB % blockLength;
+
+        // compute dot product down column vectors
+        for( int i = A.row0; i < A.row1; i += blockLength ) {
+
+            int height = Math.min( blockLength , A.row1 - i );
+
+            int indexA = i*A.original.numCols + height*colBlockA + colA;
+            int indexB = i*A.original.numCols + height*colBlockB + colB;
+
+            if( i == A.row0 ) {
+                // handle leading zeros
+                indexA += widthA*(colA + 1);
+                indexB += widthB*colA;
+
+                // handle leading one
+                total = data[indexB];
+
+                indexB += widthB;
+
+                // standard vector dot product
+                int endA = indexA + (height - colA - 1)*widthA;
+                for( ; indexA != endA; indexA += widthA, indexB += widthB ) {
+//                    for( int k = col+1; k < height; k++ , indexU += width, indexA += width ) {
+                    total += data[indexA] * data[indexB];
+                }
+            } else {
+                // standard vector dot product
+                int endA = indexA + widthA*height;
+//                    for( int k = 0; k < height; k++ ) {
+                for( ; indexA != endA; indexA += widthA, indexB += widthB ) {
+                    total += data[indexA] * data[indexB];
+                }
+            }
+        }
+        return total;
+    }
+
+    /**
+     * <p>
+     * Computes the inner product of row vector 'rowA' against row vector 'rowB' while taking account leading zeros and one.<br>
+     * <br>
+     * ret = a<sup>T</sup>*b
+     * </p>
+     *
+     * <p>
+     * Row A is assumed to be a householder vector.  Element at 'colStartA' is one and previous elements are zero.
+     * </p>
+     *
+     * @param blockLength
+     * @param A block aligned submatrix.
+     * @param rowA Row index inside the sub-matrix of first row vector has zeros and ones..
+     * @param rowB Row index inside the sub-matrix of second row vector.
+     * @return dot product of the two vectors.
+     */
+    public static double innerProdRow(int blockLength,
+                                      D1Submatrix64F A,
+                                      int rowA,
+                                      D1Submatrix64F B,
+                                      int rowB, int zeroOffset ) {
+        int offset = rowA + zeroOffset;
+        if( offset + B.col0 >= B.col1 )
+            return 0;
+
+        // take in account the one in 'A'
+        double total = B.get(rowB,offset);
+
+        total += BlockVectorOps.dot_row(blockLength,A,rowA,B,rowB,offset+1,A.col1-A.col0);
+
+        return total;
+    }
+
+    public static void add_row( final int blockLength ,
+                                D1Submatrix64F A , int rowA , double alpha ,
+                                D1Submatrix64F B , int rowB , double beta ,
+                                D1Submatrix64F C , int rowC ,
+                                int zeroOffset , int end ) {
+        int offset = rowA+zeroOffset;
+
+        if( C.col0 + offset >= C.col1 )
+            return;
+        // handle leading one
+        C.set(rowC,offset,alpha+B.get(rowB,offset)*beta);
+
+        BlockVectorOps.add_row(blockLength,A,rowA,alpha,B,rowB,beta,C,rowC,offset+1,end);
+    }
+
+    /**
+     * Divides the elements at the specified column by 'val'.  Takes in account
+     * leading zeros and one.
+     */
+    public static void divideElementsCol( final int blockLength ,
+                                       final D1Submatrix64F Y , final int col , final double val ) {
+        final int width = Math.min(blockLength,Y.col1-Y.col0);
+
+        final double dataY[] = Y.original.data;
+
+        for( int i = Y.row0; i < Y.row1; i += blockLength ) {
+            int height = Math.min( blockLength , Y.row1 - i );
+
+            int index = i*Y.original.numCols + height*Y.col0 + col;
+
+            if( i == Y.row0 ) {
+                index += width*(col+1);
+
+                for( int k = col+1; k < height; k++ , index += width ) {
+                    dataY[index] /= val;
+                }
+            } else {
+                int endIndex = index + width*height;
+                //for( int k = 0; k < height; k++
+                for( ; index != endIndex; index += width ) {
+                    dataY[index] /= val;
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Scales the elements in the specified row starting at element colStart by 'val'.<br>
+     * W = val*Y
+     *
+     * Takes in account zeros and leading one automatically.
+     *
+     * @param zeroOffset How far off the diagonal is the first element in the vector.
+     */
+    public static void scale_row( final int blockLength ,
+                                  final D1Submatrix64F Y ,
+                                  final D1Submatrix64F W ,
+                                  final int row ,
+                                  final int zeroOffset,
+                                  final double val ) {
+
+
+        int offset = row+zeroOffset;
+
+        if( offset >= W.col1-W.col0 )
+            return;
+        
+        // handle the one
+        W.set(row,offset,val);
+
+        // scale rest of the vector
+        BlockVectorOps.scale_row(blockLength,Y,row,val,W,row,offset+1,Y.col1-Y.col0);
+    }
+
+    /**
+     * <p>
+     * From the specified column of Y tau is computed and each element is divided by 'max'.
+     * See code below:
+     * </p>
+     *
+     * <pre>
+     * for i=col:Y.numRows
+     *   Y[i][col] = u[i][col] / max
+     *   tau = tau + u[i][col]*u[i][col]
+     * end
+     * tau = sqrt(tau)
+     * if( Y[col][col] < 0 )
+     *    tau = -tau;
+     * </pre>
+     *
+     */
+    public static double computeTauAndDivideCol( final int blockLength ,
+                                                 final D1Submatrix64F Y ,
+                                                 final int col , final double max ) {
+        final int width = Math.min(blockLength,Y.col1-Y.col0);
+
+        final double dataY[] = Y.original.data;
+
+        double top=0;
+        double norm2 = 0;
+
+        for( int i = Y.row0; i < Y.row1; i += blockLength ) {
+            int height = Math.min( blockLength , Y.row1 - i );
+
+            int index = i*Y.original.numCols + height*Y.col0 + col;
+
+            if( i == Y.row0 ) {
+                index += width*col;
+                // save this value so that the sign can be determined later on
+                top = dataY[index] /= max;
+                norm2 += top*top;
+                index += width;
+
+                for( int k = col+1; k < height; k++ , index += width ) {
+                    double val = dataY[index] /= max;
+                    norm2 += val*val;
+                }
+            } else {
+                for( int k = 0; k < height; k++ , index += width ) {
+                    double val = dataY[index] /= max;
+                    norm2 += val*val;
+                }
+            }
+        }
+
+        norm2 = Math.sqrt(norm2);
+
+        if( top < 0 )
+            norm2 = -norm2;
+
+        return norm2;
+    }
+
+    /**
+     * <p>
+     * From the specified row of Y tau is computed and each element is divided by 'max'.
+     * See code below:
+     * </p>
+     *
+     * <pre>
+     * for j=row:Y.numCols
+     *   Y[row][j] = u[row][j] / max
+     *   tau = tau + u[row][j]*u[row][j]
+     * end
+     * tau = sqrt(tau)
+     * if( Y[row][row] < 0 )
+     *    tau = -tau;
+     * </pre>
+     *
+     * @param row Which row in the block will be processed
+     * @param colStart The first column that computation of tau will start at
+     * @param max used to normalize and prevent buffer over flow
+     *
+     */
+    public static double computeTauAndDivideRow( final int blockLength ,
+                                                 final D1Submatrix64F Y ,
+                                                 final int row , int colStart , final double max ) {
+        final int height = Math.min(blockLength , Y.row1-Y.row0);
+
+        final double dataY[] = Y.original.data;
+
+        double top=0;
+        double norm2 = 0;
+
+        int startJ = Y.col0 + colStart - colStart%blockLength;
+        colStart = colStart%blockLength;
+
+        for( int j = startJ; j < Y.col1; j += blockLength ) {
+            int width = Math.min( blockLength , Y.col1 - j );
+
+            int index = Y.row0*Y.original.numCols + height*j + row*width;
+
+            if( j == startJ ) {
+                index += colStart;
+                // save this value so that the sign can be determined later on
+                top = dataY[index] /= max;
+                norm2 += top*top;
+                index++;
+
+                for( int k = colStart+1; k < width; k++ ) {
+                    double val = dataY[index++] /= max;
+                    norm2 += val*val;
+                }
+            } else {
+                for( int k = 0; k < width; k++ ) {
+                    double val = dataY[index++] /= max;
+                    norm2 += val*val;
+                }
+            }
+        }
+
+        norm2 = Math.sqrt(norm2);
+
+        if( top < 0 )
+            norm2 = -norm2;
+
+        return norm2;
+    }
+
+    /**
+     * Finds the element in the column with the largest absolute value. The offset
+     * from zero is automatically taken in account based on the column.
+     */
+    public static double findMaxCol( final int blockLength , final D1Submatrix64F Y , final int col )
+    {
+        final int width = Math.min(blockLength,Y.col1-Y.col0);
+
+        final double dataY[] = Y.original.data;
+
+        double max = 0;
+
+        for( int i = Y.row0; i < Y.row1; i += blockLength ) {
+            int height = Math.min( blockLength , Y.row1 - i );
+
+            int index = i*Y.original.numCols + height*Y.col0 + col;
+
+            if( i == Y.row0 ) {
+                index += width*col;
+                for( int k = col; k < height; k++ , index += width ) {
+                    double v = Math.abs(dataY[index]);
+                    if( v > max ) {
+                        max = v;
+                    }
+                }
+            } else {
+                for( int k = 0; k < height; k++ , index += width ) {
+                    double v = Math.abs(dataY[index]);
+                    if( v > max ) {
+                        max = v;
+                    }
+                }
+            }
+        }
+
+        return max;
+    }
+
+    /**
+     * Finds the element in the column with the largest absolute value. The offset
+     * from zero is automatically taken in account based on the column.
+     */
+    public static double findMaxRow( final int blockLength ,
+                                          final D1Submatrix64F Y ,
+                                          final int row , final int colStart ) {
+        final int height = Math.min(blockLength , Y.row1-Y.row0);
+
+        final double dataY[] = Y.original.data;
+
+        double max = 0;
+
+        for( int j = Y.col0; j < Y.col1; j += blockLength ) {
+            int width = Math.min( blockLength , Y.col1 - j );
+
+            int index = Y.row0*Y.original.numCols + height*j + row*width;
+
+            if( j == Y.col0 ) {
+                index += colStart;
+
+                for( int k = colStart; k < width; k++ ) {
+                    double v = Math.abs(dataY[index++]);
+                    if( v > max ) {
+                        max = v;
+                    }
+                }
+            } else {
+                for( int k = 0; k < width; k++ ) {
+                    double v = Math.abs(dataY[index++]);
+                    if( v > max ) {
+                        max = v;
+                    }
+                }
+            }
+        }
+
+        return max;
+    }
+
+    /**
+     * <p>
+     * Computes W from the householder reflectors stored in the columns of the column block
+     * submatrix Y.
+     * </p>
+     *
+     * <p>
+     * Y = v<sup>(1)</sup><br>
+     * W = -β<sub>1</sub>v<sup>(1)</sup><br>
+     * for j=2:r<br>
+     *   z = -β(I +WY<sup>T</sup>)v<sup>(j)</sup> <br>
+     *   W = [W z]<br>
+     *   Y = [Y v<sup>(j)</sup>]<br>
+     * end<br>
+     * <br>
+     * where v<sup>(.)</sup> are the house holder vectors, and r is the block length.  Note that
+     * Y already contains the householder vectors so it does not need to be modified.
+     * </p>
+     *
+     * <p>
+     * Y and W are assumed to have the same number of rows and columns.
+     * </p>
+     *
+     * @param Y Input matrix containing householder vectors.  Not modified.
+     * @param W Resulting W matrix. Modified.
+     * @param temp Used internally.  Must have W.numCols elements.
+     * @param beta Beta's for householder vectors.
+     * @param betaIndex Index of first relevant beta.
+     */
+    public static void computeW_Column( final int blockLength ,
+                                        final D1Submatrix64F Y , final D1Submatrix64F W ,
+                                        final double temp[], final double beta[] , int betaIndex ) {
+
+        final int widthB = W.col1-W.col0;
+
+        // set the first column in W
+        initializeW(blockLength, W, Y, widthB, beta[betaIndex++]);
+
+        final int min = Math.min(widthB,W.row1-W.row0);
+
+        // set up rest of the columns
+        for( int j = 1; j < min; j++ ) {
+            //compute the z vector and insert it into W
+            computeY_t_V(blockLength,Y,j,temp);
+            computeZ(blockLength,Y,W,j,temp,beta[betaIndex++]);
+        }
+    }
+
+    /**
+     * <p>
+     * Sets W to its initial value using the first column of 'y' and the value of 'b':
+     * <br>
+     * W = -βv<br>
+     * <br>
+     * where v = Y(:,0).
+     * </p>
+     *
+     * @param blockLength size of the inner block
+     * @param W Submatrix being initialized.
+     * @param Y Contains householder vector
+     * @param widthB How wide the W block matrix is.
+     * @param b beta
+     */
+    public static void initializeW( final int blockLength,
+                                    final D1Submatrix64F W, final D1Submatrix64F Y,
+                                    final int widthB, final double b) {
+
+        final double dataW[] = W.original.data;
+        final double dataY[] = Y.original.data;
+
+        for( int i = W.row0; i < W.row1; i += blockLength ) {
+            int heightW = Math.min( blockLength , W.row1 - i );
+
+            int indexW = i*W.original.numCols + heightW*W.col0;
+            int indexY = i*Y.original.numCols + heightW*Y.col0;
+
+            // take in account the first element in V being 1
+            if( i == W.row0 ) {
+                dataW[indexW] = -b;
+                indexW += widthB;
+                indexY += widthB;
+                for( int k = 1; k < heightW; k++ , indexW += widthB , indexY += widthB ) {
+                    dataW[indexW] = -b* dataY[indexY];
+                }
+            } else {
+                for( int k = 0; k < heightW; k++ , indexW += widthB , indexY += widthB ) {
+                    dataW[indexW] = -b* dataY[indexY];
+                }
+            }
+        }
+    }
+
+    /**
+     * Computes the vector z and inserts it into 'W':<br>
+     * <br>
+     * z = - β<sub>j</sub>*(V<sup>j</sup> + W*h)<br>
+     * <br>
+     * where h is a vector of length 'col' and was computed using {@link #computeY_t_V}.
+     * V is a column in the Y matrix. Z is a column in the W matrix.  Both Z and V are
+     * column 'col'.
+     */
+    public static void computeZ( final int blockLength , final D1Submatrix64F Y , final D1Submatrix64F W,
+                                 final int col , final double []temp , final double beta )
+    {
+        final int width = Y.col1-Y.col0;
+
+        final double dataW[] = W.original.data;
+        final double dataY[] = Y.original.data;
+
+        final int colsW = W.original.numCols;
+
+        final double beta_neg = -beta;
+
+        for( int i = Y.row0; i < Y.row1; i += blockLength ) {
+            int heightW = Math.min( blockLength , Y.row1 - i );
+
+            int indexW = i*colsW + heightW*W.col0;
+            int indexZ = i*colsW + heightW*W.col0 + col;
+            int indexV = i*Y.original.numCols + heightW*Y.col0 + col;
+
+            if( i == Y.row0 ) {
+                // handle the triangular portion with the leading zeros and the one
+                for( int k = 0; k < heightW; k++ , indexZ += width, indexW += width , indexV += width ) {
+                    // compute the rows of W * h
+                    double total = 0;
+
+                    for( int j = 0; j < col; j++ ) {
+                        total += dataW[indexW+j] * temp[j];
+                    }
+
+                    // add the two vectors together and multiply by -beta
+                    if( k < col ) {  // zeros
+                        dataW[indexZ] = -beta*total;
+                    } else if( k == col ) { // one
+                        dataW[indexZ] = beta_neg*(1.0 + total);
+                    } else { // normal data
+                        dataW[indexZ] = beta_neg*(dataY[indexV] + total);
+                    }
+                }
+            } else {
+                int endZ = indexZ + width*heightW;
+//                for( int k = 0; k < heightW; k++ ,
+                while( indexZ != endZ ) {
+                    // compute the rows of W * h
+                    double total = 0;
+
+                    for( int j = 0; j < col; j++ ) {
+                        total += dataW[indexW+j] * temp[j];
+                    }
+
+                    // add the two vectors together and multiply by -beta
+                    dataW[indexZ] = beta_neg*(dataY[indexV] + total);
+
+                    indexZ += width; indexW += width; indexV += width;
+                }
+            }
+        }
+    }
+
+    /**
+     * Computes Y<sup>T</sup>v<sup>(j)</sup>.  Where Y are the columns before 'col' and v is the column
+     * at 'col'.  The zeros and ones are taken in account.  The solution is a vector with 'col' elements.
+     *
+     * width of Y must be along the block of original matrix A
+     *
+     * @param temp Temporary storage of least length 'col'
+     */
+    public static void computeY_t_V( final int blockLength , final D1Submatrix64F Y ,
+                                     final int col , final double []temp )
+    {
+        final int widthB = Y.col1-Y.col0;
+
+        for( int j = 0; j < col; j++ ) {
+            temp[j] = innerProdCol(blockLength,Y,col,widthB,j,widthB);
+        }
+    }
+
+    /**
+     * Special multiplication that takes in account the zeros and one in Y, which
+     * is the matrix that stores the householder vectors.
+     *
+     */
+    public static void multAdd_zeros( final int blockLength ,
+                                      final D1Submatrix64F Y , final D1Submatrix64F B ,
+                                      final D1Submatrix64F C )
+    {
+        int widthY = Y.col1 - Y.col0;
+
+        for( int i = Y.row0; i < Y.row1; i += blockLength ) {
+            int heightY = Math.min( blockLength , Y.row1 - i );
+
+            for( int j = B.col0; j < B.col1; j += blockLength ) {
+                int widthB = Math.min( blockLength , B.col1 - j );
+
+                int indexC = (i-Y.row0+C.row0)*C.original.numCols + (j-B.col0+C.col0)*heightY;
+
+                for( int k = Y.col0; k < Y.col1; k += blockLength ) {
+                    int indexY = i*Y.original.numCols + k*heightY;
+                    int indexB = (k-Y.col0+B.row0)*B.original.numCols + j*widthY;
+
+                    if( i == Y.row0 ) {
+                        multBlockAdd_zerosone(Y.original.data,B.original.data,C.original.data,
+                            indexY,indexB,indexC,heightY,widthY,widthB);
+                    } else {
+                        BlockInnerMultiplication.blockMultPlus(Y.original.data,B.original.data,C.original.data,
+                                indexY,indexB,indexC,heightY,widthY,widthB);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Inner block mult add operation that takes in account the zeros and on in dataA,
+     * which is the top part of the Y block vector that has the householder vectors.<br>
+     * <br>
+     * C = C + A * B
+     * </p>
+     */
+    public static void multBlockAdd_zerosone( double[] dataA, double []dataB, double []dataC,
+                                              int indexA, int indexB, int indexC,
+                                              final int heightA, final int widthA, final int widthC) {
+
+
+        for( int i = 0; i < heightA; i++ ) {
+            for( int j = 0; j < widthC; j++ ) {
+                double val = i < widthA ? dataB[i*widthC+j+indexB] : 0;
+
+                int end = Math.min(i,widthA);
+
+                for( int k = 0; k < end; k++ ) {
+                    val += dataA[i*widthA + k + indexA] * dataB[k*widthC + j + indexB];
+                }
+
+                dataC[ i*widthC + j + indexC ] += val;
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs a matrix multiplication on the block aligned submatrices.  A is
+     * assumed to be block column vector that is lower triangular with diagonal elements set to 1.<br>
+     * <br>
+     * C = A^T * B
+     * </p>
+     */
+    public static void multTransA_vecCol( final int blockLength ,
+                                          D1Submatrix64F A , D1Submatrix64F B ,
+                                          D1Submatrix64F C )
+    {
+        int widthA = A.col1 - A.col0;
+        if( widthA > blockLength )
+            throw new IllegalArgumentException("A is expected to be at most one block wide.");
+
+        for( int j = B.col0; j < B.col1; j += blockLength ) {
+            int widthB = Math.min( blockLength , B.col1 - j );
+
+            int indexC = C.row0*C.original.numCols + (j-B.col0+C.col0)*widthA;
+
+            for( int k = A.row0; k < A.row1; k += blockLength ) {
+                int heightA = Math.min( blockLength , A.row1 - k );
+
+                int indexA = k*A.original.numCols + A.col0*heightA;
+                int indexB = (k-A.row0+B.row0)*B.original.numCols + j*heightA;
+
+                if( k == A.row0 )
+                    multTransABlockSet_lowerTriag(A.original.data,B.original.data,C.original.data,
+                            indexA,indexB,indexC,heightA,widthA,widthB);
+                else
+                    BlockInnerMultiplication.blockMultPlusTransA(A.original.data,B.original.data,C.original.data,
+                            indexA,indexB,indexC,heightA,widthA,widthB);
+            }
+        }
+    }
+
+    /**
+     * Performs a matrix multiplication on an single inner block where A is assumed to be lower triangular with diagonal
+     * elements equal to 1.<br>
+     * <br>
+     * C = A^T * B
+     */
+    protected static void multTransABlockSet_lowerTriag( double[] dataA, double []dataB, double []dataC,
+                                                         int indexA, int indexB, int indexC,
+                                                         final int heightA, final int widthA, final int widthC) {
+        for( int i = 0; i < widthA; i++ ) {
+            for( int j = 0; j < widthC; j++ ) {
+                double val = i < heightA ? dataB[i*widthC + j + indexB] : 0;
+
+                for( int k = i+1; k < heightA; k++ ) {
+                    val += dataA[k*widthA + i + indexA] * dataB[k*widthC + j + indexB];
+                }
+
+                dataC[ i*widthC + j + indexC ] = val;
+            }
+        }
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/block/decomposition/qr/QRDecompositionHouseholder_B64.java b/main/dense64/src/org/ejml/alg/block/decomposition/qr/QRDecompositionHouseholder_B64.java
new file mode 100644
index 0000000..0494d38
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/block/decomposition/qr/QRDecompositionHouseholder_B64.java
@@ -0,0 +1,417 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block.decomposition.qr;
+
+import org.ejml.alg.block.BlockMatrixOps;
+import org.ejml.alg.block.BlockMultiplication;
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.D1Submatrix64F;
+import org.ejml.interfaces.decomposition.QRDecomposition;
+
+
+/**
+ * <p>
+ * QR decomposition for {@link BlockMatrix64F} using householder reflectors.  The decomposition is
+ * performed by computing a QR decomposition for each block column as is normally done, see {@link org.ejml.alg.dense.decomposition.qr.QRDecompositionHouseholder_D64}.
+ * The reflectors are then combined and applied to the remainder of the matrix.  This process is repeated
+ * until all the block columns have been processed
+ * </p>
+ *
+ * <p>
+ * The input matrix is modified and used to store the decomposition.  Reflectors are stored in the lower triangle
+ * columns.  The first element of the reflector is implicitly assumed to be one.
+ * </p>
+ *
+ * <p>
+ * Each iteration can be sketched as follows:
+ * <pre>
+ * QR_Decomposition( A(:,i-r to i) )
+ * W=computeW( A(:,i-r to i) )
+ * A(:,i:n) = (I + W*Y<sup>T</sup>)<sup>T</sup>A(:,i:n)
+ * </pre>
+ * Where r is the block size, i is the submatrix being considered, A is the input matrix,
+ * Y is a matrix containing the reflectors just computed,
+ * and W is computed using {@link BlockHouseHolder#computeW_Column}.
+ * </p>
+ *
+ * <p>
+ * Based upon "Block Householder QR Factorization" pg 255 in "Matrix Computations"
+ * 3rd Ed. 1996 by Gene H. Golub and Charles F. Van Loan.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class QRDecompositionHouseholder_B64
+        implements QRDecomposition<BlockMatrix64F> {
+
+    // the input matrix which is overwritten with the decomposition.
+    // Reflectors are stored in the lower triangular portion. The R matrix is stored
+    // in the upper triangle portion
+    private BlockMatrix64F dataA;
+
+    // where the computed W matrix is stored
+    private BlockMatrix64F dataW = new BlockMatrix64F(1,1);
+    // Matrix used to store an intermediate calculation
+    private BlockMatrix64F dataWTA = new BlockMatrix64F(1,1);
+
+    // size of the inner matrix block.
+    private int blockLength;
+    
+    // The submatrices which are being manipulated in each iteration
+    private D1Submatrix64F A = new D1Submatrix64F();
+    private D1Submatrix64F Y = new D1Submatrix64F();
+    private D1Submatrix64F W = new D1Submatrix64F(dataW);
+    private D1Submatrix64F WTA = new D1Submatrix64F(dataWTA);
+    private double temp[] = new double[1];
+    // stores the computed gammas
+    private double gammas[] = new double[1];
+
+    // save the W matrix the first time it is computed in the decomposition
+    private boolean saveW = false;
+
+    /**
+     * This is the input matrix after it has been overwritten with the decomposition.
+     *
+     * @return Internal matrix used to store decomposition.
+     */
+    public BlockMatrix64F getQR() {
+        return dataA;
+    }
+
+    /**
+     * <p>
+     * Sets if it should internally save the W matrix before performing the decomposition.  Must
+     * be set before decomposition the matrix.
+     * </p>
+     *
+     * <p>
+     * Saving W can result in about a 5% savings when solving systems around a height of 5k.  The
+     * price is that it needs to save a matrix the size of the input matrix.
+     * </p>
+     *
+     * @param saveW If the W matrix should be saved or not.
+     */
+    public void setSaveW(boolean saveW) {
+        this.saveW = saveW;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    @Override
+    public BlockMatrix64F getQ(BlockMatrix64F Q, boolean compact) {
+        Q = initializeQ(Q, dataA.numRows , dataA.numCols  , blockLength , compact);
+ 
+        applyQ(Q,true);
+
+        return Q;
+    }
+
+    /**
+     * Sanity checks the input or declares a new matrix.  Return matrix is an identity matrix.
+     */
+    public static BlockMatrix64F initializeQ(BlockMatrix64F Q,
+                                              int numRows , int numCols , int blockLength ,
+                                              boolean compact) {
+        int minLength = Math.min(numRows,numCols);
+        if( compact ) {
+            if( Q == null ) {
+                Q = new BlockMatrix64F(numRows,minLength,blockLength);
+                BlockMatrixOps.setIdentity(Q);
+            } else {
+                if( Q.numRows != numRows || Q.numCols != minLength ) {
+                    throw new IllegalArgumentException("Unexpected matrix dimension. Found "+Q.numRows+" "+Q.numCols);
+                } else {
+                    BlockMatrixOps.setIdentity(Q);
+                }
+            }
+        } else {
+            if( Q == null ) {
+                Q = new BlockMatrix64F(numRows,numRows,blockLength);
+                BlockMatrixOps.setIdentity(Q);
+            } else {
+                if( Q.numRows != numRows || Q.numCols != numRows ) {
+                    throw new IllegalArgumentException("Unexpected matrix dimension. Found "+Q.numRows+" "+Q.numCols);
+                } else {
+                    BlockMatrixOps.setIdentity(Q);
+                }
+            }
+        }
+        return Q;
+    }
+
+    /**
+     * <p>
+     * Multiplies the provided matrix by Q using householder reflectors.  This is more
+     * efficient that computing Q then applying it to the matrix.
+     * </p>
+     *
+     * <p>
+     * B = Q * B
+     * </p>
+     *
+     * @param B Matrix which Q is applied to.  Modified.
+     */
+    public void applyQ( BlockMatrix64F B ) {
+        applyQ(B,false);
+    }
+
+    /**
+     * Specialized version of applyQ() that allows the zeros in an identity matrix
+     * to be taken advantage of depending on if isIdentity is true or not.
+     *
+     * @param B
+     * @param isIdentity If B is an identity matrix.
+     */
+    public void applyQ( BlockMatrix64F B , boolean isIdentity ) {
+        int minDimen = Math.min(dataA.numCols,dataA.numRows);
+
+        D1Submatrix64F subB = new D1Submatrix64F(B);
+
+        W.col0 = W.row0 = 0;
+        Y.row1 = W.row1 = dataA.numRows;
+        WTA.row0 = WTA.col0 = 0;
+
+        int start = minDimen - minDimen % blockLength;
+        if( start == minDimen )
+            start -= blockLength;
+        if( start < 0 )
+            start = 0;
+
+        // (Q1^T * (Q2^T * (Q3^t * A)))
+        for( int i = start; i >= 0; i -= blockLength ) {
+
+            Y.col0 = i;
+            Y.col1 = Math.min(Y.col0+blockLength,dataA.numCols);
+            Y.row0 = i;
+            if( isIdentity )
+                subB.col0 = i;
+            subB.row0 = i;
+
+            setW();
+            WTA.row1 = Y.col1-Y.col0;
+            WTA.col1 = subB.col1-subB.col0;
+            WTA.original.reshape(WTA.row1,WTA.col1,false);
+
+            // Compute W matrix from reflectors stored in Y
+            if( !saveW )
+                BlockHouseHolder.computeW_Column(blockLength,Y,W,temp, gammas,Y.col0);
+
+            // Apply the Qi to Q
+            BlockHouseHolder.multTransA_vecCol(blockLength,Y,subB,WTA);
+            BlockMultiplication.multPlus(blockLength,W,WTA,subB);
+        }
+    }
+
+    /**
+     * <p>
+     * Multiplies the provided matrix by Q<sup>T</sup> using householder reflectors.  This is more
+     * efficient that computing Q then applying it to the matrix.
+     * </p>
+     *
+     * <p>
+     * Q = Q*(I - γ W*Y^T)<br>
+     * QR = A => R = Q^T*A  = (Q3^T * (Q2^T * (Q1^t * A)))
+     * </p>
+     *
+     * @param B Matrix which Q is applied to.  Modified.
+     */
+    public void applyQTran( BlockMatrix64F B ) {
+        int minDimen = Math.min(dataA.numCols,dataA.numRows);
+
+        D1Submatrix64F subB = new D1Submatrix64F(B);
+
+        W.col0 = W.row0 = 0;
+        Y.row1 = W.row1 = dataA.numRows;
+        WTA.row0 = WTA.col0 = 0;
+
+        // (Q3^T * (Q2^T * (Q1^t * A)))
+        for( int i = 0; i < minDimen; i += blockLength ) {
+
+            Y.col0 = i;
+            Y.col1 = Math.min(Y.col0+blockLength,dataA.numCols);
+            Y.row0 = i;
+            
+            subB.row0 = i;
+//            subB.row1 = B.numRows;
+//            subB.col0 = 0;
+//            subB.col1 = B.numCols;
+
+            setW();
+//            W.original.reshape(W.row1,W.col1,false);
+            WTA.row0 = 0;
+            WTA.col0 = 0;
+            WTA.row1 = W.col1-W.col0;
+            WTA.col1 = subB.col1-subB.col0;
+            WTA.original.reshape(WTA.row1,WTA.col1,false);
+
+            // Compute W matrix from reflectors stored in Y
+            if( !saveW )
+                BlockHouseHolder.computeW_Column(blockLength,Y,W,temp, gammas,Y.col0);
+
+            // Apply the Qi to Q
+            BlockMultiplication.multTransA(blockLength,W,subB,WTA);
+            BlockHouseHolder.multAdd_zeros(blockLength,Y,WTA,subB);
+        }
+    }
+
+    /**
+     * @inheritDoc
+     */
+    @Override
+    public BlockMatrix64F getR(BlockMatrix64F R, boolean compact) {
+        int min = Math.min(dataA.numRows,dataA.numCols);
+
+        if( R == null ) {
+            if( compact ) {
+                R = new BlockMatrix64F(min,dataA.numCols,blockLength);
+            } else {
+                R = new BlockMatrix64F(dataA.numRows,dataA.numCols,blockLength);
+            }
+        } else {
+            if( compact ) {
+                if( R.numCols != dataA.numCols || R.numRows != min ) {
+                    throw new IllegalArgumentException("Unexpected dimension.");
+                }
+            } else if( R.numCols != dataA.numCols || R.numRows != dataA.numRows ) {
+                throw new IllegalArgumentException("Unexpected dimension.");
+            }
+        }
+
+        BlockMatrixOps.zeroTriangle(false,R);
+        BlockMatrixOps.copyTriangle(true,dataA,R);
+
+        return R;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    @Override
+    public boolean decompose(BlockMatrix64F orig) {
+        setup(orig);
+
+        int m = Math.min(orig.numCols,orig.numRows);
+
+        // process the matrix one column block at a time and overwrite the input matrix
+        for( int j = 0; j < m; j += blockLength ) {
+            Y.col0 = j;
+            Y.col1 = Math.min( orig.numCols , Y.col0 + blockLength );
+            Y.row0 = j;
+
+            // compute the QR decomposition of the left most block column
+            // this overwrites the original input matrix
+            if( !BlockHouseHolder.decomposeQR_block_col(blockLength,Y,gammas) ) {
+                return false;
+            }
+
+            // Update the remainder of the matrix using the reflectors just computed
+            updateA(A);
+        }
+
+        return true;
+    }
+
+    /**
+     * Adjust submatrices and helper data structures for the input matrix.  Must be called
+     * before the decomposition can be computed.
+     *
+     * @param orig
+     */
+    private void setup(BlockMatrix64F orig) {
+        blockLength = orig.blockLength;
+        dataW.blockLength = blockLength;
+        dataWTA.blockLength = blockLength;
+
+        this.dataA = orig;
+        A.original = dataA;
+
+        int l = Math.min(blockLength,orig.numCols);
+        dataW.reshape(orig.numRows,l,false);
+        dataWTA.reshape(l,orig.numRows,false);
+        Y.original = orig;
+        Y.row1 = W.row1 = orig.numRows;
+        if( temp.length < blockLength )
+            temp = new double[blockLength];
+        if( gammas.length < orig.numCols )
+            gammas = new double[ orig.numCols ];
+
+        if( saveW ) {
+            dataW.reshape(orig.numRows,orig.numCols,false);
+        }
+    }
+
+    /**
+     * <p>
+     * A = (I + W Y<sup>T</sup>)<sup>T</sup>A<BR>
+     * A = A + Y (W<sup>T</sup>A)<BR>
+     * <br>
+     * where A is a submatrix of the input matrix.
+     * </p>
+     */
+    protected void updateA( D1Submatrix64F A )
+    {
+        setW();
+
+        A.row0 = Y.row0;
+        A.row1 = Y.row1;
+        A.col0 = Y.col1;
+        A.col1 = Y.original.numCols;
+
+        WTA.row0 = 0;
+        WTA.col0 = 0;
+        WTA.row1 = W.col1-W.col0;
+        WTA.col1 = A.col1-A.col0;
+        WTA.original.reshape(WTA.row1,WTA.col1,false);
+
+        if( A.col1 > A.col0 ) {
+            BlockHouseHolder.computeW_Column(blockLength,Y,W,temp, gammas,Y.col0);
+
+            BlockMultiplication.multTransA(blockLength,W,A,WTA);
+            BlockHouseHolder.multAdd_zeros(blockLength,Y,WTA,A);
+        } else if( saveW ) {
+            BlockHouseHolder.computeW_Column(blockLength,Y,W,temp, gammas,Y.col0);
+        }
+    }
+
+    /**
+     * Sets the submatrix of W up give Y is already configured and if it is being cached or not.
+     */
+    private void setW() {
+        if( saveW ) {
+            W.col0 = Y.col0;
+            W.col1 = Y.col1;
+            W.row0 = Y.row0;
+            W.row1 = Y.row1;
+        } else {
+            W.col1 = Y.col1 - Y.col0;
+            W.row0 = Y.row0;
+        }
+    }
+
+    /**
+     * The input matrix is always modified.
+     *
+     * @return Returns true since the input matrix is modified.
+     */
+    @Override
+    public boolean inputModified() {
+        return true;
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/block/linsol/chol/BlockCholeskyOuterSolver.java b/main/dense64/src/org/ejml/alg/block/linsol/chol/BlockCholeskyOuterSolver.java
new file mode 100644
index 0000000..e2ce1be
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/block/linsol/chol/BlockCholeskyOuterSolver.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block.linsol.chol;
+
+import org.ejml.alg.block.BlockMatrixOps;
+import org.ejml.alg.block.BlockTriangularSolver;
+import org.ejml.alg.block.decomposition.chol.CholeskyOuterForm_B64;
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.D1Submatrix64F;
+import org.ejml.interfaces.decomposition.CholeskyDecomposition;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.ejml.ops.SpecializedOps;
+
+
+/**
+ * <p> Linear solver that uses a block cholesky decomposition. </p>
+ *
+ * <p>
+ * Solver works by using the standard Cholesky solving strategy:<br>
+ * A=L*L<sup>T</sup> <br>
+ * A*x=b<br>
+ * L*L<sup>T</sup>*x = b <br>
+ * L*y = b<br>
+ * L<sup>T</sup>*x = y<br>
+ * x = L<sup>-T</sup>y
+ * </p>
+ *
+ * <p>
+ * It is also possible to use the upper triangular cholesky decomposition.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class BlockCholeskyOuterSolver implements LinearSolver<BlockMatrix64F> {
+
+    // cholesky decomposition
+    private CholeskyOuterForm_B64 decomposer = new CholeskyOuterForm_B64(true);
+
+    // size of a block take from input matrix
+    private int blockLength;
+
+    // temporary data structure used in some calculation.
+    private double temp[];
+
+    /**
+     * Decomposes and overwrites the input matrix.
+     *
+     * @param A Semi-Positive Definite (SPD) system matrix. Modified. Reference saved.
+     * @return If the matrix can be decomposed.  Will always return false of not SPD.
+     */
+    @Override
+    public boolean setA(BlockMatrix64F A) {
+        // Extract a lower triangular solution
+        if( !decomposer.decompose(A) )
+            return false;
+
+        blockLength = A.blockLength;
+
+        return true;
+    }
+
+    @Override
+    public double quality() {
+        return SpecializedOps.qualityTriangular(decomposer.getT(null));
+    }
+
+    /**
+     * If X == null then the solution is written into B.  Otherwise the solution is copied
+     * from B into X.
+     */
+    @Override
+    public void solve(BlockMatrix64F B, BlockMatrix64F X) {
+        if( B.blockLength != blockLength )
+            throw new IllegalArgumentException("Unexpected blocklength in B.");
+
+        D1Submatrix64F L = new D1Submatrix64F(decomposer.getT(null));
+
+        if( X != null ) {
+            if( X.blockLength != blockLength )
+                throw new IllegalArgumentException("Unexpected blocklength in X.");
+            if( X.numRows != L.col1 ) throw new IllegalArgumentException("Not enough rows in X");
+        }
+        
+        if( B.numRows != L.col1 ) throw new IllegalArgumentException("Not enough rows in B");
+
+        //  L * L^T*X = B
+
+        // Solve for Y:  L*Y = B
+        BlockTriangularSolver.solve(blockLength,false,L,new D1Submatrix64F(B),false);
+
+        // L^T * X = Y
+        BlockTriangularSolver.solve(blockLength,false,L,new D1Submatrix64F(B),true);
+
+        if( X != null ) {
+            // copy the solution from B into X
+            BlockMatrixOps.extractAligned(B,X);
+        }
+
+    }
+
+    @Override
+    public void invert(BlockMatrix64F A_inv) {
+        BlockMatrix64F T = decomposer.getT(null);
+        if( A_inv.numRows != T.numRows || A_inv.numCols != T.numCols )
+            throw new IllegalArgumentException("Unexpected number or rows and/or columns");
+
+
+        if( temp == null || temp.length < blockLength*blockLength )
+            temp = new double[ blockLength* blockLength ];
+
+        // zero the upper triangular portion of A_inv
+        BlockMatrixOps.zeroTriangle(true,A_inv);
+
+        D1Submatrix64F L = new D1Submatrix64F(T);
+        D1Submatrix64F B = new D1Submatrix64F(A_inv);
+
+        // invert L from cholesky decomposition and write the solution into the lower
+        // triangular portion of A_inv
+        // B = inv(L)
+        BlockTriangularSolver.invert(blockLength,false,L,B,temp);
+
+        // B = L^-T * B
+        // todo could speed up by taking advantage of B being lower triangular
+        // todo take advantage of symmetry
+        BlockTriangularSolver.solveL(blockLength,L,B,true);
+    }
+
+    @Override
+    public boolean modifiesA() {
+        return decomposer.inputModified();
+    }
+
+    @Override
+    public boolean modifiesB() {
+        return true;
+    }
+
+    @Override
+    public CholeskyDecomposition<BlockMatrix64F> getDecomposition() {
+        return decomposer;
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/block/linsol/qr/BlockQrHouseHolderSolver.java b/main/dense64/src/org/ejml/alg/block/linsol/qr/BlockQrHouseHolderSolver.java
new file mode 100644
index 0000000..3626f4c
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/block/linsol/qr/BlockQrHouseHolderSolver.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block.linsol.qr;
+
+import org.ejml.alg.block.BlockMatrixOps;
+import org.ejml.alg.block.BlockTriangularSolver;
+import org.ejml.alg.block.decomposition.qr.QRDecompositionHouseholder_B64;
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.D1Submatrix64F;
+import org.ejml.interfaces.decomposition.QRDecomposition;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.ejml.ops.SpecializedOps;
+
+
+/**
+ * <p>
+ * A solver for {@link org.ejml.alg.block.decomposition.qr.QRDecompositionHouseholder_B64}.  Systems are solved for using the standard
+ * QR decomposition method, sketched below.
+ * </p>
+ *
+ * <p>
+ * A = Q*R<br>
+ * A*x = b<br>
+ * Q*R*x = b <br>
+ * R*x = y = Q<sup>T</sup>b<br>
+ * x = R<sup>-1</sup>y<br>
+ * <br>
+ * Where A is the m by n matrix being decomposed. Q is an orthogonal matrix. R is upper triangular matrix.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class BlockQrHouseHolderSolver implements LinearSolver<BlockMatrix64F> {
+
+    // QR decomposition algorithm
+    protected QRDecompositionHouseholder_B64 decomposer = new QRDecompositionHouseholder_B64();
+
+    // the input matrix which has been decomposed
+    protected BlockMatrix64F QR;
+
+
+    public BlockQrHouseHolderSolver() {
+        decomposer.setSaveW(false);
+    }
+
+    /**
+     * Computes the QR decomposition of A and store the results in A.
+     *
+     * @param A The A matrix in the linear equation. Modified. Reference saved.
+     * @return true if the decomposition was successful.
+     */
+    @Override
+    public boolean setA(BlockMatrix64F A) {
+        if( A.numRows < A.numCols )
+            throw new IllegalArgumentException("Number of rows must be more than or equal to the number of columns.  " +
+                    "Can't solve an underdetermined system.");
+
+        if( !decomposer.decompose(A))
+            return false;
+
+        this.QR = decomposer.getQR();
+
+        return true;
+    }
+
+    /**
+     * Computes the quality using diagonal elements the triangular R matrix in the QR decomposition.
+     *
+     * @return Solutions quality.
+     */
+    @Override
+    public double quality() {
+        return SpecializedOps.qualityTriangular(decomposer.getQR());
+    }
+
+    @Override
+    public void solve(BlockMatrix64F B, BlockMatrix64F X) {
+
+        if( B.numCols != X.numCols )
+            throw new IllegalArgumentException("Columns of B and X do not match");
+        if( QR.numCols != X.numRows )
+            throw new IllegalArgumentException("Rows in X do not match the columns in A");
+        if( QR.numRows != B.numRows )
+            throw new IllegalArgumentException("Rows in B do not match the rows in A.");
+        if( B.blockLength != QR.blockLength || X.blockLength != QR.blockLength )
+            throw new IllegalArgumentException("All matrices must have the same block length.");
+
+        // The system being solved for can be described as:
+        // Q*R*X = B
+
+        // First apply householder reflectors to B
+        // Y = Q^T*B
+        decomposer.applyQTran(B);
+
+        // Second solve for Y using the upper triangle matrix R and the just computed Y
+        // X = R^-1 * Y
+        BlockMatrixOps.extractAligned(B,X);
+
+        // extract a block aligned matrix
+        int M = Math.min(QR.numRows,QR.numCols);
+
+        BlockTriangularSolver.solve(QR.blockLength,true,
+                new D1Submatrix64F(QR,0,M,0,M),new D1Submatrix64F(X),false);
+
+    }
+
+    /**
+     * Invert by solving for against an identity matrix.
+     *
+     * @param A_inv Where the inverted matrix saved. Modified.
+     */
+    @Override
+    public void invert(BlockMatrix64F A_inv) {
+        int M = Math.min(QR.numRows,QR.numCols);
+        if( A_inv.numRows != M || A_inv.numCols != M )
+            throw new IllegalArgumentException("A_inv must be square an have dimension "+M);
+
+
+        // Solve for A^-1
+        // Q*R*A^-1 = I
+
+        // Apply householder reflectors to the identity matrix
+        // y = Q^T*I = Q^T
+        BlockMatrixOps.setIdentity(A_inv);
+        decomposer.applyQTran(A_inv);
+
+        // Solve using upper triangular R matrix
+        // R*A^-1 = y
+        // A^-1 = R^-1*y
+        BlockTriangularSolver.solve(QR.blockLength,true,
+                new D1Submatrix64F(QR,0,M,0,M),new D1Submatrix64F(A_inv),false);
+    }
+
+    @Override
+    public boolean modifiesA() {
+        return decomposer.inputModified();
+    }
+
+    @Override
+    public boolean modifiesB() {
+        return true;
+    }
+
+    @Override
+    public QRDecomposition<BlockMatrix64F> getDecomposition() {
+        return decomposer;
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/BaseDecomposition_B64_to_D64.java b/main/dense64/src/org/ejml/alg/dense/decomposition/BaseDecomposition_B64_to_D64.java
new file mode 100644
index 0000000..6d72123
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/BaseDecomposition_B64_to_D64.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition;
+
+import org.ejml.alg.block.BlockMatrixOps;
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.DecompositionInterface;
+
+
+/**
+ * Generic interface for wrapping a {@link BlockMatrix64F} decomposition for
+ * processing of {@link org.ejml.data.DenseMatrix64F}.
+ *
+ * @author Peter Abeles
+ */
+public class BaseDecomposition_B64_to_D64 implements DecompositionInterface<DenseMatrix64F> {
+
+    protected DecompositionInterface<BlockMatrix64F> alg;
+
+    protected double[]tmp;
+    protected BlockMatrix64F Ablock = new BlockMatrix64F();
+    protected int blockLength;
+
+    public BaseDecomposition_B64_to_D64(DecompositionInterface<BlockMatrix64F> alg,
+                                        int blockLength) {
+        this.alg = alg;
+        this.blockLength = blockLength;
+    }
+
+    @Override
+    public boolean decompose(DenseMatrix64F A) {
+        Ablock.numRows = A.numRows;
+        Ablock.numCols = A.numCols;
+        Ablock.blockLength = blockLength;
+        Ablock.data = A.data;
+
+        int tmpLength = Math.min( Ablock.blockLength , A.numRows ) * A.numCols;
+
+        if( tmp == null || tmp.length < tmpLength )
+            tmp = new double[ tmpLength ];
+
+        // doing an in-place convert is much more memory efficient at the cost of a little
+        // but of CPU
+        BlockMatrixOps.convertRowToBlock(A.numRows,A.numCols,Ablock.blockLength,A.data,tmp);
+
+        boolean ret = alg.decompose(Ablock);
+
+        // convert it back to the normal format if it wouldn't have been modified
+        if( !alg.inputModified() ) {
+            BlockMatrixOps.convertBlockToRow(A.numRows,A.numCols,Ablock.blockLength,A.data,tmp);
+        }
+
+        return ret;
+    }
+
+    public void convertBlockToRow(int numRows , int numCols , int blockLength ,
+                                  double[] data) {
+        int tmpLength = Math.min( blockLength , numRows ) * numCols;
+
+        if( tmp == null || tmp.length < tmpLength )
+            tmp = new double[ tmpLength ];
+
+        BlockMatrixOps.convertBlockToRow(numRows,numCols,Ablock.blockLength,data,tmp);
+    }
+
+    @Override
+    public boolean inputModified() {
+        return alg.inputModified();
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/TriangularSolver.java b/main/dense64/src/org/ejml/alg/dense/decomposition/TriangularSolver.java
new file mode 100644
index 0000000..8c2829e
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/TriangularSolver.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition;
+
+/**
+ * <p>
+ * This contains algorithms for solving systems of equations where T is a
+ * non-singular triangular matrix:<br>
+ * <br>
+ * T*x = b<br>
+ * <br>
+ * where x and b are vectors, and T is an n by n matrix. T can either be a lower or upper triangular matrix.<br>
+ * </p>
+ * <p>
+ * These functions are designed for use inside of other algorithms.  To use them directly
+ * is dangerous since no sanity checks are performed.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class TriangularSolver {
+
+    /**
+     * <p>
+     * Inverts a square lower triangular matrix:  L = L<sup>-1</sup>
+     * </p>
+     *
+     *
+     * @param L
+     * @param m
+     */
+    public static void invertLower( double L[] , int m ) {
+        for( int i = 0; i < m; i++ ) {
+            double L_ii = L[ i*m + i ];
+            for( int j = 0; j < i; j++ ) {
+                double val = 0;
+                for( int k = j; k < i; k++ ) {
+                    val += L[ i*m + k] * L[ k*m + j ];
+                }
+                L[ i*m + j ] = -val / L_ii;
+            }
+            L[ i*m + i ] =  1.0 / L_ii;
+        }
+    }
+
+    public static void invertLower( double L[] , double L_inv[] , int m ) {
+        for( int i = 0; i < m; i++ ) {
+            double L_ii = L[ i*m + i ];
+            for( int j = 0; j < i; j++ ) {
+                double val = 0;
+                for( int k = j; k < i; k++ ) {
+                    val -= L[ i*m + k] * L_inv[ k*m + j ];
+                }
+                L_inv[ i*m + j ] = val / L_ii;
+            }
+            L_inv[ i*m + i ] =  1.0 / L_ii;
+        }
+    }
+
+    /**
+     * <p>
+     * Solves for non-singular lower triangular matrices using forward substitution.
+     * <br>
+     * b = L<sup>-1</sup>b<br>
+     * <br>
+     * where b is a vector, L is an n by n matrix.<br>
+     * </p>
+     *
+     * @param L An n by n non-singular lower triangular matrix. Not modified.
+     * @param b A vector of length n. Modified.
+     * @param n The size of the matrices.
+     */
+    public static void solveL( double L[] , double []b , int n )
+    {
+//        for( int i = 0; i < n; i++ ) {
+//            double sum = b[i];
+//            for( int k=0; k<i; k++ ) {
+//                sum -= L[i*n+k]* b[k];
+//            }
+//            b[i] = sum / L[i*n+i];
+//        }
+        for( int i = 0; i < n; i++ ) {
+            double sum = b[i];
+            int indexL = i*n;
+            for( int k=0; k<i; k++ ) {
+                sum -= L[indexL++]* b[k];
+            }
+            b[i] = sum / L[indexL];
+        }
+    }
+
+    /**
+     *
+     * L is a m by m matrix
+     * B is a m by n matrix
+     *
+     * @param L
+     * @param b
+     * @param m
+     * @param n
+     */
+    public static void solveL( double L[] , double []b , int m , int n )
+    {
+        for( int j = 0; j < n; j++ ) {
+            for( int i = 0; i < m; i++ ) {
+                double sum = b[i*n+j];
+                for( int k=0; k<i; k++ ) {
+                    sum -= L[i*m+k]* b[k*n+j];
+                }
+                b[i*n+j] = sum / L[i*m+i];
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * This is a forward substitution solver for non-singular lower triangular matrices.
+     * <br>
+     * b = (L<sup>T</sup>)<sup>-1</sup>b<br>
+     * <br>
+     * where b is a vector, L is an n by n matrix.<br>
+     * </p>
+     * <p>
+     * L is a lower triangular matrix, but it comes up with a solution as if it was
+     * an upper triangular matrix that was computed by transposing L.
+     * </p>
+     *
+     * @param L An n by n non-singular lower triangular matrix. Not modified.
+     * @param b A vector of length n. Modified.
+     * @param n The size of the matrices.
+     */
+    public static void solveTranL( double L[] , double []b , int n )
+    {
+        for( int i =n-1; i>=0; i-- ) {
+            double sum = b[i];
+            for( int k = i+1; k <n; k++ ) {
+                sum -= L[k*n+i]* b[k];
+            }
+            b[i] = sum/L[i*n+i];
+        }
+    }
+
+    /**
+     * <p>
+     * This is a forward substitution solver for non-singular upper triangular matrices.
+     * <br>
+     * b = U<sup>-1</sup>b<br>
+     * <br>
+     * where b is a vector, U is an n by n matrix.<br>
+     * </p>
+     *
+     * @param U An n by n non-singular upper triangular matrix. Not modified.
+     * @param b A vector of length n. Modified.
+     * @param n The size of the matrices.
+     */
+    public static void solveU( double U[] , double []b , int n )
+    {
+//        for( int i =n-1; i>=0; i-- ) {
+//            double sum = b[i];
+//            for( int j = i+1; j <n; j++ ) {
+//                sum -= U[i*n+j]* b[j];
+//            }
+//            b[i] = sum/U[i*n+i];
+//        }
+        for( int i =n-1; i>=0; i-- ) {
+            double sum = b[i];
+            int indexU = i*n+i+1;
+            for( int j = i+1; j <n; j++ ) {
+                sum -= U[indexU++]* b[j];
+            }
+            b[i] = sum/U[i*n+i];
+        }
+    }
+
+    public static void solveU( double U[] , double []b , int sideLength , int minRow , int maxRow )
+    {
+//        for( int i =maxRow-1; i>=minRow; i-- ) {
+//            double sum = b[i];
+//            for( int j = i+1; j <maxRow; j++ ) {
+//                sum -= U[i*sideLength+j]* b[j];
+//            }
+//            b[i] = sum/U[i*sideLength+i];
+//        }
+        for( int i =maxRow-1; i>=minRow; i-- ) {
+            double sum = b[i];
+            int indexU = i*sideLength+i+1;
+            for( int j = i+1; j <maxRow; j++ ) {
+                sum -= U[indexU++]* b[j];
+            }
+            b[i] = sum/U[i*sideLength+i];
+        }
+    }
+
+    /**
+     * <p>
+     * This is a forward substitution solver for non-singular upper triangular matrices which are
+     * a sub-matrix inside a larger.  The columns of 'b' are solved for individually
+     * <br>
+     * b = U<sup>-1</sup>b<br>
+     * <br>
+     * where b is a matrix, U is an n by n matrix.<br>
+     * </p>
+     *
+     * @param U Matrix containing the upper triangle system
+     * @param startU Index of the first element in U
+     * @param strideU stride between rows
+     * @param widthU How wide the square matrix is
+     * @param b Matrix containing the solution to the system.  Overwritten with the solution.
+     * @param startB Index of the first element in B
+     * @param strideB stride between rows
+     * @param widthB How wide the matrix is.  Length is the same as U's width
+     */
+    public static void solveU( double []U , int startU , int strideU , int widthU ,
+                               double []b , int startB , int strideB , int widthB )
+    {
+        for( int colB = 0; colB < widthB; colB++ ) {
+            for( int i =widthU-1; i>=0; i-- ) {
+                double sum = b[startB + i*strideB + colB];
+                for( int j = i+1; j <widthU; j++ ) {
+                    sum -= U[startU + i*strideU+j]* b[startB + j*strideB + colB ];
+                }
+                b[startB + i*strideB + colB] = sum/U[ startU + i*strideU + i ];
+            }
+        }
+
+        // todo comment out the above and optimize it
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/bidiagonal/BidiagonalDecompositionRow_D64.java b/main/dense64/src/org/ejml/alg/dense/decomposition/bidiagonal/BidiagonalDecompositionRow_D64.java
new file mode 100644
index 0000000..df2a685
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/bidiagonal/BidiagonalDecompositionRow_D64.java
@@ -0,0 +1,393 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.bidiagonal;
+
+import org.ejml.alg.dense.decomposition.qr.QrHelperFunctions_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.BidiagonalDecomposition;
+import org.ejml.ops.CommonOps;
+
+/**
+ * <p>
+ * Performs a {@link org.ejml.interfaces.decomposition.BidiagonalDecomposition} using
+ * householder reflectors.  This is efficient on wide or square matrices.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class BidiagonalDecompositionRow_D64
+        implements BidiagonalDecomposition<DenseMatrix64F>
+{
+    // A combined matrix that stores te upper Hessenberg matrix and the orthogonal matrix.
+    private DenseMatrix64F UBV;
+
+    // number of rows
+    private int m;
+    // number of columns
+    private int n;
+    // the smaller of m or n
+    private int min;
+
+    // the first element in the orthogonal vectors
+    private double gammasU[];
+    private double gammasV[];
+    // temporary storage
+    private double b[];
+    private double u[];
+
+    /**
+     * Creates a decompose that defines the specified amount of memory.
+     *
+     * @param numElements number of elements in the matrix.
+     */
+    public BidiagonalDecompositionRow_D64(int numElements) {
+
+        UBV = new DenseMatrix64F(numElements);
+        gammasU = new double[ numElements ];
+        gammasV = new double[ numElements ];
+        b = new double[ numElements ];
+        u = new double[ numElements ];
+    }
+
+    public BidiagonalDecompositionRow_D64() {
+        this(1);
+    }
+
+    /**
+     * Computes the decomposition of the provided matrix.  If no errors are detected then true is returned,
+     * false otherwise.
+     *
+     * @param A  The matrix that is being decomposed.  Not modified.
+     * @return If it detects any errors or not.
+     */
+    @Override
+    public boolean decompose( DenseMatrix64F A  )
+    {
+        init(A);
+        return _decompose();
+    }
+
+    /**
+     * Sets up internal data structures and creates a copy of the input matrix.
+     *
+     * @param A The input matrix.  Not modified.
+     */
+    protected void init(DenseMatrix64F A ) {
+        UBV = A;
+
+        m = UBV.numRows;
+        n = UBV.numCols;
+
+        min = Math.min(m,n);
+        int max = Math.max(m,n);
+
+        if( b.length < max+1 ) {
+            b = new double[ max+1 ];
+            u = new double[ max+1 ];
+        }
+        if( gammasU.length < m ) {
+            gammasU = new double[ m ];
+        }
+        if( gammasV.length < n ) {
+            gammasV = new double[ n ];
+        }
+    }
+
+    /**
+     * The raw UBV matrix that is stored internally.
+     *
+     * @return UBV matrix.
+     */
+    public DenseMatrix64F getUBV() {
+        return UBV;
+    }
+
+    @Override
+    public void getDiagonal(double[] diag, double[] off) {
+        diag[0] = UBV.get(0);
+        for( int i = 1; i < n; i++ ) {
+            diag[i] = UBV.unsafe_get(i,i);
+            off[i-1] = UBV.unsafe_get(i-1,i);
+        }
+    }
+
+    /**
+     * Returns the bidiagonal matrix.
+     *
+     * @param B If not null the results are stored here, if null a new matrix is created.
+     * @return The bidiagonal matrix.
+     */
+    @Override
+    public DenseMatrix64F getB( DenseMatrix64F B , boolean compact ) {
+        B = handleB(B, compact,m,n,min);
+
+        //System.arraycopy(UBV.data, 0, B.data, 0, UBV.getNumElements());
+
+        B.set(0,0,UBV.get(0,0));
+        for( int i = 1; i < min; i++ ) {
+            B.set(i,i, UBV.get(i,i));
+            B.set(i-1,i, UBV.get(i-1,i));
+        }
+        if( n > m )
+            B.set(min-1,min,UBV.get(min-1,min));
+
+        return B;
+    }
+
+    public static DenseMatrix64F handleB(DenseMatrix64F B, boolean compact,
+                                          int m , int n , int min ) {
+        int w = n > m ? min + 1 : min;
+
+        if( compact ) {
+            if( B == null ) {
+                B = new DenseMatrix64F(min,w);
+            } else {
+                B.reshape(min,w, false);
+                B.zero();
+            }
+        } else {
+            if( B == null ) {
+                B = new DenseMatrix64F(m,n);
+            } else {
+                B.reshape(m,n, false);
+                B.zero();
+            }
+        }
+        return B;
+    }
+
+    /**
+     * Returns the orthogonal U matrix.
+     *
+     * @param U If not null then the results will be stored here.  Otherwise a new matrix will be created.
+     * @return The extracted Q matrix.
+     */
+    @Override
+    public DenseMatrix64F getU( DenseMatrix64F U , boolean transpose , boolean compact ) {
+        U = handleU(U, transpose, compact,m,n,min);
+        CommonOps.setIdentity(U);
+
+        for( int i = 0; i < m; i++ ) u[i] = 0;
+
+        for( int j = min-1; j >= 0; j-- ) {
+            u[j] = 1;
+            for( int i = j+1; i < m; i++ ) {
+                u[i] = UBV.get(i,j);
+            }
+            if( transpose )
+                QrHelperFunctions_D64.rank1UpdateMultL(U, u, gammasU[j], j, j, m);
+            else
+                QrHelperFunctions_D64.rank1UpdateMultR(U, u, gammasU[j], j, j, m, this.b);
+        }
+
+        return U;
+    }
+
+    public static DenseMatrix64F handleU(DenseMatrix64F U,
+                                         boolean transpose, boolean compact,
+                                         int m, int n , int min ) {
+        if( compact ){
+            if( transpose ) {
+                if( U == null )
+                    U = new DenseMatrix64F(min,m);
+                else {
+                    U.reshape(min,m, false);
+                }
+            } else {
+                if( U == null )
+                    U = new DenseMatrix64F(m,min);
+                else
+                    U.reshape(m,min, false);
+            }
+        } else  {
+            if( U == null )
+                U = new DenseMatrix64F(m,m);
+            else
+                U.reshape(m,m, false);
+        }
+
+        return U;
+    }
+
+    /**
+     * Returns the orthogonal V matrix.
+     *
+     * @param V If not null then the results will be stored here.  Otherwise a new matrix will be created.
+     * @return The extracted Q matrix.
+     */
+    @Override
+    public DenseMatrix64F getV( DenseMatrix64F V , boolean transpose , boolean compact ) {
+        V = handleV(V, transpose, compact,m,n,min);
+        CommonOps.setIdentity(V);
+
+//        UBV.print();
+
+        // todo the very first multiplication can be avoided by setting to the rank1update output
+        for( int j = min-1; j >= 0; j-- ) {
+            u[j+1] = 1;
+            for( int i = j+2; i < n; i++ ) {
+                u[i] = UBV.get(j,i);
+            }
+            if( transpose )
+                QrHelperFunctions_D64.rank1UpdateMultL(V, u, gammasV[j], j + 1, j + 1, n);
+            else
+                QrHelperFunctions_D64.rank1UpdateMultR(V, u, gammasV[j], j + 1, j + 1, n, this.b);
+        }
+
+        return V;
+    }
+
+    public static DenseMatrix64F handleV(DenseMatrix64F V, boolean transpose, boolean compact,
+                                   int m , int n , int min ) {
+        int w = n > m ? min + 1 : min;
+
+        if( compact ) {
+            if( transpose ) {
+                if( V == null ) {
+                    V = new DenseMatrix64F(w,n);
+                } else
+                    V.reshape(w,n, false);
+            } else {
+                if( V == null ) {
+                    V = new DenseMatrix64F(n,w);
+                } else
+                    V.reshape(n,w, false);
+            }
+        } else {
+            if( V == null ) {
+                V = new DenseMatrix64F(n,n);
+            } else
+                V.reshape(n,n, false);
+        }
+
+        return V;
+    }
+
+    /**
+     * Internal function for computing the decomposition.
+     */
+    private boolean _decompose() {
+        for( int k = 0; k < min; k++ ) {
+//            UBV.print();
+            computeU(k);
+//            System.out.println("--- after U");
+//            UBV.print();
+            computeV(k);
+//            System.out.println("--- after V");
+//            UBV.print();
+        }
+
+        return true;
+    }
+
+    protected void computeU( int k) {
+        double b[] = UBV.data;
+
+        // find the largest value in this column
+        // this is used to normalize the column and mitigate overflow/underflow
+        double max = 0;
+
+        for( int i = k; i < m; i++ ) {
+            // copy the householder vector to vector outside of the matrix to reduce caching issues
+            // big improvement on larger matrices and a relatively small performance hit on small matrices.
+            double val = u[i] = b[i*n+k];
+            val = Math.abs(val);
+            if( val > max )
+                max = val;
+        }
+
+        if( max > 0 ) {
+            // -------- set up the reflector Q_k
+            double tau = QrHelperFunctions_D64.computeTauAndDivide(k, m, u, max);
+
+            // write the reflector into the lower left column of the matrix
+            // while dividing u by nu
+            double nu = u[k] + tau;
+            QrHelperFunctions_D64.divideElements_Bcol(k + 1, m, n, u, b, k, nu);
+            u[k] = 1.0;
+
+            double gamma = nu/tau;
+            gammasU[k] = gamma;
+
+            // ---------- multiply on the left by Q_k
+            QrHelperFunctions_D64.rank1UpdateMultR(UBV, u, gamma, k + 1, k, m, this.b);
+
+            b[k*n+k] = -tau*max;
+        } else {
+            gammasU[k] = 0;
+        }
+    }
+
+    protected void computeV(int k) {
+        double b[] = UBV.data;
+
+        int row = k*n;
+
+        // find the largest value in this column
+        // this is used to normalize the column and mitigate overflow/underflow
+        double max = QrHelperFunctions_D64.findMax(b, row + k + 1, n - k - 1);
+
+        if( max > 0 ) {
+            // -------- set up the reflector Q_k
+
+            double tau = QrHelperFunctions_D64.computeTauAndDivide(k + 1, n, b, row, max);
+
+            // write the reflector into the lower left column of the matrix
+            double nu = b[row+k+1] + tau;
+            QrHelperFunctions_D64.divideElements_Brow(k + 2, n, u, b, row, nu);
+
+            u[k+1] = 1.0;
+
+            double gamma = nu/tau;
+            gammasV[k] = gamma;
+
+            // writing to u could be avoided by working directly with b.
+            // requires writing a custom rank1Update function
+            // ---------- multiply on the left by Q_k
+            QrHelperFunctions_D64.rank1UpdateMultL(UBV, u, gamma, k + 1, k + 1, n);
+
+            b[row+k+1] = -tau*max;
+        } else {
+            gammasV[k] = 0;
+        }
+    }
+
+    /**
+     * Returns gammas from the householder operations for the U matrix.
+     *
+     * @return gammas for householder operations
+     */
+    public double[] getGammasU() {
+        return gammasU;
+    }
+
+    /**
+     * Returns gammas from the householder operations for the V matrix.
+     *
+     * @return gammas for householder operations
+     */
+    public double[] getGammasV() {
+        return gammasV;
+    }
+
+    @Override
+    public boolean inputModified() {
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/bidiagonal/BidiagonalDecompositionTall_D64.java b/main/dense64/src/org/ejml/alg/dense/decomposition/bidiagonal/BidiagonalDecompositionTall_D64.java
new file mode 100644
index 0000000..7afedad
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/bidiagonal/BidiagonalDecompositionTall_D64.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.bidiagonal;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.DecompositionFactory;
+import org.ejml.interfaces.decomposition.BidiagonalDecomposition;
+import org.ejml.interfaces.decomposition.QRPDecomposition;
+import org.ejml.ops.CommonOps;
+
+
+/**
+ * <p>
+ * {@link org.ejml.interfaces.decomposition.BidiagonalDecomposition} specifically designed for tall matrices.
+ * First step is to perform QR decomposition on the input matrix.  Then R is decomposed using
+ * a bidiagonal decomposition.  By performing the bidiagonal decomposition on the smaller matrix
+ * computations can be saved if m/n > 5/3 and if U is NOT needed.
+ * </p>
+ *
+ * <p>
+ * A = [Q<sub>1</sub> Q<sub>2</sub>][U1 0; 0 I] [B1;0] V<sup>T</sup><br>
+ * U=[Q<sub>1</sub>*U1 Q<sub>2</sub>]<br>
+ * B=[B1;0]<br>
+ * A = U*B*V<sup>T</sup>
+ * </p>
+ *
+ * <p>
+ * A QRP decomposition is used internally.  That decomposition relies an a fixed threshold for selecting singular
+ * values and is known to be less stable than SVD.  There is the potential for a degregation of stability
+ * by using BidiagonalDecompositionTall instead of BidiagonalDecomposition. A few simple tests have shown
+ * that loss in stability to be insignificant.
+ * </p>
+ *
+ * <p>
+ * See page 404 in "Fundamentals of Matrix Computations", 2nd by David S. Watkins.
+ * </p>
+ *
+ *
+ * @author Peter Abeles
+ */
+// TODO optimize this code
+public class BidiagonalDecompositionTall_D64
+        implements BidiagonalDecomposition<DenseMatrix64F>
+{
+    QRPDecomposition<DenseMatrix64F> decompQRP = DecompositionFactory.qrp(500, 100); // todo this should be passed in
+    BidiagonalDecomposition<DenseMatrix64F> decompBi = new BidiagonalDecompositionRow_D64();
+
+    DenseMatrix64F B = new DenseMatrix64F(1,1);
+
+    // number of rows
+    int m;
+    // number of column
+    int n;
+    // min(m,n)
+    int min;
+
+    @Override
+    public void getDiagonal(double[] diag, double[] off) {
+        diag[0] = B.get(0);
+        for( int i = 1; i < n; i++ ) {
+            diag[i] = B.unsafe_get(i,i);
+            off[i-1] = B.unsafe_get(i-1,i);
+        }
+    }
+
+    @Override
+    public DenseMatrix64F getB(DenseMatrix64F B, boolean compact) {
+        B = BidiagonalDecompositionRow_D64.handleB(B, compact, m, n, min);
+
+        B.set(0,0,this.B.get(0,0));
+        for( int i = 1; i < min; i++ ) {
+            B.set(i,i, this.B.get(i,i));
+            B.set(i-1,i, this.B.get(i-1,i));
+        }
+        if( n > m)
+            B.set(min-1,min,this.B.get(min-1,min));
+
+        return B;
+    }
+
+    @Override
+    public DenseMatrix64F getU(DenseMatrix64F U, boolean transpose, boolean compact) {
+        U = BidiagonalDecompositionRow_D64.handleU(U, false, compact, m, n, min);
+
+        if( compact ) {
+            // U = Q*U1
+            DenseMatrix64F Q1 = decompQRP.getQ(null,true);
+            DenseMatrix64F U1 = decompBi.getU(null,false,true);
+            CommonOps.mult(Q1,U1,U);
+        } else {
+           // U = [Q1*U1 Q2]
+            DenseMatrix64F Q = decompQRP.getQ(U,false);
+            DenseMatrix64F U1 = decompBi.getU(null,false,true);
+            DenseMatrix64F Q1 = CommonOps.extract(Q,0,Q.numRows,0,min);
+            DenseMatrix64F tmp = new DenseMatrix64F(Q1.numRows,U1.numCols);
+            CommonOps.mult(Q1,U1,tmp);
+            CommonOps.insert(tmp,Q,0,0);
+        }
+
+        if( transpose )
+            CommonOps.transpose(U);
+
+        return U;
+    }
+
+    @Override
+    public DenseMatrix64F getV(DenseMatrix64F V, boolean transpose, boolean compact) {
+        return decompBi.getV(V,transpose,compact);
+    }
+
+    @Override
+    public boolean decompose(DenseMatrix64F orig) {
+
+        if( !decompQRP.decompose(orig) ) {
+            return false;
+        }
+
+        m = orig.numRows;
+        n = orig.numCols;
+        min = Math.min(m, n);
+        B.reshape(min, n,false);
+
+        decompQRP.getR(B,true);
+
+        // apply the column pivots.
+        // TODO this is horribly inefficient
+        DenseMatrix64F result = new DenseMatrix64F(min,n);
+        DenseMatrix64F P = decompQRP.getPivotMatrix(null);
+        CommonOps.multTransB(B, P, result);
+        B.set(result);
+
+        return decompBi.decompose(B);
+    }
+
+    @Override
+    public boolean inputModified() {
+        return decompQRP.inputModified();
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/chol/CholeskyBlockHelper_D64.java b/main/dense64/src/org/ejml/alg/dense/decomposition/chol/CholeskyBlockHelper_D64.java
new file mode 100644
index 0000000..60be5cc
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/chol/CholeskyBlockHelper_D64.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.chol;
+
+import org.ejml.data.DenseMatrix64F;
+
+
+/**
+ * A specialized Cholesky decomposition algorithm that is designed to help out
+ * {@link CholeskyDecompositionBlock_D64} perform its calculations.  While decomposing
+ * the matrix it will modify its internal lower triangular matrix and the original
+ * that is being modified.
+ *
+ * @author Peter Abeles
+ */
+class CholeskyBlockHelper_D64 {
+
+    // the decomposed matrix
+    private DenseMatrix64F L;
+    private double[] el;
+
+    /**
+     * Creates a CholeksyDecomposition capable of decomposing a matrix that is
+     * n by n, where n is the width.
+     *
+     * @param widthMax The maximum width of a matrix that can be processed.
+     */
+    public CholeskyBlockHelper_D64(int widthMax) {
+
+        this.L = new DenseMatrix64F(widthMax,widthMax);
+        this.el = L.data;
+    }
+
+    /**
+     * Decomposes a submatrix.  The results are written to the submatrix
+     * and to its internal matrix L.
+     *
+     * @param mat A matrix which has a submatrix that needs to be inverted
+     * @param indexStart the first index of the submatrix
+     * @param n The width of the submatrix that is to be inverted.
+     * @return True if it was able to finish the decomposition.
+     */
+    public boolean decompose( DenseMatrix64F mat , int indexStart , int n ) {
+        double m[] = mat.data;
+
+        double el_ii;
+        double div_el_ii=0;
+
+        for( int i = 0; i < n; i++ ) {
+            for( int j = i; j < n; j++ ) {
+                double sum = m[indexStart+i*mat.numCols+j];
+
+                int iEl = i*n;
+                int jEl = j*n;
+                int end = iEl+i;
+                // k = 0:i-1
+                for( ; iEl<end; iEl++,jEl++ ) {
+//                    sum -= el[i*n+k]*el[j*n+k];
+                    sum -= el[iEl]*el[jEl];
+                }
+
+                if( i == j ) {
+                    // is it positive-definate?
+                    if( sum <= 0.0 )
+                        return false;
+
+                    el_ii = Math.sqrt(sum);
+                    el[i*n+i] = el_ii;
+                    m[indexStart+i*mat.numCols+i] = el_ii;
+                    div_el_ii = 1.0/el_ii;
+                } else {
+                    double v = sum*div_el_ii;
+                    el[j*n+i] = v;
+                    m[indexStart+j*mat.numCols+i] = v;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns L matrix from the decomposition.<br>
+     * L*L<sup>T</sup>=A
+     *
+     * @return A lower triangular matrix.
+     */
+    public DenseMatrix64F getL() {
+        return L;
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/chol/CholeskyDecompositionBlock_D64.java b/main/dense64/src/org/ejml/alg/dense/decomposition/chol/CholeskyDecompositionBlock_D64.java
new file mode 100644
index 0000000..56dc341
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/chol/CholeskyDecompositionBlock_D64.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.chol;
+
+import org.ejml.data.DenseMatrix64F;
+
+
+/**
+ * This is an implementation of Cholesky that processes internal submatrices as blocks.  This is
+ * done to reduce the number of cache issues.
+ *
+ * @author Peter Abeles
+ */
+public class CholeskyDecompositionBlock_D64 extends CholeskyDecompositionCommon_D64 {
+
+    private int blockWidth; // how wide the blocks should be
+    private DenseMatrix64F B; // row rectangular matrix
+
+    private CholeskyBlockHelper_D64 chol;
+
+    /**
+     * Creates a CholeksyDecomposition capable of decomposing a matrix that is
+     * n by n, where n is the width.
+     *
+     * @param blockWidth The width of a block.
+     */
+    public CholeskyDecompositionBlock_D64(int blockWidth) {
+        super(true);
+
+        this.blockWidth = blockWidth;
+
+    }
+
+    /**
+     * Declares additional internal data structures.
+     */
+    @Override
+    public void setExpectedMaxSize( int numRows , int numCols ) {
+        super.setExpectedMaxSize(numRows,numCols);
+
+        // if the matrix that is being decomposed is smaller than the block we really don't
+        // see the B matrix.
+        if( numRows < blockWidth)
+            B = new DenseMatrix64F(0,0);
+        else
+            B = new DenseMatrix64F(blockWidth,maxWidth);
+
+        chol = new CholeskyBlockHelper_D64(blockWidth);
+    }
+
+    /**
+     * <p>
+     * Performs Choleksy decomposition on the provided matrix.
+     * </p>
+     *
+     * <p>
+     * If the matrix is not positive definite then this function will return
+     * false since it can't complete its computations.  Not all errors will be
+     * found.
+     * </p>
+     * @return True if it was able to finish the decomposition.
+     */
+    @Override
+    protected boolean decomposeLower() {
+
+        if( n < blockWidth)
+            B.reshape(0,0, false);
+        else
+            B.reshape(blockWidth,n-blockWidth, false);
+
+        int numBlocks = n / blockWidth;
+        int remainder = n % blockWidth;
+
+        if( remainder > 0 ) {
+            numBlocks++;
+        }
+
+        B.numCols = n;
+
+        for( int i = 0; i < numBlocks; i++ ) {
+            B.numCols -= blockWidth;
+
+            if( B.numCols > 0 ) {
+                // apply cholesky to the current block
+                if( !chol.decompose(T,(i*blockWidth)* T.numCols + i*blockWidth,blockWidth) )  return false;
+
+                int indexSrc = i*blockWidth* T.numCols + (i+1)*blockWidth;
+                int indexDst = (i+1)*blockWidth* T.numCols + i*blockWidth;
+
+                // B = L^(-1) * B
+                solveL_special(chol.getL().data, T,indexSrc,indexDst,B);
+
+                int indexL = (i+1)*blockWidth*n + (i+1)*blockWidth;
+
+                // c = c - a^T*a
+                symmRankTranA_sub(B, T,indexL);
+            } else {
+                int width = remainder > 0 ? remainder : blockWidth;
+                if( !chol.decompose(T,(i*blockWidth)* T.numCols + i*blockWidth,width) )  return false;
+            }
+        }
+
+
+        // zero the top right corner.
+        for( int i = 0; i < n; i++ ) {
+            for( int j = i+1; j < n; j++ ) {
+                t[i*n+j] = 0.0;
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    protected boolean decomposeUpper() {
+        throw new RuntimeException("Not implemented.  Do a lower decomposition and transpose it...");
+    }
+
+    /**
+     * This is a variation on the {@link org.ejml.alg.dense.decomposition.TriangularSolver#solveL} function.
+     * It grabs the input from the top right row rectangle of the source matrix then writes the results
+     * to the lower bottom column rectangle.  The rectangle matrices just matrices are submatrices
+     * of the matrix that is being decomposed.  The results are also written to B.
+     *
+     * @param L A lower triangular matrix.
+     * @param b_src matrix with the vectors that are to be solved for
+     * @param indexSrc First index of the submatrix where the inputs are coming from.
+     * @param indexDst First index of the submatrix where the results are going to.
+     * @param B
+     */
+    public static void solveL_special( final double L[] ,
+                                       final DenseMatrix64F b_src,
+                                       final int indexSrc , final int indexDst ,
+                                       final DenseMatrix64F B )
+    {
+        final double dataSrc[] = b_src.data;
+
+        final double b[]= B.data;
+        final int m = B.numRows;
+        final int n = B.numCols;
+        final int widthL = m;
+
+//        for( int j = 0; j < n; j++ ) {
+//            for( int i = 0; i < widthL; i++ ) {
+//                double sum = dataSrc[indexSrc+i*b_src.numCols+j];
+//                for( int k=0; k<i; k++ ) {
+//                    sum -= L[i*widthL+k]* b[k*n+j];
+//                }
+//                double val = sum / L[i*widthL+i];
+//                dataSrc[indexDst+j*b_src.numCols+i] = val;
+//                b[i*n+j] = val;
+//            }
+//        }
+
+        for( int j = 0; j < n; j++ ) {
+            int indexb = j;
+            int rowL = 0;
+            
+            //for( int i = 0; i < widthL; i++
+            for( int i = 0; i < widthL; i++ ,  indexb += n, rowL += widthL ) {
+                double sum = dataSrc[indexSrc+i*b_src.numCols+j];
+
+                int indexL = rowL;
+                int endL = indexL + i;
+                int indexB = j;
+                //for( int k=0; k<i; k++ ) {
+                for( ; indexL != endL; indexB += n) {
+                    sum -= L[indexL++]* b[indexB];
+                }
+                double val = sum / L[i*widthL+i];
+                dataSrc[indexDst+j*b_src.numCols+i] = val;
+                b[indexb] = val;
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs this operation:<br>
+     * <br>
+     * c = c - a<sup>T</sup>a <br>
+     * where c is a submatrix.
+     * </p>
+     *
+     * Only the upper triangle is updated.
+     *
+     * @param a A matrix.
+     * @param c A matrix.
+     * @param startIndexC start of the submatrix in c.
+     */
+    public static void symmRankTranA_sub( DenseMatrix64F a , DenseMatrix64F c ,
+                                          int startIndexC )
+    {
+        // TODO update so that it doesn't modify/read parts that it shouldn't
+        final double dataA[] = a.data;
+        final double dataC[] = c.data;
+
+//        for( int i = 0; i < a.numCols; i++ ) {
+//            for( int k = 0; k < a.numRows; k++ ) {
+//                double valA = dataA[k*a.numCols+i];
+//
+//                for( int j = i; j < a.numCols; j++ ) {
+//                    dataC[startIndexC+i*c.numCols+j] -= valA * dataA[k*a.numCols+j];
+//                }
+//            }
+//        }
+
+        final int strideC = c.numCols + 1;
+        for( int i = 0; i < a.numCols; i++ ) {
+            int indexA = i;
+            int endR = a.numCols;
+
+            for( int k = 0; k < a.numRows; k++ , indexA += a.numCols , endR += a.numCols) {
+                int indexC = startIndexC;
+                final double valA = dataA[indexA];
+                int indexR = indexA;
+
+                while( indexR < endR ) {
+                    dataC[indexC++] -= valA * dataA[indexR++];
+                }
+            }
+            startIndexC += strideC;
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/chol/CholeskyDecompositionCommon_D64.java b/main/dense64/src/org/ejml/alg/dense/decomposition/chol/CholeskyDecompositionCommon_D64.java
new file mode 100644
index 0000000..2e7f97d
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/chol/CholeskyDecompositionCommon_D64.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.chol;
+
+
+import org.ejml.data.Complex64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.CholeskyDecomposition;
+import org.ejml.ops.CommonOps;
+
+
+/**
+ *
+ * <p>
+ * This is an abstract class for a Cholesky decomposition.  It provides the solvers, but the actual
+ * decomposition is provided in other classes.
+ * </p>
+ *
+ * @see org.ejml.interfaces.decomposition.CholeskyDecomposition
+ * @author Peter Abeles
+ */
+public abstract class CholeskyDecompositionCommon_D64
+        implements CholeskyDecomposition<DenseMatrix64F> {
+
+    // it can decompose a matrix up to this width
+    protected int maxWidth=-1;
+
+    // width and height of the matrix
+    protected int n;
+
+    // the decomposed matrix
+    protected DenseMatrix64F T;
+    protected double[] t;
+
+    // tempoary variable used by various functions
+    protected double vv[];
+
+    // is it a lower triangular matrix or an upper triangular matrix
+    protected boolean lower;
+
+    // storage for computed determinant
+    protected Complex64F det = new Complex64F();
+
+    /**
+     * Specifies if a lower or upper variant should be constructed.
+     *
+     * @param lower should a lower or upper triangular matrix be used.
+     */
+    public CholeskyDecompositionCommon_D64(boolean lower) {
+        this.lower = lower;
+    }
+
+    public void setExpectedMaxSize( int numRows , int numCols ) {
+        if( numRows != numCols ) {
+            throw new IllegalArgumentException("Can only decompose square matrices");
+        }
+
+        this.maxWidth = numCols;
+
+        this.vv = new double[maxWidth];
+    }
+
+    /**
+     * If true the decomposition was for a lower triangular matrix.
+     * If false it was for an upper triangular matrix.
+     *
+     * @return True if lower, false if upper.
+     */
+    @Override
+    public boolean isLower() {
+        return lower;
+    }
+
+    /**
+     * <p>
+     * Performs Choleksy decomposition on the provided matrix.
+     * </p>
+     *
+     * <p>
+     * If the matrix is not positive definite then this function will return
+     * false since it can't complete its computations.  Not all errors will be
+     * found.  This is an efficient way to check for positive definiteness.
+     * </p>
+     * @param mat A symmetric positive definite matrix with n <= widthMax.
+     * @return True if it was able to finish the decomposition.
+     */
+    @Override
+    public boolean decompose( DenseMatrix64F mat ) {
+        if( mat.numRows > maxWidth ) {
+            setExpectedMaxSize(mat.numRows,mat.numCols);
+        } else if( mat.numRows != mat.numCols ) {
+            throw new IllegalArgumentException("Must be a square matrix.");
+        }
+
+        n = mat.numRows;
+
+        T = mat;
+        t = T.data;
+
+        if(lower) {
+            return decomposeLower();
+        } else {
+            return decomposeUpper();
+        }
+    }
+
+    @Override
+    public boolean inputModified() {
+        return true;
+    }
+
+    /**
+     * Performs an lower triangular decomposition.
+     *
+     * @return true if the matrix was decomposed.
+     */
+    protected abstract boolean decomposeLower();
+
+    /**
+     * Performs an upper triangular decomposition.
+     *
+     * @return true if the matrix was decomposed.
+     */
+    protected abstract boolean decomposeUpper();
+
+    @Override
+    public DenseMatrix64F getT( DenseMatrix64F T ) {
+        // see if it needs to declare a new matrix or not
+        if( T == null ) {
+            T = new DenseMatrix64F(n,n);
+        } else {
+            if( T.numRows != n || T.numCols != n )
+                throw new IllegalArgumentException("Unexpected matrix dimension for T.");
+
+            CommonOps.fill(T, 0);
+        }
+
+        // write the values to T
+        if( lower ) {
+            for( int i = 0; i < n; i++ ) {
+                for( int j = 0; j <= i; j++ ) {
+                    T.unsafe_set(i,j,this.T.unsafe_get(i,j));
+                }
+            }
+        } else {
+            for( int i = 0; i < n; i++ ) {
+                for( int j = i; j < n; j++ ) {
+                    T.unsafe_set(i,j,this.T.unsafe_get(i,j));
+                }
+            }
+        }
+
+        return T;
+    }
+
+    /**
+     * Returns the triangular matrix from the decomposition.
+     *
+     * @return A lower or upper triangular matrix.
+     */
+    public DenseMatrix64F getT() {
+        return T;
+    }
+
+    public double[] _getVV() {
+        return vv;
+    }
+
+    @Override
+    public Complex64F computeDeterminant() {
+        double prod = 1;
+
+        int total = n*n;
+        for( int i = 0; i < total; i += n + 1 ) {
+            prod *= t[i];
+        }
+
+        det.real = prod*prod;
+        det.imaginary = 0;
+
+        return det;
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/chol/CholeskyDecompositionInner_D64.java b/main/dense64/src/org/ejml/alg/dense/decomposition/chol/CholeskyDecompositionInner_D64.java
new file mode 100644
index 0000000..d03b56f
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/chol/CholeskyDecompositionInner_D64.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.chol;
+
+/**
+ * <p>
+ * This implementation of a Cholesky decomposition using the inner-product form.
+ * For large matrices a block implementation is better.  On larger matrices the lower triangular
+ * decomposition is significantly faster.  This is faster on smaller matrices than {@link CholeskyDecompositionBlock_D64}
+ * but much slower on larger matrices.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class CholeskyDecompositionInner_D64 extends CholeskyDecompositionCommon_D64 {
+
+    public CholeskyDecompositionInner_D64() {
+        super(true);
+    }
+
+    public CholeskyDecompositionInner_D64(boolean lower) {
+        super(lower);
+    }
+
+    @Override
+    protected boolean decomposeLower() {
+        double el_ii;
+        double div_el_ii=0;
+
+        for( int i = 0; i < n; i++ ) {
+            for( int j = i; j < n; j++ ) {
+                double sum = t[i*n+j];
+
+                int iEl = i*n;
+                int jEl = j*n;
+                int end = iEl+i;
+                // k = 0:i-1
+                for( ; iEl<end; iEl++,jEl++ ) {
+//                    sum -= el[i*n+k]*el[j*n+k];
+                    sum -= t[iEl]* t[jEl];
+                }
+
+                if( i == j ) {
+                    // is it positive-definite?
+                    if( sum <= 0.0 )
+                        return false;
+
+                    el_ii = Math.sqrt(sum);
+                    t[i*n+i] = el_ii;
+                    div_el_ii = 1.0/el_ii;
+                } else {
+                    t[j*n+i] = sum*div_el_ii;
+                }
+            }
+        }
+
+        // zero the top right corner.
+        for( int i = 0; i < n; i++ ) {
+            for( int j = i+1; j < n; j++ ) {
+                t[i*n+j] = 0.0;
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    protected boolean decomposeUpper() {
+        double el_ii;
+        double div_el_ii=0;
+
+        for( int i = 0; i < n; i++ ) {
+            for( int j = i; j < n; j++ ) {
+                double sum = t[i*n+j];
+
+                for( int k = 0; k < i; k++ ) {
+                    sum -= t[k*n+i]* t[k*n+j];
+                }
+
+                if( i == j ) {
+                    // is it positive-definite?
+                    if( sum <= 0.0 )
+                        return false;
+
+                    // I suspect that the sqrt is slowing this down relative to MTJ
+                    el_ii = Math.sqrt(sum);
+                    t[i*n+i] = el_ii;
+                    div_el_ii = 1.0/el_ii;
+                } else {
+                    t[i*n+j] = sum*div_el_ii;
+                }
+            }
+        }
+        // zero the lower left corner.
+        for( int i = 0; i < n; i++ ) {
+            for( int j = 0; j < i; j++ ) {
+                t[i*n+j] = 0.0;
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/chol/CholeskyDecompositionLDL_D64.java b/main/dense64/src/org/ejml/alg/dense/decomposition/chol/CholeskyDecompositionLDL_D64.java
new file mode 100644
index 0000000..6ef138f
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/chol/CholeskyDecompositionLDL_D64.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.chol;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.CholeskyLDLDecomposition;
+import org.ejml.ops.CommonOps;
+
+
+/**
+ * <p>
+ * This variant on the Cholesky decomposition avoid the need to take the square root
+ * by performing the following decomposition:<br>
+ * <br>
+ * L*D*L<sup>T</sup>=A<br>
+ * <br>
+ * where L is a lower triangular matrix with zeros on the diagonal. D is a diagonal matrix.
+ * The diagonal elements of L are equal to one.
+ * </p>
+ * <p>
+ * Unfortunately the speed advantage of not computing the square root is washed out by the
+ * increased number of array accesses.  There only appears to be a slight speed boost for
+ * very small matrices.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class CholeskyDecompositionLDL_D64
+        implements CholeskyLDLDecomposition<DenseMatrix64F> {
+
+    // it can decompose a matrix up to this width
+    private int maxWidth;
+    // width and height of the matrix
+    private int n;
+
+    // the decomposed matrix
+    private DenseMatrix64F L;
+    private double[] el;
+
+    // the D vector
+    private double[] d;
+
+    // tempoary variable used by various functions
+    double vv[];
+
+    public void setExpectedMaxSize( int numRows , int numCols ) {
+        if( numRows != numCols ) {
+            throw new IllegalArgumentException("Can only decompose square matrices");
+        }
+
+        this.maxWidth = numRows;
+
+        this.L = new DenseMatrix64F(maxWidth,maxWidth);
+        this.el = L.data;
+
+        this.vv = new double[maxWidth];
+        this.d = new double[maxWidth];
+    }
+
+    /**
+     * <p>
+     * Performs Choleksy decomposition on the provided matrix.
+     * </p>
+     *
+     * <p>
+     * If the matrix is not positive definite then this function will return
+     * false since it can't complete its computations.  Not all errors will be
+     * found.
+     * </p>
+     * @param mat A symetric n by n positive definite matrix.
+     * @return True if it was able to finish the decomposition.
+     */
+    public boolean decompose( DenseMatrix64F mat ) {
+        if( mat.numRows > maxWidth ) {
+            setExpectedMaxSize(mat.numRows,mat.numCols);
+        } else if( mat.numRows != mat.numCols ) {
+            throw new RuntimeException("Can only decompose square matrices");
+        }
+        n = mat.numRows;
+
+        L.set(mat);
+
+        double d_inv=0;
+        for( int i = 0; i < n; i++ ) {
+            for( int j = i; j < n; j++ ) {
+                double sum = el[i*n+j];
+
+                for( int k = 0; k < i; k++ ) {
+                    sum -= el[i*n+k]*el[j*n+k]*d[k];
+                }
+
+                if( i == j ) {
+                    // is it positive-definate?
+                    if( sum <= 0.0 )
+                        return false;
+
+                    d[i] = sum;
+                    d_inv = 1.0/sum;
+                    el[i*n+i] = 1;
+                } else {
+                    el[j*n+i] = sum*d_inv;
+                }
+            }
+        }
+        // zero the top right corner.
+        for( int i = 0; i < n; i++ ) {
+            for( int j = i+1; j < n; j++ ) {
+                el[i*n+j] = 0.0;
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public boolean inputModified() {
+        return false;
+    }
+
+    /**
+     * Diagonal elements of the diagonal D matrix.
+     *
+     * @return diagonal elements of D
+     */
+    @Override
+    public double[] getDiagonal() {
+        return d;
+    }
+
+    /**
+     * Returns L matrix from the decomposition.<br>
+     * L*D*L<sup>T</sup>=A
+     *
+     * @return A lower triangular matrix.
+     */
+    public DenseMatrix64F getL() {
+        return L;
+    }
+
+    public double[] _getVV() {
+        return vv;
+    }
+
+    @Override
+    public DenseMatrix64F getL(DenseMatrix64F L) {
+        if( L == null ) {
+            L = this.L.copy();
+        } else {
+            L.set(this.L);
+        }
+
+        return L;
+    }
+
+    @Override
+    public DenseMatrix64F getD(DenseMatrix64F D) {
+        return CommonOps.diag(D,L.numCols,d);
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/chol/CholeskyDecomposition_B64_to_D64.java b/main/dense64/src/org/ejml/alg/dense/decomposition/chol/CholeskyDecomposition_B64_to_D64.java
new file mode 100644
index 0000000..9bcde10
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/chol/CholeskyDecomposition_B64_to_D64.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.chol;
+
+import org.ejml.EjmlParameters;
+import org.ejml.alg.block.BlockMatrixOps;
+import org.ejml.alg.block.decomposition.chol.CholeskyOuterForm_B64;
+import org.ejml.alg.dense.decomposition.BaseDecomposition_B64_to_D64;
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.Complex64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.CholeskyDecomposition;
+
+
+/**
+ * Wrapper around {@link org.ejml.alg.block.decomposition.chol.CholeskyOuterForm_B64} that allows
+ * it to process DenseMatrix64F.
+ *
+ * @author Peter Abeles
+ */
+public class CholeskyDecomposition_B64_to_D64
+        extends BaseDecomposition_B64_to_D64 implements CholeskyDecomposition<DenseMatrix64F> {
+
+    public CholeskyDecomposition_B64_to_D64(boolean lower) {
+        super(new CholeskyOuterForm_B64(lower), EjmlParameters.BLOCK_WIDTH);
+    }
+
+    @Override
+    public boolean isLower() {
+        return ((CholeskyOuterForm_B64)alg).isLower();
+    }
+
+    @Override
+    public DenseMatrix64F getT(DenseMatrix64F T) {
+        BlockMatrix64F T_block = ((CholeskyOuterForm_B64)alg).getT(null);
+
+        if( T == null ) {
+            T = new DenseMatrix64F(T_block.numRows,T_block.numCols);
+        }
+
+        BlockMatrixOps.convert(T_block,T);
+        // todo set zeros
+        return T;
+    }
+
+    @Override
+    public Complex64F computeDeterminant() {
+        return ((CholeskyOuterForm_B64)alg).computeDeterminant();
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/eig/EigenPowerMethod.java b/main/dense64/src/org/ejml/alg/dense/decomposition/eig/EigenPowerMethod.java
new file mode 100644
index 0000000..bbeb4c7
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/eig/EigenPowerMethod.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.eig;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.LinearSolverFactory;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.NormOps;
+import org.ejml.ops.SpecializedOps;
+
+
+/**
+ * <p>
+ * The power method is an iterative method that can be used to find dominant eigen vector in
+ * a matrix.  Computing <b>A<sup>n</sup>q</b> for larger and larger values of n, where q is a vector.  Eventually the
+ * dominant (if there is any) eigen vector will "win".
+ * <p>
+ *
+ * <p>
+ * Shift implementations find the eigen value of the matrix B=A-pI instead.  This matrix has the
+ * same eigen vectors, but can converge much faster if p is chosen wisely.
+ * </p>
+ *
+ * <p>
+ * See section 5.3 in "Fundamentals of Matrix Computations" Second Edition, David S. Watkins.
+ * </p>
+ *
+ * <p>
+ * WARNING:  These functions have well known conditions where they will not converge or converge
+ * very slowly and are only used in special situations in practice.  I have also seen it converge
+ * to none dominant eigen vectors.
+ * </p>
+ *
+ *
+ * @author Peter Abeles
+ */
+public class EigenPowerMethod {
+
+    // used to determine convergence
+    private double tol = 1e-10;
+
+
+    private DenseMatrix64F q0,q1,q2;
+
+    private int maxIterations = 20;
+
+    private DenseMatrix64F B;
+    private DenseMatrix64F seed;
+
+    /**
+     *
+     * @param size The size of the matrix which can be processed.
+     */
+    public EigenPowerMethod( int size ) {
+        q0 = new DenseMatrix64F(size,1);
+        q1 = new DenseMatrix64F(size,1);
+        q2 = new DenseMatrix64F(size,1);
+
+        B = new DenseMatrix64F(size,size);
+    }
+
+    /**
+     * Sets the value of the vector to use in the start of the iterations.
+     *
+     * @param seed The initial seed vector in the iteration.
+     */
+    public void setSeed(DenseMatrix64F seed) {
+        this.seed = seed;
+    }
+
+    /**
+     *
+     * @param maxIterations
+     * @param tolerance
+     */
+    public void setOptions(int maxIterations, double tolerance) {
+        this.maxIterations = maxIterations;
+        this.tol = tolerance;
+    }
+
+    /**
+     * This method computes the eigen vector with the largest eigen value by using the
+     * direct power method. This technique is the easiest to implement, but the slowest to converge.
+     * Works only if all the eigenvalues are real.
+     * 
+     * @param A The matrix. Not modified.
+     * @return If it converged or not.
+     */
+    public boolean computeDirect( DenseMatrix64F A ) {
+
+        initPower(A);
+
+        boolean converged = false;
+
+        for( int i = 0; i < maxIterations && !converged; i++ ) {
+//            q0.print();
+            
+            CommonOps.mult(A,q0,q1);
+            double s = NormOps.normPInf(q1);
+            CommonOps.divide(q1,s,q2);
+
+            converged = checkConverged(A);
+        }
+
+        return converged;
+    }
+
+
+    private void initPower(DenseMatrix64F A ) {
+        if( A.numRows != A.numCols )
+            throw new IllegalArgumentException("A must be a square matrix.");
+
+        if( seed != null ) {
+            q0.set(seed);
+        } else {
+            for( int i = 0; i < A.numRows; i++ ) {
+                q0.data[i] = 1;
+            }
+        }
+    }
+
+    /**
+     * Test for convergence by seeing if the element with the largest change
+     * is smaller than the tolerance.  In some test cases it alternated between
+     * the + and - values of the eigen vector.  When this happens it seems to have "converged"
+     * to a non-dominant eigen vector.    At least in the case I looked at.  I haven't devoted
+     * a lot of time into this issue...
+     */
+    private boolean checkConverged(DenseMatrix64F A) {
+        double worst = 0;
+        double worst2 = 0;
+        for( int j = 0; j < A.numRows; j++ ) {
+            double val = Math.abs(q2.data[j] - q0.data[j]);
+            if( val > worst ) worst = val;
+            val = Math.abs(q2.data[j] + q0.data[j]);
+            if( val > worst2 ) worst2 = val;
+        }
+
+        // swap vectors
+        DenseMatrix64F temp = q0;
+        q0 = q2;
+        q2 = temp;
+
+        if( worst < tol )
+            return true;
+        else if( worst2 < tol )
+            return true;
+        else
+            return false;
+    }
+
+    /**
+     * Computes the most dominant eigen vector of A using a shifted matrix.
+     * The shifted matrix is defined as <b>B = A - αI</b> and can converge faster
+     * if α is chosen wisely.  In general it is easier to choose a value for α
+     * that will converge faster with the shift-invert strategy than this one.
+     *
+     * @param A The matrix.
+     * @param alpha Shifting factor.
+     * @return If it converged or not.
+     */
+    public boolean computeShiftDirect( DenseMatrix64F A ,double alpha) {
+        SpecializedOps.addIdentity(A,B,-alpha);
+
+        return computeDirect(B);
+    }
+
+    /**
+     * Computes the most dominant eigen vector of A using an inverted shifted matrix.
+     * The inverted shifted matrix is defined as <b>B = (A - αI)<sup>-1</sup></b> and
+     * can converge faster if α is chosen wisely.
+     *
+     * @param A An invertible square matrix matrix.
+     * @param alpha Shifting factor.
+     * @return If it converged or not.
+     */
+    public boolean computeShiftInvert( DenseMatrix64F A , double alpha ) {
+        initPower(A);
+
+        LinearSolver solver = LinearSolverFactory.linear(A.numCols);
+
+        SpecializedOps.addIdentity(A,B,-alpha);
+        solver.setA(B);
+
+        boolean converged = false;
+
+        for( int i = 0; i < maxIterations && !converged; i++ ) {
+            solver.solve(q0,q1);
+            double s = NormOps.normPInf(q1);
+            CommonOps.divide(q1,s,q2);
+
+            converged = checkConverged(A);
+        }
+
+         return converged;
+    }
+
+    public DenseMatrix64F getEigenVector() {
+        return q0;
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/eig/EigenvalueExtractor.java b/main/dense64/src/org/ejml/alg/dense/decomposition/eig/EigenvalueExtractor.java
new file mode 100644
index 0000000..f25ab60
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/eig/EigenvalueExtractor.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.eig;
+
+import org.ejml.data.Complex64F;
+import org.ejml.data.DenseMatrix64F;
+
+
+/**
+ * @author Peter Abeles
+ */
+public interface EigenvalueExtractor {
+
+    public boolean process( DenseMatrix64F A );
+
+    public int getNumberOfEigenvalues();
+
+    public Complex64F[] getEigenvalues();
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/eig/EigenvalueSmall.java b/main/dense64/src/org/ejml/alg/dense/decomposition/eig/EigenvalueSmall.java
new file mode 100644
index 0000000..2f6fe78
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/eig/EigenvalueSmall.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.eig;
+
+import org.ejml.data.Complex64F;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class EigenvalueSmall {
+
+    public Complex64F value0 = new Complex64F();
+    public Complex64F value1 = new Complex64F();
+
+    // if |a11-a22| >> |a12+a21| there might be a better way.  see pg371
+    public void value2x2( double a11 , double a12, double a21 , double a22 )
+    {
+        // apply a rotators such that th a11 and a22 elements are the same
+        double c,s;
+
+        if( a12 + a21 == 0 ) { // is this pointless since
+            c = s = 1.0/Math.sqrt(2);
+        } else {
+            double aa = (a11-a22);
+            double bb = (a12+a21);
+
+            double t_hat = aa/bb;
+            double t = t_hat/(1.0+Math.sqrt(1.0+t_hat*t_hat));
+
+            c = 1.0/Math.sqrt(1.0+t*t);
+            s = c*t;
+        }
+
+        double c2 = c*c;
+        double s2 = s*s;
+        double cs = c*s;
+
+        double b11 = c2*a11 + s2*a22 - cs*(a12+a21);
+        double b12 = c2*a12 - s2*a21 + cs*(a11-a22);
+        double b21 = c2*a21 - s2*a12 + cs*(a11-a22);
+//        double b22 = c2*a22 + s2*a11 + cs*(a12+a21);
+
+        // apply second rotator to make A upper triangular if real eigenvalues
+        if( b21*b12 >= 0 ) {
+            if( b12 == 0 ) {
+                c = 0;
+                s = 1;
+            } else {
+                s = Math.sqrt(b21/(b12+b21));
+                c = Math.sqrt(b12/(b12+b21));
+            }
+
+//            c2 = b12;//c*c;
+//            s2 = b21;//s*s;
+            cs = c*s;
+
+            a11 = b11 - cs*(b12 + b21);
+//            a12 = c2*b12 - s2*b21;
+//            a21 = c2*b21 - s2*b12;
+            a22 = b11 + cs*(b12 + b21);
+
+            value0.real = a11;
+            value1.real = a22;
+
+            value0.imaginary = value1.imaginary = 0;
+
+        } else {
+            value0.real = value1.real = b11;
+            value0.imaginary = Math.sqrt(-b21*b12);
+            value1.imaginary = -value0.imaginary;
+        }
+    }
+
+    /**
+     * Computes the eigenvalues of a 2 by 2 matrix using a faster but more prone to errors method.  This
+     * is the typical method.
+     */
+    public void value2x2_fast( double a11 , double a12, double a21 , double a22 )
+    {
+        double left = (a11+a22)/2.0;
+        double inside = 4.0*a12*a21 + (a11-a22)*(a11-a22);
+
+        if( inside < 0 ) {
+            value0.real = value1.real = left;
+            value0.imaginary = Math.sqrt(-inside)/2.0;
+            value1.imaginary = -value0.imaginary;
+        } else {
+            double right = Math.sqrt(inside)/2.0;
+            value0.real = (left+right);
+            value1.real = (left-right);
+            value0.imaginary = value1.imaginary = 0.0;
+        }
+    }
+
+    /**
+     * Compute the symmetric eigenvalue using a slightly safer technique
+     */
+    // See page 385 of Fundamentals of Matrix Computations 2nd
+    public void symm2x2_fast( double a11 , double a12, double a22 )
+    {
+//        double p = (a11 - a22)*0.5;
+//        double r = Math.sqrt(p*p + a12*a12);
+//
+//        value0.real = a22 + a12*a12/(r-p);
+//        value1.real = a22 - a12*a12/(r+p);
+//    }
+//
+//    public void symm2x2_std( double a11 , double a12, double a22 )
+//    {
+        double left  = (a11+a22)*0.5;
+        double b     = (a11-a22)*0.5;
+        double right = Math.sqrt(b*b+a12*a12);
+        value0.real = left + right;
+        value1.real = left - right;
+    }
+
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/eig/SwitchingEigenDecomposition.java b/main/dense64/src/org/ejml/alg/dense/decomposition/eig/SwitchingEigenDecomposition.java
new file mode 100644
index 0000000..c74e7e7
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/eig/SwitchingEigenDecomposition.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.eig;
+
+import org.ejml.data.Complex64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.DecompositionFactory;
+import org.ejml.interfaces.decomposition.EigenDecomposition;
+import org.ejml.ops.MatrixFeatures;
+
+
+/**
+ * Checks to see what type of matrix is being decomposed and calls different eigenvalue decomposition
+ * algorithms depending on the results.  This primarily checks to see if the matrix is symmetric or not.
+ *
+ *
+ * @author Peter Abeles
+ */
+public class SwitchingEigenDecomposition
+        implements EigenDecomposition<DenseMatrix64F> {
+    // tolerance used in deciding if a matrix is symmetric or not
+    private double tol;
+
+    EigenDecomposition<DenseMatrix64F> symmetricAlg;
+    EigenDecomposition<DenseMatrix64F> generalAlg;
+
+    boolean symmetric;
+    // should it compute eigenvectors or just eigenvalues?
+    boolean computeVectors;
+
+    DenseMatrix64F A = new DenseMatrix64F(1,1);
+
+    /**
+     *
+     * @param computeVectors
+     * @param tol Tolerance for a matrix being symmetric
+     */
+    public SwitchingEigenDecomposition( int matrixSize , boolean computeVectors , double tol ) {
+        symmetricAlg = DecompositionFactory.eig(matrixSize,computeVectors,true);
+        generalAlg = DecompositionFactory.eig(matrixSize,computeVectors,false);
+        this.computeVectors = computeVectors;
+        this.tol = tol;
+    }
+
+    public SwitchingEigenDecomposition( int matrixSize ) {
+        this(matrixSize,true,1e-8);
+    }
+
+    @Override
+    public int getNumberOfEigenvalues() {
+        return symmetric ? symmetricAlg.getNumberOfEigenvalues() :
+                generalAlg.getNumberOfEigenvalues();
+    }
+
+    @Override
+    public Complex64F getEigenvalue(int index) {
+        return symmetric ? symmetricAlg.getEigenvalue(index) :
+                generalAlg.getEigenvalue(index);
+    }
+
+    @Override
+    public DenseMatrix64F getEigenVector(int index) {
+        if( !computeVectors )
+            throw new IllegalArgumentException("Configured to not compute eignevectors");
+
+        return symmetric ? symmetricAlg.getEigenVector(index) :
+                generalAlg.getEigenVector(index);
+    }
+
+    @Override
+    public boolean decompose(DenseMatrix64F orig) {
+        A.set(orig);
+
+        symmetric = MatrixFeatures.isSymmetric(A,tol);
+
+        return symmetric ?
+                symmetricAlg.decompose(A) :
+                generalAlg.decompose(A);
+
+    }
+
+    @Override
+    public boolean inputModified() {
+        // since it doesn't know which algorithm will be used until a matrix is provided make a copy
+        // of all inputs
+        return false;
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/eig/SymmetricQRAlgorithmDecomposition_D64.java b/main/dense64/src/org/ejml/alg/dense/decomposition/eig/SymmetricQRAlgorithmDecomposition_D64.java
new file mode 100644
index 0000000..84a9908
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/eig/SymmetricQRAlgorithmDecomposition_D64.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.eig;
+
+import org.ejml.alg.dense.decomposition.eig.symm.SymmetricQREigenHelper;
+import org.ejml.alg.dense.decomposition.eig.symm.SymmetricQrAlgorithm;
+import org.ejml.data.Complex64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.DecompositionFactory;
+import org.ejml.interfaces.decomposition.EigenDecomposition;
+import org.ejml.interfaces.decomposition.TridiagonalSimilarDecomposition;
+import org.ejml.ops.CommonOps;
+
+
+/**
+ * <p>
+ * Computes the eigenvalues and eigenvectors of a real symmetric matrix using the symmetric implicit QR algorithm.
+ * Inside each iteration a QR decomposition of A<sub>i</sub>-p<sub>i</sub>I is implicitly computed.
+ * </p>
+ * <p>
+ * This implementation is based on the algorithm is sketched out in:<br>
+ * David S. Watkins, "Fundamentals of Matrix Computations," Second Edition. page 377-385
+ * </p>
+ *
+ * @see org.ejml.alg.dense.decomposition.eig.symm.SymmetricQrAlgorithm
+ * @see org.ejml.alg.dense.decomposition.hessenberg.TridiagonalDecompositionHouseholder_D64
+ *
+ * @author Peter Abeles
+ */
+public class SymmetricQRAlgorithmDecomposition_D64
+        implements EigenDecomposition<DenseMatrix64F> {
+
+    // computes a tridiagonal matrix whose eigenvalues are the same as the original
+    // matrix and can be easily computed.
+    private TridiagonalSimilarDecomposition<DenseMatrix64F> decomp;
+    // helper class for eigenvalue and eigenvector algorithms
+    private SymmetricQREigenHelper helper;
+    // computes the eigenvectors
+    private SymmetricQrAlgorithm vector;
+
+    // should it compute eigenvectors at the same time as the eigenvalues?
+    private boolean computeVectorsWithValues = false;
+
+    // where the found eigenvalues are stored
+    private double values[];
+
+    // where the tridiagonal matrix is stored
+    private double diag[];
+    private double off[];
+
+    private double diagSaved[];
+    private double offSaved[];
+
+    // temporary variable used to store/compute eigenvectors
+    private DenseMatrix64F V;
+    // the extracted eigenvectors
+    private DenseMatrix64F eigenvectors[];
+
+    // should it compute eigenvectors or just eigenvalues
+    boolean computeVectors;
+
+    public SymmetricQRAlgorithmDecomposition_D64(TridiagonalSimilarDecomposition<DenseMatrix64F> decomp,
+                                                 boolean computeVectors) {
+
+        this.decomp = decomp;
+        this.computeVectors = computeVectors;
+
+        helper = new SymmetricQREigenHelper();
+
+        vector = new SymmetricQrAlgorithm(helper);
+    }
+
+    public SymmetricQRAlgorithmDecomposition_D64(boolean computeVectors) {
+
+        this(DecompositionFactory.tridiagonal(0),computeVectors);
+    }
+
+    public void setComputeVectorsWithValues(boolean computeVectorsWithValues) {
+        if( !computeVectors )
+            throw new IllegalArgumentException("Compute eigenvalues has been set to false");
+
+        this.computeVectorsWithValues = computeVectorsWithValues;
+    }
+
+    /**
+     * Used to limit the number of internal QR iterations that the QR algorithm performs.  20
+     * should be enough for most applications.
+     *
+     * @param max The maximum number of QR iterations it will perform.
+     */
+    public void setMaxIterations( int max ) {
+        vector.setMaxIterations(max);
+    }
+
+    @Override
+    public int getNumberOfEigenvalues() {
+        return helper.getMatrixSize();
+    }
+
+    @Override
+    public Complex64F getEigenvalue(int index) {
+        return new Complex64F(values[index],0);
+    }
+
+    @Override
+    public DenseMatrix64F getEigenVector(int index) {
+        return eigenvectors[index];
+    }
+
+    /**
+     * Decomposes the matrix using the QR algorithm.  Care was taken to minimize unnecessary memory copying
+     * and cache skipping.
+     *
+     * @param orig The matrix which is being decomposed.  Not modified.
+     * @return true if it decomposed the matrix or false if an error was detected.  This will not catch all errors.
+     */
+    @Override
+    public boolean decompose(DenseMatrix64F orig) {
+        if( orig.numCols != orig.numRows )
+            throw new IllegalArgumentException("Matrix must be square.");
+        if( orig.numCols <= 0 )
+            return false;
+
+        int N = orig.numRows;
+
+        // compute a similar tridiagonal matrix
+        if( !decomp.decompose(orig) )
+            return false;
+
+        if( diag == null || diag.length < N) {
+            diag = new double[N];
+            off = new double[N-1];
+        }
+        decomp.getDiagonal(diag,off);
+
+        // Tell the helper to work with this matrix
+        helper.init(diag,off,N);
+
+        if( computeVectors ) {
+            if( computeVectorsWithValues ) {
+                return extractTogether();
+            }  else {
+                return extractSeparate(N);
+            }
+        } else {
+            return computeEigenValues();
+        }
+    }
+
+    @Override
+    public boolean inputModified() {
+        return decomp.inputModified();
+    }
+
+    private boolean extractTogether() {
+        // extract the orthogonal from the similar transform
+        V = decomp.getQ(V,true);
+
+        // tell eigenvector algorithm to update this matrix as it computes the rotators
+        helper.setQ(V);
+
+        vector.setFastEigenvalues(false);
+
+        // extract the eigenvalues
+        if( !vector.process(-1,null,null) )
+            return false;
+
+        // the V matrix contains the eigenvectors.  Convert those into column vectors
+        eigenvectors = CommonOps.rowsToVector(V,eigenvectors);
+
+        // save a copy of them since this data structure will be recycled next
+        values = helper.copyEigenvalues(values);
+
+        return true;
+    }
+
+    private boolean extractSeparate(int numCols) {
+        if (!computeEigenValues())
+            return false;
+
+        // ---- set up the helper to decompose the same tridiagonal matrix
+        // swap arrays instead of copying them to make it slightly faster
+        helper.reset(numCols);
+        diagSaved = helper.swapDiag(diagSaved);
+        offSaved = helper.swapOff(offSaved);
+
+        // extract the orthogonal from the similar transform
+        V = decomp.getQ(V,true);
+
+        // tell eigenvector algorithm to update this matrix as it computes the rotators
+        vector.setQ(V);
+
+        // extract eigenvectors
+        if( !vector.process(-1,null,null, values) )
+            return false;
+
+        // the ordering of the eigenvalues might have changed
+        values = helper.copyEigenvalues(values);
+        // the V matrix contains the eigenvectors.  Convert those into column vectors
+        eigenvectors = CommonOps.rowsToVector(V,eigenvectors);
+
+        return true;
+    }
+
+   /**
+     * Computes eigenvalues only
+    *
+     * @return
+     */
+    private boolean computeEigenValues() {
+       // make a copy of the internal tridiagonal matrix data for later use
+       diagSaved = helper.copyDiag(diagSaved);
+       offSaved = helper.copyOff(offSaved);
+
+       vector.setQ(null);
+       vector.setFastEigenvalues(true);
+
+       // extract the eigenvalues
+       if( !vector.process(-1,null,null) )
+           return false;
+
+       // save a copy of them since this data structure will be recycled next
+       values = helper.copyEigenvalues(values);
+       return true;
+   }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/eig/WatchedDoubleStepQRDecomposition_D64.java b/main/dense64/src/org/ejml/alg/dense/decomposition/eig/WatchedDoubleStepQRDecomposition_D64.java
new file mode 100644
index 0000000..358b608
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/eig/WatchedDoubleStepQRDecomposition_D64.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.eig;
+
+import org.ejml.alg.dense.decomposition.eig.watched.WatchedDoubleStepQREigenvalue;
+import org.ejml.alg.dense.decomposition.eig.watched.WatchedDoubleStepQREigenvector;
+import org.ejml.alg.dense.decomposition.hessenberg.HessenbergSimilarDecomposition_D64;
+import org.ejml.data.Complex64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.EigenDecomposition;
+
+
+/**
+ * <p>
+ * Finds the eigenvalue decomposition of an arbitrary square matrix using the implicit double-step QR algorithm.
+ * Watched is included in its name because it is designed to print out internal debugging information.  This
+ * class is still underdevelopment and has yet to be optimized.
+ * </p>
+ *
+ * <p>
+ * Based off the description found in:<br>
+ * David S. Watkins, "Fundamentals of Matrix Computations." Second Edition.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+//TODO looks like there might be some pointless copying of arrays going on
+public class WatchedDoubleStepQRDecomposition_D64
+        implements EigenDecomposition<DenseMatrix64F> {
+
+    HessenbergSimilarDecomposition_D64 hessenberg;
+    WatchedDoubleStepQREigenvalue algValue;
+    WatchedDoubleStepQREigenvector algVector;
+
+    DenseMatrix64F H;
+
+    // should it compute eigenvectors or just eigenvalues
+    boolean computeVectors;
+
+    public WatchedDoubleStepQRDecomposition_D64(boolean computeVectors) {
+        hessenberg = new HessenbergSimilarDecomposition_D64(10);
+        algValue = new WatchedDoubleStepQREigenvalue();
+        algVector = new WatchedDoubleStepQREigenvector();
+
+        this.computeVectors = computeVectors;
+    }
+
+    @Override
+    public boolean decompose(DenseMatrix64F A) {
+
+        if( !hessenberg.decompose(A) )
+            return false;
+
+        H = hessenberg.getH(null);
+
+        algValue.getImplicitQR().createR = false;
+//        algValue.getImplicitQR().setChecks(true,true,true);
+
+        if( !algValue.process(H) )
+            return false;
+
+//        for( int i = 0; i < A.numRows; i++ ) {
+//            System.out.println(algValue.getEigenvalues()[i]);
+//        }
+
+        algValue.getImplicitQR().createR = true;
+
+        if( computeVectors )
+            return algVector.process(algValue.getImplicitQR(), H, hessenberg.getQ(null));
+        else
+            return true;
+    }
+
+    @Override
+    public boolean inputModified() {
+        return hessenberg.inputModified();
+    }
+
+    @Override
+    public int getNumberOfEigenvalues() {
+        return algValue.getEigenvalues().length;
+    }
+
+    @Override
+    public Complex64F getEigenvalue(int index) {
+        return algValue.getEigenvalues()[index];
+    }
+
+    @Override
+    public DenseMatrix64F getEigenVector(int index) {
+        return algVector.getEigenvectors()[index];
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/eig/symm/SymmetricQREigenHelper.java b/main/dense64/src/org/ejml/alg/dense/decomposition/eig/symm/SymmetricQREigenHelper.java
new file mode 100644
index 0000000..ee800fe
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/eig/symm/SymmetricQREigenHelper.java
@@ -0,0 +1,494 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.eig.symm;
+
+import org.ejml.UtilEjml;
+import org.ejml.alg.dense.decomposition.eig.EigenvalueSmall;
+import org.ejml.data.DenseMatrix64F;
+
+import java.util.Random;
+
+
+/**
+ * A helper class for the symmetric matrix implicit QR algorithm for eigenvalue decomposition.
+ * Performs most of the basic operations needed to extract eigenvalues and eigenvectors.
+ *
+ * @author Peter Abeles
+ */
+public class SymmetricQREigenHelper {
+
+    // used in exceptional shifts
+    protected Random rand = new Random(0x34671e);
+
+    // how many steps has it taken
+    protected int steps;
+
+    // how many exception shifts has it performed
+    protected int numExceptional;
+    // the step number of the last exception shift
+    protected int lastExceptional;
+
+    // used to compute eigenvalues directly
+    protected EigenvalueSmall eigenSmall = new EigenvalueSmall();
+
+    // orthogonal matrix used in similar transform.  optional
+    protected DenseMatrix64F Q;
+
+    // size of the matrix being processed
+    protected int N;
+    // diagonal elements in the matrix
+    protected double diag[];
+    // the off diagonal elements
+    protected double off[];
+
+    // which submatrix is being processed
+    protected int x1;
+    protected int x2;
+
+    // where splits are performed
+    protected int splits[];
+    protected int numSplits;
+
+    // current value of the bulge
+    private double bulge;
+
+    // local helper functions
+    private double c,s,c2,s2,cs;
+
+    public SymmetricQREigenHelper() {
+        splits = new int[1];
+    }
+
+    public void printMatrix() {
+        System.out.print("Off Diag[ ");
+        for( int j = 0; j < N-1; j++ ) {
+            System.out.printf("%5.2f ",off[j]);
+        }
+        System.out.println();
+        System.out.print("    Diag[ ");
+        for( int j = 0; j < N; j++ ) {
+            System.out.printf("%5.2f ",diag[j]);
+        }
+        System.out.println();
+    }
+
+    public void setQ(DenseMatrix64F q) {
+        Q = q;
+    }
+
+    public void incrementSteps() {
+        steps++;
+    }
+
+    /**
+     * Sets up and declares internal data structures.
+     *
+     * @param diag Diagonal elements from tridiagonal matrix. Modified.
+     * @param off Off diagonal elements from tridiagonal matrix. Modified.
+     * @param numCols number of columns (and rows) in the matrix.
+     */
+    public void init( double diag[] ,
+                      double off[],
+                      int numCols ) {
+        reset(numCols);
+
+        this.diag = diag;
+        this.off = off;
+    }
+
+    /**
+     * Exchanges the internal array of the diagonal elements for the provided one.
+     */
+    public double[] swapDiag( double diag[] ) {
+        double[] ret = this.diag;
+        this.diag = diag;
+
+        return ret;
+    }
+
+    /**
+     * Exchanges the internal array of the off diagonal elements for the provided one.
+     */
+    public double[] swapOff( double off[] ) {
+        double[] ret = this.off;
+        this.off = off;
+
+        return ret;
+    }
+
+    /**
+     * Sets the size of the matrix being decomposed, declares new memory if needed,
+     * and sets all helper functions to their initial value.
+     */
+    public void reset( int N ) {
+        this.N = N;
+
+        this.diag = null;
+        this.off = null;
+
+        if( splits.length < N ) {
+            splits = new int[N];
+        }
+
+        numSplits = 0;
+
+        x1 = 0;
+        x2 = N-1;
+
+        steps = numExceptional = lastExceptional = 0;
+
+        this.Q = null;
+    }
+
+    public double[] copyDiag( double []ret ) {
+        if( ret == null || ret.length < N ) {
+            ret = new double[N];
+        }
+
+        System.arraycopy(diag,0,ret,0,N);
+
+        return ret;
+    }
+
+    public double[] copyOff( double []ret ) {
+        if( ret == null || ret.length < N-1 ) {
+            ret = new double[N-1];
+        }
+
+        System.arraycopy(off,0,ret,0,N-1);
+
+        return ret;
+    }
+
+    public double[] copyEigenvalues( double []ret ) {
+        if( ret == null || ret.length < N ) {
+            ret = new double[N];
+        }
+
+        System.arraycopy(diag,0,ret,0,N);
+
+        return ret;
+    }
+
+    /**
+     * Sets which submatrix is being processed.
+     * @param x1 Lower bound, inclusive.
+     * @param x2 Upper bound, inclusive.
+     */
+    public void setSubmatrix( int x1 , int x2 ) {
+        this.x1 = x1;
+        this.x2 = x2;
+    }
+
+    /**
+     * Checks to see if the specified off diagonal element is zero using a relative metric.
+     */
+    protected boolean isZero( int index ) {
+        double bottom = Math.abs(diag[index])+Math.abs(diag[index+1]);
+
+        return( Math.abs(off[index]) <= bottom*UtilEjml.EPS);
+    }
+
+    protected void performImplicitSingleStep( double lambda , boolean byAngle )
+    {
+        if( x2-x1 == 1  ) {
+            createBulge2by2(x1,lambda,byAngle);
+        } else {
+            createBulge(x1,lambda,byAngle);
+
+            for( int i = x1; i < x2-2 && bulge != 0.0; i++ ) {
+                removeBulge(i);
+
+            }
+            if( bulge != 0.0 )
+                removeBulgeEnd(x2-2);
+        }
+    }
+
+    protected void updateQ( int m , int n , double c ,  double s )
+    {
+        int rowA = m*N;
+        int rowB = n*N;
+
+//        for( int i = 0; i < N; i++ ) {
+//            double a = Q.data[rowA+i];
+//            double b = Q.data[rowB+i];
+//            Q.data[rowA+i] = c*a + s*b;
+//            Q.data[rowB+i] = -s*a + c*b;
+//        }
+        int endA = rowA + N;
+        while( rowA < endA ) {
+            double a = Q.data[rowA];
+            double b = Q.data[rowB];
+            Q.data[rowA++] = c*a + s*b;
+            Q.data[rowB++] = -s*a + c*b;
+        }
+    }
+
+    /**
+     * Performs a similar transform on A-pI
+     */
+    protected void createBulge( int x1 , double p , boolean byAngle ) {
+        double a11 = diag[x1];
+        double a22 = diag[x1+1];
+        double a12 = off[x1];
+        double a23 = off[x1+1];
+
+        if( byAngle ) {
+            c = Math.cos(p);
+            s = Math.sin(p);
+
+            c2 = c*c;
+            s2 = s*s;
+            cs = c*s;
+        } else {
+            computeRotation(a11-p, a12);
+        }
+
+        // multiply the rotator on the top left.
+        diag[x1]   = c2*a11 + 2.0*cs*a12 + s2*a22;
+        diag[x1+1] = c2*a22 - 2.0*cs*a12 + s2*a11;
+        off[x1]    = a12*(c2-s2) + cs*(a22 - a11);
+        off[x1+1]  = c*a23;
+        bulge = s*a23;
+
+        if( Q != null )
+            updateQ(x1,x1+1,c,s);
+    }
+
+    protected void createBulge2by2( int x1 , double p , boolean byAngle ) {
+        double a11 = diag[x1];
+        double a22 = diag[x1+1];
+        double a12 = off[x1];
+
+        if( byAngle ) {
+            c = Math.cos(p);
+            s = Math.sin(p);
+
+            c2 = c*c;
+            s2 = s*s;
+            cs = c*s;
+        } else {
+            computeRotation(a11-p, a12);
+        }
+
+        // multiply the rotator on the top left.
+        diag[x1]   = c2*a11 + 2.0*cs*a12 + s2*a22;
+        diag[x1+1] = c2*a22 - 2.0*cs*a12 + s2*a11;
+        off[x1]    = a12*(c2-s2) + cs*(a22 - a11);
+
+        if( Q != null )
+            updateQ(x1,x1+1,c,s);
+    }
+
+    /**
+     * Computes the rotation and stores it in (c,s)
+     */
+    private void computeRotation(double run, double rise) {
+//        double alpha = Math.sqrt(run*run + rise*rise);
+//        c = run/alpha;
+//        s = rise/alpha;
+
+        if( Math.abs(rise) > Math.abs(run)) {
+            double k = run/rise;
+
+            double bottom = 1.0 + k*k;
+            double bottom_sq = Math.sqrt(bottom);
+
+            s2 = 1.0/bottom;
+            c2 = k*k/bottom;
+            cs = k/bottom;
+            s = 1.0/bottom_sq;
+            c = k/bottom_sq;
+        } else {
+            double t = rise/run;
+
+            double bottom = 1.0 + t*t;
+            double bottom_sq = Math.sqrt(bottom);
+
+            c2 = 1.0/bottom;
+            s2 = t*t/bottom;
+            cs = t/bottom;
+            c = 1.0/bottom_sq;
+            s = t/bottom_sq;
+        }
+    }
+
+    protected void removeBulge( int x1 ) {
+        double a22 = diag[x1+1];
+        double a33 = diag[x1+2];
+        double a12 = off[x1];
+        double a23 = off[x1+1];
+        double a34 = off[x1+2];
+
+        computeRotation(a12, bulge);
+
+        // multiply the rotator on the top left.
+        diag[x1+1] = c2*a22 + 2.0*cs*a23 + s2*a33;
+        diag[x1+2] = c2*a33 - 2.0*cs*a23 + s2*a22;
+        off[x1] = c*a12 + s*bulge;
+        off[x1+1] = a23*(c2-s2) + cs*(a33 - a22);
+        off[x1+2] = c*a34;
+        bulge = s*a34;
+
+        if( Q != null )
+            updateQ(x1+1,x1+2,c,s);
+    }
+
+    /**
+     * Rotator to remove the bulge
+     */
+    protected void removeBulgeEnd( int x1 ) {
+        double a22 = diag[x1+1];
+        double a12 = off[x1];
+        double a23 = off[x1+1];
+        double a33 = diag[x1+2];
+
+        computeRotation(a12, bulge);
+
+        // multiply the rotator on the top left.
+        diag[x1+1] = c2*a22 + 2.0*cs*a23 + s2*a33;
+        diag[x1+2] = c2*a33 - 2.0*cs*a23 + s2*a22;
+        off[x1] = c*a12 + s*bulge;
+        off[x1+1] = a23*(c2-s2) + cs*(a33 - a22);
+
+        if( Q != null )
+            updateQ(x1+1,x1+2,c,s);
+    }
+
+    /**
+     * Computes the eigenvalue of the 2 by 2 matrix.
+     */
+    protected void eigenvalue2by2( int x1 ) {
+        double a = diag[x1];
+        double b = off[x1];
+        double c = diag[x1+1];
+
+        // normalize to reduce overflow
+        double absA = Math.abs(a);
+        double absB = Math.abs(b);
+        double absC = Math.abs(c);
+
+        double scale = absA > absB ? absA : absB;
+        if( absC > scale ) scale = absC;
+
+        // see if it is a pathological case.  the diagonal must already be zero
+        // and the eigenvalues are all zero.  so just return
+        if( scale == 0 ) {
+            off[x1] = 0;
+            diag[x1] = 0;
+            diag[x1+1] = 0;
+            return;
+        }
+
+        a /= scale;
+        b /= scale;
+        c /= scale;
+
+        eigenSmall.symm2x2_fast(a,b,c);
+
+        off[x1] = 0;
+        diag[x1] = scale*eigenSmall.value0.real;
+        diag[x1+1] = scale*eigenSmall.value1.real;
+    }
+
+    /**
+     * Perform a shift in a random direction that is of the same magnitude as the elements in the matrix.
+     */
+    public void exceptionalShift() {
+        // rotating by a random angle handles at least one case using a random lambda
+        // does not handle well:
+        // - two identical eigenvalues are next to each other and a very small diagonal element
+        numExceptional++;
+        double mag = 0.05*numExceptional;
+        if( mag > 1.0 ) mag = 1.0;
+
+        double theta = 2.0*(rand.nextDouble()-0.5)*mag;
+        performImplicitSingleStep(theta,true);
+
+        lastExceptional = steps;
+    }
+
+    /**
+     * Tells it to process the submatrix at the next split.  Should be called after the
+     * current submatrix has been processed.
+     */
+    public boolean nextSplit() {
+        if( numSplits == 0 )
+            return false;
+        x2 = splits[--numSplits];
+        if( numSplits > 0 )
+            x1 = splits[numSplits-1]+1;
+        else
+            x1 = 0;
+
+        return true;
+    }
+
+    public double computeShift() {
+        if( x2-x1 >= 1 )
+            return computeWilkinsonShift();
+        else
+            return diag[x2];
+    }
+
+    public double computeWilkinsonShift() {
+        double a = diag[x2-1];
+        double b = off[x2-1];
+        double c = diag[x2];
+
+        // normalize to reduce overflow
+        double absA = Math.abs(a);
+        double absB = Math.abs(b);
+        double absC = Math.abs(c);
+
+        double scale = absA > absB ? absA : absB;
+        if( absC > scale ) scale = absC;
+
+        if( scale == 0 ) {
+            throw new RuntimeException("this should never happen");
+        }
+
+        a /= scale;
+        b /= scale;
+        c /= scale;
+
+        // TODO see 385
+
+        eigenSmall.symm2x2_fast(a,b,c);
+
+        // return the eigenvalue closest to c
+        double diff0 = Math.abs(eigenSmall.value0.real-c);
+        double diff1 = Math.abs(eigenSmall.value1.real-c);
+
+        if( diff0 < diff1 )
+            return scale*eigenSmall.value0.real;
+        else
+            return scale*eigenSmall.value1.real;
+    }
+
+    public int getMatrixSize() {
+        return N;
+    }
+
+    public void resetSteps() {
+        steps = 0;
+        lastExceptional = 0;
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/eig/symm/SymmetricQrAlgorithm.java b/main/dense64/src/org/ejml/alg/dense/decomposition/eig/symm/SymmetricQrAlgorithm.java
new file mode 100644
index 0000000..97fde0f
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/eig/symm/SymmetricQrAlgorithm.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.eig.symm;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+
+
+/**
+ * <p>
+ * Computes the eigenvalues and eigenvectors of a symmetric tridiagonal matrix using the symmetric QR algorithm.
+ * </p>
+ * <p>
+ * This implementation is based on the algorithm is sketched out in:<br>
+ * David S. Watkins, "Fundamentals of Matrix Computations," Second Edition. page 377-385
+ * </p>
+ * @author Peter Abeles
+ */
+public class SymmetricQrAlgorithm {
+
+    // performs many of the low level calculations
+    private SymmetricQREigenHelper helper;
+
+    // transpose of the orthogonal matrix
+    private DenseMatrix64F Q;
+
+    // the eigenvalues previously computed
+    private double eigenvalues[];
+
+    private int exceptionalThresh = 15;
+    private int maxIterations = exceptionalThresh*15;
+
+    // should it ever analytically compute eigenvalues
+    // if this is true then it can't compute eigenvalues at the same time
+    private boolean fastEigenvalues;
+
+    // is it following a script or not
+    private boolean followingScript;
+
+    public SymmetricQrAlgorithm(SymmetricQREigenHelper helper ) {
+        this.helper = helper;
+    }
+
+    /**
+     * Creates a new SymmetricQREigenvalue class that declares its own SymmetricQREigenHelper.
+     */
+    public SymmetricQrAlgorithm() {
+        this.helper = new SymmetricQREigenHelper();
+    }
+
+    public void setMaxIterations(int maxIterations) {
+        this.maxIterations = maxIterations;
+    }
+
+    public DenseMatrix64F getQ() {
+        return Q;
+    }
+
+    public void setQ(DenseMatrix64F q) {
+        Q = q;
+    }
+
+    public void setFastEigenvalues(boolean fastEigenvalues) {
+        this.fastEigenvalues = fastEigenvalues;
+    }
+
+    /**
+     * Returns the eigenvalue at the specified index.
+     *
+     * @param index Which eigenvalue.
+     * @return The eigenvalue.
+     */
+    public double getEigenvalue( int index ) {
+       return helper.diag[index];
+    }
+
+    /**
+     * Returns the number of eigenvalues available.
+     *
+     * @return How many eigenvalues there are.
+     */
+    public int getNumberOfEigenvalues() {
+        return helper.N;
+    }
+
+    /**
+     * Computes the eigenvalue of the provided tridiagonal matrix.  Note that only the upper portion
+     * needs to be tridiagonal.  The bottom diagonal is assumed to be the same as the top.
+     *
+     * @param sideLength Number of rows and columns in the input matrix.
+     * @param diag Diagonal elements from tridiagonal matrix. Modified.
+     * @param off Off diagonal elements from tridiagonal matrix. Modified.
+     * @return true if it succeeds and false if it fails.
+     */
+    public boolean process( int sideLength,
+                            double diag[] ,
+                            double off[] ,
+                            double eigenvalues[] ) {
+        if( diag != null )
+            helper.init(diag,off,sideLength);
+        if( Q == null )
+            Q = CommonOps.identity(helper.N);
+        helper.setQ(Q);
+
+        this.followingScript = true;
+        this.eigenvalues = eigenvalues;
+        this.fastEigenvalues = false;
+
+        return _process();
+    }
+
+    public boolean process( int sideLength,
+                            double diag[] ,
+                            double off[] ) {
+        if( diag != null )
+            helper.init(diag,off,sideLength);
+
+        this.followingScript = false;
+        this.eigenvalues = null;
+
+        return _process();
+    }
+
+
+    private boolean _process() {
+        while( helper.x2 >= 0 ) {
+            // if it has cycled too many times give up
+            if( helper.steps > maxIterations ) {
+                return false;
+            }
+
+            if( helper.x1 == helper.x2 ) {
+//                System.out.println("Steps = "+helper.steps);
+                // see if it is done processing this submatrix
+                helper.resetSteps();
+                if( !helper.nextSplit() )
+                    break;
+            } else if( fastEigenvalues && helper.x2-helper.x1 == 1 ) {
+                // There are analytical solutions to this case. Just compute them directly.
+                // TODO might be able to speed this up by doing the 3 by 3 case also
+                helper.resetSteps();
+                helper.eigenvalue2by2(helper.x1);
+                helper.setSubmatrix(helper.x2,helper.x2);
+            } else if( helper.steps-helper.lastExceptional > exceptionalThresh ){
+                // it isn't a good sign if exceptional shifts are being done here
+                helper.exceptionalShift();
+            } else {
+                performStep();
+            }
+            helper.incrementSteps();
+//            helper.printMatrix();
+        }
+
+//        helper.printMatrix();
+        return true;
+    }
+
+    /**
+     * First looks for zeros and then performs the implicit single step in the QR Algorithm.
+     */
+    public void performStep() {
+        // check for zeros
+        for( int i = helper.x2-1; i >= helper.x1; i-- ) {
+            if( helper.isZero(i) ) {
+                helper.splits[helper.numSplits++] = i;
+                helper.x1 = i+1;
+                return;
+            }
+        }
+
+        double lambda;
+
+        if( followingScript ) {
+            if( helper.steps > 10 ) {
+                followingScript = false;
+                return;
+            } else {
+                // Using the true eigenvalues will in general lead to the fastest convergence
+                // typically takes 1 or 2 steps
+                lambda = eigenvalues[helper.x2];
+            }
+        } else {
+            // the current eigenvalue isn't working so try something else
+            lambda = helper.computeShift();
+        }
+
+        // similar transforms
+        helper.performImplicitSingleStep(lambda,false);
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/eig/watched/WatchedDoubleStepQREigen.java b/main/dense64/src/org/ejml/alg/dense/decomposition/eig/watched/WatchedDoubleStepQREigen.java
new file mode 100644
index 0000000..c1772b9
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/eig/watched/WatchedDoubleStepQREigen.java
@@ -0,0 +1,602 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.eig.watched;
+
+import org.ejml.UtilEjml;
+import org.ejml.alg.dense.decomposition.eig.EigenvalueSmall;
+import org.ejml.alg.dense.decomposition.qr.QrHelperFunctions_D64;
+import org.ejml.data.Complex64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.MatrixFeatures;
+
+import java.util.Random;
+
+/**
+ * <p>
+ * The double step implicit Eigenvalue decomposition algorithm is fairly complicated and needs to be designed so that
+ * it can handle several special cases.  To aid in development and debugging this class was created.  It allows
+ * individual components to be tested and to print out their results.  This shows how each step is performed.
+ * </p>
+ *
+ * <p>
+ * Do not use this class to compute the eigenvalues since it is much slower than a non-debug implementation.
+ * </p>
+ *
+ */
+// TODO make rank1UpdateMultR efficient once again by setting 0 to x1 and creating a new one that updates all the rows
+// TODO option of modifying original matrix
+public class WatchedDoubleStepQREigen {
+
+    private Random rand = new Random(0x2342);
+
+    private int N;
+
+    DenseMatrix64F A;
+    private DenseMatrix64F u;
+    private double gamma;
+
+    private DenseMatrix64F _temp;
+
+    // how many steps did it take to find the eigenvalue
+    int numStepsFind[];
+    int steps;
+    Complex64F eigenvalues[];
+    int numEigen;
+
+    // computes eigenvalues for 2 by 2 submatrices
+    private EigenvalueSmall valueSmall = new EigenvalueSmall();
+
+    private double temp[] = new double[9];
+
+    private boolean printHumps = false;
+    boolean checkHessenberg = false;
+    private boolean checkOrthogonal = false;
+    private boolean checkUncountable = false;
+
+    private boolean useStandardEq = false;
+    private boolean useCareful2x2 = true;
+
+    private boolean normalize = true;
+
+    int lastExceptional;
+    int numExceptional;
+    int exceptionalThreshold = 20;
+    int maxIterations = exceptionalThreshold*20;
+
+    public boolean createR = true;
+
+    public DenseMatrix64F Q;
+
+    public void incrementSteps() {
+        steps++;
+    }
+
+    public void setQ( DenseMatrix64F Q ) {
+        this.Q = Q;
+    }
+
+    private void addEigenvalue( double v ) {
+        numStepsFind[numEigen] = steps;
+        eigenvalues[numEigen].set(v,0);
+        numEigen++;
+        steps = 0;
+        lastExceptional = 0;
+    }
+
+    private void addEigenvalue( double v , double i ) {
+        numStepsFind[numEigen] = steps;
+        eigenvalues[numEigen].set(v,i);
+        numEigen++;
+        steps = 0;
+        lastExceptional = 0;
+    }
+
+    public void setChecks( boolean hessenberg , boolean orthogonal , boolean uncountable ) {
+        this.checkHessenberg = hessenberg;
+        this.checkOrthogonal = orthogonal;
+        this.checkUncountable = uncountable;
+    }
+
+
+    public boolean isZero( int x1 , int x2 ) {
+        // this provides a relative threshold for when dealing with very large/small numbers
+        double target = Math.abs(A.get(x1,x2));
+
+        double above = Math.abs(A.get(x1-1,x2));
+
+        // according to Matrix Computations page 352 this is what is done in Eispack
+        double right = Math.abs(A.get(x1,x2+1));
+        return target <= 0.5*UtilEjml.EPS*(above+right);
+    }
+
+    public void setup( DenseMatrix64F A ) {
+        if( A.numRows != A.numCols )
+            throw new RuntimeException("Must be square") ;
+
+        if( N != A.numRows ) {
+            N = A.numRows;
+
+            this.A = A.copy();
+            u = new DenseMatrix64F(A.numRows,1);
+
+            _temp = new DenseMatrix64F(A.numRows,1);
+            numStepsFind = new int[ A.numRows ];
+        } else {
+            this.A.set(A);
+            UtilEjml.memset(numStepsFind,0,numStepsFind.length);
+        }
+
+        // zero all the off numbers that should be zero for a hessenberg matrix
+        for( int i = 2; i < N; i++ ) {
+            for( int j = 0; j < i-1; j++ ) {
+                this.A.set(i,j,0);
+            }
+        }
+
+        eigenvalues = new Complex64F[ A.numRows ];
+        for( int i = 0; i < eigenvalues.length; i++ ) {
+            eigenvalues[i] = new Complex64F();
+        }
+
+        numEigen = 0;
+        lastExceptional = 0;
+        numExceptional = 0;
+        steps = 0;
+    }
+
+    /**
+     * Perform a shift in a random direction that is of the same magnitude as the elements in the matrix.
+     */
+    public void exceptionalShift( int x1 , int x2) {
+         if( printHumps )
+            System.out.println("Performing exceptional implicit double step");
+
+        // perform a random shift that is of the same magnitude as the matrix
+        double val = Math.abs(A.get(x2,x2));
+
+        if( val == 0 )
+            val = 1;
+
+        numExceptional++;
+        // the closer the value is the better it handles identical eigenvalues cases
+        double p = 1-Math.pow(0.1,numExceptional);
+        val *= p+2.0*(1.0-p)*(rand.nextDouble()-0.5);
+
+        if( rand.nextBoolean() )
+            val = -val;
+
+        performImplicitSingleStep(x1,x2,val);
+
+        lastExceptional = steps;
+    }
+
+    /**
+     * Performs an implicit double step using the values contained in the lower right hand side
+     * of the submatrix for the estimated eigenvector values.
+     * @param x1
+     * @param x2
+     */
+    public void implicitDoubleStep( int x1 , int x2 ) {
+        if( printHumps )
+            System.out.println("Performing implicit double step");
+
+         // compute the wilkinson shift
+        double z11 = A.get(x2 - 1, x2 - 1);
+        double z12 = A.get(x2 - 1, x2);
+        double z21 = A.get(x2, x2 - 1);
+        double z22 = A.get(x2, x2);
+
+        double a11 = A.get(x1,x1);
+        double a21 = A.get(x1+1,x1);
+        double a12 = A.get(x1,x1+1);
+        double a22 = A.get(x1+1,x1+1);
+        double a32 = A.get(x1+2,x1+1);
+
+        if( normalize ) {
+            temp[0] = a11;temp[1] = a21;temp[2] = a12;temp[3] = a22;temp[4] = a32;
+            temp[5] = z11;temp[6] = z22;temp[7] = z12;temp[8] = z21;
+
+            double max = Math.abs(temp[0]);
+            for( int j = 1; j < temp.length; j++ ) {
+                if( Math.abs(temp[j]) > max )
+                    max = Math.abs(temp[j]);
+            }
+            a11 /= max;a21 /= max;a12 /= max;a22 /= max;a32 /= max;
+            z11 /= max;z22 /= max;z12 /= max;z21 /= max;
+        }
+
+        // these equations are derived when the eigenvalues are extracted from the lower right
+        // 2 by 2 matrix.  See page 388 of Fundamentals of Matrix Computations 2nd ed for details.
+        double b11,b21,b31;
+        if( useStandardEq ) {
+            b11 = ((a11- z11)*(a11- z22)- z21 * z12)/a21 + a12;
+            b21 = a11 + a22 - z11 - z22;
+            b31 = a32;
+        } else {
+            // this is different from the version in the book and seems in my testing to be more resilient to
+            // over flow issues
+            b11 = ((a11- z11)*(a11- z22)- z21 * z12) + a12*a21;
+            b21 = (a11 + a22 - z11 - z22)*a21;
+            b31 = a32*a21;
+        }
+
+        performImplicitDoubleStep(x1, x2, b11 , b21 , b31 );
+    }
+
+    /**
+     * Performs an implicit double step given the set of two imaginary eigenvalues provided.
+     * Since one eigenvalue is the complex conjugate of the other only one set of real and imaginary
+     * numbers is needed.
+     *
+     * @param x1 upper index of submatrix.
+     * @param x2 lower index of submatrix.
+     * @param real Real component of each of the eigenvalues.
+     * @param img Imaginary component of one of the eigenvalues.
+     */
+    public void performImplicitDoubleStep(int x1, int x2 , double real , double img  ) {
+        double a11 = A.get(x1,x1);
+        double a21 = A.get(x1+1,x1);
+        double a12 = A.get(x1,x1+1);
+        double a22 = A.get(x1+1,x1+1);
+        double a32 = A.get(x1+2,x1+1);
+
+        double p_plus_t = 2.0*real;
+        double p_times_t = real*real + img*img;
+
+        double b11,b21,b31;
+        if( useStandardEq ) {
+            b11 = (a11*a11 - p_plus_t*a11+p_times_t)/a21 + a12;
+            b21 = a11+a22-p_plus_t;
+            b31 = a32;
+        } else {
+            // this is different from the version in the book and seems in my testing to be more resilient to
+            // over flow issues
+            b11 = (a11*a11 - p_plus_t*a11+p_times_t) + a12*a21;
+            b21 = (a11+a22-p_plus_t)*a21;
+            b31 = a32*a21;
+        }
+
+        performImplicitDoubleStep(x1, x2, b11, b21, b31);
+    }
+
+    private void performImplicitDoubleStep(int x1, int x2,
+                                           double b11 , double b21 , double b31 ) {
+        if( !bulgeDoubleStepQn(x1,b11,b21,b31,0,false) )
+            return;
+
+        // get rid of the bump
+        if( Q != null ) {
+            QrHelperFunctions_D64.rank1UpdateMultR(Q, u.data, gamma, 0, x1, x1 + 3, _temp.data);
+            if( checkOrthogonal && !MatrixFeatures.isOrthogonal(Q,1e-8) ) {
+                u.print();
+
+                Q.print();
+                throw new RuntimeException("Bad");
+            }
+        }
+
+        if( printHumps ) {
+            System.out.println("Applied first Q matrix, it should be humped now. A = ");
+            A.print("%12.3e");
+            System.out.println("Pushing the hump off the matrix.");
+        }
+
+        // perform double steps
+        for( int i = x1; i < x2-2; i++ ) {
+            if( bulgeDoubleStepQn(i) && Q != null ) {
+                QrHelperFunctions_D64.rank1UpdateMultR(Q, u.data, gamma, 0, i + 1, i + 4, _temp.data);
+                if( checkOrthogonal && !MatrixFeatures.isOrthogonal(Q,1e-8) )
+                    throw new RuntimeException("Bad");
+            }
+
+            if( printHumps ) {
+                System.out.println("i = "+i+" A = ");
+                A.print("%12.3e");
+            }
+        }
+        if( printHumps )
+            System.out.println("removing last bump");
+        // the last one has to be a single step
+        if( x2-2 >= 0 && bulgeSingleStepQn(x2-2) && Q != null ) {
+            QrHelperFunctions_D64.rank1UpdateMultR(Q, u.data, gamma, 0, x2 - 1, x2 + 1, _temp.data);
+            if( checkOrthogonal && !MatrixFeatures.isOrthogonal(Q,1e-8) )
+                throw new RuntimeException("Bad");
+
+        }
+        if( printHumps ) {
+            System.out.println(" A = ");
+            A.print("%12.3e");
+        }
+//        A.print("%12.3e");
+
+        if( checkHessenberg && !MatrixFeatures.isUpperTriangle(A,1,1e-12)) {
+            A.print("%12.3e");
+            throw new RuntimeException("Bad matrix");
+        }
+    }
+
+
+    public void performImplicitSingleStep(int x1, int x2 , double eigenvalue ) {
+        if( !createBulgeSingleStep(x1,eigenvalue) )
+            return;
+
+        // get rid of the bump
+        if( Q != null ) {
+            QrHelperFunctions_D64.rank1UpdateMultR(Q, u.data, gamma, 0, x1, x1 + 2, _temp.data);
+            if( checkOrthogonal && !MatrixFeatures.isOrthogonal(Q,1e-8) )
+                throw new RuntimeException("Bad");
+        }
+
+        if( printHumps ) {
+            System.out.println("Applied first Q matrix, it should be humped now. A = ");
+            A.print("%12.3e");
+            System.out.println("Pushing the hump off the matrix.");
+        }
+
+        // perform simple steps
+        for( int i = x1; i < x2-1; i++ ) {
+            if( bulgeSingleStepQn(i) && Q != null ) {
+                QrHelperFunctions_D64.rank1UpdateMultR(Q, u.data, gamma, 0, i + 1, i + 3, _temp.data);
+                if( checkOrthogonal && !MatrixFeatures.isOrthogonal(Q,1e-8) )
+                    throw new RuntimeException("Bad");
+            }
+
+            if( printHumps ) {
+                System.out.println("i = "+i+" A = ");
+                A.print("%12.3e");
+            }
+        }
+
+        if( checkHessenberg && !MatrixFeatures.isUpperTriangle(A,1,1e-12)) {
+            A.print("%12.3e");
+            throw new RuntimeException("Bad matrix");
+        }
+    }
+
+    public boolean createBulgeSingleStep( int x1 , double eigenvalue ) {
+
+        double b11 = A.get(x1,x1) - eigenvalue;
+        double b21 = A.get(x1+1,x1);
+
+        double threshold = Math.abs(A.get(x1,x1))*UtilEjml.EPS;
+
+        return bulgeSingleStepQn(x1,b11,b21,threshold,false);
+    }
+
+    public boolean bulgeDoubleStepQn( int i ) {
+        double a11 = A.get(i+1,i);
+        double a21 = A.get(i+2,i);
+        double a31 = A.get(i+3,i);
+
+        double threshold = Math.abs(A.get(i,i))*UtilEjml.EPS;
+
+        return bulgeDoubleStepQn(i+1,a11,a21,a31,threshold,true);
+    }
+
+    public boolean bulgeDoubleStepQn( int i ,
+                                      double a11, double a21 , double a31,
+                                      double threshold , boolean set )
+    {
+        double max;
+        if( normalize ) {
+            double absA11 = Math.abs(a11);
+            double absA21 = Math.abs(a21);
+            double absA31 = Math.abs(a31);
+
+            max = absA11 > absA21 ? absA11 : absA21;
+            if( absA31 > max ) max = absA31;
+
+//           if( max <= Math.abs(A.get(i,i))*UtilEjml.EPS ) {
+            if( max <= threshold ) {
+                if( set ) {
+                    A.set(i,i-1,0);
+                    A.set(i+1,i-1,0);
+                    A.set(i+2,i-1,0);
+                }
+                return false;
+            }
+
+            a11 /= max;
+            a21 /= max;
+            a31 /= max;
+        } else {
+            max = 1;
+        }
+
+        // compute the reflector using the b's above
+
+        double tau = Math.sqrt(a11*a11 + a21*a21 + a31*a31);
+        if( a11 < 0 ) tau = -tau;
+
+        double div = a11+tau;
+
+        u.set(i,0,1);
+        u.set(i+1,0,a21/div);
+        u.set(i+2,0,a31/div);
+
+        gamma = div/tau;
+
+        // compute A_1 = Q_1^T * A * Q_1
+
+        // apply Q*A  - just do the 3 rows
+        QrHelperFunctions_D64.rank1UpdateMultR(A, u.data, gamma, 0, i, i + 3, _temp.data);
+
+        if( set ) {
+            A.set(i,i-1,-max*tau);
+            A.set(i+1,i-1,0);
+            A.set(i+2,i-1,0);
+        }
+
+        if( printHumps ) {
+            System.out.println("  After Q.   A =");
+            A.print();
+        }
+
+        // apply A*Q - just the three things
+        QrHelperFunctions_D64.rank1UpdateMultL(A, u.data, gamma, 0, i, i + 3);
+
+//        System.out.println("  after Q*A*Q ");
+//        A.print();
+
+        if(checkUncountable && MatrixFeatures.hasUncountable(A)) {
+            throw new RuntimeException("bad matrix");
+        }
+
+        return true;
+    }
+
+    public boolean bulgeSingleStepQn( int i )
+    {
+        double a11 = A.get(i+1,i);
+        double a21 = A.get(i+2,i);
+
+        double threshold = Math.abs(A.get(i,i))*UtilEjml.EPS;
+
+        return bulgeSingleStepQn(i+1,a11,a21,threshold,true);
+    }
+
+    public boolean bulgeSingleStepQn( int i ,
+                                      double a11 , double a21 ,
+                                      double threshold , boolean set)
+    {
+
+        double max;
+        if( normalize ) {
+            max = Math.abs(a11);
+            if( max < Math.abs(a21)) max = Math.abs(a21);
+
+//            if( max <= Math.abs(A.get(i,i))*UtilEjml.EPS ) {
+            if( max <= threshold ) {
+//                System.out.println("i = "+i);
+//                A.print();
+                if( set ) {
+                    A.set(i,i-1,0);
+                    A.set(i+1,i-1,0);
+                }
+                return false;
+            }
+
+            a11 /= max;
+            a21 /= max;
+        } else {
+            max = 1;
+        }
+
+        // compute the reflector using the b's above
+
+        double tau = Math.sqrt(a11*a11 + a21*a21);
+        if( a11 < 0 ) tau = -tau;
+
+        double div = a11+tau;
+
+        u.set(i,0,1);
+        u.set(i+1,0,a21/div);
+
+        gamma = div/tau;
+
+        // compute A_1 = Q_1^T * A * Q_1
+
+        // apply Q*A  - just do the 3 rows
+        QrHelperFunctions_D64.rank1UpdateMultR(A, u.data, gamma, 0, i, i + 2, _temp.data);
+
+        if( set ) {
+            A.set(i,i-1,-max*tau);
+            A.set(i+1,i-1,0);
+        }
+
+        // apply A*Q - just the three things
+        QrHelperFunctions_D64.rank1UpdateMultL(A, u.data, gamma, 0, i, i + 2);
+
+        if(checkUncountable && MatrixFeatures.hasUncountable(A)) {
+            throw new RuntimeException("bad matrix");
+        }
+
+        return true;
+    }
+
+    public void eigen2by2_scale( double a11 , double a12 , double a21 , double a22 )
+    {
+        double abs11 = Math.abs(a11);
+        double abs22 = Math.abs(a22);
+        double abs12 = Math.abs(a12);
+        double abs21 = Math.abs(a21);
+
+        double max = abs11 > abs22 ? abs11 : abs22;
+        if( max < abs12 ) max = abs12;
+        if( max < abs21 ) max = abs21;
+
+        if( max == 0 ) {
+            valueSmall.value0.real = 0;
+            valueSmall.value0.imaginary = 0;
+            valueSmall.value1.real = 0;
+            valueSmall.value1.imaginary = 0;
+        } else {
+            a12 /= max; a21 /= max; a11/=max;a22/=max;
+
+            if( useCareful2x2 ) {
+                valueSmall.value2x2(a11,a12,a21,a22);
+            } else {
+                valueSmall.value2x2_fast(a11,a12,a21,a22);
+            }
+            valueSmall.value0.real *= max;
+            valueSmall.value0.imaginary *= max;
+            valueSmall.value1.real *= max;
+            valueSmall.value1.imaginary *= max;
+
+        }
+
+//        System.out.printf("eigen (%6.3f , %6.3f) (%6.3f , %6.3f)\n",p0_real,p0_img,p1_real,p1_img);
+    }
+
+    public int getNumberOfEigenvalues() {
+        return numEigen;
+    }
+
+    public Complex64F[] getEigenvalues() {
+        return eigenvalues;
+    }
+
+    public void addComputedEigen2x2(int x1,int x2) {
+        eigen2by2_scale(A.get(x1,x1),A.get(x1,x2),A.get(x2,x1),A.get(x2,x2));
+
+        if( checkUncountable &&
+                (Double.isNaN(valueSmall.value0.real) || Double.isNaN(valueSmall.value1.real)) ) {
+            throw new RuntimeException("Uncountable");
+        }
+
+        addEigenvalue(valueSmall.value0.real,valueSmall.value0.imaginary);
+        addEigenvalue(valueSmall.value1.real,valueSmall.value1.imaginary);
+    }
+
+    public boolean isReal2x2( int x1 , int x2 ) {
+        eigen2by2_scale(A.get(x1,x1),A.get(x1,x2),A.get(x2,x1),A.get(x2,x2));
+
+        return valueSmall.value0.isReal();
+    }
+
+    public void addEigenAt( int x1 ) {
+        addEigenvalue(A.get(x1,x1));
+    }
+
+    public void printSteps() {
+        for( int i = 0; i < N; i++ ) {
+            System.out.println("Step["+i+"] = "+numStepsFind[i]);
+        }
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/eig/watched/WatchedDoubleStepQREigenvalue.java b/main/dense64/src/org/ejml/alg/dense/decomposition/eig/watched/WatchedDoubleStepQREigenvalue.java
new file mode 100644
index 0000000..ca058bd
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/eig/watched/WatchedDoubleStepQREigenvalue.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.eig.watched;
+
+import org.ejml.alg.dense.decomposition.eig.EigenvalueExtractor;
+import org.ejml.data.Complex64F;
+import org.ejml.data.DenseMatrix64F;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class WatchedDoubleStepQREigenvalue implements EigenvalueExtractor {
+
+    WatchedDoubleStepQREigen implicitQR;
+
+    int splits[];
+    int numSplits;
+
+    int x1;
+    int x2;
+
+    public WatchedDoubleStepQREigenvalue() {
+        implicitQR = new WatchedDoubleStepQREigen();
+    }
+
+    public void setup( DenseMatrix64F A ) {
+        implicitQR.setup(A);
+        implicitQR.setQ(null);
+
+        splits = new int[ A.numRows ];
+        numSplits = 0;
+    }
+
+    @Override
+    public boolean process(DenseMatrix64F origA) {
+        setup(origA);
+
+        x1 = 0;
+        x2 = origA.numRows-1;
+
+        while( implicitQR.numEigen < origA.numRows ) {
+            if( implicitQR.steps > implicitQR.maxIterations )
+                return false;
+
+            implicitQR.incrementSteps();
+
+            if( x2 < x1 ) {
+                moveToNextSplit();
+            } else if( x2-x1 == 0 ) {
+//                implicitQR.A.print();
+                implicitQR.addEigenAt(x1);
+                x2--;
+            } else if( x2-x1 == 1 ) {
+//                implicitQR.A.print();
+                implicitQR.addComputedEigen2x2(x1,x2);
+                x2 -= 2;
+            } else if( implicitQR.steps-implicitQR.lastExceptional > implicitQR.exceptionalThreshold ) {
+                // see if the matrix blew up
+                if( Double.isNaN(implicitQR.A.get(x2,x2))) {
+                    return false;
+                }
+
+                implicitQR.exceptionalShift(x1,x2);
+            } else if( implicitQR.isZero(x2,x2-1) ) {
+//                implicitQR.A.print();
+                implicitQR.addEigenAt(x2);
+                x2--;
+            }else {
+                performIteration();
+            }
+        }
+
+        return true;
+    }
+
+    private void moveToNextSplit() {
+        if( numSplits <= 0 )
+            throw new RuntimeException("bad");
+
+        x2 = splits[--numSplits];
+
+        if( numSplits > 0 ) {
+            x1 = splits[numSplits-1]+1;
+        } else {
+            x1 = 0;
+        }
+    }
+
+    private void performIteration() {
+        boolean changed = false;
+
+        // see if it can perform a split
+        for( int i = x2; i > x1; i-- ) {
+            if( implicitQR.isZero(i,i-1)) {
+                x1 = i;
+                splits[numSplits++] = i-1;
+                changed = true;
+                // reduce the scope of what it is looking at
+                break;
+            }
+        }
+
+        if( !changed )
+            implicitQR.implicitDoubleStep(x1,x2);
+    }
+
+    @Override
+    public int getNumberOfEigenvalues() {
+        return implicitQR.getNumberOfEigenvalues();
+    }
+
+    @Override
+    public Complex64F[] getEigenvalues() {
+        return implicitQR.getEigenvalues();
+    }
+
+    public WatchedDoubleStepQREigen getImplicitQR() {
+        return implicitQR;
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/eig/watched/WatchedDoubleStepQREigenvector.java b/main/dense64/src/org/ejml/alg/dense/decomposition/eig/watched/WatchedDoubleStepQREigenvector.java
new file mode 100644
index 0000000..ea3270b
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/eig/watched/WatchedDoubleStepQREigenvector.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.eig.watched;
+
+import org.ejml.UtilEjml;
+import org.ejml.alg.dense.decomposition.TriangularSolver;
+import org.ejml.data.Complex64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.LinearSolverFactory;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.NormOps;
+import org.ejml.ops.SpecializedOps;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class WatchedDoubleStepQREigenvector {
+
+    WatchedDoubleStepQREigen implicit;
+
+    // Q matrix from double step QR
+    DenseMatrix64F Q;
+
+
+    DenseMatrix64F eigenvectors[];
+
+    DenseMatrix64F eigenvectorTemp;
+
+    LinearSolver solver;
+
+    Complex64F origEigenvalues[];
+    int N;
+
+    int splits[];
+    int numSplits;
+
+    int x1,x2;
+
+    int indexVal;
+    boolean onscript;
+
+    public boolean process( WatchedDoubleStepQREigen implicit , DenseMatrix64F A , DenseMatrix64F Q_h )
+    {
+        this.implicit = implicit;
+
+        if( N != A.numRows ) {
+            N = A.numRows;
+            Q = new DenseMatrix64F(N,N);
+            splits = new int[N];
+            origEigenvalues = new Complex64F[N];
+            eigenvectors = new DenseMatrix64F[N];
+            eigenvectorTemp = new DenseMatrix64F(N,1);
+
+            solver = LinearSolverFactory.linear(0);
+        } else {
+//            UtilEjml.setnull(eigenvectors);
+            eigenvectors = new DenseMatrix64F[N];
+        }
+        System.arraycopy(implicit.eigenvalues,0,origEigenvalues,0,N);
+
+        implicit.setup(A);
+        implicit.setQ(Q);
+        numSplits = 0;
+        onscript = true;
+
+//        System.out.println("Orig A");
+//        A.print("%12.10f");
+
+        if( !findQandR() )
+            return false;
+
+        return extractVectors(Q_h);
+    }
+
+    public boolean extractVectors( DenseMatrix64F Q_h ) {
+
+        UtilEjml.memset(eigenvectorTemp.data,0);
+        // extract eigenvectors from the shur matrix
+        // start at the top left corner of the matrix
+        boolean triangular = true;
+        for( int i = 0; i < N; i++ ) {
+
+            Complex64F c = implicit.eigenvalues[N-i-1];
+
+            if( triangular && !c.isReal() )
+                triangular = false;
+
+            if( c.isReal() && eigenvectors[N-i-1] == null) {
+                solveEigenvectorDuplicateEigenvalue(c.real,i,triangular);
+            }
+        }
+
+        // translate the eigenvectors into the frame of the original matrix
+        if( Q_h != null ) {
+            DenseMatrix64F temp = new DenseMatrix64F(N,1);
+            for( int i = 0; i < N; i++ ) {
+                DenseMatrix64F v = eigenvectors[i];
+
+                if( v != null ) {
+                    CommonOps.mult(Q_h,v,temp);
+                    eigenvectors[i] = temp;
+                    temp = v;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    private void solveEigenvectorDuplicateEigenvalue( double real , int first , boolean isTriangle ) {
+
+        double scale = Math.abs(real);
+        if( scale == 0 ) scale = 1;
+
+        eigenvectorTemp.reshape(N,1, false);
+        eigenvectorTemp.zero();
+
+        if( first > 0 ) {
+            if( isTriangle ) {
+                solveUsingTriangle(real, first , eigenvectorTemp);
+            } else {
+                solveWithLU(real, first , eigenvectorTemp);
+            }
+        }
+
+        eigenvectorTemp.reshape(N,1, false);
+
+        for( int i = first; i < N; i++ ) {
+            Complex64F c = implicit.eigenvalues[N-i-1];
+
+            if( c.isReal() && Math.abs(c.real-real)/scale < 100.0*UtilEjml.EPS ) {
+                eigenvectorTemp.data[i] = 1;
+
+                DenseMatrix64F v = new DenseMatrix64F(N,1);
+                CommonOps.multTransA(Q,eigenvectorTemp,v);
+                eigenvectors[N-i-1] = v;
+                NormOps.normalizeF(v);
+
+                eigenvectorTemp.data[i] = 0;
+            }
+        }
+    }
+
+    private void solveUsingTriangle(double real, int index, DenseMatrix64F r ) {
+        for( int i = 0; i < index; i++ ) {
+            implicit.A.add(i,i,-real);
+        }
+
+        SpecializedOps.subvector(implicit.A,0,index,index,false,0,r);
+        CommonOps.changeSign(r);
+
+        TriangularSolver.solveU(implicit.A.data,r.data,implicit.A.numRows,0,index);
+
+        for( int i = 0; i < index; i++ ) {
+            implicit.A.add(i,i,real);
+        }
+    }
+
+    private void solveWithLU(double real, int index, DenseMatrix64F r ) {
+        DenseMatrix64F A = new DenseMatrix64F(index,index);
+
+        CommonOps.extract(implicit.A,0,index,0,index,A,0,0);
+
+        for( int i = 0; i < index; i++ ) {
+            A.add(i,i,-real);
+        }
+
+        r.reshape(index,1, false);
+
+        SpecializedOps.subvector(implicit.A,0,index,index,false,0,r);
+        CommonOps.changeSign(r);
+
+        // TODO this must be very inefficient
+        if( !solver.setA(A))
+            throw new RuntimeException("Solve failed");
+        solver.solve(r,r);
+    }
+
+    public boolean findQandR() {
+        CommonOps.setIdentity(Q);
+
+        x1 = 0;
+        x2 = N-1;
+
+        // use the already computed eigenvalues to recompute the Q and R matrices
+        indexVal = 0;
+        while( indexVal < N ) {
+            if (!findNextEigenvalue()) {
+                return false;
+            }
+        }
+
+//        Q.print("%1.10f");
+//
+//        implicit.A.print("%1.10f");
+
+        return true;
+    }
+
+    private boolean findNextEigenvalue() {
+        boolean foundEigen = false;
+        while( !foundEigen && implicit.steps < implicit.maxIterations ) {
+//            implicit.A.print();
+            implicit.incrementSteps();
+
+            if( x2 < x1 ) {
+                moveToNextSplit();
+            } else if( x2-x1 == 0 ) {
+                implicit.addEigenAt(x1);
+                x2--;
+                indexVal++;
+                foundEigen = true;
+            } else if( x2-x1 == 1 && !implicit.isReal2x2(x1,x2)) {
+                implicit.addComputedEigen2x2(x1,x2);
+                x2 -= 2;
+                indexVal += 2;
+                foundEigen = true;
+            } else if( implicit.steps-implicit.lastExceptional > implicit.exceptionalThreshold ) {
+//                implicit.A.print("%e");
+                //System.err.println("If it needs to do an exceptional shift then something went very bad.");
+//                return false;
+                implicit.exceptionalShift(x1,x2);
+                implicit.lastExceptional = implicit.steps;
+            } else if( implicit.isZero(x2,x2-1)) {
+                // check for convergence
+                implicit.addEigenAt(x2);
+                foundEigen = true;
+                x2--;
+                indexVal++;
+            } else {
+                checkSplitPerformImplicit();
+            }
+        }
+        return foundEigen;
+    }
+
+
+    private void checkSplitPerformImplicit() {
+        // check for splits
+        for( int i = x2; i > x1; i-- ) {
+            if( implicit.isZero(i,i-1)) {
+                x1 = i;
+                splits[numSplits++] = i-1;
+                // reduce the scope of what it is looking at
+                return;
+            }
+        }
+        // first try using known eigenvalues in the same order they were originally found
+        if( onscript) {
+            if( implicit.steps > implicit.exceptionalThreshold/2  ) {
+                onscript = false;
+            } else {
+                Complex64F a = origEigenvalues[indexVal];
+
+                // if no splits are found perform an implicit step
+                if( a.isReal() ) {
+                    implicit.performImplicitSingleStep(x1,x2, a.getReal());
+                } else if( x2 < N-2 ) {
+                    implicit.performImplicitDoubleStep(x1,x2, a.real,a.imaginary);
+                } else {
+                    onscript = false;
+                }
+            }
+        } else {
+            // that didn't work so try a modified order
+            if( x2-x1 >= 1 && x2 < N-2 )
+                implicit.implicitDoubleStep(x1,x2);
+            else
+                implicit.performImplicitSingleStep(x1,x2,implicit.A.get(x2,x2));
+        }
+    }
+
+
+    private void moveToNextSplit() {
+        if( numSplits <= 0 )
+            throw new RuntimeException("bad");
+
+        x2 = splits[--numSplits];
+
+        if( numSplits > 0 ) {
+            x1 = splits[numSplits-1]+1;
+        } else {
+            x1 = 0;
+        }
+    }
+
+    public DenseMatrix64F getQ() {
+        return Q;
+    }
+
+    public WatchedDoubleStepQREigen getImplicit() {
+        return implicit;
+    }
+
+    public DenseMatrix64F[] getEigenvectors() {
+        return eigenvectors;
+    }
+
+    public Complex64F[] getEigenvalues() {
+        return implicit.eigenvalues;
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/hessenberg/HessenbergSimilarDecomposition_D64.java b/main/dense64/src/org/ejml/alg/dense/decomposition/hessenberg/HessenbergSimilarDecomposition_D64.java
new file mode 100644
index 0000000..0043464
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/hessenberg/HessenbergSimilarDecomposition_D64.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.hessenberg;
+
+import org.ejml.alg.dense.decomposition.qr.QrHelperFunctions_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.DecompositionInterface;
+import org.ejml.ops.CommonOps;
+
+/**
+ * <p>
+ * Finds the decomposition of a matrix in the form of:<br>
+ * <br>
+ * A = OHO<sup>T</sup><br>
+ * <br>
+ * where A is an m by m matrix, O is an orthogonal matrix, and H is an upper Hessenberg matrix.
+ * </p>
+ *
+ * <p>
+ * A matrix is upper Hessenberg if a<sup>ij</sup> = 0 for all i > j+1. For example, the following matrix
+ * is upper Hessenberg.<br>
+ * <br>
+ * WRITE IT OUT USING A TABLE
+ * </p>
+ *
+ * <p>
+ * This decomposition is primarily used as a step for computing the eigenvalue decomposition of a matrix.
+ * The basic algorithm comes from David S. Watkins, "Fundamentals of MatrixComputations" Second Edition.
+ * </p>
+ */
+// TODO create a column based one similar to what was done for QR decomposition?
+public class HessenbergSimilarDecomposition_D64
+        implements DecompositionInterface<DenseMatrix64F> {
+    // A combined matrix that stores te upper Hessenberg matrix and the orthogonal matrix.
+    private DenseMatrix64F QH;
+    // number of rows and columns of the matrix being decompose
+    private int N;
+
+    // the first element in the orthogonal vectors
+    private double gammas[];
+    // temporary storage
+    private double b[];
+    private double u[];
+
+    /**
+     * Creates a decomposition that won't need to allocate new memory if it is passed matrices up to
+     * the specified size.
+     *
+     * @param initialSize Expected size of the matrices it will decompose.
+     */
+    public HessenbergSimilarDecomposition_D64(int initialSize) {
+        gammas = new double[ initialSize ];
+        b = new double[ initialSize ];
+        u = new double[ initialSize ];
+    }
+
+    public HessenbergSimilarDecomposition_D64() {
+        this(5);
+    }
+
+    /**
+     * Computes the decomposition of the provided matrix.  If no errors are detected then true is returned,
+     * false otherwise.
+     * @param A  The matrix that is being decomposed.  Not modified.
+     * @return If it detects any errors or not.
+     */
+    @Override
+    public boolean decompose( DenseMatrix64F A )
+    {
+        if( A.numRows != A.numCols )
+            throw new IllegalArgumentException("A must be square.");
+        if( A.numRows <= 0 )
+            return false;
+
+        QH = A;
+
+        N = A.numCols;
+
+        if( b.length < N ) {
+            b = new double[ N ];
+            gammas = new double[ N ];
+            u = new double[ N ];
+        }
+        return _decompose();
+    }
+
+    @Override
+    public boolean inputModified() {
+        return true;
+    }
+
+    /**
+     * The raw QH matrix that is stored internally.
+     *
+     * @return QH matrix.
+     */
+    public DenseMatrix64F getQH() {
+        return QH;
+    }
+
+    /**
+     * An upper Hessenberg matrix from the decompostion.
+     *
+     * @param H If not null then the results will be stored here.  Otherwise a new matrix will be created.
+     * @return The extracted H matrix.
+     */
+    public DenseMatrix64F getH( DenseMatrix64F H ) {
+        if( H == null ) {
+            H = new DenseMatrix64F(N,N);
+        } else if( N != H.numRows || N != H.numCols )
+            throw new IllegalArgumentException("The provided H must have the same dimensions as the decomposed matrix.");
+        else
+            H.zero();
+
+        // copy the first row
+        System.arraycopy(QH.data, 0, H.data, 0, N);
+
+        for( int i = 1; i < N; i++ ) {
+            for( int j = i-1; j < N; j++ ) {
+                H.set(i,j, QH.get(i,j));
+            }
+        }
+
+        return H;
+    }
+
+    /**
+     * An orthogonal matrix that has the following property: H = Q<sup>T</sup>AQ
+     *
+     * @param Q If not null then the results will be stored here.  Otherwise a new matrix will be created.
+     * @return The extracted Q matrix.
+     */
+    public DenseMatrix64F getQ( DenseMatrix64F Q ) {
+        if( Q == null ) {
+            Q = new DenseMatrix64F(N,N);
+            for( int i = 0; i < N; i++ ) {
+                Q.data[i*N+i] = 1;
+            }
+        } else if( N != Q.numRows || N != Q.numCols )
+            throw new IllegalArgumentException("The provided H must have the same dimensions as the decomposed matrix.");
+        else
+            CommonOps.setIdentity(Q);
+
+        for( int j = N-2; j >= 0; j-- ) {
+            u[j+1] = 1;
+            for( int i = j+2; i < N; i++ ) {
+                u[i] = QH.get(i,j);
+            }
+            QrHelperFunctions_D64.rank1UpdateMultR(Q, u, gammas[j], j + 1, j + 1, N, b);
+        }
+
+        return Q;
+    }
+
+    /**
+     * Internal function for computing the decomposition.
+     */
+    private boolean _decompose() {
+        double h[] = QH.data;
+
+        for( int k = 0; k < N-2; k++ ) {
+            // find the largest value in this column
+            // this is used to normalize the column and mitigate overflow/underflow
+            double max = 0;
+
+            for( int i = k+1; i < N; i++ ) {
+                // copy the householder vector to vector outside of the matrix to reduce caching issues
+                // big improvement on larger matrices and a relatively small performance hit on small matrices.
+                double val = u[i] = h[i*N+k];
+                val = Math.abs(val);
+                if( val > max )
+                    max = val;
+            }
+
+            if( max > 0 ) {
+                // -------- set up the reflector Q_k
+
+                double tau = 0;
+                // normalize to reduce overflow/underflow
+                // and compute tau for the reflector
+                for( int i = k+1; i < N; i++ ) {
+                    double val = u[i] /= max;
+                    tau += val*val;
+                }
+
+                tau = Math.sqrt(tau);
+
+                if( u[k+1] < 0 )
+                    tau = -tau;
+
+                // write the reflector into the lower left column of the matrix
+                double nu = u[k+1] + tau;
+                u[k+1] = 1.0;
+
+                for( int i = k+2; i < N; i++ ) {
+                    h[i*N+k] = u[i] /= nu;
+                }
+
+                double gamma = nu/tau;
+                gammas[k] = gamma;
+
+                // ---------- multiply on the left by Q_k
+                QrHelperFunctions_D64.rank1UpdateMultR(QH, u, gamma, k + 1, k + 1, N, b);
+
+                // ---------- multiply on the right by Q_k
+                QrHelperFunctions_D64.rank1UpdateMultL(QH, u, gamma, 0, k + 1, N);
+
+                // since the first element in the householder vector is known to be 1
+                // store the full upper hessenberg
+                h[(k+1)*N+k] = -tau*max;
+
+            } else {
+                gammas[k] = 0;
+            }
+
+        }
+
+        return true;
+    }
+
+    public double[] getGammas() {
+        return gammas;
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/hessenberg/TridiagonalDecompositionHouseholderOrig_D64.java b/main/dense64/src/org/ejml/alg/dense/decomposition/hessenberg/TridiagonalDecompositionHouseholderOrig_D64.java
new file mode 100644
index 0000000..6d92eb0
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/hessenberg/TridiagonalDecompositionHouseholderOrig_D64.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.hessenberg;
+
+import org.ejml.alg.dense.decomposition.qr.QrHelperFunctions_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+
+/**
+ * <p>
+ * A straight forward implementation from "Fundamentals of Matrix Computations," Second Edition.<br>
+ * <br>
+ * This is only saved to provide a point of reference in benchmarks.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class TridiagonalDecompositionHouseholderOrig_D64 {
+
+    /**
+     * Internal storage of decomposed matrix.  The tridiagonal matrix is stored in the
+     * upper tridiagonal portion of the matrix.  The householder vectors are stored
+     * in the upper rows.
+     */
+    DenseMatrix64F QT;
+
+    // The size of the matrix
+    int N;
+
+    // temporary storage
+    double w[];
+    // gammas for the householder operations
+    double gammas[];
+    // temporary storage
+    double b[];
+
+    public TridiagonalDecompositionHouseholderOrig_D64() {
+        N = 1;
+        QT = new DenseMatrix64F(N,N);
+        w = new double[N];
+        b = new double[N];
+        gammas = new double[N];
+    }
+
+    /**
+     * Returns the interal matrix where the decomposed results are stored.
+     * @return
+     */
+    public DenseMatrix64F getQT() {
+        return QT;
+    }
+
+    /**
+     * Extracts the tridiagonal matrix found in the decomposition.
+     *
+     * @param T If not null then the results will be stored here.  Otherwise a new matrix will be created.
+     * @return The extracted T matrix.
+     */
+    public DenseMatrix64F getT( DenseMatrix64F T) {
+        if( T == null ) {
+            T = new DenseMatrix64F(N,N);
+        } else if( N != T.numRows || N != T.numCols )
+            throw new IllegalArgumentException("The provided H must have the same dimensions as the decomposed matrix.");
+        else
+            T.zero();
+
+
+        T.data[0] = QT.data[0];
+        T.data[1] = QT.data[1];
+
+
+        for( int i = 1; i < N-1; i++ ) {
+            T.set(i,i, QT.get(i,i));
+            T.set(i,i+1,QT.get(i,i+1));
+            T.set(i,i-1,QT.get(i-1,i));
+        }
+
+        T.data[(N-1)*N+N-1] = QT.data[(N-1)*N+N-1];
+        T.data[(N-1)*N+N-2] = QT.data[(N-2)*N+N-1];
+
+        return T;
+    }
+
+    /**
+     * An orthogonal matrix that has the following property: T = Q<sup>T</sup>AQ
+     *
+     * @param Q If not null then the results will be stored here.  Otherwise a new matrix will be created.
+     * @return The extracted Q matrix.
+     */
+    public DenseMatrix64F getQ( DenseMatrix64F Q ) {
+        if( Q == null ) {
+            Q = new DenseMatrix64F(N,N);
+            for( int i = 0; i < N; i++ ) {
+                Q.data[i*N+i] = 1;
+            }
+        } else if( N != Q.numRows || N != Q.numCols )
+            throw new IllegalArgumentException("The provided H must have the same dimensions as the decomposed matrix.");
+        else
+            CommonOps.setIdentity(Q);
+
+        for( int i = 0; i < N; i++ ) w[i] = 0;
+
+        for( int j = N-2; j >= 0; j-- ) {
+            w[j+1] = 1;
+            for( int i = j+2; i < N; i++ ) {
+                w[i] = QT.get(j,i);
+            }
+            QrHelperFunctions_D64.rank1UpdateMultR(Q, w, gammas[j + 1], j + 1, j + 1, N, b);
+//            Q.print();
+        }
+
+        return Q;
+    }
+
+    /**
+     * Decomposes the provided symmetric matrix.
+     *
+     * @param A Symmetric matrix that is going to be decomposed.  Not modified.
+     */
+    public void decompose( DenseMatrix64F A ) {
+        init(A);
+
+        for( int k = 1; k < N; k++ ) {
+            similarTransform(k);
+//            System.out.println("k=="+k);
+//            QT.print();
+        }
+    }
+
+    /**
+     * Computes and performs the similar a transform for submatrix k.
+     */
+    private void similarTransform( int k) {
+        double t[] = QT.data;
+
+        // find the largest value in this column
+        // this is used to normalize the column and mitigate overflow/underflow
+        double max = 0;
+
+        int rowU = (k-1)*N;
+
+        for( int i = k; i < N; i++ ) {
+            double val = Math.abs(t[rowU+i]);
+            if( val > max )
+                max = val;
+        }
+
+        if( max > 0 ) {
+            // -------- set up the reflector Q_k
+
+            double tau = 0;
+            // normalize to reduce overflow/underflow
+            // and compute tau for the reflector
+            for( int i = k; i < N; i++ ) {
+                double val = t[rowU+i] /= max;
+                tau += val*val;
+            }
+
+            tau = Math.sqrt(tau);
+
+            if( t[rowU+k] < 0 )
+                tau = -tau;
+
+            // write the reflector into the lower left column of the matrix
+            double nu = t[rowU+k] + tau;
+            t[rowU+k] = 1.0;
+
+            for( int i = k+1; i < N; i++ ) {
+                t[rowU+i] /= nu;
+            }
+
+            double gamma = nu/tau;
+            gammas[k] = gamma;
+
+            // ---------- Specialized householder that takes advantage of the symmetry
+            householderSymmetric(k,gamma);
+
+            // since the first element in the householder vector is known to be 1
+            // store the full upper hessenberg
+            t[rowU+k] = -tau*max;
+        } else {
+            gammas[k] = 0;
+        }
+    }
+
+    /**
+     * Performs the householder operations on left and right and side of the matrix.  Q<sup>T</sup>AQ
+     * @param row Specifies the submatrix.
+     *
+     * @param gamma The gamma for the householder operation
+     */
+    public void householderSymmetric( int row , double gamma )
+    {
+        int startU = (row-1)*N;
+
+        // compute v = -gamma*A*u
+        for( int i = row; i < N; i++ ) {
+            double total = 0;
+            for( int j = row; j < N; j++ ) {
+                total += QT.data[i*N+j]*QT.data[startU+j];
+            }
+            w[i] = -gamma*total;
+//            System.out.println("y["+i+"] = "+w[i]);
+        }
+        // alpha = -0.5*gamma*u^T*v
+        double alpha = 0;
+
+        for( int i = row; i < N; i++ ) {
+            alpha += QT.data[startU+i]*w[i];
+        }
+        alpha *= -0.5*gamma;
+
+        // w = v + alpha*u
+        for( int i = row; i < N; i++ ) {
+            w[i] += alpha*QT.data[startU+i];
+//            System.out.println("w["+i+"] = "+w[i]);
+        }
+        // A = A + w*u^T + u*w^T
+        for( int i = row; i < N; i++ ) {
+
+            double ww = w[i];
+            double uu = QT.data[startU+i];
+//            System.out.println("u["+i+"] = "+uu);
+
+            for( int j = i; j < N; j++ ) {
+                QT.data[j*N+i] = QT.data[i*N+j] += ww*QT.data[startU+j] + w[j]*uu;
+            }
+        }
+
+    }
+
+
+    /**
+     * If needed declares and sets up internal data structures.
+     *
+     * @param A Matrix being decomposed.
+     */
+    public void init( DenseMatrix64F A ) {
+        if( A.numRows != A.numCols)
+            throw new IllegalArgumentException("Must be square");
+
+        if( A.numCols != N ) {
+            N = A.numCols;
+            QT.reshape(N,N, false);
+
+            if( w.length < N ) {
+                w = new double[ N ];
+                gammas = new double[N];
+                b = new double[N];
+            }
+        }
+
+        // just copy the top right triangle
+        QT.set(A);
+    }
+
+    public double getGamma( int index ) {
+        return gammas[index];
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/hessenberg/TridiagonalDecompositionHouseholder_D64.java b/main/dense64/src/org/ejml/alg/dense/decomposition/hessenberg/TridiagonalDecompositionHouseholder_D64.java
new file mode 100644
index 0000000..eacb583
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/hessenberg/TridiagonalDecompositionHouseholder_D64.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.hessenberg;
+
+import org.ejml.alg.dense.decomposition.qr.QrHelperFunctions_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.TridiagonalSimilarDecomposition;
+import org.ejml.ops.CommonOps;
+
+/**
+ * <p>
+ * Performs a {@link org.ejml.interfaces.decomposition.TridiagonalSimilarDecomposition similar tridiagonal decomposition} on a square symmetric input matrix.
+ * Householder vectors perform the similar operation and the symmetry is taken advantage
+ * of for good performance.
+ * </p>
+ * <p>
+ * Finds the decomposition of a matrix in the form of:<br>
+ * <br>
+ * A = O*T*O<sup>T</sup><br>
+ * <br>
+ * where A is a symmetric m by m matrix, O is an orthogonal matrix, and T is a tridiagonal matrix.
+ * </p>
+ * <p>
+ * This implementation is based off of the algorithm described in:<br>
+ * <br>
+ * David S. Watkins, "Fundamentals of Matrix Computations," Second Edition.  Page 349-355
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class TridiagonalDecompositionHouseholder_D64
+        implements TridiagonalSimilarDecomposition<DenseMatrix64F> {
+
+    /**
+     * Only the upper right triangle is used.  The Tridiagonal portion stores
+     * the tridiagonal matrix.  The rows store householder vectors.
+     */
+    private DenseMatrix64F QT;
+
+    // The size of the matrix
+    private int N;
+
+    // temporary storage
+    private double w[];
+    // gammas for the householder operations
+    private double gammas[];
+    // temporary storage
+    private double b[];
+
+    public TridiagonalDecompositionHouseholder_D64() {
+        N = 1;
+        w = new double[N];
+        b = new double[N];
+        gammas = new double[N];
+    }
+
+    /**
+     * Returns the internal matrix where the decomposed results are stored.
+     * @return
+     */
+    public DenseMatrix64F getQT() {
+        return QT;
+    }
+
+    @Override
+    public void getDiagonal(double[] diag, double[] off) {
+        for( int i = 0; i < N; i++ ) {
+            diag[i] = QT.data[i*N+i];
+
+            if( i+1 < N ) {
+                off[i] = QT.data[i*N+i+1];
+            }
+        }
+    }
+
+    /**
+     * Extracts the tridiagonal matrix found in the decomposition.
+     *
+     * @param T If not null then the results will be stored here.  Otherwise a new matrix will be created.
+     * @return The extracted T matrix.
+     */
+    @Override
+    public DenseMatrix64F getT( DenseMatrix64F T ) {
+        if( T == null ) {
+            T = new DenseMatrix64F(N,N);
+        } else if( N != T.numRows || N != T.numCols )
+            throw new IllegalArgumentException("The provided H must have the same dimensions as the decomposed matrix.");
+        else
+            T.zero();
+
+
+        T.data[0] = QT.data[0];
+
+        for( int i = 1; i < N; i++ ) {
+            T.set(i,i, QT.get(i,i));
+            double a = QT.get(i-1,i);
+            T.set(i-1,i,a);
+            T.set(i,i-1,a);
+        }
+
+        if( N > 1 ) {
+            T.data[(N-1)*N+N-1] = QT.data[(N-1)*N+N-1];
+            T.data[(N-1)*N+N-2] = QT.data[(N-2)*N+N-1];
+        }
+            
+        return T;
+    }
+
+    /**
+     * An orthogonal matrix that has the following property: T = Q<sup>T</sup>AQ
+     *
+     * @param Q If not null then the results will be stored here.  Otherwise a new matrix will be created.
+     * @return The extracted Q matrix.
+     */
+    @Override
+    public DenseMatrix64F getQ( DenseMatrix64F Q , boolean transposed ) {
+        if( Q == null ) {
+            Q = CommonOps.identity(N);
+        } else if( N != Q.numRows || N != Q.numCols )
+            throw new IllegalArgumentException("The provided H must have the same dimensions as the decomposed matrix.");
+        else
+            CommonOps.setIdentity(Q);
+
+        for( int i = 0; i < N; i++ ) w[i] = 0;
+
+        if( transposed ) {
+            for( int j = N-2; j >= 0; j-- ) {
+                w[j+1] = 1;
+                for( int i = j+2; i < N; i++ ) {
+                    w[i] = QT.data[j*N+i];
+                }
+                QrHelperFunctions_D64.rank1UpdateMultL(Q, w, gammas[j + 1], j + 1, j + 1, N);
+            }
+        } else {
+            for( int j = N-2; j >= 0; j-- ) {
+                w[j+1] = 1;
+                for( int i = j+2; i < N; i++ ) {
+                    w[i] = QT.get(j,i);
+                }
+                QrHelperFunctions_D64.rank1UpdateMultR(Q, w, gammas[j + 1], j + 1, j + 1, N, b);
+            }
+        }
+
+        return Q;
+    }
+
+    /**
+     * Decomposes the provided symmetric matrix.
+     *
+     * @param A Symmetric matrix that is going to be decomposed.  Not modified.
+     */
+    @Override
+    public boolean decompose( DenseMatrix64F A ) {
+        init(A);
+
+        for( int k = 1; k < N; k++ ) {
+            similarTransform(k);
+        }
+
+        return true;
+    }
+
+    /**
+     * Computes and performs the similar a transform for submatrix k.
+     */
+    private void similarTransform( int k) {
+        double t[] = QT.data;
+
+        // find the largest value in this column
+        // this is used to normalize the column and mitigate overflow/underflow
+        double max = 0;
+
+        int rowU = (k-1)*N;
+
+        for( int i = k; i < N; i++ ) {
+            double val = Math.abs(t[rowU+i]);
+            if( val > max )
+                max = val;
+        }
+
+        if( max > 0 ) {
+            // -------- set up the reflector Q_k
+
+            double tau = QrHelperFunctions_D64.computeTauAndDivide(k, N, t, rowU, max);
+
+            // write the reflector into the lower left column of the matrix
+            double nu = t[rowU+k] + tau;
+            QrHelperFunctions_D64.divideElements(k + 1, N, t, rowU, nu);
+            t[rowU+k] = 1.0;
+
+            double gamma = nu/tau;
+            gammas[k] = gamma;
+
+            // ---------- Specialized householder that takes advantage of the symmetry
+            householderSymmetric(k,gamma);
+
+            // since the first element in the householder vector is known to be 1
+            // store the full upper hessenberg
+            t[rowU+k] = -tau*max;
+        } else {
+            gammas[k] = 0;
+        }
+    }
+
+    /**
+     * Performs the householder operations on left and right and side of the matrix.  Q<sup>T</sup>AQ
+     * @param row Specifies the submatrix.
+     *
+     * @param gamma The gamma for the householder operation
+     */
+    public void householderSymmetric( int row , double gamma )
+    {
+        int startU = (row-1)*N;
+
+        // compute v = -gamma*A*u
+        for( int i = row; i < N; i++ ) {
+            double total = 0;
+            // the lower triangle is not written to so it needs to traverse upwards
+            // to get the information.  Reduces the number of matrix writes need
+            // improving large matrix performance
+            for( int j = row; j < i; j++ ) {
+                total += QT.data[j*N+i]*QT.data[startU+j];
+            }
+            for( int j = i; j < N; j++ ) {
+                total += QT.data[i*N+j]*QT.data[startU+j];
+            }
+            w[i] = -gamma*total;
+        }
+        // alpha = -0.5*gamma*u^T*v
+        double alpha = 0;
+
+        for( int i = row; i < N; i++ ) {
+            alpha += QT.data[startU+i]*w[i];
+        }
+        alpha *= -0.5*gamma;
+
+        // w = v + alpha*u
+        for( int i = row; i < N; i++ ) {
+            w[i] += alpha*QT.data[startU+i];
+        }
+        // A = A + w*u^T + u*w^T
+        for( int i = row; i < N; i++ ) {
+
+            double ww = w[i];
+            double uu = QT.data[startU+i];
+
+            int rowA = i*N;
+            for( int j = i; j < N; j++ ) {
+                // only write to the upper portion of the matrix
+                // this reduces the number of cache misses
+                QT.data[rowA+j] += ww*QT.data[startU+j] + w[j]*uu;
+            }
+        }
+
+    }
+
+
+    /**
+     * If needed declares and sets up internal data structures.
+     *
+     * @param A Matrix being decomposed.
+     */
+    public void init( DenseMatrix64F A ) {
+        if( A.numRows != A.numCols)
+            throw new IllegalArgumentException("Must be square");
+
+        if( A.numCols != N ) {
+            N = A.numCols;
+
+            if( w.length < N ) {
+                w = new double[ N ];
+                gammas = new double[N];
+                b = new double[N];
+            }
+        }
+
+        QT = A;
+    }
+
+    @Override
+    public boolean inputModified() {
+        return true;
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/hessenberg/TridiagonalDecomposition_B64_to_D64.java b/main/dense64/src/org/ejml/alg/dense/decomposition/hessenberg/TridiagonalDecomposition_B64_to_D64.java
new file mode 100644
index 0000000..c7b8bd9
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/hessenberg/TridiagonalDecomposition_B64_to_D64.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.hessenberg;
+
+import org.ejml.EjmlParameters;
+import org.ejml.alg.block.decomposition.hessenberg.TridiagonalDecompositionHouseholder_B64;
+import org.ejml.alg.dense.decomposition.BaseDecomposition_B64_to_D64;
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.TridiagonalSimilarDecomposition;
+import org.ejml.ops.CommonOps;
+
+
+/**
+ * Wrapper around a block implementation of TridiagonalSimilarDecomposition
+ *
+ * @author Peter Abeles
+ */
+public class TridiagonalDecomposition_B64_to_D64
+        extends BaseDecomposition_B64_to_D64
+        implements TridiagonalSimilarDecomposition<DenseMatrix64F> {
+
+
+    public TridiagonalDecomposition_B64_to_D64() {
+        this(EjmlParameters.BLOCK_WIDTH);
+    }
+
+    public TridiagonalDecomposition_B64_to_D64(int blockSize) {
+        super(new TridiagonalDecompositionHouseholder_B64(),blockSize);
+    }
+
+    @Override
+    public DenseMatrix64F getT(DenseMatrix64F T) {
+        int N = Ablock.numRows;
+
+        if( T == null ) {
+            T = new DenseMatrix64F(N,N);
+        } else {
+            CommonOps.fill(T, 0);
+        }
+
+        double[] diag = new double[ N ];
+        double[] off = new double[ N ];
+
+        ((TridiagonalDecompositionHouseholder_B64)alg).getDiagonal(diag,off);
+
+        T.unsafe_set(0,0,diag[0]);
+        for( int i = 1; i < N; i++ ) {
+            T.unsafe_set(i,i,diag[i]);
+            T.unsafe_set(i,i-1,off[i-1]);
+            T.unsafe_set(i-1,i,off[i-1]);
+        }
+
+        return T;
+    }
+
+    @Override
+    public DenseMatrix64F getQ(DenseMatrix64F Q, boolean transposed) {
+        if( Q == null ) {
+            Q = new DenseMatrix64F(Ablock.numRows,Ablock.numCols);
+        }
+
+        BlockMatrix64F Qblock = new BlockMatrix64F();
+        Qblock.numRows =  Q.numRows;
+        Qblock.numCols =  Q.numCols;
+        Qblock.blockLength = blockLength;
+        Qblock.data = Q.data;
+
+        ((TridiagonalDecompositionHouseholder_B64)alg).getQ(Qblock,transposed);
+
+        convertBlockToRow(Q.numRows,Q.numCols,Ablock.blockLength,Q.data);
+
+        return Q;
+    }
+
+    @Override
+    public void getDiagonal(double[] diag, double[] off) {
+        ((TridiagonalDecompositionHouseholder_B64)alg).getDiagonal(diag,off);
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/lu/LUDecompositionAlt_D64.java b/main/dense64/src/org/ejml/alg/dense/decomposition/lu/LUDecompositionAlt_D64.java
new file mode 100644
index 0000000..1e5a74e
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/lu/LUDecompositionAlt_D64.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.lu;
+
+import org.ejml.data.DenseMatrix64F;
+
+
+/**
+ * <p>
+ * An LU decomposition algorithm that originally came from Jama.  In general this is faster than
+ * what is in NR since it creates a cache of a column, which makes a big difference in larger
+ * matrices.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class LUDecompositionAlt_D64 extends LUDecompositionBase_D64 {
+
+    /**
+     * This is a modified version of what was found in the JAMA package.  The order that it
+     * performs its permutations in is the primary difference from NR
+     *
+     * @param a The matrix that is to be decomposed.  Not modified.
+     * @return true If the matrix can be decomposed and false if it can not.
+     */
+    public boolean decompose( DenseMatrix64F a )
+    {
+        decomposeCommonInit(a);
+
+        double LUcolj[] = vv;
+
+        for( int j = 0; j < n; j++ ) {
+
+            // make a copy of the column to avoid cache jumping issues
+            for( int i = 0; i < m; i++) {
+                LUcolj[i] = dataLU[i*n + j];
+            }
+
+            // Apply previous transformations.
+            for( int i = 0; i < m; i++ ) {
+                int rowIndex = i*n;
+
+                // Most of the time is spent in the following dot product.
+                int kmax = i < j ? i : j;
+                double s = 0.0;
+                for (int k = 0; k < kmax; k++) {
+                    s += dataLU[rowIndex+k]*LUcolj[k];
+                }
+
+                dataLU[rowIndex+j] = LUcolj[i] -= s;
+            }
+
+            // Find pivot and exchange if necessary.
+            int p = j;
+            double max = Math.abs(LUcolj[p]);
+            for (int i = j+1; i < m; i++) {
+                double v = Math.abs(LUcolj[i]);
+                if ( v > max) {
+                    p = i;
+                    max = v;
+                }
+            }
+
+            if (p != j) {
+                // swap the rows
+//                for (int k = 0; k < n; k++) {
+//                    double t = dataLU[p*n + k];
+//                    dataLU[p*n + k] = dataLU[j*n + k];
+//                    dataLU[j*n + k] = t;
+//                }
+                int rowP = p*n;
+                int rowJ = j*n;
+                int endP = rowP+n;
+                for (;rowP < endP; rowP++,rowJ++) {
+                    double t = dataLU[rowP];
+                    dataLU[rowP] = dataLU[rowJ];
+                    dataLU[rowJ] = t;
+                }
+                int k = pivot[p]; pivot[p] = pivot[j]; pivot[j] = k;
+                pivsign = -pivsign;
+            }
+            indx[j] = p;
+
+            // Compute multipliers.
+            if (j < m ) {
+                double lujj = dataLU[j*n+j];
+                if( lujj != 0 ) {
+                    for (int i = j+1; i < m; i++) {
+                        dataLU[i*n+j] /= lujj;
+                    }
+                }
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/lu/LUDecompositionBase_D64.java b/main/dense64/src/org/ejml/alg/dense/decomposition/lu/LUDecompositionBase_D64.java
new file mode 100644
index 0000000..928f26b
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/lu/LUDecompositionBase_D64.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.lu;
+
+import org.ejml.UtilEjml;
+import org.ejml.alg.dense.decomposition.TriangularSolver;
+import org.ejml.data.Complex64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.LUDecomposition;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.SpecializedOps;
+
+
+/**
+ * <p>
+ * Contains common data structures and operations for LU decomposition algorithms.
+ * </p>
+ * @author Peter Abeles
+ */
+public abstract class LUDecompositionBase_D64
+        implements LUDecomposition<DenseMatrix64F> {
+    // the decomposed matrix
+    protected DenseMatrix64F LU;
+
+    // it can decompose a matrix up to this size
+    protected int maxWidth=-1;
+
+    // the shape of the matrix
+    protected int m,n;
+    // data in the matrix
+    protected double dataLU[];
+
+    // used in set, solve, invert
+    protected double vv[];
+    // used in set
+    protected int indx[];
+    protected int pivot[];
+
+    // used by determinant
+    protected double pivsign;
+
+    Complex64F det = new Complex64F();
+
+    public void setExpectedMaxSize( int numRows , int numCols )
+    {
+        LU = new DenseMatrix64F(numRows,numCols);
+
+        this.dataLU = LU.data;
+        maxWidth = Math.max(numRows,numCols);
+
+        vv = new double[ maxWidth ];
+        indx = new int[ maxWidth ];
+        pivot = new int[ maxWidth ];
+    }
+
+    public DenseMatrix64F getLU() {
+        return LU;
+    }
+
+    public int[] getIndx() {
+        return indx;
+    }
+
+    public int[] getPivot() {
+        return pivot;
+    }
+
+    @Override
+    public boolean inputModified() {
+        return false;
+    }
+
+    /**
+     * Writes the lower triangular matrix into the specified matrix.
+     *
+     * @param lower Where the lower triangular matrix is writen to.
+     */
+    @Override
+    public DenseMatrix64F getLower( DenseMatrix64F lower )
+    {
+        int numRows = LU.numRows;
+        int numCols = LU.numRows < LU.numCols ? LU.numRows : LU.numCols;
+
+        if( lower == null ) {
+            lower = new DenseMatrix64F(numRows,numCols);
+        } else {
+            if( lower.numCols != numCols || lower.numRows != numRows )
+                throw new IllegalArgumentException("Unexpected matrix dimension");
+            CommonOps.fill(lower, 0);
+        }
+
+        for( int i = 0; i < numCols; i++ ) {
+            lower.set(i,i,1.0);
+
+            for( int j = 0; j < i; j++ ) {
+                lower.set(i,j, LU.get(i,j));
+            }
+        }
+
+        if( numRows > numCols ) {
+            for( int i = numCols; i < numRows; i++ ) {
+                for( int j = 0; j < numCols; j++ ) {
+                    lower.set(i,j, LU.get(i,j));
+                }
+            }
+        }
+        return lower;
+    }
+
+    /**
+     * Writes the upper triangular matrix into the specified matrix.
+     *
+     * @param upper Where the upper triangular matrix is writen to.
+     */
+    @Override
+    public DenseMatrix64F getUpper( DenseMatrix64F upper )
+    {
+        int numRows = LU.numRows < LU.numCols ? LU.numRows : LU.numCols;
+        int numCols = LU.numCols;
+
+        if( upper == null ) {
+            upper = new DenseMatrix64F(numRows, numCols);
+        } else {
+            if( upper.numCols != numCols || upper.numRows != numRows )
+                throw new IllegalArgumentException("Unexpected matrix dimension");
+            CommonOps.fill(upper, 0);
+        }
+
+        for( int i = 0; i < numRows; i++ ) {
+            for( int j = i; j < numCols; j++ ) {
+                upper.set(i,j, LU.get(i,j));
+            }
+        }
+
+        return upper;
+    }
+
+    public DenseMatrix64F getPivot( DenseMatrix64F pivot ) {
+        return SpecializedOps.pivotMatrix(pivot, this.pivot, LU.numRows, false);
+    }
+
+    protected void decomposeCommonInit(DenseMatrix64F a) {
+        if( a.numRows > maxWidth || a.numCols > maxWidth ) {
+            setExpectedMaxSize(a.numRows,a.numCols);
+        }
+
+        m = a.numRows;
+        n = a.numCols;
+
+        LU.set(a);
+        for (int i = 0; i < m; i++) {
+            pivot[i] = i;
+        }
+        pivsign = 1;
+    }
+
+    /**
+     * Determines if the decomposed matrix is singular.  This function can return
+     * false and the matrix be almost singular, which is still bad.
+     *
+     * @return true if singular false otherwise.
+     */
+    @Override
+    public boolean isSingular() {
+        for( int i = 0; i < m; i++ ) {
+            if( Math.abs(dataLU[i* n +i]) < UtilEjml.EPS )
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Computes the determinant from the LU decomposition.
+     *
+     * @return The matrix's determinant.
+     */
+    @Override
+    public Complex64F computeDeterminant() {
+        if( m != n )
+            throw new IllegalArgumentException("Must be a square matrix.");
+
+        double ret = pivsign;
+
+        int total = m*n;
+        for( int i = 0; i < total; i += n + 1 ) {
+            ret *= dataLU[i];
+        }
+
+        det.real = ret;
+        det.imaginary = 0;
+
+        return det;
+    }
+
+    public double quality() {
+        return SpecializedOps.qualityTriangular(LU);
+    }
+
+    /**
+     * a specialized version of solve that avoid additional checks that are not needed.
+     */
+    public void _solveVectorInternal( double []vv )
+    {
+        // Solve L*Y = B
+        int ii = 0;
+
+        for( int i = 0; i < n; i++ ) {
+            int ip = indx[i];
+            double sum = vv[ip];
+            vv[ip] = vv[i];
+            if( ii != 0 ) {
+//                for( int j = ii-1; j < i; j++ )
+//                    sum -= dataLU[i* n +j]*vv[j];
+                int index = i*n + ii-1;
+                for( int j = ii-1; j < i; j++ )
+                    sum -= dataLU[index++]*vv[j];
+            } else if( sum != 0.0 ) {
+                ii=i+1;
+            }
+            vv[i] = sum;
+        }
+
+        // Solve U*X = Y;
+        TriangularSolver.solveU(dataLU,vv,n);
+    }
+
+    public double[] _getVV() {
+        return vv;
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/qr/QRColPivDecompositionHouseholderColumn_D64.java b/main/dense64/src/org/ejml/alg/dense/decomposition/qr/QRColPivDecompositionHouseholderColumn_D64.java
new file mode 100644
index 0000000..dbed7ed
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/qr/QRColPivDecompositionHouseholderColumn_D64.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.qr;
+
+import org.ejml.UtilEjml;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.QRPDecomposition;
+import org.ejml.ops.CommonOps;
+
+/**
+ * <p>
+ * Performs QR decomposition with column pivoting.  To prevent overflow/underflow the whole matrix
+ * is normalized by the max value, but columns are not normalized individually any more. To enable
+ * code reuse it extends {@link QRDecompositionHouseholderColumn_D64} and functions from that class
+ * are used whenever possible.  Columns are transposed into single arrays, which allow for
+ * fast pivots.
+ * </p>
+ *
+ * <p>
+ * Decomposition: A*P = Q*R
+ * </p>
+ *
+ * <p>
+ * Based off the description in "Fundamentals of Matrix Computations", 2nd by David S. Watkins.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class QRColPivDecompositionHouseholderColumn_D64
+        extends QRDecompositionHouseholderColumn_D64
+        implements QRPDecomposition<DenseMatrix64F>
+{
+    // the ordering of each column, the current column i is the original column pivots[i]
+    protected int pivots[];
+    // F-norm  squared for each column
+    protected double normsCol[];
+
+    // value of the maximum abs element
+    protected double maxAbs;
+
+    // threshold used to determine when a column is considered to be singular
+    // Threshold is relative to the maxAbs
+    protected double singularThreshold = UtilEjml.EPS;
+
+    // the matrix's rank
+    protected int rank;
+
+    /**
+     * Configure parameters.
+     *
+     * @param singularThreshold The singular threshold.
+     */
+    public QRColPivDecompositionHouseholderColumn_D64(double singularThreshold) {
+        this.singularThreshold = singularThreshold;
+    }
+
+    public QRColPivDecompositionHouseholderColumn_D64() {
+    }
+
+    @Override
+    public void setSingularThreshold( double threshold ) {
+        this.singularThreshold = threshold;
+    }
+
+    @Override
+    public void setExpectedMaxSize( int numRows , int numCols ) {
+        super.setExpectedMaxSize(numRows,numCols);
+
+        if( pivots == null || pivots.length < numCols  ) {
+            pivots = new int[numCols];
+            normsCol = new double[numCols];
+        }
+    }
+
+    /**
+     * Computes the Q matrix from the information stored in the QR matrix.  This
+     * operation requires about 4(m<sup>2</sup>n-mn<sup>2</sup>+n<sup>3</sup>/3) flops.
+     *
+     * @param Q The orthogonal Q matrix.
+     */
+    @Override
+    public DenseMatrix64F getQ( DenseMatrix64F Q , boolean compact ) {
+        if( compact ) {
+            if( Q == null ) {
+                Q = CommonOps.identity(numRows,minLength);
+            } else {
+                if( Q.numRows != numRows || Q.numCols != minLength ) {
+                    throw new IllegalArgumentException("Unexpected matrix dimension.");
+                } else {
+                    CommonOps.setIdentity(Q);
+                }
+            }
+        } else {
+            if( Q == null ) {
+                Q = CommonOps.identity(numRows);
+            } else {
+                if( Q.numRows != numRows || Q.numCols != numRows ) {
+                    throw new IllegalArgumentException("Unexpected matrix dimension.");
+                } else {
+                    CommonOps.setIdentity(Q);
+                }
+            }
+        }
+
+        for( int j = rank-1; j >= 0; j-- ) {
+            double u[] = dataQR[j];
+
+            double vv = u[j];
+            u[j] = 1;
+            QrHelperFunctions_D64.rank1UpdateMultR(Q, u, gammas[j], j, j, numRows, v);
+            u[j] = vv;
+        }
+
+        return Q;
+    }
+
+    /**
+     * <p>
+     * To decompose the matrix 'A' it must have full rank.  'A' is a 'm' by 'n' matrix.
+     * It requires about 2n*m<sup>2</sup>-2m<sup>2</sup>/3 flops.
+     * </p>
+     *
+     * <p>
+     * The matrix provided here can be of different
+     * dimension than the one specified in the constructor.  It just has to be smaller than or equal
+     * to it.
+     * </p>
+     */
+    @Override
+    public boolean decompose( DenseMatrix64F A ) {
+        setExpectedMaxSize(A.numRows, A.numCols);
+
+        convertToColumnMajor(A);
+
+        maxAbs = CommonOps.elementMaxAbs(A);
+        // initialize pivot variables
+        setupPivotInfo();
+
+        // go through each column and perform the decomposition
+        for( int j = 0; j < minLength; j++ ) {
+            if( j > 0 )
+                updateNorms(j);
+            swapColumns(j);
+            // if its degenerate stop processing
+            if( !householderPivot(j) )
+                break;
+            updateA(j);
+            rank = j+1;
+        }
+
+        return true;
+    }
+
+    /**
+     * Sets the initial pivot ordering and compute the F-norm squared for each column
+     */
+    private void setupPivotInfo() {
+        for( int col = 0; col < numCols; col++ ) {
+            pivots[col] = col;
+            double c[] = dataQR[col];
+            double norm = 0;
+            for( int row = 0; row < numRows; row++ ) {
+                double element = c[row];
+                norm += element*element;
+            }
+            normsCol[col] = norm;
+        }
+    }
+
+
+    /**
+     * Performs an efficient update of each columns' norm
+     */
+    private void updateNorms( int j ) {
+        boolean foundNegative = false;
+        for( int col = j; col < numCols; col++ ) {
+            double e = dataQR[col][j-1];
+            normsCol[col] -= e*e;
+
+            if( normsCol[col] < 0 ) {
+                foundNegative = true;
+                break;
+            }
+        }
+
+        // if a negative sum has been found then clearly too much precision has been last
+        // and it should recompute the column norms from scratch
+        if( foundNegative ) {
+            for( int col = j; col < numCols; col++ ) {
+                double u[] = dataQR[col];
+                double actual = 0;
+                for( int i=j; i < numRows; i++ ) {
+                    double v = u[i];
+                    actual += v*v;
+                }
+                normsCol[col] = actual;
+            }
+        }
+    }
+
+    /**
+     * Finds the column with the largest normal and makes that the first column
+     *
+     * @param j Current column being inspected
+     */
+    private void swapColumns( int j ) {
+
+        // find the column with the largest norm
+        int largestIndex = j;
+        double largestNorm = normsCol[j];
+        for( int col = j+1; col < numCols; col++ ) {
+            double n = normsCol[col];
+            if( n > largestNorm ) {
+                largestNorm = n;
+                largestIndex = col;
+            }
+        }
+        // swap the columns
+        double []tempC = dataQR[j];
+        dataQR[j] = dataQR[largestIndex];
+        dataQR[largestIndex] = tempC;
+        double tempN = normsCol[j];
+        normsCol[j] = normsCol[largestIndex];
+        normsCol[largestIndex] = tempN;
+        int tempP = pivots[j];
+        pivots[j] = pivots[largestIndex];
+        pivots[largestIndex] = tempP;
+    }
+
+    /**
+     * <p>
+     * Computes the householder vector "u" for the first column of submatrix j. The already computed
+     * norm is used and checks to see if the matrix is singular at this point.
+     * </p>
+     * <p>
+     * Q = I - γuu<sup>T</sup>
+     * </p>
+     * <p>
+     * This function finds the values of 'u' and 'γ'.
+     * </p>
+     *
+     * @param j Which submatrix to work off of.
+     * @return false if it is degenerate
+     */
+    protected boolean householderPivot(int j)
+    {
+        final double u[] = dataQR[j];
+
+        // find the largest value in this column
+        // this is used to normalize the column and mitigate overflow/underflow
+        final double max = QrHelperFunctions_D64.findMax(u, j, numRows - j);
+
+        if( max <= 0 ) {
+            return false;
+        } else {
+            // computes tau and normalizes u by max
+            tau = QrHelperFunctions_D64.computeTauAndDivide(j, numRows, u, max);
+            
+            // divide u by u_0
+            double u_0 = u[j] + tau;
+            QrHelperFunctions_D64.divideElements(j + 1, numRows, u, u_0);
+
+            gamma = u_0/tau;
+            tau *= max;
+
+            u[j] = -tau;
+
+            if( Math.abs(tau) <= singularThreshold ) {
+                return false;
+            }
+        }
+
+        gammas[j] = gamma;
+
+        return true;
+    }
+
+    @Override
+    public int getRank() {
+        return rank;
+    }
+
+    @Override
+    public int[] getPivots() {
+        return pivots;
+    }
+
+    @Override
+    public DenseMatrix64F getPivotMatrix(DenseMatrix64F P) {
+        if( P == null )
+            P = new DenseMatrix64F(numCols,numCols);
+        else if( P.numRows != numCols )
+            throw new IllegalArgumentException("Number of rows must be "+numCols);
+        else if( P.numCols != numCols )
+            throw new IllegalArgumentException("Number of columns must be "+numCols);
+        else {
+            P.zero();
+        }
+
+        for( int i = 0; i < numCols; i++ ) {
+            P.set(pivots[i],i,1);
+        }
+
+        return P;
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/qr/QRDecompositionHouseholderColumn_D64.java b/main/dense64/src/org/ejml/alg/dense/decomposition/qr/QRDecompositionHouseholderColumn_D64.java
new file mode 100644
index 0000000..c9e5dd1
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/qr/QRDecompositionHouseholderColumn_D64.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.qr;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.QRDecomposition;
+import org.ejml.ops.CommonOps;
+
+
+/**
+ * <p>
+ * Householder QR decomposition is rich in operations along the columns of the matrix.  This can be
+ * taken advantage of by solving for the Q matrix in a column major format to reduce the number
+ * of CPU cache misses and the number of copies that are performed.
+ * </p>
+ *
+ * @see QRDecompositionHouseholder_D64
+ *
+ * @author Peter Abeles
+ */
+public class QRDecompositionHouseholderColumn_D64 implements QRDecomposition<DenseMatrix64F> {
+
+    /**
+     * Where the Q and R matrices are stored.  R is stored in the
+     * upper triangular portion and Q on the lower bit.  Lower columns
+     * are where u is stored.  Q_k = (I - gamma_k*u_k*u_k^T).
+     */
+    protected double dataQR[][]; // [ column][ row ]
+
+    // used internally to store temporary data
+    protected double v[];
+
+    // dimension of the decomposed matrices
+    protected int numCols; // this is 'n'
+    protected int numRows; // this is 'm'
+    protected int minLength;
+
+    // the computed gamma for Q_k matrix
+    protected double gammas[];
+    // local variables
+    protected double gamma;
+    protected double tau;
+
+    // did it encounter an error?
+    protected boolean error;
+
+    public void setExpectedMaxSize( int numRows , int numCols ) {
+        this.numCols = numCols;
+        this.numRows = numRows;
+        minLength = Math.min(numCols,numRows);
+        int maxLength = Math.max(numCols,numRows);
+
+        if( dataQR == null || dataQR.length < numCols || dataQR[0].length < numRows ) {
+            dataQR = new double[ numCols ][  numRows ];
+            v = new double[ maxLength ];
+            gammas = new double[ minLength ];
+        }
+
+        if( v.length < maxLength ) {
+            v = new double[ maxLength ];
+        }
+        if( gammas.length < minLength ) {
+            gammas = new double[ minLength ];
+        }
+    }
+
+    /**
+     * Returns the combined QR matrix in a 2D array format that is column major.
+     *
+     * @return The QR matrix in a 2D matrix column major format. [ column ][ row ]
+     */
+    public double[][] getQR() {
+        return dataQR;
+    }
+
+    /**
+     * Computes the Q matrix from the imformation stored in the QR matrix.  This
+     * operation requires about 4(m<sup>2</sup>n-mn<sup>2</sup>+n<sup>3</sup>/3) flops.
+     *
+     * @param Q The orthogonal Q matrix.
+     */
+    @Override
+    public DenseMatrix64F getQ( DenseMatrix64F Q , boolean compact ) {
+        if( compact ) {
+            if( Q == null ) {
+                Q = CommonOps.identity(numRows,minLength);
+            } else {
+                if( Q.numRows != numRows || Q.numCols != minLength ) {
+                    throw new IllegalArgumentException("Unexpected matrix dimension.");
+                } else {
+                    CommonOps.setIdentity(Q);
+                }
+            }
+        } else {
+            if( Q == null ) {
+                Q = CommonOps.identity(numRows);
+            } else {
+                if( Q.numRows != numRows || Q.numCols != numRows ) {
+                    throw new IllegalArgumentException("Unexpected matrix dimension.");
+                } else {
+                    CommonOps.setIdentity(Q);
+                }
+            }
+        }
+
+        for( int j = minLength-1; j >= 0; j-- ) {
+            double u[] = dataQR[j];
+
+            double vv = u[j];
+            u[j] = 1;
+            QrHelperFunctions_D64.rank1UpdateMultR(Q, u, gammas[j], j, j, numRows, v);
+            u[j] = vv;
+        }
+
+        return Q;
+    }
+
+    /**
+     * Returns an upper triangular matrix which is the R in the QR decomposition.  If compact then the input
+     * expected to be size = [min(rows,cols) , numCols] otherwise size = [numRows,numCols].
+     *
+     * @param R Storage for upper triangular matrix.
+     * @param compact If true then a compact matrix is expected.
+     */
+    @Override
+    public DenseMatrix64F getR(DenseMatrix64F R, boolean compact) {
+        if( R == null ) {
+            if( compact ) {
+                R = new DenseMatrix64F(minLength,numCols);
+            } else
+                R = new DenseMatrix64F(numRows,numCols);
+        } else {
+            if( compact ) {
+                if( R.numCols != numCols || R.numRows != minLength )
+                    throw new IllegalArgumentException(
+                            "Unexpected dimensions: found( "+R.numRows+" "+R.numCols+" ) expected( "+minLength+" "+numCols+" )");
+            } else {
+                if( R.numCols != numCols || R.numRows != numRows )
+                    throw new IllegalArgumentException("Unexpected dimensions");
+            }
+
+            for( int i = 0; i < R.numRows; i++ ) {
+                int min = Math.min(i,R.numCols);
+                for( int j = 0; j < min; j++ ) {
+                    R.set(i,j,0);
+                }
+            }
+        }
+
+        for( int j = 0; j < numCols; j++ ) {
+            double colR[] = dataQR[j];
+            int l = Math.min(j,numRows-1);
+            for( int i = 0; i <= l; i++ ) {
+                double val = colR[i];
+                R.set(i,j,val);
+            }
+        }
+
+        return R;
+    }
+
+    /**
+     * <p>
+     * To decompose the matrix 'A' it must have full rank.  'A' is a 'm' by 'n' matrix.
+     * It requires about 2n*m<sup>2</sup>-2m<sup>2</sup>/3 flops.
+     * </p>
+     *
+     * <p>
+     * The matrix provided here can be of different
+     * dimension than the one specified in the constructor.  It just has to be smaller than or equal
+     * to it.
+     * </p>
+     */
+    @Override
+    public boolean decompose( DenseMatrix64F A ) {
+        setExpectedMaxSize(A.numRows, A.numCols);
+
+        convertToColumnMajor(A);
+
+        error = false;
+
+        for( int j = 0; j < minLength; j++ ) {
+            householder(j);
+            updateA(j);
+        }
+
+        return !error;
+    }
+
+    @Override
+    public boolean inputModified() {
+        return false;
+    }
+
+    /**
+     * Converts the standard row-major matrix into a column-major vector
+     * that is advantageous for this problem.
+     *
+     * @param A original matrix that is to be decomposed.
+     */
+    protected void convertToColumnMajor(DenseMatrix64F A) {
+        for( int x = 0; x < numCols; x++ ) {
+            double colQ[] = dataQR[x];
+            for( int y = 0; y < numRows; y++ ) {
+                colQ[y] = A.data[y*numCols+x];
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Computes the householder vector "u" for the first column of submatrix j.  Note this is
+     * a specialized householder for this problem.  There is some protection against
+     * overfloaw and underflow.
+     * </p>
+     * <p>
+     * Q = I - γuu<sup>T</sup>
+     * </p>
+     * <p>
+     * This function finds the values of 'u' and 'γ'.
+     * </p>
+     *
+     * @param j Which submatrix to work off of.
+     */
+    protected void householder( int j )
+    {
+        final double u[] = dataQR[j];
+
+        // find the largest value in this column
+        // this is used to normalize the column and mitigate overflow/underflow
+        final double max = QrHelperFunctions_D64.findMax(u, j, numRows - j);
+
+        if( max == 0.0 ) {
+            gamma = 0;
+            error = true;
+        } else {
+            // computes tau and normalizes u by max
+            tau = QrHelperFunctions_D64.computeTauAndDivide(j, numRows, u, max);
+
+            // divide u by u_0
+            double u_0 = u[j] + tau;
+            QrHelperFunctions_D64.divideElements(j + 1, numRows, u, u_0);
+
+            gamma = u_0/tau;
+            tau *= max;
+
+            u[j] = -tau;
+        }
+
+        gammas[j] = gamma;
+    }
+
+    /**
+     * <p>
+     * Takes the results from the householder computation and updates the 'A' matrix.<br>
+     * <br>
+     * A = (I - γ*u*u<sup>T</sup>)A
+     * </p>
+     *
+     * @param w The submatrix.
+     */
+    protected void updateA( int w )
+    {
+        final double u[] = dataQR[w];
+
+        for( int j = w+1; j < numCols; j++ ) {
+
+            final double colQ[] = dataQR[j];
+            double val = colQ[w];
+
+            for( int k = w+1; k < numRows; k++ ) {
+                val += u[k]*colQ[k];
+            }
+            val *= gamma;
+
+            colQ[w] -= val;
+            for( int i = w+1; i < numRows; i++ ) {
+                colQ[i] -= u[i]*val;
+            }
+        }
+    }
+
+    public double[] getGammas() {
+        return gammas;
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/qr/QRDecompositionHouseholderTran_D64.java b/main/dense64/src/org/ejml/alg/dense/decomposition/qr/QRDecompositionHouseholderTran_D64.java
new file mode 100644
index 0000000..818c08d
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/qr/QRDecompositionHouseholderTran_D64.java
@@ -0,0 +1,345 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.qr;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.QRDecomposition;
+import org.ejml.ops.CommonOps;
+
+
+/**
+ * <p>
+ * Householder QR decomposition is rich in operations along the columns of the matrix.  This can be
+ * taken advantage of by solving for the Q matrix in a column major format to reduce the number
+ * of CPU cache misses and the number of copies that are performed.
+ * </p>
+ *
+ * @see QRDecompositionHouseholder_D64
+ *
+ * @author Peter Abeles
+ */
+// TODO remove QR Col and replace with this one?
+// -- On small matrices col seems to be about 10% faster
+public class QRDecompositionHouseholderTran_D64 implements QRDecomposition<DenseMatrix64F> {
+
+    /**
+     * Where the Q and R matrices are stored.  For speed reasons
+     * this is transposed
+     */
+    protected DenseMatrix64F QR;
+
+    // used internally to store temporary data
+    protected double v[];
+
+    // dimension of the decomposed matrices
+    protected int numCols; // this is 'n'
+    protected int numRows; // this is 'm'
+    protected int minLength;
+
+    // the computed gamma for Q_k matrix
+    protected double gammas[];
+    // local variables
+    protected double gamma;
+    protected double tau;
+
+    // did it encounter an error?
+    protected boolean error;
+
+    public void setExpectedMaxSize( int numRows , int numCols ) {
+        this.numCols = numCols;
+        this.numRows = numRows;
+        minLength = Math.min(numCols,numRows);
+        int maxLength = Math.max(numCols,numRows);
+
+        if( QR == null ) {
+            QR = new DenseMatrix64F(numCols,numRows);
+            v = new double[ maxLength ];
+            gammas = new double[ minLength ];
+        } else {
+            QR.reshape(numCols,numRows,false);
+        }
+
+        if( v.length < maxLength ) {
+            v = new double[ maxLength ];
+        }
+        if( gammas.length < minLength ) {
+            gammas = new double[ minLength ];
+        }
+    }
+
+    /**
+     * Inner matrix that stores the decomposition
+     */
+    public DenseMatrix64F getQR() {
+        return QR;
+    }
+
+    /**
+     * Computes the Q matrix from the information stored in the QR matrix.  This
+     * operation requires about 4(m<sup2</sup>n-mn<sup>2</sup>+n<sup>3</sup>/3) flops.
+     *
+     * @param Q The orthogonal Q matrix.
+     */
+    @Override
+    public DenseMatrix64F getQ( DenseMatrix64F Q , boolean compact ) {
+        if( compact ) {
+            if( Q == null ) {
+                Q = CommonOps.identity(numRows,minLength);
+            } else {
+                if( Q.numRows != numRows || Q.numCols != minLength ) {
+                    throw new IllegalArgumentException("Unexpected matrix dimension.");
+                } else {
+                    CommonOps.setIdentity(Q);
+                }
+            }
+        } else {
+            if( Q == null ) {
+                Q = CommonOps.identity(numRows);
+            } else {
+                if( Q.numRows != numRows || Q.numCols != numRows ) {
+                    throw new IllegalArgumentException("Unexpected matrix dimension.");
+                } else {
+                    CommonOps.setIdentity(Q);
+                }
+            }
+        }
+
+        // Unlike applyQ() this takes advantage of zeros in the identity matrix
+        // by not multiplying across all rows.
+        for( int j = minLength-1; j >= 0; j-- ) {
+            int diagIndex = j*numRows+j;
+            double before = QR.data[diagIndex];
+            QR.data[diagIndex] = 1;
+            QrHelperFunctions_D64.rank1UpdateMultR(Q, QR.data, j * numRows, gammas[j], j, j, numRows, v);
+            QR.data[diagIndex] = before;
+        }
+
+        return Q;
+    }
+
+    /**
+     * A = Q*A
+     *
+     * @param A Matrix that is being multiplied by Q.  Is modified.
+     */
+    public void applyQ( DenseMatrix64F A ) {
+        if( A.numRows != numRows )
+            throw new IllegalArgumentException("A must have at least "+numRows+" rows.");
+
+        for( int j = minLength-1; j >= 0; j-- ) {
+            int diagIndex = j*numRows+j;
+            double before = QR.data[diagIndex];
+            QR.data[diagIndex] = 1;
+            QrHelperFunctions_D64.rank1UpdateMultR(A, QR.data, j * numRows, gammas[j], 0, j, numRows, v);
+            QR.data[diagIndex] = before;
+        }
+    }
+
+    /**
+     * A = Q<sup>T</sup>*A
+     *
+     * @param A Matrix that is being multiplied by Q<sup>T</sup>.  Is modified.
+     */
+    public void applyTranQ( DenseMatrix64F A ) {
+        for( int j = 0; j < minLength; j++ ) {
+            int diagIndex = j*numRows+j;
+            double before = QR.data[diagIndex];
+            QR.data[diagIndex] = 1;
+            QrHelperFunctions_D64.rank1UpdateMultR(A, QR.data, j * numRows, gammas[j], 0, j, numRows, v);
+            QR.data[diagIndex] = before;
+        }
+    }
+
+    /**
+     * Returns an upper triangular matrix which is the R in the QR decomposition.
+     *
+     * @param R An upper triangular matrix.
+     * @param compact
+     */
+    @Override
+    public DenseMatrix64F getR(DenseMatrix64F R, boolean compact) {
+        if( R == null ) {
+            if( compact ) {
+                R = new DenseMatrix64F(minLength,numCols);
+            } else
+                R = new DenseMatrix64F(numRows,numCols);
+        } else {
+            if( compact ) {
+                if( R.numCols != numCols || R.numRows != minLength )
+                    throw new IllegalArgumentException("Unexpected dimensions");
+            } else {
+                if( R.numCols != numCols || R.numRows != numRows )
+                    throw new IllegalArgumentException("Unexpected dimensions");
+            }
+
+            for( int i = 0; i < R.numRows; i++ ) {
+                int min = Math.min(i,R.numCols);
+                for( int j = 0; j < min; j++ ) {
+                    R.unsafe_set(i,j,0);
+                }
+            }
+        }
+
+        for( int i = 0; i < R.numRows; i++ ) {
+            for( int j = i; j < R.numCols; j++ ) {
+                R.unsafe_set(i,j,QR.unsafe_get(j,i));
+            }
+        }
+
+
+        return R;
+    }
+
+    /**
+     * <p>
+     * To decompose the matrix 'A' it must have full rank.  'A' is a 'm' by 'n' matrix.
+     * It requires about 2n*m<sup>2</sup>-2m<sup>2</sup>/3 flops.
+     * </p>
+     *
+     * <p>
+     * The matrix provided here can be of different
+     * dimension than the one specified in the constructor.  It just has to be smaller than or equal
+     * to it.
+     * </p>
+     */
+    @Override
+    public boolean decompose( DenseMatrix64F A ) {
+        setExpectedMaxSize(A.numRows, A.numCols);
+
+        CommonOps.transpose(A,QR);
+
+        error = false;
+
+        for( int j = 0; j < minLength; j++ ) {
+            householder(j);
+            updateA(j);
+        }
+
+        return !error;
+    }
+
+    @Override
+    public boolean inputModified() {
+        return false;
+    }
+
+    /**
+     * <p>
+     * Computes the householder vector "u" for the first column of submatrix j.  Note this is
+     * a specialized householder for this problem.  There is some protection against
+     * overflow and underflow.
+     * </p>
+     * <p>
+     * Q = I - γuu<sup>T</sup>
+     * </p>
+     * <p>
+     * This function finds the values of 'u' and 'γ'.
+     * </p>
+     *
+     * @param j Which submatrix to work off of.
+     */
+    protected void householder( final int j )
+    {
+        int startQR = j*numRows;
+        int endQR = startQR+numRows;
+        startQR += j;
+
+        final double max = QrHelperFunctions_D64.findMax(QR.data, startQR, numRows - j);
+
+        if( max == 0.0 ) {
+            gamma = 0;
+            error = true;
+        } else {
+            // computes tau and normalizes u by max
+            tau = QrHelperFunctions_D64.computeTauAndDivide(startQR, endQR, QR.data, max);
+
+            // divide u by u_0
+            double u_0 = QR.data[startQR] + tau;
+            QrHelperFunctions_D64.divideElements(startQR + 1, endQR, QR.data, u_0);
+
+            gamma = u_0/tau;
+            tau *= max;
+
+            QR.data[startQR] = -tau;
+        }
+
+        gammas[j] = gamma;
+    }
+
+    /**
+     * <p>
+     * Takes the results from the householder computation and updates the 'A' matrix.<br>
+     * <br>
+     * A = (I - γ*u*u<sup>T</sup>)A
+     * </p>
+     *
+     * @param w The submatrix.
+     */
+    protected void updateA( final int w )
+    {
+//        int rowW = w*numRows;
+//        int rowJ = rowW + numRows;
+//
+//        for( int j = w+1; j < numCols; j++ , rowJ += numRows) {
+//            double val = QR.data[rowJ + w];
+//
+//            // val = gamma*u^T * A
+//            for( int k = w+1; k < numRows; k++ ) {
+//                val += QR.data[rowW + k]*QR.data[rowJ + k];
+//            }
+//            val *= gamma;
+//
+//            // A - val*u
+//            QR.data[rowJ + w] -= val;
+//            for( int i = w+1; i < numRows; i++ ) {
+//                QR.data[rowJ + i] -= QR.data[rowW + i]*val;
+//            }
+//        }
+
+        final double data[] = QR.data;
+        final int rowW = w*numRows + w + 1;
+        int rowJ = rowW + numRows;
+        final int rowJEnd = rowJ + (numCols-w-1)*numRows;
+        final int indexWEnd = rowW + numRows - w - 1;
+
+        for( ; rowJEnd != rowJ; rowJ += numRows) {
+            // assume the first element in u is 1
+            double val = data[rowJ - 1];
+
+            int indexW = rowW;
+            int indexJ = rowJ;
+
+            while( indexW != indexWEnd ) {
+                val += data[indexW++]*data[indexJ++];
+            }
+            val *= gamma;
+
+            data[rowJ - 1] -= val;
+            indexW = rowW;
+            indexJ = rowJ;
+            while( indexW != indexWEnd ) {
+                data[indexJ++] -= data[indexW++]*val;
+            }
+        }
+    }
+
+    public double[] getGammas() {
+        return gammas;
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/qr/QRDecompositionHouseholder_D64.java b/main/dense64/src/org/ejml/alg/dense/decomposition/qr/QRDecompositionHouseholder_D64.java
new file mode 100644
index 0000000..0042562
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/qr/QRDecompositionHouseholder_D64.java
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.qr;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.QRDecomposition;
+import org.ejml.ops.CommonOps;
+
+
+/**
+ * <p>
+ * This variation of QR decomposition uses reflections to compute the Q matrix.
+ * Each reflection uses a householder operations, hence its name.  To provide a meaningful solution
+ * the original matrix must have full rank.  This is intended for processing of small to medium matrices.
+ * </p>
+ * <p>
+ * Both Q and R are stored in the same m by n matrix.  Q is not stored directly, instead the u from
+ * Q<sub>k</sub>=(I-γ*u*u<sup>T</sup>) is stored.  Decomposition requires about 2n*m<sup>2</sup>-2m<sup>2</sup>/3 flops.
+ * </p>
+ *
+ * <p>
+ * See the QR reflections algorithm described in:<br>
+ * David S. Watkins, "Fundamentals of Matrix Computations" 2nd Edition, 2002
+ * </p>
+ *
+ * <p>
+ * For the most part this is a straight forward implementation.  To improve performance on large matrices a column is writen to an array and the order
+ * of some of the loops has been changed.  This will degrade performance noticeably on small matrices.  Since
+ * it is unlikely that the QR decomposition would be a bottle neck when small matrices are involved only
+ * one implementation is provided.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class QRDecompositionHouseholder_D64 implements QRDecomposition<DenseMatrix64F> {
+
+    /**
+     * Where the Q and R matrices are stored.  R is stored in the
+     * upper triangular portion and Q on the lower bit.  Lower columns
+     * are where u is stored.  Q_k = (I - gamma_k*u_k*u_k^T).
+     */
+    protected DenseMatrix64F QR;
+
+    // used internally to store temporary data
+    protected double u[],v[];
+
+    // dimension of the decomposed matrices
+    protected int numCols; // this is 'n'
+    protected int numRows; // this is 'm'
+    protected int minLength;
+
+    protected double dataQR[];
+
+    // the computed gamma for Q_k matrix
+    protected double gammas[];
+    // local variables
+    protected double gamma;
+    protected double tau;
+
+    // did it encounter an error?
+    protected boolean error;
+
+    public void setExpectedMaxSize( int numRows , int numCols ) {
+        error = false;
+
+        this.numCols = numCols;
+        this.numRows = numRows;
+        minLength = Math.min(numRows,numCols);
+        int maxLength = Math.max(numRows,numCols);
+
+        if( QR == null ) {
+            QR = new DenseMatrix64F(numRows,numCols);
+            u = new double[ maxLength ];
+            v = new double[ maxLength ];
+            gammas = new double[ minLength ];
+        } else {
+            QR.reshape(numRows,numCols,false);
+        }
+
+        dataQR = QR.data;
+
+        if( u.length < maxLength ) {
+            u = new double[ maxLength ];
+            v = new double[ maxLength ];
+        }
+
+        if( gammas.length < minLength ) {
+            gammas = new double[ minLength ];
+        }
+    }
+
+    /**
+     * Returns a single matrix which contains the combined values of Q and R.  This
+     * is possible since Q is symmetric and R is upper triangular.
+     *
+     * @return The combined Q R matrix.
+     */
+    public DenseMatrix64F getQR() {
+        return QR;
+    }
+
+    /**
+     * Computes the Q matrix from the imformation stored in the QR matrix.  This
+     * operation requires about 4(m<sup>2</sup>n-mn<sup>2</sup>+n<sup>3</sup>/3) flops.
+     *
+     * @param Q The orthogonal Q matrix.
+     */
+    @Override
+    public DenseMatrix64F getQ( DenseMatrix64F Q , boolean compact ) {
+        if( compact ) {
+            if( Q == null ) {
+                Q = CommonOps.identity(numRows,minLength);
+            } else {
+                if( Q.numRows != numRows || Q.numCols != minLength ) {
+                    throw new IllegalArgumentException("Unexpected matrix dimension.");
+                } else {
+                    CommonOps.setIdentity(Q);
+                }
+            }
+        } else {
+            if( Q == null ) {
+                Q = CommonOps.identity(numRows);
+            } else {
+                if( Q.numRows != numRows || Q.numCols != numRows ) {
+                    throw new IllegalArgumentException("Unexpected matrix dimension.");
+                } else {
+                    CommonOps.setIdentity(Q);
+                }
+            }
+        }
+
+        for( int j = minLength-1; j >= 0; j-- ) {
+            u[j] = 1;
+            for( int i = j+1; i < numRows; i++ ) {
+                u[i] = QR.get(i,j);
+            }
+            QrHelperFunctions_D64.rank1UpdateMultR(Q, u, gammas[j], j, j, numRows, v);
+        }
+
+        return Q;
+    }
+
+    /**
+     * Returns an upper triangular matrix which is the R in the QR decomposition.
+     *
+     * @param R An upper triangular matrix.
+     * @param compact
+     */
+    @Override
+    public DenseMatrix64F getR(DenseMatrix64F R, boolean compact) {
+        if( R == null ) {
+            if( compact ) {
+                R = new DenseMatrix64F(minLength,numCols);
+            } else
+                R = new DenseMatrix64F(numRows,numCols);
+        } else {
+            if( compact ) {
+                if( R.numCols != numCols || R.numRows != minLength )
+                    throw new IllegalArgumentException("Unexpected dimensions");
+            } else {
+                if( R.numCols != numCols || R.numRows != numRows )
+                    throw new IllegalArgumentException("Unexpected dimensions");
+            }
+
+            for( int i = 0; i < R.numRows; i++ ) {
+                int min = Math.min(i,R.numCols);
+                for( int j = 0; j < min; j++ ) {
+                    R.set(i,j,0);
+                }
+            }
+        }
+
+        for( int i = 0; i < minLength; i++ ) {
+            for( int j = i; j < numCols; j++ ) {
+                double val = QR.get(i,j);
+                R.set(i,j,val);
+            }
+        }
+
+        return R;
+    }
+
+    /**
+     * <p>
+     * In order to decompose the matrix 'A' it must have full rank.  'A' is a 'm' by 'n' matrix.
+     * It requires about 2n*m<sup>2</sup>-2m<sup>2</sup>/3 flops.
+     * </p>
+     *
+     * <p>
+     * The matrix provided here can be of different
+     * dimension than the one specified in the constructor.  It just has to be smaller than or equal
+     * to it.
+     * </p>
+     */
+    @Override
+    public boolean decompose( DenseMatrix64F A ) {
+        commonSetup(A);
+
+        for( int j = 0; j < minLength; j++ ) {
+            householder(j);
+            updateA(j);
+        }
+
+        return !error;
+    }
+
+    @Override
+    public boolean inputModified() {
+        return false;
+    }
+
+    /**
+     * <p>
+     * Computes the householder vector "u" for the first column of submatrix j.  Note this is
+     * a specialized householder for this problem.  There is some protection against
+     * overflow and underflow.
+     * </p>
+     * <p>
+     * Q = I - γuu<sup>T</sup>
+     * </p>
+     * <p>
+     * This function finds the values of 'u' and 'γ'.
+     * </p>
+     *
+     * @param j Which submatrix to work off of.
+     */
+    protected void householder( int j )
+    {
+        // find the element with the largest absolute value in the column and make a copy
+        int index = j+j*numCols;
+        double max = 0;
+        for( int i = j; i < numRows; i++ ) {
+
+            double d = u[i] = dataQR[index];
+
+            // absolute value of d
+            if( d < 0 ) d = -d;
+            if( max < d ) {
+                max = d;
+            }
+            index += numCols;
+        }
+
+        if( max == 0.0 ) {
+            gamma = 0;
+            error = true;
+        } else {
+            // compute the norm2 of the matrix, with each element
+            // normalized by the max value to avoid overflow problems
+            tau = 0;
+            for( int i = j; i < numRows; i++ ) {
+                u[i] /= max;
+                double d = u[i];
+                tau += d*d;
+            }
+            tau = Math.sqrt(tau);
+
+            if( u[j] < 0 )
+                tau = -tau;
+
+            double u_0 = u[j] + tau;
+            gamma = u_0/tau;
+            for( int i = j+1; i < numRows; i++ ) {
+                u[i] /= u_0;
+            }
+            u[j] = 1;
+            tau *= max;
+        }
+
+        gammas[j] = gamma;
+    }
+
+    /**
+     * <p>
+     * Takes the results from the householder computation and updates the 'A' matrix.<br>
+     * <br>
+     * A = (I - γ*u*u<sup>T</sup>)A
+     * </p>
+     *
+     * @param w The submatrix.
+     */
+    protected void updateA( int w )
+    {
+        // much of the code below is equivalent to the rank1Update function
+        // however, since τ has already been computed there is no need to
+        // recompute it, saving a few multiplication operations
+//        for( int i = w+1; i < numCols; i++ ) {
+//            double val = 0;
+//
+//            for( int k = w; k < numRows; k++ ) {
+//                val += u[k]*dataQR[k*numCols +i];
+//            }
+//            v[i] = gamma*val;
+//        }
+
+        // This is functionally the same as the above code but the order has been changed
+        // to avoid jumping the cpu cache
+        for( int i = w+1; i < numCols; i++ ) {
+            v[i] = u[w]*dataQR[w*numCols +i];
+        }
+
+        for( int k = w+1; k < numRows; k++ ) {
+            int indexQR = k*numCols+w+1;
+            for( int i = w+1; i < numCols; i++ ) {
+//                v[i] += u[k]*dataQR[k*numCols +i];
+                v[i] += u[k]*dataQR[indexQR++];
+            }
+        }
+
+        for( int i = w+1; i < numCols; i++ ) {
+            v[i] *= gamma;
+        }
+
+        // end of reordered code
+
+        for( int i = w; i < numRows; i++ ) {
+            double valU = u[i];
+
+            int indexQR = i*numCols+w+1;
+            for( int j = w+1; j < numCols; j++ ) {
+//                dataQR[i*numCols+j] -= valU*v[j];
+                dataQR[indexQR++] -= valU*v[j];
+            }
+        }
+
+        if( w < numCols ) {
+            dataQR[w+w*numCols] = -tau;
+        }
+
+        // save the Q matrix in the lower portion of QR
+        for( int i = w+1; i < numRows; i++ ) {
+            dataQR[w+i*numCols] = u[i];
+        }
+    }
+
+    /**
+     * This function performs sanity check on the input for decompose and sets up the QR matrix.
+     *
+     * @param A
+     */
+    protected void commonSetup(DenseMatrix64F A) {
+        setExpectedMaxSize(A.numRows,A.numCols);
+
+        QR.set(A);
+    }
+
+    public double[] getGammas() {
+        return gammas;
+    }
+
+}
\ No newline at end of file
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/qr/QRDecomposition_B64_to_D64.java b/main/dense64/src/org/ejml/alg/dense/decomposition/qr/QRDecomposition_B64_to_D64.java
new file mode 100644
index 0000000..d3868dc
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/qr/QRDecomposition_B64_to_D64.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.qr;
+
+import org.ejml.EjmlParameters;
+import org.ejml.alg.block.BlockMatrixOps;
+import org.ejml.alg.block.decomposition.qr.QRDecompositionHouseholder_B64;
+import org.ejml.alg.dense.decomposition.BaseDecomposition_B64_to_D64;
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.QRDecomposition;
+import org.ejml.ops.CommonOps;
+
+
+/**
+ * Wrapper that allows {@link QRDecomposition}(BlockMatrix64F) to be used
+ * as a {@link QRDecomposition}(DenseMatrix64F).
+ *
+ * @author Peter Abeles
+ */
+public class QRDecomposition_B64_to_D64
+        extends BaseDecomposition_B64_to_D64 implements QRDecomposition<DenseMatrix64F>  {
+
+    public QRDecomposition_B64_to_D64() {
+        super(new QRDecompositionHouseholder_B64(), EjmlParameters.BLOCK_WIDTH);
+    }
+
+    @Override
+    public DenseMatrix64F getQ(DenseMatrix64F Q, boolean compact) {
+
+        int minLength = Math.min(Ablock.numRows,Ablock.numCols);
+        if( Q == null  ) {
+            if( compact ) {
+                Q = new DenseMatrix64F(Ablock.numRows,minLength);
+                CommonOps.setIdentity(Q);
+            } else {
+                Q = new DenseMatrix64F(Ablock.numRows,Ablock.numRows);
+                CommonOps.setIdentity(Q);
+            }
+        }
+
+        BlockMatrix64F Qblock = new BlockMatrix64F();
+        Qblock.numRows =  Q.numRows;
+        Qblock.numCols =  Q.numCols;
+        Qblock.blockLength = blockLength;
+        Qblock.data = Q.data;
+
+        ((QRDecompositionHouseholder_B64)alg).getQ(Qblock,compact);
+
+        convertBlockToRow(Q.numRows,Q.numCols,Ablock.blockLength,Q.data);
+
+        return Q;
+    }
+
+    @Override
+    public DenseMatrix64F getR(DenseMatrix64F R, boolean compact) {
+        BlockMatrix64F Rblock;
+
+        Rblock = ((QRDecompositionHouseholder_B64)alg).getR(null,compact);
+
+        if( R == null ) {
+            R = new DenseMatrix64F(Rblock.numRows,Rblock.numCols);
+        }
+        BlockMatrixOps.convert(Rblock,R);
+
+        return R;
+    }
+
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/qr/QrHelperFunctions_D64.java b/main/dense64/src/org/ejml/alg/dense/decomposition/qr/QrHelperFunctions_D64.java
new file mode 100644
index 0000000..6d2e95b
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/qr/QrHelperFunctions_D64.java
@@ -0,0 +1,334 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.qr;
+
+import org.ejml.data.DenseMatrix64F;
+
+
+/**
+ * <p>
+ * Contains different functions that are useful for computing the QR decomposition of a matrix.
+ * </p>
+ *
+ * <p>
+ * Two different families of functions are provided for help in computing reflectors.  Internally
+ * both of these functions switch between normalization by division or multiplication.  Multiplication
+ * is most often significantly faster than division (2 or 3 times) but produces less accurate results
+ * on very small numbers.  It checks to see if round off error is significant and decides which
+ * one it should do.
+ * </p>
+ *
+ * <p>
+ * Tests were done using the stability benchmark in jmatbench and there doesn't seem to be
+ * any advantage to always dividing by the max instead of checking and deciding.  The most
+ * noticeable difference between the two methods is with very small numbers.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class QrHelperFunctions_D64 {
+
+    public static double findMax( double[] u, int startU , int length ) {
+        double max = -1;
+
+        int index = startU;
+        int stopIndex = startU + length;
+        for( ; index < stopIndex; index++ ) {
+            double val = u[index];
+            val = (val < 0.0D) ? -val : val;
+            if( val > max )
+                max = val;
+        }
+
+        return max;
+    }
+
+    public static void divideElements(final int j, final int numRows ,
+                                      final double[] u, final double u_0 ) {
+//        double div_u = 1.0/u_0;
+//
+//        if( Double.isInfinite(div_u)) {
+            for( int i = j; i < numRows; i++ ) {
+                u[i] /= u_0;
+            }
+//        } else {
+//            for( int i = j; i < numRows; i++ ) {
+//                u[i] *= div_u;
+//            }
+//        }
+    }
+
+    public static void divideElements(int j, int numRows , double[] u, int startU , double u_0 ) {
+//        double div_u = 1.0/u_0;
+//
+//        if( Double.isInfinite(div_u)) {
+            for( int i = j; i < numRows; i++ ) {
+                u[i+startU] /= u_0;
+            }
+//        } else {
+//            for( int i = j; i < numRows; i++ ) {
+//                u[i+startU] *= div_u;
+//            }
+//        }
+    }
+
+    public static void divideElements_Brow(int j, int numRows , double[] u,
+                                             double b[] , int startB ,
+                                             double u_0 ) {
+//        double div_u = 1.0/u_0;
+//
+//        if( Double.isInfinite(div_u)) {
+            for( int i = j; i < numRows; i++ ) {
+                u[i] = b[i+startB] /= u_0;
+            }
+//        } else {
+//            for( int i = j; i < numRows; i++ ) {
+//                u[i] = b[i+startB] *= div_u;
+//            }
+//        }
+    }
+
+    public static void divideElements_Bcol(int j, int numRows , int numCols ,
+                                             double[] u,
+                                             double b[] , int startB ,
+                                             double u_0 ) {
+//        double div_u = 1.0/u_0;
+//
+//        if( Double.isInfinite(div_u)) {
+            int indexB = j*numCols+startB;
+            for( int i = j; i < numRows; i++ , indexB += numCols ) {
+                b[indexB] = u[i] /= u_0;
+            }
+//        } else {
+//            int indexB = j*numCols+startB;
+//            for( int i = j; i < numRows; i++ , indexB += numCols ) {
+//                b[indexB] = u[i] *= div_u;
+//            }
+//        }
+    }
+
+    public static double computeTauAndDivide(int j, int numRows , double[] u, int startU , double max) {
+        // compute the norm2 of the matrix, with each element
+        // normalized by the max value to avoid overflow problems
+        double tau = 0;
+//        double div_max = 1.0/max;
+//        if( Double.isInfinite(div_max)) {
+            // more accurate
+            for( int i = j; i < numRows; i++ ) {
+                double d = u[startU+i] /= max;
+                tau += d*d;
+            }
+//        } else {
+//            // faster
+//            for( int i = j; i < numRows; i++ ) {
+//                double d = u[startU+i] *= div_max;
+//                tau += d*d;
+//            }
+//        }
+        tau = Math.sqrt(tau);
+
+        if( u[startU+j] < 0 )
+            tau = -tau;
+
+        return tau;
+    }
+
+    /**
+     * Normalizes elements in 'u' by dividing by max and computes the norm2 of the normalized
+     * array u.  Adjust the sign of the returned value depending on the size of the first
+     * element in 'u'. Normalization is done to avoid overflow.
+     *
+     * <pre>
+     * for i=j:numRows
+     *   u[i] = u[i] / max
+     *   tau = tau + u[i]*u[i]
+     * end
+     * tau = sqrt(tau)
+     * if( u[j] < 0 )
+     *    tau = -tau;
+     * </pre>
+     *
+     * @param j Element in 'u' that it starts at.
+     * @param numRows Element in 'u' that it stops at.
+     * @param u Array
+     * @param max Max value in 'u' that is used to normalize it.
+     * @return norm2 of 'u'
+     */
+    public static double computeTauAndDivide(final int j, final int numRows ,
+                                             final double[] u , final double max) {
+        double tau = 0;
+//        double div_max = 1.0/max;
+//        if( Double.isInfinite(div_max)) {
+            for( int i = j; i < numRows; i++ ) {
+                double d = u[i] /= max;
+                tau += d*d;
+            }
+//        } else {
+//            for( int i = j; i < numRows; i++ ) {
+//                double d = u[i] *= div_max;
+//                tau += d*d;
+//            }
+//        }
+        tau = Math.sqrt(tau);
+
+        if( u[j] < 0 )
+            tau = -tau;
+
+        return tau;
+    }
+
+    /**
+     * <p>
+     * Performs a rank-1 update operation on the submatrix specified by w with the multiply on the right.<br>
+     * <br>
+     * A = (I - γ*u*u<sup>T</sup>)*A<br>
+     * </p>
+     * <p>
+     * The order that matrix multiplies are performed has been carefully selected
+     * to minimize the number of operations.
+     * </p>
+     *
+     * <p>
+     * Before this can become a truly generic operation the submatrix specification needs
+     * to be made more generic.
+     * </p>
+     */
+    public static void rank1UpdateMultR( DenseMatrix64F A , double u[] , double gamma ,
+                                         int colA0,
+                                         int w0, int w1 ,
+                                         double _temp[] )
+    {
+//        for( int i = colA0; i < A.numCols; i++ ) {
+//            double val = 0;
+//
+//            for( int k = w0; k < w1; k++ ) {
+//                val += u[k]*A.data[k*A.numCols +i];
+//            }
+//            _temp[i] = gamma*val;
+//        }
+
+        // reordered to reduce cpu cache issues
+        for( int i = colA0; i < A.numCols; i++ ) {
+            _temp[i] = u[w0]*A.data[w0 *A.numCols +i];
+        }
+
+        for( int k = w0+1; k < w1; k++ ) {
+            int indexA = k*A.numCols + colA0;
+            double valU = u[k];
+            for( int i = colA0; i < A.numCols; i++ ) {
+                _temp[i] += valU*A.data[indexA++];
+            }
+        }
+        for( int i = colA0; i < A.numCols; i++ ) {
+            _temp[i] *= gamma;
+        }
+
+        // end of reorder
+
+        for( int i = w0; i < w1; i++ ) {
+            double valU = u[i];
+
+            int indexA = i*A.numCols + colA0;
+            for( int j = colA0; j < A.numCols; j++ ) {
+                A.data[indexA++] -= valU*_temp[j];
+            }
+        }
+    }
+
+    public static void rank1UpdateMultR(DenseMatrix64F A,
+                                        double u[], int offsetU,
+                                        double gamma,
+                                        int colA0,
+                                        int w0, int w1,
+                                        double _temp[])
+    {
+//        for( int i = colA0; i < A.numCols; i++ ) {
+//            double val = 0;
+//
+//            for( int k = w0; k < w1; k++ ) {
+//                val += u[k+offsetU]*A.data[k*A.numCols +i];
+//            }
+//            _temp[i] = gamma*val;
+//        }
+
+        // reordered to reduce cpu cache issues
+        for( int i = colA0; i < A.numCols; i++ ) {
+            _temp[i] = u[w0+offsetU]*A.data[w0 *A.numCols +i];
+        }
+
+        for( int k = w0+1; k < w1; k++ ) {
+            int indexA = k*A.numCols + colA0;
+            double valU = u[k+offsetU];
+            for( int i = colA0; i < A.numCols; i++ ) {
+                _temp[i] += valU*A.data[indexA++];
+            }
+        }
+        for( int i = colA0; i < A.numCols; i++ ) {
+            _temp[i] *= gamma;
+        }
+
+        // end of reorder
+
+        for( int i = w0; i < w1; i++ ) {
+            double valU = u[i+offsetU];
+
+            int indexA = i*A.numCols + colA0;
+            for( int j = colA0; j < A.numCols; j++ ) {
+                A.data[indexA++] -= valU*_temp[j];
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs a rank-1 update operation on the submatrix specified by w with the multiply on the left.<br>
+     * <br>
+     * A = A(I - γ*u*u<sup>T</sup>)<br>
+     * </p>
+     * <p>
+     * The order that matrix multiplies are performed has been carefully selected
+     * to minimize the number of operations.
+     * </p>
+     *
+     * <p>
+     * Before this can become a truly generic operation the submatrix specification needs
+     * to be made more generic.
+     * </p>
+     */
+    public static void rank1UpdateMultL( DenseMatrix64F A , double u[] ,
+                                         double gamma ,
+                                         int colA0,
+                                         int w0 , int w1 )
+    {
+        for( int i = colA0; i < A.numRows; i++ ) {
+            int startIndex = i*A.numCols+w0;
+            double sum = 0;
+            int rowIndex = startIndex;
+            for( int j = w0; j < w1; j++ ) {
+                sum += A.data[rowIndex++]*u[j];
+            }
+            sum = -gamma*sum;
+
+            rowIndex = startIndex;
+            for( int j = w0; j < w1; j++ ) {
+                A.data[rowIndex++] += sum*u[j];
+            }
+        }
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/qr/QrUpdate.java b/main/dense64/src/org/ejml/alg/dense/decomposition/qr/QrUpdate.java
new file mode 100644
index 0000000..f9b84eb
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/qr/QrUpdate.java
@@ -0,0 +1,426 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.qr;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+
+
+/**
+ * <p>
+ * The effects of adding and removing rows from the A matrix in a QR decomposition can
+ * be computed much faster than simply recomputing the whole decomposition.  There are many real
+ * world situations where this is useful.  For example, when computing a rolling solution to
+ * the most recent N measurements.
+ * </p>
+ *
+ * <p>
+ * Definitions: A ∈ ℜ <sup>m × n</sup>, m ≥ n, rank(A) = n and that A = QR, where
+ * Q ∈ ℜ <sup>m × m</sup> is orthogonal, and R ∈ ℜ <sup>m × n</sup> is
+ * upper triangular.
+ * </p>
+ * 
+ * <p>
+ * ** IMPORTANT USAGE NOTE ** If auto grow is set to true then the internal data structures will grow automatically
+ * to accommodate the matrices passed in.  When adding elements to the decomposition the matrices must have enough
+ * data elements to grow before hand.
+ * </p>
+ *
+ * <p>
+ * For more information see David S. Watkins, "Fundamentals of Matrix Computations" 2nd edition, pages 249-259.
+ * It is also possible to add and remove columns efficiently, but this is less common and is not supported at
+ * this time.
+ * </p>
+ * @author Peter Abeles
+ */
+public class QrUpdate {
+
+    // the decomposition that is being adjusted
+    private DenseMatrix64F Q,R;
+    // product of planar multiplications
+    private DenseMatrix64F U_tran; // using transpose of U reduces cache misses
+    private DenseMatrix64F Qm;
+
+    // used to temporarially store data
+    private double r_row[];
+
+    // it can process matrices up to this size
+    private int maxCols;
+    private int maxRows;
+
+    // number of rows and columns in the original A matrix that was decomposed
+    private int m,n;
+    // number of rows in the adjusted matrices
+    private int m_m;
+
+    // should it declare new internal data when what currently exists is too small or throw
+    // and exception.
+    private boolean autoGrow;
+
+    /**
+     * Creates an update which can decompose matrices up to the specified size.  Autogrow
+     * is set to false.
+     *
+     * @param maxRows
+     * @param maxCols
+     */
+    public QrUpdate( int maxRows , int maxCols ) {
+        autoGrow = false;
+        declareInternalData(maxRows, maxCols);
+    }
+
+    /**
+     * Creates an update which can decompose matrices up to the specified size.  Autogrow
+     * is configurable.
+     *
+     * @param maxRows
+     * @param maxCols
+     * @param autoGrow
+     */
+    public QrUpdate( int maxRows , int maxCols , boolean autoGrow ) {
+        this.autoGrow = autoGrow;
+        declareInternalData(maxRows, maxCols);
+    }
+
+    /**
+     * Does not predeclare data and it will autogrow.
+     */
+    public QrUpdate(){
+        autoGrow = true;
+    }
+
+    /**
+     * Declares the internal data structures so that it can process matrices up to the specified size.
+     *
+     * @param maxRows
+     * @param maxCols
+     */
+    public void declareInternalData(int maxRows, int maxCols) {
+        this.maxRows = maxRows;
+        this.maxCols = maxCols;
+
+        U_tran = new DenseMatrix64F(maxRows,maxRows);
+        Qm = new DenseMatrix64F(maxRows,maxRows);
+
+        r_row = new double[ maxCols ];
+    }
+
+    /**
+     * <p>
+     * Adjusts the values of the Q and R matrices to take in account the effects of inserting
+     * a row to the 'A' matrix at the specified location.  This operation requires about 6mn + O(n) flops.
+     * </p>
+     *
+     * <p>
+     * If Q and/or R does not have enough data elements to grow then an exception is thrown.
+     * </p>
+     *
+     * <p>
+     * The adjustment done is by computing a series of planar Givens rotations that make the adjusted R
+     * matrix upper triangular again.  This is then used to modify the Q matrix.
+     * </p>
+     *
+     * @param Q The Q matrix which is to be modified, must be big enough to grow.  Must be n by n..  Is modified.
+     * @param R The R matrix which is to be modified, must be big enough to grow.  Must be m by n.  Is modified.
+     * @param row The row being inserted.  Not modified.
+     * @param rowIndex Which row index it is to be inserted at.
+     * @param resizeR Should the number of rows in R be changed?  The additional rows are all zero.
+     */
+    public void addRow( DenseMatrix64F Q , DenseMatrix64F R , double []row , int rowIndex , boolean resizeR ) {
+        // memory management and check precoditions
+        setQR(Q,R,1);
+        m_m = m+1;
+
+        if( Q.data.length < m_m*m_m )
+            throw new IllegalArgumentException("Q matrix does not have enough data to grow");
+
+        if( resizeR && R.data.length < m_m*n )
+            throw new IllegalArgumentException("R matrix does not have enough data to grow");
+
+        if( resizeR )
+            R.reshape(m_m,n, false);
+
+        U_tran.reshape(m_m,m_m, false);
+
+        // apply givens rotation to the first two rows of the augmented R matrix
+        applyFirstGivens(row);
+        applyLaterGivens();
+        // compute new Q matrix
+        updateInsertQ(rowIndex);
+
+        // discard the reference since it is no longer needed
+        this.Q = this.R = null;
+    }
+
+    /**
+     * <p>
+     * Adjusts the values of the Q and R matrices to take in account the effects of removing
+     * a row from the 'A' matrix at the specified location.  This operation requires about 6mn + O(n) flops.
+     * </p>
+     *
+     * <p>
+     * The adjustment is done by computing a series of planar Givens rotations that make the removed row in Q
+     * equal to [1 0 ... 0].
+     * </p>
+     *
+     * @param Q The Q matrix.  Is modified.
+     * @param R The R matrix.  Is modified.
+     * @param rowIndex Which index of the row that is being removed.
+     * @param resizeR should the shape of R be adjusted?
+     */
+    public void deleteRow( DenseMatrix64F Q , DenseMatrix64F R , int rowIndex , boolean resizeR ) {
+        setQR(Q,R,0);
+        if( m - 1 < n ) {
+            throw new IllegalArgumentException("Removing any row would make the system under determined.");
+        }
+
+        m_m = m - 1;
+        U_tran.reshape(m,m, false);
+
+        if( resizeR )
+            R.reshape(m_m,n, false);
+        
+        computeRemoveGivens(rowIndex);
+        updateRemoveQ(rowIndex);
+
+        updateRemoveR();
+
+        // discard the reference since it is no longer needed
+        this.Q = this.R = null;
+    }
+
+    /**
+     * Provides the results of a QR decomposition.  These will be modified by adding or removing
+     * rows from the original 'A' matrix.
+     * 
+     * @param Q The Q matrix which is to be modified.  Is modified later and reference saved.
+     * @param R The R matrix which is to be modified.  Is modified later and reference saved.
+     */
+    private void setQR( DenseMatrix64F Q , DenseMatrix64F R , int growRows ) {
+        if( Q.numRows != Q.numCols ) {
+            throw new IllegalArgumentException("Q should be square.");
+        }
+
+        this.Q = Q;
+        this.R = R;
+
+        m = Q.numRows;
+        n = R.numCols;
+
+        if( m+growRows > maxRows || n > maxCols ) {
+            if( autoGrow ) {
+                declareInternalData(m+growRows,n);
+            } else {
+                throw new IllegalArgumentException("Autogrow has been set to false and the maximum number of rows" +
+                        " or columns has been exceeded.");
+            }
+        }
+    }
+
+    /**
+     * Updates the Q matrix to take in account the inserted matrix.
+     *
+     * @param rowIndex where the matrix has been inserted.
+     */
+    private void updateInsertQ( int rowIndex ) {
+        Qm.set(Q);
+        Q.reshape(m_m,m_m, false);
+
+        for( int i = 0; i < rowIndex; i++ ) {
+            for( int j = 0; j < m_m; j++ ) {
+                double sum = 0;
+                for( int k = 0; k < m; k++ ) {
+                    sum += Qm.data[i*m+k]* U_tran.data[j*m_m+k+1];
+                }
+                Q.data[i*m_m+j] = sum;
+            }
+        }
+
+        for( int j = 0; j < m_m; j++ ) {
+            Q.data[rowIndex*m_m+j] = U_tran.data[j*m_m];
+        }
+
+        for( int i = rowIndex+1; i < m_m; i++ ) {
+            for( int j = 0; j < m_m; j++ ) {
+                double sum = 0;
+                for( int k = 0; k < m; k++ ) {
+                    sum += Qm.data[(i-1)*m+k]* U_tran.data[j*m_m+k+1];
+                }
+                Q.data[i*m_m+j] = sum;
+            }
+        }
+    }
+
+    /**
+     * Updates the Q matrix to take inaccount the row that was removed by only multiplying e
+     * lements that need to be.  There is still some room for improvement here...
+     * @param rowIndex
+     */
+    private void updateRemoveQ( int rowIndex ) {
+        Qm.set(Q);
+        Q.reshape(m_m,m_m, false);
+
+        for( int i = 0; i < rowIndex; i++ ) {
+            for( int j = 1; j < m; j++ ) {
+                double sum = 0;
+                for( int k = 0; k < m; k++ ) {
+                    sum += Qm.data[i*m+k]* U_tran.data[j*m+k];
+                }
+                Q.data[i*m_m+j-1] = sum;
+            }
+        }
+
+        for( int i = rowIndex+1; i < m; i++ ) {
+            for( int j = 1; j < m; j++ ) {
+                double sum = 0;
+                for( int k = 0; k < m; k++ ) {
+                    sum += Qm.data[i*m+k]* U_tran.data[j*m+k];
+                }
+                Q.data[(i-1)*m_m+j-1] = sum;
+            }
+        }
+    }
+
+    /**
+     * Updates the R matrix to take in account the removed row.
+     */
+    private void updateRemoveR() {
+        for( int i = 1; i < n+1; i++ ) {
+            for( int j = 0; j < n; j++ ) {
+                double sum = 0;
+                for( int k = i-1; k <= j; k++ ) {
+                    sum += U_tran.data[i*m+k] * R.data[k*n+j];
+                }
+                R.data[(i-1)*n+j] = sum;
+            }
+        }
+    }
+
+    private void applyFirstGivens(double[] row) {
+        double c,s;
+        double xi = row[0];
+        double xj = R.data[0];
+
+        double r = xi*xi + xj*xj;
+        if( r != 0 ) {
+            r = Math.sqrt(r);
+            c = xi/r;
+            s = xj/r;
+
+        } else {
+            c = 1;
+            s = 0;
+        }
+
+        R.data[0] = r;
+        for( int col = 1; col < n; col++ ) {
+            double vali = row[col];
+            double valj = R.data[col];
+
+            R.data[col] = c*vali + s*valj;
+            r_row[col] = c*valj - s*vali;
+        }
+
+        // set U to its initial values
+        CommonOps.setIdentity(U_tran);
+        U_tran.data[0] = c;
+        U_tran.data[1] = s;
+        U_tran.data[m_m] = -s;
+        U_tran.data[m_m+1] = c;
+    }
+
+    private void applyLaterGivens()
+    {
+        for( int row = 1; row < n; row++ ) {
+            // first compute the rotation
+            double c,s;
+            double xi = r_row[row];
+            double xj = R.data[n*row+row];
+
+            double r = xi*xi + xj*xj;
+            if( r != 0 ) {
+                r = Math.sqrt(r);
+                c = xi/r;
+                s = xj/r;
+
+            } else {
+                c = 1;
+                s = 0;
+            }
+
+            // update R matrix
+            R.data[n*row+row] = r;
+            for( int col = row+1; col < n; col++ ) {
+                double vali = r_row[col];
+                double valj = R.data[n*row+col];
+
+                R.data[n*row+col] = c*vali + s*valj;
+                r_row[col] = c*valj - s*vali;
+            }
+
+            // compute U^T = U^T_(x+1) * U^T_x
+            for( int col = 0; col <= row+1; col++ ) {
+                double q1 = U_tran.data[row*m_m+col];
+                double q2 = U_tran.data[(row+1)*m_m+col];
+
+                U_tran.data[row*m_m+col] = c*q1 + s*q2;
+                U_tran.data[(row+1)*m_m+col] = c*q2 - s*q1;
+            }
+        }
+    }
+
+    private void computeRemoveGivens( int selectedRow )
+    {
+        CommonOps.setIdentity(U_tran);
+
+        double xj = Q.data[selectedRow*m+m-1];
+
+        for( int j = m-2; j >= 0; j-- ) {
+            // first compute the rotation
+            double c,s;
+            double xi = Q.data[selectedRow*m+j];
+
+            double r = xi*xi + xj*xj;
+            if( r != 0 ) {
+                r = Math.sqrt(r);
+                c = xi/r;
+                s = xj/r;
+            } else {
+                c = 1;
+                s = 0;
+            }
+
+            // in the next iteration xj is r
+            xj = r;
+
+            // compute U^T = U^T_(x+1) * U^T_x
+            for( int col = j; col < m; col++ ) {
+                double q1 = U_tran.data[j*m+col];
+                double q2 = U_tran.data[(j+1)*m+col];
+
+                U_tran.data[j*m+col] = c*q1 + s*q2;
+                U_tran.data[(j+1)*m+col] = c*q2 - s*q1;
+            }
+        }
+    }
+
+    public DenseMatrix64F getU_tran() {
+        return U_tran;
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/svd/SafeSvd.java b/main/dense64/src/org/ejml/alg/dense/decomposition/svd/SafeSvd.java
new file mode 100644
index 0000000..44a0122
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/svd/SafeSvd.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.svd;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.SingularValueDecomposition;
+
+/**
+ * Wraps around a {@link SingularValueDecomposition} and ensures that the input is not modified.
+ *
+ * @author Peter Abeles
+ */
+public class SafeSvd
+        implements SingularValueDecomposition<DenseMatrix64F>
+{
+    // the decomposition algorithm
+    SingularValueDecomposition<DenseMatrix64F> alg;
+    // storage for the input if it would be modified
+    DenseMatrix64F work = new DenseMatrix64F(1,1);
+
+    public SafeSvd(SingularValueDecomposition<DenseMatrix64F> alg) {
+        this.alg = alg;
+    }
+
+    @Override
+    public double[] getSingularValues() {
+        return alg.getSingularValues();
+    }
+
+    @Override
+    public int numberOfSingularValues() {
+        return alg.numberOfSingularValues();
+    }
+
+    @Override
+    public boolean isCompact() {
+        return alg.isCompact();
+    }
+
+    @Override
+    public DenseMatrix64F getU(DenseMatrix64F U, boolean transposed) {
+        return alg.getU(U,transposed);
+    }
+
+    @Override
+    public DenseMatrix64F getV(DenseMatrix64F V, boolean transposed) {
+        return alg.getV(V,transposed);
+    }
+
+    @Override
+    public DenseMatrix64F getW(DenseMatrix64F W) {
+        return alg.getW(W);
+    }
+
+    @Override
+    public int numRows() {
+        return alg.numRows();
+    }
+
+    @Override
+    public int numCols() {
+        return alg.numCols();
+    }
+
+    @Override
+    public boolean decompose(DenseMatrix64F orig) {
+        if( alg.inputModified() ) {
+            work.reshape(orig.numRows,orig.numCols);
+            work.set(orig);
+            return alg.decompose(work);
+        } else {
+            return alg.decompose(orig);
+        }
+    }
+
+    @Override
+    public boolean inputModified() {
+        return false;
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/svd/SvdImplicitQrDecompose_D64.java b/main/dense64/src/org/ejml/alg/dense/decomposition/svd/SvdImplicitQrDecompose_D64.java
new file mode 100644
index 0000000..0c9f447
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/svd/SvdImplicitQrDecompose_D64.java
@@ -0,0 +1,345 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.svd;
+
+import org.ejml.alg.dense.decomposition.bidiagonal.BidiagonalDecompositionRow_D64;
+import org.ejml.alg.dense.decomposition.bidiagonal.BidiagonalDecompositionTall_D64;
+import org.ejml.alg.dense.decomposition.svd.implicitqr.SvdImplicitQrAlgorithm;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.BidiagonalDecomposition;
+import org.ejml.interfaces.decomposition.SingularValueDecomposition;
+import org.ejml.ops.CommonOps;
+
+
+/**
+ * <p>
+ * Computes the Singular value decomposition of a matrix using the implicit QR algorithm
+ * for singular value decomposition.  It works by first by transforming the matrix
+ * to a bidiagonal A=U*B*V<sup>T</sup> form, then it implicitly computing the eigenvalues of the B<sup>T</sup>B matrix,
+ * which are the same as the singular values in the original A matrix.
+ * </p>
+ *
+ * <p>
+ * Based off of the description provided in:<br>
+ * <br>
+ * David S. Watkins, "Fundamentals of Matrix Computations," Second Edition. Page 404-411
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class SvdImplicitQrDecompose_D64 implements SingularValueDecomposition<DenseMatrix64F> {
+
+    private int numRows;
+    private int numCols;
+
+    // dimensions of transposed matrix
+    private int numRowsT;
+    private int numColsT;
+
+    // if true then it can use the special Bidiagonal decomposition
+    private boolean canUseTallBidiagonal;
+
+    // If U is not being computed and the input matrix is 'tall' then a special bidiagonal decomposition
+    // can be used which is faster.
+    private BidiagonalDecomposition<DenseMatrix64F> bidiag;
+    private SvdImplicitQrAlgorithm qralg = new SvdImplicitQrAlgorithm();
+
+    double diag[];
+    double off[];
+
+    private DenseMatrix64F Ut;
+    private DenseMatrix64F Vt;
+
+    private double singularValues[];
+    private int numSingular;
+
+    // compute a compact SVD
+    private boolean compact;
+    // What is actually computed
+    private boolean computeU;
+    private boolean computeV;
+
+    // What the user requested to be computed
+    // If the transpose is computed instead then what is actually computed is swapped
+    private boolean prefComputeU;
+    private boolean prefComputeV;
+
+    // Should it compute the transpose instead
+    private boolean transposed;
+
+    // Either a copy of the input matrix or a copy of it transposed
+    private DenseMatrix64F A_mod = new DenseMatrix64F(1,1);
+
+    /**
+     * Configures the class
+     *
+     * @param compact Compute a compact SVD
+     * @param computeU If true it will compute the U matrix
+     * @param computeV If true it will compute the V matrix
+     * @param canUseTallBidiagonal If true then it can choose to use a tall Bidiagonal decomposition to improve runtime performance.
+     */
+    public SvdImplicitQrDecompose_D64(boolean compact, boolean computeU, boolean computeV,
+                                      boolean canUseTallBidiagonal)
+    {
+        this.compact = compact;
+        this.prefComputeU = computeU;
+        this.prefComputeV = computeV;
+        this.canUseTallBidiagonal = canUseTallBidiagonal;
+    }
+
+    @Override
+    public double[] getSingularValues() {
+        return singularValues;
+    }
+
+    @Override
+    public int numberOfSingularValues() {
+        return numSingular;
+    }
+
+    @Override
+    public boolean isCompact() {
+        return compact;
+    }
+
+    @Override
+    public DenseMatrix64F getU( DenseMatrix64F U , boolean transpose) {
+        if( !prefComputeU )
+            throw new IllegalArgumentException("As requested U was not computed.");
+        if( transpose ) {
+            if( U == null )
+                return Ut;
+            else if( U.numRows != Ut.numRows || U.numCols != Ut.numCols )
+                throw new IllegalArgumentException("Unexpected shape of U");
+
+            U.set(Ut);
+        } else {
+            if( U == null )
+                U = new DenseMatrix64F(Ut.numCols,Ut.numRows);
+            else if( U.numRows != Ut.numCols || U.numCols != Ut.numRows )
+                throw new IllegalArgumentException("Unexpected shape of U");
+
+            CommonOps.transpose(Ut,U);
+        }
+
+        return U;
+    }
+
+    @Override
+    public DenseMatrix64F getV( DenseMatrix64F V , boolean transpose ) {
+        if( !prefComputeV )
+            throw new IllegalArgumentException("As requested V was not computed.");
+        if( transpose ) {
+            if( V == null )
+                return Vt;
+            else if( V.numRows != Vt.numRows || V.numCols != Vt.numCols )
+                throw new IllegalArgumentException("Unexpected shape of V");
+
+            V.set(Vt);
+        } else {
+            if( V == null )
+                V = new DenseMatrix64F(Vt.numCols,Vt.numRows);
+            else if( V.numRows != Vt.numCols || V.numCols != Vt.numRows )
+                throw new IllegalArgumentException("Unexpected shape of V");
+            CommonOps.transpose(Vt,V);
+        }
+
+        return V;
+    }
+
+    @Override
+    public DenseMatrix64F getW( DenseMatrix64F W ) {
+        int m = compact ? numSingular : numRows;
+        int n = compact ? numSingular : numCols;
+
+        if( W == null )
+            W = new DenseMatrix64F(m,n);
+        else {
+            W.reshape(m,n, false);
+            W.zero();
+        }
+
+        for( int i = 0; i < numSingular; i++ ) {
+            W.unsafe_set(i,i, singularValues[i]);
+        }
+
+        return W;
+    }
+
+    @Override
+    public boolean decompose(DenseMatrix64F orig) {
+        if( !setup(orig) )
+            return false;
+
+        if (bidiagonalization(orig))
+            return false;
+
+        if( computeUWV() )
+            return false;
+
+        // make sure all the singular values or positive
+        makeSingularPositive();
+
+        // if transposed undo the transposition
+        undoTranspose();
+
+        return true;
+    }
+
+    @Override
+    public boolean inputModified() {
+        return false;
+    }
+
+    private boolean bidiagonalization(DenseMatrix64F orig) {
+        // change the matrix to bidiagonal form
+        if( transposed ) {
+            A_mod.reshape(orig.numCols,orig.numRows,false);
+            CommonOps.transpose(orig,A_mod);
+        } else {
+            A_mod.reshape(orig.numRows,orig.numCols,false);
+            A_mod.set(orig);
+        }
+        return !bidiag.decompose(A_mod);
+    }
+
+    /**
+     * If the transpose was computed instead do some additional computations
+     */
+    private void undoTranspose() {
+        if( transposed ) {
+            DenseMatrix64F temp = Vt;
+            Vt = Ut;
+            Ut = temp;
+        }
+    }
+
+    /**
+     * Compute singular values and U and V at the same time
+     */
+    private boolean computeUWV() {
+        bidiag.getDiagonal(diag,off);
+        qralg.setMatrix(numRowsT,numColsT,diag,off);
+
+//        long pointA = System.currentTimeMillis();
+        // compute U and V matrices
+        if( computeU )
+            Ut = bidiag.getU(Ut,true,compact);
+        if( computeV )
+            Vt = bidiag.getV(Vt,true,compact);
+
+        qralg.setFastValues(false);
+        if( computeU )
+            qralg.setUt(Ut);
+        else
+            qralg.setUt(null);
+        if( computeV )
+            qralg.setVt(Vt);
+        else
+            qralg.setVt(null);
+
+//        long pointB = System.currentTimeMillis();
+
+        boolean ret = !qralg.process();
+
+//        long pointC = System.currentTimeMillis();
+//        System.out.println("  compute UV "+(pointB-pointA)+"  QR = "+(pointC-pointB));
+
+        return ret;
+    }
+
+    private boolean setup(DenseMatrix64F orig) {
+        transposed = orig.numCols > orig.numRows;
+
+        // flag what should be computed and what should not be computed
+        if( transposed ) {
+            computeU = prefComputeV;
+            computeV = prefComputeU;
+            numRowsT = orig.numCols;
+            numColsT = orig.numRows;
+        } else {
+            computeU = prefComputeU;
+            computeV = prefComputeV;
+            numRowsT = orig.numRows;
+            numColsT = orig.numCols;
+        }
+
+        numRows = orig.numRows;
+        numCols = orig.numCols;
+
+        if( numRows == 0 || numCols == 0 )
+            return false;
+
+        if( diag == null || diag.length < numColsT ) {
+            diag = new double[ numColsT ];
+            off = new double[ numColsT-1 ];
+        }
+
+        // if it is a tall matrix and U is not needed then there is faster decomposition algorithm
+        if( canUseTallBidiagonal && numRows > numCols * 2 && !computeU ) {
+            if( bidiag == null || !(bidiag instanceof BidiagonalDecompositionTall_D64) ) {
+                bidiag = new BidiagonalDecompositionTall_D64();
+            }
+        } else if( bidiag == null || !(bidiag instanceof BidiagonalDecompositionRow_D64) ) {
+            bidiag = new BidiagonalDecompositionRow_D64();
+        }
+
+        return true;
+    }
+
+    /**
+     * With the QR algorithm it is possible for the found singular values to be negative.  This
+     * makes them all positive by multiplying it by a diagonal matrix that has
+     */
+    private void makeSingularPositive() {
+        numSingular = qralg.getNumberOfSingularValues();
+        singularValues = qralg.getSingularValues();
+
+        for( int i = 0; i < numSingular; i++ ) {
+            double val = qralg.getSingularValue(i);
+
+            if( val < 0 ) {
+                singularValues[i] = 0.0d - val;
+
+                if( computeU ) {
+                    // compute the results of multiplying it by an element of -1 at this location in
+                    // a diagonal matrix.
+                    int start = i* Ut.numCols;
+                    int stop = start+ Ut.numCols;
+
+                    for( int j = start; j < stop; j++ ) {
+                        Ut.set(j, 0.0d - Ut.get(j));
+                    }
+                }
+            } else {
+                singularValues[i] = val;
+            }
+        }
+    }
+
+    @Override
+    public int numRows() {
+        return numRows;
+    }
+
+    @Override
+    public int numCols() {
+        return numCols;
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/decomposition/svd/implicitqr/SvdImplicitQrAlgorithm.java b/main/dense64/src/org/ejml/alg/dense/decomposition/svd/implicitqr/SvdImplicitQrAlgorithm.java
new file mode 100644
index 0000000..edad2c2
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/decomposition/svd/implicitqr/SvdImplicitQrAlgorithm.java
@@ -0,0 +1,821 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.svd.implicitqr;
+
+import org.ejml.UtilEjml;
+import org.ejml.alg.dense.decomposition.eig.EigenvalueSmall;
+import org.ejml.data.DenseMatrix64F;
+
+import java.util.Random;
+
+
+/**
+ * <p>
+ * Computes the QR decomposition of a bidiagonal matrix.  Internally this matrix is stored as
+ * two arrays.  Shifts can either be provided to it or it can generate the shifts on its own.
+ * It optionally computes the U and V matrices.  This comparability allows it to be used to
+ * compute singular values and associated matrices efficiently.<br>
+ * <br>
+ * A = U*S*V<sup>T</sup><br>
+ * where A is the original m by n matrix.
+ * </p>
+ *
+ * <p>
+ * Based off of the outline provided in:<br>
+ * <br>
+ * David S. Watkins, "Fundamentals of Matrix Computations," Second Edition. Page 404-411
+ * </p>
+ *
+ * <p>
+ * Note: To watch it process the matrix step by step uncomment commented out code.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class SvdImplicitQrAlgorithm {
+
+    // used in exceptional shifts
+    protected Random rand = new Random(0x34671e);
+
+    // U and V matrices in singular value decomposition.  Stored in the transpose
+    // to reduce cache jumps
+    protected DenseMatrix64F Ut;
+    protected DenseMatrix64F Vt;
+
+    // number of times it has performed an implicit step, the most costly part of the
+    // algorithm
+    protected int totalSteps;
+
+    // max value in original matrix.  used to test for zeros
+    protected double maxValue;
+
+    // matrix's size
+    protected int N;
+
+    // used to compute eigenvalues directly
+    protected EigenvalueSmall eigenSmall = new EigenvalueSmall();
+
+    // how many exception shifts has it performed
+    protected int numExceptional;
+    // the step number of the last exception shift
+    protected int nextExceptional;
+
+    // diagonal elements in the matrix
+    protected double diag[];
+    // the off diagonal elements
+    protected double off[];
+    // value of the bulge
+    double bulge;
+
+    // the submatrix its working on
+    protected int x1;
+    protected int x2;
+
+    // how many cycles has it run through looking for the current singular value
+    int steps;
+
+    // where splits are performed
+    protected int splits[];
+    protected int numSplits;
+
+    // After this many iterations it will perform an exceptional
+    private int exceptionalThresh = 15;
+    private int maxIterations = exceptionalThresh*100;
+
+    // should the steps use a sequence of predefined lambdas?
+    boolean followScript;
+
+    // --------- variables for scripted step
+    // if following a sequence of steps, this is the point at which it decides its
+    // going no where and needs to use a different step
+    private static final int giveUpOnKnown = 10;
+    private double values[];
+
+    //can it compute singularvalues directly
+    private boolean fastValues = false;
+
+    // if not in scripted mode is it looking for new zeros first?
+    private boolean findingZeros;
+
+    double c,s;
+
+    // for debugging
+//    SimpleMatrix B;
+
+    public SvdImplicitQrAlgorithm( boolean fastValues ) {
+        this.fastValues = fastValues;
+    }
+
+    public SvdImplicitQrAlgorithm() {
+
+    }
+
+    public DenseMatrix64F getUt() {
+        return Ut;
+    }
+
+    public void setUt(DenseMatrix64F ut) {
+        Ut = ut;
+    }
+
+    public DenseMatrix64F getVt() {
+        return Vt;
+    }
+
+    public void setVt(DenseMatrix64F vt) {
+        Vt = vt;
+    }
+
+    /**
+     *
+     */
+    public void setMatrix( int numRows , int numCols, double diag[], double off[] ) {
+        initParam(numRows,numCols);
+        this.diag = diag;
+        this.off = off;
+
+        maxValue = Math.abs(diag[0]);
+        for( int i = 1; i < N; i++ ) {
+            double a = Math.abs(diag[i]);
+            double b = Math.abs(off[i-1]);
+
+            if( a > maxValue ) {
+                maxValue = Math.abs(a);
+            }
+            if( b > maxValue ) {
+                maxValue = Math.abs(b);
+            }
+        }
+    }
+
+    public double[] swapDiag( double diag[] ) {
+        double[] ret = this.diag;
+        this.diag = diag;
+        return ret;
+    }
+
+    public double[] swapOff( double off[] ) {
+        double[] ret = this.off;
+        this.off = off;
+        return ret;
+    }
+
+    public void setMaxValue(double maxValue) {
+        this.maxValue = maxValue;
+    }
+
+    public void initParam( int M , int N ) {
+        if( N > M )
+            throw new RuntimeException("Must be a square or tall matrix");
+
+        this.N = N;
+
+        if( splits == null || splits.length < N ) {
+            splits = new int[N];
+        }
+
+        x1 = 0;
+        x2 = this.N-1;
+
+        steps = 0;
+        totalSteps = 0;
+        numSplits = 0;
+        numExceptional = 0;
+        nextExceptional = exceptionalThresh;
+    }
+
+    public boolean process() {
+        this.followScript = false;
+        findingZeros = true;
+
+        return _process();
+    }
+
+    /**
+     * Perform a sequence of steps based off of the singular values provided.
+     *
+     * @param values
+     * @return
+     */
+    public boolean process(double values[] ) {
+        this.followScript = true;
+        this.values = values;
+        this.findingZeros = false;
+
+        return _process();
+    }
+
+    public boolean _process() {
+        // it is a zero matrix
+        if( maxValue == 0 )
+            return true;
+        while( x2 >= 0 ) {
+            // if it has cycled too many times give up
+            if( steps > maxIterations ) {
+                return false;
+            }
+
+            if( x1 == x2 ) {
+//                System.out.println("steps = "+steps+"  script = "+followScript+" at "+x1);
+//                System.out.println("Split");
+                // see if it is done processing this submatrix
+                resetSteps();
+                if( !nextSplit() )
+                    break;
+            } else if( fastValues && x2-x1 == 1 ) {
+                // There are analytical solutions to this case. Just compute them directly.
+                resetSteps();
+                eigenBB_2x2(x1);
+                setSubmatrix(x2,x2);
+            } else if( steps >= nextExceptional ){
+                exceptionShift();
+            } else {
+                // perform a step
+                if (!checkForAndHandleZeros()) {
+                    if( followScript ) {
+                        performScriptedStep();
+                    } else {
+                        performDynamicStep();
+                    }
+                }
+            }
+
+//            printMatrix();
+        }
+
+        return true;
+    }
+
+    /**
+     * Here the lambda in the implicit step is determined dynamically.  At first
+     * it selects zeros to quickly reveal singular values that are zero or close to zero.
+     * Then it computes it using a Wilkinson shift.
+     */
+    private void performDynamicStep() {
+        // initially look for singular values of zero
+        if( findingZeros ) {
+            if( steps > 6 ) {
+                findingZeros = false;
+            } else {
+                double scale = computeBulgeScale();
+                performImplicitSingleStep(scale,0,false);
+            }
+        } else {
+            // For very large and very small numbers the only way to prevent overflow/underflow
+            // is to have a common scale between the wilkinson shift and the implicit single step
+            // What happens if you don't is that when the wilkinson shift returns the value it
+            // computed it multiplies it by the scale twice, which will cause an overflow
+            double scale = computeBulgeScale();
+            // use the wilkinson shift to perform a step
+            double lambda = selectWilkinsonShift(scale);
+
+            performImplicitSingleStep(scale,lambda,false);
+        }
+    }
+
+    /**
+     * Shifts are performed based upon singular values computed previously.  If it does not converge
+     * using one of those singular values it uses a Wilkinson shift instead.
+     */
+    private void performScriptedStep() {
+        double scale = computeBulgeScale();
+        if( steps > giveUpOnKnown ) {
+            // give up on the script
+            followScript = false;
+        } else {
+            // use previous singular value to step
+            double s = values[x2]/scale;
+            performImplicitSingleStep(scale,s*s,false);
+        }
+    }
+
+    public void incrementSteps() {
+        steps++;
+        totalSteps++;
+    }
+
+    public boolean isOffZero(int i) {
+        double bottom = Math.abs(diag[i])+Math.abs(diag[i+1]);
+
+        return Math.abs(off[i]) <= bottom* UtilEjml.EPS;
+    }
+
+    public boolean isDiagonalZero(int i) {
+//        return Math.abs(diag[i]) <= maxValue* UtilEjml.EPS;
+
+        double bottom = Math.abs(diag[i+1])+Math.abs(off[i]);
+
+        return Math.abs(diag[i]) <= bottom* UtilEjml.EPS;
+    }
+
+    public void resetSteps() {
+        steps = 0;
+        nextExceptional = exceptionalThresh;
+        numExceptional = 0;
+    }
+
+    /**
+     * Tells it to process the submatrix at the next split.  Should be called after the
+     * current submatrix has been processed.
+     */
+    public boolean nextSplit() {
+        if( numSplits == 0 )
+            return false;
+        x2 = splits[--numSplits];
+        if( numSplits > 0 )
+            x1 = splits[numSplits-1]+1;
+        else
+            x1 = 0;
+
+        return true;
+    }
+
+    /**
+     * Given the lambda value perform an implicit QR step on the matrix.
+     *
+     * B^T*B-lambda*I
+     *
+     * @param lambda Stepping factor.
+     */
+    public void performImplicitSingleStep(double scale , double lambda , boolean byAngle) {
+        createBulge(x1,lambda,scale,byAngle);
+
+        for( int i = x1; i < x2-1 && bulge != 0.0; i++ ) {
+            removeBulgeLeft(i,true);
+            if( bulge == 0 )
+                break;
+            removeBulgeRight(i);
+        }
+
+        if( bulge != 0 )
+            removeBulgeLeft(x2-1,false);
+
+        incrementSteps();
+    }
+
+    /**
+     * Multiplied a transpose orthogonal matrix Q by the specified rotator.  This is used
+     * to update the U and V matrices.  Updating the transpose of the matrix is faster
+     * since it only modifies the rows.
+     *
+     *
+     * @param Q Orthogonal matrix
+     * @param m Coordinate of rotator.
+     * @param n Coordinate of rotator.
+     * @param c cosine of rotator.
+     * @param s sine of rotator.
+     */
+    protected void updateRotator( DenseMatrix64F Q , int m, int n, double c, double s) {
+        int rowA = m*Q.numCols;
+        int rowB = n*Q.numCols;
+
+//        for( int i = 0; i < Q.numCols; i++ ) {
+//            double a = Q.get(rowA+i);
+//            double b = Q.get(rowB+i);
+//            Q.set( rowA+i, c*a + s*b);
+//            Q.set( rowB+i, -s*a + c*b);
+//        }
+//        System.out.println("------ AFter Update Rotator "+m+" "+n);
+//        Q.print();
+//        System.out.println();
+        int endA = rowA + Q.numCols;
+        for( ; rowA != endA; rowA++ , rowB++ ) {
+            double a = Q.get(rowA);
+            double b = Q.get(rowB);
+            Q.set(rowA, c*a + s*b);
+            Q.set(rowB, -s*a + c*b);
+        }
+    }
+
+    private double computeBulgeScale() {
+        double b11 = diag[x1];
+        double b12 = off[x1];
+
+        return Math.max( Math.abs(b11) , Math.abs(b12));
+//
+//        double b22 = diag[x1+1];
+//
+//        double scale = Math.max( Math.abs(b11) , Math.abs(b12));
+//
+//        return Math.max(scale,Math.abs(b22));
+    }
+
+    /**
+     * Performs a similar transform on B<sup>T</sup>B-pI
+     */
+    protected void createBulge( int x1 , double p , double scale , boolean byAngle ) {
+        double b11 = diag[x1];
+        double b12 = off[x1];
+        double b22 = diag[x1+1];
+
+        if( byAngle ) {
+            c = Math.cos(p);
+            s = Math.sin(p);
+        } else {
+            // normalize to improve resistance to overflow/underflow
+            double u1 = (b11/scale)*(b11/scale)-p;
+            double u2 = (b12/scale)*(b11/scale);
+
+            double gamma = Math.sqrt(u1*u1 + u2*u2);
+
+            c = u1/gamma;
+            s = u2/gamma;
+        }
+
+        // multiply the rotator on the top left.
+        diag[x1] = b11*c + b12*s;
+        off[x1] = b12*c - b11*s;
+        diag[x1+1] = b22*c;
+        bulge = b22*s;
+
+//        SimpleMatrix Q = createQ(x1, c, s, false);
+//        B=B.mult(Q);
+//
+//        B.print();
+//        printMatrix();
+//        System.out.println("  bulge = "+bulge);
+
+        if( Vt != null ) {
+            updateRotator(Vt,x1,x1+1,c,s);
+
+//            SimpleMatrix.wrap(Ut).mult(B).mult(SimpleMatrix.wrap(Vt).transpose()).print();
+//            printMatrix();
+//            System.out.println("bulge = "+bulge);
+//            System.out.println();
+        }
+    }
+
+    /**
+     * Computes a rotator that will set run to zero (?)
+     */
+    protected void computeRotator( double rise , double run )
+    {
+//        double gamma = Math.sqrt(rise*rise + run*run);
+//
+//        c = rise/gamma;
+//        s = run/gamma;
+
+        // See page 384 of Fundamentals of Matrix Computations 2nd
+          if( Math.abs(rise) < Math.abs(run)) {
+              double k = rise/run;
+
+              double bottom = Math.sqrt(1.0d+k*k);
+              s = 1.0/bottom;
+              c = k/bottom;
+          } else {
+              double t = run/rise;
+              double bottom = Math.sqrt(1.0d + t*t);
+              c = 1.0/bottom;
+              s = t/bottom;
+          }
+    }
+
+    protected void removeBulgeLeft( int x1 , boolean notLast ) {
+        double b11 = diag[x1];
+        double b12 = off[x1];
+        double b22 = diag[x1+1];
+
+        computeRotator(b11,bulge);
+
+        // apply rotator on the left
+        diag[x1] = c*b11 + s*bulge;
+        off[x1] = c*b12 + s*b22;
+        diag[x1+1] = c*b22-s*b12;
+
+        if( notLast ) {
+            double b23 = off[x1+1];
+            bulge = s*b23;
+            off[x1+1] = c*b23;
+        }
+
+//        SimpleMatrix Q = createQ(x1, c, s, true);
+//        B=Q.mult(B);
+//
+//        B.print();
+//        printMatrix();
+//        System.out.println("  bulge = "+bulge);
+
+        if( Ut != null ) {
+            updateRotator(Ut,x1,x1+1,c,s);
+
+//            SimpleMatrix.wrap(Ut).mult(B).mult(SimpleMatrix.wrap(Vt).transpose()).print();
+//            printMatrix();
+//            System.out.println("bulge = "+bulge);
+//            System.out.println();
+        }
+    }
+
+    protected void removeBulgeRight( int x1 ) {
+        double b12 = off[x1];
+        double b22 = diag[x1+1];
+        double b23 = off[x1+1];
+
+        computeRotator(b12,bulge);
+
+        // apply rotator on the right
+        off[x1] = b12*c + bulge*s;
+        diag[x1+1] = b22*c + b23*s;
+        off[x1+1] = -b22*s + b23*c;
+
+        double b33 = diag[x1+2];
+        diag[x1+2] = b33*c;
+        bulge = b33*s;
+
+//        SimpleMatrix Q = createQ(x1+1, c, s, false);
+//        B=B.mult(Q);
+//
+//        B.print();
+//        printMatrix();
+//        System.out.println("  bulge = "+bulge);
+
+        if( Vt != null ) {
+            updateRotator(Vt,x1+1,x1+2,c,s);
+
+//            SimpleMatrix.wrap(Ut).mult(B).mult(SimpleMatrix.wrap(Vt).transpose()).print();
+//            printMatrix();
+//            System.out.println("bulge = "+bulge);
+//            System.out.println();
+        }
+    }
+
+
+    public void setSubmatrix(int x1, int x2) {
+        this.x1 = x1;
+        this.x2 = x2;
+    }
+
+    /**
+     * Selects the Wilkinson's shift for B<sup>T</sup>B.  See page 410.  It is guaranteed to converge
+     * and converges fast in practice.
+     *
+     * @param scale Scale factor used to help prevent overflow/underflow
+     * @return Shifting factor lambda/(scale*scale)
+     */
+    public double selectWilkinsonShift( double scale ) {
+
+        double a11,a22;
+
+        if( x2-x1 > 1 ) {
+            double d1 = diag[x2-1] / scale;
+            double o1 = off[x2-2] / scale;
+            double d2 = diag[x2] / scale;
+            double o2 = off[x2-1] / scale;
+
+            a11 = o1*o1 + d1*d1;
+            a22 = o2*o2 + d2*d2;
+
+            eigenSmall.symm2x2_fast(a11 , o2*d1 , a22);
+        } else {
+            double a = diag[x2-1]/scale;
+            double b = off[x2-1]/scale;
+            double c = diag[x2]/scale;
+
+            a11 = a*a;
+            a22 = b*b + c*c;
+
+            eigenSmall.symm2x2_fast(a11, a*b , a22);
+        }
+
+        // return the eigenvalue closest to a22
+        double diff0 = Math.abs(eigenSmall.value0.real-a22);
+        double diff1 = Math.abs(eigenSmall.value1.real-a22);
+
+        return diff0 < diff1 ? eigenSmall.value0.real :  eigenSmall.value1.real;
+    }
+
+    /**
+     * Computes the eigenvalue of the 2 by 2 matrix B<sup>T</sup>B
+     */
+    protected void eigenBB_2x2( int x1 ) {
+        double b11 = diag[x1];
+        double b12 = off[x1];
+        double b22 = diag[x1+1];
+
+        // normalize to reduce overflow
+        double absA = Math.abs(b11);
+        double absB = Math.abs(b12);
+        double absC = Math.abs(b22);
+
+        double scale = absA > absB ? absA : absB;
+        if( absC > scale ) scale = absC;
+
+        // see if it is a pathological case.  the diagonal must already be zero
+        // and the eigenvalues are all zero.  so just return
+        if( scale == 0 )
+            return;
+
+        b11 /= scale;
+        b12 /= scale;
+        b22 /= scale;
+
+        eigenSmall.symm2x2_fast(b11*b11, b11*b12 , b12*b12+b22*b22);
+
+        off[x1] = 0;
+        diag[x1] = scale*Math.sqrt(eigenSmall.value0.real);
+        double sgn = Math.signum(eigenSmall.value1.real);
+        diag[x1+1] = sgn*scale*Math.sqrt(Math.abs(eigenSmall.value1.real));
+
+    }
+
+
+    /**
+     * Checks to see if either the diagonal element or off diagonal element is zero.  If one is
+     * then it performs a split or pushes it off the matrix.
+     *
+     * @return True if there was a zero.
+     */
+    protected boolean checkForAndHandleZeros() {
+        // check for zeros along off diagonal
+        for( int i = x2-1; i >= x1; i-- ) {
+            if( isOffZero(i) ) {
+//                System.out.println("steps at split = "+steps);
+                resetSteps();
+                splits[numSplits++] = i;
+                x1 = i+1;
+                return true;
+            }
+        }
+
+        // check for zeros along diagonal
+        for( int i = x2-1; i >= x1; i-- ) {
+            if( isDiagonalZero(i)) {
+//                System.out.println("steps at split = "+steps);
+                pushRight(i);
+                resetSteps();
+                splits[numSplits++] = i;
+                x1 = i+1;
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * If there is a zero on the diagonal element, the off diagonal element needs pushed
+     * off so that all the algorithms assumptions are two and so that it can split the matrix.
+     */
+    private void pushRight( int row ) {
+        if( isOffZero(row))
+            return;
+
+//        B = createB();
+//        B.print();
+        rotatorPushRight(row);
+        int end = N-2-row;
+        for( int i = 0; i < end && bulge != 0; i++ ) {
+            rotatorPushRight2(row,i+2);
+        }
+//        }
+    }
+
+    /**
+     * Start pushing the element off to the right.
+     */
+    private void rotatorPushRight( int m )
+    {
+        double b11 = off[m];
+        double b21 = diag[m+1];
+
+        computeRotator(b21,-b11);
+
+        // apply rotator on the right
+        off[m] = 0;
+        diag[m+1] = b21*c-b11*s;
+
+        if( m+2 < N) {
+            double b22 = off[m+1];
+            off[m+1] = b22*c;
+            bulge =  b22*s;
+        }  else {
+            bulge = 0;
+        }
+
+//        SimpleMatrix Q = createQ(m,m+1, c, s, true);
+//        B=Q.mult(B);
+//
+//        B.print();
+//        printMatrix();
+//        System.out.println("  bulge = "+bulge);
+//        System.out.println();
+
+        if( Ut != null ) {
+            updateRotator(Ut,m,m+1,c,s);
+
+//            SimpleMatrix.wrap(Ut).mult(B).mult(SimpleMatrix.wrap(Vt).transpose()).print();
+//            printMatrix();
+//            System.out.println("bulge = "+bulge);
+//            System.out.println();
+        }
+    }
+
+    /**
+     * Used to finish up pushing the bulge off the matrix.
+     */
+    private void rotatorPushRight2( int m , int offset)
+    {
+        double b11 = bulge;
+        double b12 = diag[m+offset];
+
+        computeRotator(b12,-b11);
+
+        diag[m+offset] = b12*c-b11*s;
+
+        if( m+offset<N-1) {
+            double b22 = off[m+offset];
+            off[m+offset] = b22*c;
+            bulge = b22*s;
+        }
+
+//        SimpleMatrix Q = createQ(m,m+offset, c, s, true);
+//        B=Q.mult(B);
+//
+//        B.print();
+//        printMatrix();
+//        System.out.println("  bulge = "+bulge);
+//        System.out.println();
+
+        if( Ut != null ) {
+            updateRotator(Ut,m,m+offset,c,s);
+
+//            SimpleMatrix.wrap(Ut).mult(B).mult(SimpleMatrix.wrap(Vt).transpose()).print();
+//            printMatrix();
+//            System.out.println("bulge = "+bulge);
+//            System.out.println();
+        }
+    }
+
+    /**
+     * It is possible for the QR algorithm to get stuck in a loop because of symmetries.  This happens
+     * more often with larger matrices.  By taking a random step it can break the symmetry and finish.
+     */
+    public void exceptionShift() {
+        numExceptional++;
+        double mag = 0.05 * numExceptional;
+        if (mag > 1.0) mag = 1.0;
+
+        double angle = 2.0 * Math.PI * (rand.nextDouble() - 0.5) * mag;
+        performImplicitSingleStep(0, angle, true);
+
+        // allow more convergence time
+        nextExceptional = steps + exceptionalThresh;  // (numExceptional+1)*
+    }
+
+    public void printMatrix() {
+        System.out.print("Off Diag[ ");
+        for( int j = 0; j < N-1; j++ ) {
+            System.out.printf("%5.2f ",off[j]);
+        }
+        System.out.println();
+        System.out.print("    Diag[ ");
+        for( int j = 0; j < N; j++ ) {
+            System.out.printf("%5.2f ",diag[j]);
+        }
+        System.out.println();
+    }
+
+
+    public int getNumberOfSingularValues() {
+        return N;
+    }
+
+    public double getSingularValue( int index ) {
+        return diag[index];
+    }
+
+    public void setFastValues(boolean b) {
+        fastValues = b;
+    }
+
+
+    public double[] getSingularValues() {
+        return diag;
+    }
+
+    public double[] getDiag() {
+        return diag;
+    }
+
+    public double[] getOff() {
+        return off;
+    }
+
+    public double getMaxValue() {
+        return maxValue;
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/linsol/AdjustableLinearSolver.java b/main/dense64/src/org/ejml/alg/dense/linsol/AdjustableLinearSolver.java
new file mode 100644
index 0000000..ac71dd7
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/linsol/AdjustableLinearSolver.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+
+/**
+ * In many situations solutions to linear systems that share many of the same data points are needed.
+ * This can happen when solving using the most recent data or when rejecting outliers.  In these situations
+ * it is possible to solve these related systems much faster than solving the entire data set again.
+ *
+ * @see org.ejml.interfaces.linsol.LinearSolver
+ *
+ * @author Peter Abeles
+ */
+public interface AdjustableLinearSolver extends LinearSolver<DenseMatrix64F> {
+
+
+    /**
+     * Adds a row to A.  This has the same effect as creating a new A and calling {@link #setA}.
+     *
+     * @param A_row The row in A.
+     * @param rowIndex Where the row appears in A.
+     * @return if it succeeded or not.
+     */
+    public boolean addRowToA( double []A_row , int rowIndex );
+
+    /**
+     * Removes a row from A.  This has the same effect as creating a new A and calling {@link #setA}.
+     *
+     * @param index which row is removed from A.
+     * @return If it succeeded or not.
+     */
+    public boolean removeRowFromA( int index );
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/linsol/InvertUsingSolve.java b/main/dense64/src/org/ejml/alg/dense/linsol/InvertUsingSolve.java
new file mode 100644
index 0000000..cbcfcbe
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/linsol/InvertUsingSolve.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.data.RowD1Matrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.ejml.ops.CommonOps;
+
+
+/**
+ * A matrix can be easily inverted by solving a system with an identify matrix.  The only
+ * disadvantage of this approach is that additional computations are required compared to
+ * a specialized solution.
+ *
+ * @author Peter Abeles
+ */
+public class InvertUsingSolve {
+
+    public static void invert( LinearSolver<DenseMatrix64F> solver , RowD1Matrix64F A , DenseMatrix64F A_inv , DenseMatrix64F storage) {
+
+        if( A.numRows != A_inv.numRows || A.numCols != A_inv.numCols) {
+            throw new IllegalArgumentException("A and A_inv must have the same dimensions");
+        }
+
+        CommonOps.setIdentity(storage);
+
+        solver.solve(storage,A_inv);
+    }
+
+    public static void invert( LinearSolver<DenseMatrix64F> solver , RowD1Matrix64F A , DenseMatrix64F A_inv ) {
+
+        if( A.numRows != A_inv.numRows || A.numCols != A_inv.numCols) {
+            throw new IllegalArgumentException("A and A_inv must have the same dimensions");
+        }
+
+        CommonOps.setIdentity(A_inv);
+
+        solver.solve(A_inv,A_inv);
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/linsol/LinearSolverAbstract_D64.java b/main/dense64/src/org/ejml/alg/dense/linsol/LinearSolverAbstract_D64.java
new file mode 100644
index 0000000..67ad960
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/linsol/LinearSolverAbstract_D64.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+
+
+/**
+ * <p>
+ * An abstract class that provides some common functionality and a default implementation
+ * of invert that uses the solve function of the child class.
+ * </p>
+ *
+ * <p>
+ * The extending class must explicity call {@link #_setA(org.ejml.data.DenseMatrix64F)}
+ * inside of its {@link #setA} function.
+ * </p>
+ * 
+ * @author Peter Abeles
+ */
+public abstract class LinearSolverAbstract_D64 implements LinearSolver<DenseMatrix64F> {
+
+    protected DenseMatrix64F A;
+    protected int numRows;
+    protected int numCols;
+
+    public DenseMatrix64F getA() {
+        return A;
+    }
+
+    protected void _setA(DenseMatrix64F A) {
+        this.A = A;
+        this.numRows = A.numRows;
+        this.numCols = A.numCols;
+    }
+
+    @Override
+    public void invert(DenseMatrix64F A_inv) {
+        InvertUsingSolve.invert(this,A,A_inv);
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/linsol/LinearSolverUnrolled.java b/main/dense64/src/org/ejml/alg/dense/linsol/LinearSolverUnrolled.java
new file mode 100644
index 0000000..5ccdb84
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/linsol/LinearSolverUnrolled.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol;
+
+import org.ejml.alg.dense.misc.UnrolledInverseFromMinor;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.DecompositionInterface;
+import org.ejml.interfaces.linsol.LinearSolver;
+
+
+/**
+ * Solver which uses an unrolled inverse to compute the inverse.  This can only invert matrices and not solve.
+ * This is faster than LU inverse but only supports small matrices..
+ *
+ * @author Peter Abeles
+ */
+public class LinearSolverUnrolled implements LinearSolver<DenseMatrix64F> {
+    DenseMatrix64F A;
+
+    @Override
+    public boolean setA(DenseMatrix64F A) {
+        if( A.numRows != A.numCols)
+            return false;
+
+        this.A = A;
+        return A.numRows <= UnrolledInverseFromMinor.MAX;
+    }
+
+    @Override
+    public double quality() {
+        throw new IllegalArgumentException("Not supported by this solver.");
+    }
+
+    @Override
+    public void solve(DenseMatrix64F B, DenseMatrix64F X) {
+        throw new RuntimeException("Not supported");
+    }
+
+    @Override
+    public void invert(DenseMatrix64F A_inv) {
+        if( A.numRows == 1 )
+            A_inv.set(0,  1.0/A.get(0));
+        UnrolledInverseFromMinor.inv(A,A_inv);
+    }
+
+    @Override
+    public boolean modifiesA() {
+        return false;
+    }
+
+    @Override
+    public boolean modifiesB() {
+        return false;
+    }
+
+    @Override
+    public <D extends DecompositionInterface> D getDecomposition() {
+        return null;
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/linsol/LinearSolver_B64_to_D64.java b/main/dense64/src/org/ejml/alg/dense/linsol/LinearSolver_B64_to_D64.java
new file mode 100644
index 0000000..e4e3aec
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/linsol/LinearSolver_B64_to_D64.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol;
+
+import org.ejml.alg.block.BlockMatrixOps;
+import org.ejml.alg.block.linsol.chol.BlockCholeskyOuterSolver;
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.DecompositionInterface;
+import org.ejml.interfaces.linsol.LinearSolver;
+
+
+/**
+ * Wrapper that allows {@link org.ejml.interfaces.linsol.LinearSolver <BlockMatrix64F>} to implements {@link org.ejml.interfaces.linsol.LinearSolver}.  It works
+ * by converting {@link DenseMatrix64F} into {@link BlockMatrix64F} and calling the equivalent
+ * functions.  Since a local copy is made all input matrices are never modified.
+ *
+ * @author Peter Abeles
+ */
+public class LinearSolver_B64_to_D64 implements LinearSolver<DenseMatrix64F> {
+    protected LinearSolver<BlockMatrix64F> alg = new BlockCholeskyOuterSolver();
+
+    // block matrix copy of the system A matrix.
+    protected BlockMatrix64F blockA = new BlockMatrix64F(1,1);
+    // block matrix copy of B matrix passed into solve
+    protected BlockMatrix64F blockB = new BlockMatrix64F(1,1);
+    // block matrix copy of X matrix passed into solve
+    protected BlockMatrix64F blockX = new BlockMatrix64F(1,1);
+
+    public LinearSolver_B64_to_D64(LinearSolver<BlockMatrix64F> alg) {
+        this.alg = alg;
+    }
+
+    /**
+     * Converts 'A' into a block matrix and call setA() on the block matrix solver.
+     *
+     * @param A The A matrix in the linear equation. Not modified. Reference saved.
+     * @return true if it can solve the system.
+     */
+    @Override
+    public boolean setA(DenseMatrix64F A) {
+        blockA.reshape(A.numRows,A.numCols,false);
+        BlockMatrixOps.convert(A,blockA);
+
+        return alg.setA(blockA);
+    }
+
+    @Override
+    public double quality() {
+        return alg.quality();
+    }
+
+    /**
+     * Converts B and X into block matrices and calls the block matrix solve routine.
+     *
+     * @param B A matrix ℜ <sup>m × p</sup>.  Not modified.
+     * @param X A matrix ℜ <sup>n × p</sup>, where the solution is written to.  Modified.
+     */
+    @Override
+    public void solve(DenseMatrix64F B, DenseMatrix64F X) {
+        blockB.reshape(B.numRows,B.numCols,false);
+        blockX.reshape(X.numRows,X.numCols,false);
+        BlockMatrixOps.convert(B,blockB);
+
+        alg.solve(blockB,blockX);
+
+        BlockMatrixOps.convert(blockX,X);
+    }
+
+    /**
+     * Creates a block matrix the same size as A_inv, inverts the matrix and copies the results back
+     * onto A_inv.
+     * 
+     * @param A_inv Where the inverted matrix saved. Modified.
+     */
+    @Override
+    public void invert(DenseMatrix64F A_inv) {
+        blockB.reshape(A_inv.numRows,A_inv.numCols,false);
+
+        alg.invert(blockB);
+
+        BlockMatrixOps.convert(blockB,A_inv);
+    }
+
+    @Override
+    public boolean modifiesA() {
+        return false;
+    }
+
+    @Override
+    public boolean modifiesB() {
+        return false;
+    }
+
+    @Override
+    public <D extends DecompositionInterface> D getDecomposition() {
+        return alg.getDecomposition();
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/linsol/chol/LinearSolverCholLDL_D64.java b/main/dense64/src/org/ejml/alg/dense/linsol/chol/LinearSolverCholLDL_D64.java
new file mode 100644
index 0000000..200faa7
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/linsol/chol/LinearSolverCholLDL_D64.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.chol;
+
+import org.ejml.alg.dense.decomposition.TriangularSolver;
+import org.ejml.alg.dense.decomposition.chol.CholeskyDecompositionLDL_D64;
+import org.ejml.alg.dense.linsol.LinearSolverAbstract_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.CholeskyLDLDecomposition;
+import org.ejml.ops.SpecializedOps;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class LinearSolverCholLDL_D64 extends LinearSolverAbstract_D64 {
+
+    private CholeskyDecompositionLDL_D64 decomposer;
+    private int n;
+    private double vv[];
+    private double el[];
+    private double d[];
+
+    public LinearSolverCholLDL_D64(CholeskyDecompositionLDL_D64 decomposer) {
+        this.decomposer = decomposer;
+    }
+
+    public LinearSolverCholLDL_D64() {
+        this.decomposer = new CholeskyDecompositionLDL_D64();
+    }
+
+    @Override
+    public boolean setA(DenseMatrix64F A) {
+        _setA(A);
+
+        if( decomposer.decompose(A) ){
+            n = A.numCols;
+            vv = decomposer._getVV();
+            el = decomposer.getL().data;
+            d = decomposer.getDiagonal();
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public double quality() {
+        return Math.abs(SpecializedOps.diagProd(decomposer.getL()));
+    }
+
+    /**
+     * <p>
+     * Using the decomposition, finds the value of 'X' in the linear equation below:<br>
+     *
+     * A*x = b<br>
+     *
+     * where A has dimension of n by n, x and b are n by m dimension.
+     * </p>
+     * <p>
+     * *Note* that 'b' and 'x' can be the same matrix instance.
+     * </p>
+     *
+     * @param B A matrix that is n by m.  Not modified.
+     * @param X An n by m matrix where the solution is writen to.  Modified.
+     */
+    @Override
+    public void solve( DenseMatrix64F B , DenseMatrix64F X ) {
+        if( B.numCols != X.numCols && B.numRows != n && X.numRows != n) {
+            throw new IllegalArgumentException("Unexpected matrix size");
+        }
+
+        int numCols = B.numCols;
+
+        double dataB[] = B.data;
+        double dataX[] = X.data;
+
+        for( int j = 0; j < numCols; j++ ) {
+            for( int i = 0; i < n; i++ ) vv[i] = dataB[i*numCols+j];
+            solveInternal();
+            for( int i = 0; i < n; i++ ) dataX[i*numCols+j] = vv[i];
+        }
+    }
+
+    /**
+     * Used internally to find the solution to a single column vector.
+     */
+    private void solveInternal() {
+        // solve L*s=b storing y in x
+        TriangularSolver.solveL(el,vv,n);
+
+        // solve D*y=s
+        for( int i = 0; i < n; i++ ) {
+            vv[i] /= d[i];
+        }
+
+        // solve L^T*x=y
+        TriangularSolver.solveTranL(el,vv,n);
+    }
+
+    /**
+     * Sets the matrix 'inv' equal to the inverse of the matrix that was decomposed.
+     *
+     * @param inv Where the value of the inverse will be stored.  Modified.
+     */
+    @Override
+    public void invert( DenseMatrix64F inv ) {
+        if( inv.numRows != n || inv.numCols != n ) {
+            throw new RuntimeException("Unexpected matrix dimension");
+        }
+
+        double a[] = inv.data;
+
+        // solve L*z = b
+        for( int i =0; i < n; i++ ) {
+            for( int j = 0; j <= i; j++ ) {
+                double sum = (i==j) ? 1.0 : 0.0;
+                for( int k=i-1; k >=j; k-- ) {
+                    sum -= el[i*n+k]*a[j*n+k];
+                }
+                a[j*n+i] = sum;
+            }
+        }
+
+        // solve D*y=z
+        for( int i =0; i < n; i++ ) {
+            double inv_d = 1.0/d[i];
+            for( int j = 0; j <= i; j++ ) {
+                a[j*n+i] *= inv_d;
+            }
+        }
+
+        // solve L^T*x = y
+        for( int i=n-1; i>=0; i-- ) {
+            for( int j = 0; j <= i; j++ ) {
+                double sum = (i<j) ? 0 : a[j*n+i];
+                for( int k=i+1;k<n;k++) {
+                    sum -= el[k*n+i]*a[j*n+k];
+                }
+                a[i*n+j] = a[j*n+i] = sum;
+            }
+        }
+    }
+
+    @Override
+    public boolean modifiesA() {
+        return decomposer.inputModified();
+    }
+
+    @Override
+    public boolean modifiesB() {
+        return false;
+    }
+
+    @Override
+    public CholeskyLDLDecomposition<DenseMatrix64F> getDecomposition() {
+        return decomposer;
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/src/org/ejml/alg/dense/linsol/chol/LinearSolverChol_B64.java b/main/dense64/src/org/ejml/alg/dense/linsol/chol/LinearSolverChol_B64.java
new file mode 100644
index 0000000..d67ef1d
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/linsol/chol/LinearSolverChol_B64.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.chol;
+
+import org.ejml.alg.block.BlockMatrixOps;
+import org.ejml.alg.block.linsol.chol.BlockCholeskyOuterSolver;
+import org.ejml.alg.dense.linsol.LinearSolver_B64_to_D64;
+import org.ejml.data.DenseMatrix64F;
+
+
+/**
+ * A wrapper around {@link org.ejml.interfaces.decomposition.CholeskyDecomposition}(BlockMatrix64F) that allows
+ * it to be easily used with {@link org.ejml.data.DenseMatrix64F}.
+ *
+ * @author Peter Abeles
+ */
+public class LinearSolverChol_B64 extends LinearSolver_B64_to_D64 {
+
+    public LinearSolverChol_B64() {
+        super(new BlockCholeskyOuterSolver());
+    }
+
+    /**
+     * Only converts the B matrix and passes that onto solve.  Te result is then copied into
+     * the input 'X' matrix.
+     * 
+     * @param B A matrix ℜ <sup>m × p</sup>.  Not modified.
+     * @param X A matrix ℜ <sup>n × p</sup>, where the solution is written to.  Modified.
+     */
+    @Override
+    public void solve(DenseMatrix64F B, DenseMatrix64F X) {
+        blockB.reshape(B.numRows,B.numCols,false);
+        BlockMatrixOps.convert(B,blockB);
+
+        // since overwrite B is true X does not need to be passed in
+        alg.solve(blockB,null);
+
+        BlockMatrixOps.convert(blockB,X);
+    }
+
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/linsol/chol/LinearSolverChol_D64.java b/main/dense64/src/org/ejml/alg/dense/linsol/chol/LinearSolverChol_D64.java
new file mode 100644
index 0000000..3bc1507
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/linsol/chol/LinearSolverChol_D64.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.chol;
+
+import org.ejml.alg.dense.decomposition.TriangularSolver;
+import org.ejml.alg.dense.decomposition.chol.CholeskyDecompositionCommon_D64;
+import org.ejml.alg.dense.linsol.LinearSolverAbstract_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.CholeskyDecomposition;
+import org.ejml.ops.SpecializedOps;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class LinearSolverChol_D64 extends LinearSolverAbstract_D64 {
+
+    CholeskyDecompositionCommon_D64 decomposer;
+    int n;
+    double vv[];
+    double t[];
+
+    public LinearSolverChol_D64(CholeskyDecompositionCommon_D64 decomposer) {
+        this.decomposer = decomposer;
+    }
+
+    @Override
+    public boolean setA(DenseMatrix64F A) {
+        if( A.numRows != A.numCols )
+            throw new IllegalArgumentException("Matrix must be square");
+
+        _setA(A);
+
+        if( decomposer.decompose(A) ){
+            n = A.numCols;
+            vv = decomposer._getVV();
+            t = decomposer.getT().data;
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public double quality() {
+        return SpecializedOps.qualityTriangular(decomposer.getT());
+    }
+
+    /**
+     * <p>
+     * Using the decomposition, finds the value of 'X' in the linear equation below:<br>
+     *
+     * A*x = b<br>
+     *
+     * where A has dimension of n by n, x and b are n by m dimension.
+     * </p>
+     * <p>
+     * *Note* that 'b' and 'x' can be the same matrix instance.
+     * </p>
+     *
+     * @param B A matrix that is n by m.  Not modified.
+     * @param X An n by m matrix where the solution is writen to.  Modified.
+     */
+    @Override
+    public void solve( DenseMatrix64F B , DenseMatrix64F X ) {
+        if( B.numCols != X.numCols || B.numRows != n || X.numRows != n) {
+            throw new IllegalArgumentException("Unexpected matrix size");
+        }
+
+        int numCols = B.numCols;
+
+        double dataB[] = B.data;
+        double dataX[] = X.data;
+
+        if(decomposer.isLower()) {
+            for( int j = 0; j < numCols; j++ ) {
+                for( int i = 0; i < n; i++ ) vv[i] = dataB[i*numCols+j];
+                solveInternalL();
+                for( int i = 0; i < n; i++ ) dataX[i*numCols+j] = vv[i];
+            }
+        } else {
+            throw new RuntimeException("Implement");
+        }
+    }
+
+    /**
+     * Used internally to find the solution to a single column vector.
+     */
+    private void solveInternalL() {
+        // solve L*y=b storing y in x
+        TriangularSolver.solveL(t,vv,n);
+
+        // solve L^T*x=y
+        TriangularSolver.solveTranL(t,vv,n);
+    }
+
+    /**
+     * Sets the matrix 'inv' equal to the inverse of the matrix that was decomposed.
+     *
+     * @param inv Where the value of the inverse will be stored.  Modified.
+     */
+    @Override
+    public void invert( DenseMatrix64F inv ) {
+        if( inv.numRows != n || inv.numCols != n ) {
+            throw new RuntimeException("Unexpected matrix dimension");
+        }
+        if( inv.data == t ) {
+            throw new IllegalArgumentException("Passing in the same matrix that was decomposed.");
+        }
+
+        double a[] = inv.data;
+
+        if(decomposer.isLower()) {
+            setToInverseL(a);
+        } else {
+            throw new RuntimeException("Implement");
+        }
+    }
+
+    /**
+     * Sets the matrix to the inverse using a lower triangular matrix.
+     */
+    public void setToInverseL( double a[] ) {
+        // TODO reorder these operations to avoid cache misses
+        
+        // inverts the lower triangular system and saves the result
+        // in the upper triangle to minimize cache misses
+        for( int i =0; i < n; i++ ) {
+            double el_ii = t[i*n+i];
+            for( int j = 0; j <= i; j++ ) {
+                double sum = (i==j) ? 1.0 : 0;
+                for( int k=i-1; k >=j; k-- ) {
+                    sum -= t[i*n+k]*a[j*n+k];
+                }
+                a[j*n+i] = sum / el_ii;
+            }
+        }
+        // solve the system and handle the previous solution being in the upper triangle
+        // takes advantage of symmetry
+        for( int i=n-1; i>=0; i-- ) {
+            double el_ii = t[i*n+i];
+
+            for( int j = 0; j <= i; j++ ) {
+                double sum = (i<j) ? 0 : a[j*n+i];
+                for( int k=i+1;k<n;k++) {
+                    sum -= t[k*n+i]*a[j*n+k];
+                }
+                a[i*n+j] = a[j*n+i] = sum / el_ii;
+            }
+        }
+    }
+
+    @Override
+    public boolean modifiesA() {
+        return decomposer.inputModified();
+    }
+
+    @Override
+    public boolean modifiesB() {
+        return false;
+    }
+
+    @Override
+    public CholeskyDecomposition<DenseMatrix64F> getDecomposition() {
+        return decomposer;
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/linsol/lu/LinearSolverLuBase_D64.java b/main/dense64/src/org/ejml/alg/dense/linsol/lu/LinearSolverLuBase_D64.java
new file mode 100644
index 0000000..20e89ea
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/linsol/lu/LinearSolverLuBase_D64.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.lu;
+
+import org.ejml.alg.dense.decomposition.lu.LUDecompositionBase_D64;
+import org.ejml.alg.dense.linsol.LinearSolverAbstract_D64;
+import org.ejml.data.DenseMatrix64F;
+
+
+/**
+ * @author Peter Abeles
+ */
+public abstract class LinearSolverLuBase_D64 extends LinearSolverAbstract_D64 {
+
+    protected LUDecompositionBase_D64 decomp;
+
+    public LinearSolverLuBase_D64(LUDecompositionBase_D64 decomp) {
+        this.decomp = decomp;
+
+    }
+
+    @Override
+    public boolean setA(DenseMatrix64F A) {
+        _setA(A);
+
+        return decomp.decompose(A);
+    }
+
+    @Override
+    public double quality() {
+        return decomp.quality();
+    }
+
+    @Override
+    public void invert(DenseMatrix64F A_inv) {
+        double []vv = decomp._getVV();
+        DenseMatrix64F LU = decomp.getLU();
+
+        if( A_inv.numCols != LU.numCols || A_inv.numRows != LU.numRows )
+            throw new IllegalArgumentException("Unexpected matrix dimension");
+
+        int n = A.numCols;
+
+        double dataInv[] = A_inv.data;
+
+        for( int j = 0; j < n; j++ ) {
+            // don't need to change inv into an identity matrix before hand
+            for( int i = 0; i < n; i++ ) vv[i] = i == j ? 1 : 0;
+            decomp._solveVectorInternal(vv);
+//            for( int i = 0; i < n; i++ ) dataInv[i* n +j] = vv[i];
+            int index = j;
+            for( int i = 0; i < n; i++ , index += n) dataInv[ index ] = vv[i];
+        }
+    }
+
+    /**
+     * This attempts to improve upon the solution generated by account
+     * for numerical imprecisions.  See numerical recipes for more information.  It
+     * is assumed that solve has already been run on 'b' and 'x' at least once.
+     *
+     * @param b A matrix. Not modified.
+     * @param x A matrix. Modified.
+     */
+    public void improveSol( DenseMatrix64F b , DenseMatrix64F x )
+    {
+        if( b.numCols != x.numCols ) {
+            throw new IllegalArgumentException("bad shapes");
+        }
+
+        double dataA[] = A.data;
+        double dataB[] = b.data;
+        double dataX[] = x.data;
+
+        final int nc = b.numCols;
+        final int n = b.numCols;
+
+        double []vv = decomp._getVV();
+
+//        BigDecimal sdp = new BigDecimal(0);
+        for( int k = 0; k < nc; k++ ) {
+            for( int i = 0; i < n; i++ ) {
+                // *NOTE* in the book this is a long double.  extra precision might be required
+                double sdp = -dataB[ i * nc + k];
+//                BigDecimal sdp = new BigDecimal(-dataB[ i * nc + k]);
+                for( int j = 0; j < n; j++ ) {
+                    sdp += dataA[i* n +j] * dataX[ j * nc + k];
+//                    sdp = sdp.add( BigDecimal.valueOf(dataA[i* n +j] * dataX[ j * nc + k]));
+                }
+                vv[i] = sdp;
+//                vv[i] = sdp.doubleValue();
+            }
+            decomp._solveVectorInternal(vv);
+            for( int i = 0; i < n; i++ ) {
+                dataX[i*nc + k] -= vv[i];
+            }
+        }
+    }
+
+    @Override
+    public boolean modifiesA() {
+        return false;
+    }
+
+    @Override
+    public boolean modifiesB() {
+        return false;
+    }
+
+    @Override
+    public LUDecompositionBase_D64 getDecomposition() {
+        return decomp;
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/src/org/ejml/alg/dense/linsol/lu/LinearSolverLuKJI_D64.java b/main/dense64/src/org/ejml/alg/dense/linsol/lu/LinearSolverLuKJI_D64.java
new file mode 100644
index 0000000..6b082a6
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/linsol/lu/LinearSolverLuKJI_D64.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.lu;
+
+import org.ejml.alg.dense.decomposition.lu.LUDecompositionBase_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.SpecializedOps;
+
+
+/**
+ * To avoid cpu cache issues the order in which the arrays are traversed have been changed.
+ * There seems to be no performance benit relative to {@link LinearSolverLu_D64} in this approach
+ * and b and x can't be the same instance, which means it has slightly less functionality.
+ *
+ * @author Peter Abeles
+ */
+public class LinearSolverLuKJI_D64 extends LinearSolverLuBase_D64 {
+
+    private double []dataLU;
+    private int[] pivot;
+
+    public LinearSolverLuKJI_D64(LUDecompositionBase_D64 decomp) {
+        super(decomp);
+
+    }
+
+    @Override
+    public boolean setA(DenseMatrix64F A) {
+        boolean ret = super.setA(A);
+
+        pivot = decomp.getPivot();
+        dataLU = decomp.getLU().data;
+
+        return ret;
+    }
+
+    /**
+     * An other implementation of solve() that processes the matrices in a different order.
+     * It seems to have the same runtime performance as {@link #solve} and is more complicated.
+     * It is being kept around to avoid future replication of work.
+     *
+     * @param b A matrix that is n by m.  Not modified.
+     * @param x An n by m matrix where the solution is writen to.  Modified.
+     */
+    @Override
+    public void solve(DenseMatrix64F b, DenseMatrix64F x) {
+        if( b.numCols != x.numCols || b.numRows != numRows || x.numRows != numCols) {
+            throw new IllegalArgumentException("Unexpected matrix size");
+        }
+
+        if( b != x ) {
+            SpecializedOps.copyChangeRow(pivot,b,x);
+        } else {
+            throw new IllegalArgumentException("Current doesn't support using the same matrix instance");
+        }
+
+        // Copy right hand side with pivoting
+        int nx = b.numCols;
+        double[] dataX = x.data;
+
+        // Solve L*Y = B(piv,:)
+        for (int k = 0; k < numCols; k++) {
+            for (int i = k+1; i < numCols; i++) {
+                for (int j = 0; j < nx; j++) {
+                    dataX[i*nx+j] -= dataX[k*nx+j]*dataLU[i* numCols +k];
+                }
+            }
+        }
+        // Solve U*X = Y;
+        for (int k = numCols -1; k >= 0; k--) {
+            for (int j = 0; j < nx; j++) {
+                dataX[k*nx+j] /= dataLU[k* numCols +k];
+            }
+            for (int i = 0; i < k; i++) {
+                for (int j = 0; j < nx; j++) {
+                    dataX[i*nx+j] -= dataX[k*nx+j]*dataLU[i* numCols +k];
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/src/org/ejml/alg/dense/linsol/lu/LinearSolverLu_D64.java b/main/dense64/src/org/ejml/alg/dense/linsol/lu/LinearSolverLu_D64.java
new file mode 100644
index 0000000..92c55cf
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/linsol/lu/LinearSolverLu_D64.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.lu;
+
+import org.ejml.alg.dense.decomposition.lu.LUDecompositionBase_D64;
+import org.ejml.data.DenseMatrix64F;
+
+
+/**
+ * For each column in the B matrix it makes a copy, which is then solved for and
+ * writen into X.  By making a copy of the column cpu cache issues are reduced.
+ *
+ * @author Peter Abeles
+ */
+public class LinearSolverLu_D64 extends LinearSolverLuBase_D64 {
+
+    boolean doImprove = false;
+
+    public LinearSolverLu_D64(LUDecompositionBase_D64 decomp) {
+        super(decomp);
+    }
+
+    public LinearSolverLu_D64(LUDecompositionBase_D64 decomp, boolean doImprove) {
+        super(decomp);
+        this.doImprove = doImprove;
+    }
+
+
+    @Override
+    public void solve(DenseMatrix64F b, DenseMatrix64F x) {
+        if( b.numCols != x.numCols || b.numRows != numRows || x.numRows != numCols) {
+            throw new IllegalArgumentException("Unexpected matrix size");
+        }
+
+        int numCols = b.numCols;
+
+        double dataB[] = b.data;
+        double dataX[] = x.data;
+
+        double []vv = decomp._getVV();
+
+//        for( int j = 0; j < numCols; j++ ) {
+//            for( int i = 0; i < this.numCols; i++ ) vv[i] = dataB[i*numCols+j];
+//            decomp._solveVectorInternal(vv);
+//            for( int i = 0; i < this.numCols; i++ ) dataX[i*numCols+j] = vv[i];
+//        }
+        for( int j = 0; j < numCols; j++ ) {
+            int index = j;
+            for( int i = 0; i < this.numCols; i++ , index += numCols ) vv[i] = dataB[index];
+            decomp._solveVectorInternal(vv);
+            index = j;
+            for( int i = 0; i < this.numCols; i++ , index += numCols ) dataX[index] = vv[i];
+        }
+
+        if( doImprove ) {
+            improveSol(b,x);
+        }
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/linsol/qr/AdjLinearSolverQr_D64.java b/main/dense64/src/org/ejml/alg/dense/linsol/qr/AdjLinearSolverQr_D64.java
new file mode 100644
index 0000000..777b435
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/linsol/qr/AdjLinearSolverQr_D64.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.qr;
+
+import org.ejml.alg.dense.decomposition.qr.QRDecompositionHouseholderColumn_D64;
+import org.ejml.alg.dense.decomposition.qr.QrUpdate;
+import org.ejml.alg.dense.linsol.AdjustableLinearSolver;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+
+
+/**
+ * A solver for QR decomposition that can efficiently modify the previous decomposition when
+ * data is added or removed.
+ *
+ * @author Peter Abeles
+ */
+public class AdjLinearSolverQr_D64 extends LinearSolverQr_D64 implements AdjustableLinearSolver {
+
+    private QrUpdate update;
+
+    private DenseMatrix64F A;
+
+    public AdjLinearSolverQr_D64() {
+        super( new QRDecompositionHouseholderColumn_D64() );
+    }
+
+    @Override
+    public void setMaxSize( int maxRows , int maxCols ) {
+        // allow it some room to grow
+        maxRows += 5;
+
+        super.setMaxSize(maxRows,maxCols);
+
+        update = new QrUpdate(maxRows,maxCols,true);
+        A = new DenseMatrix64F(maxRows,maxCols);
+    }
+
+    /**
+     * Compute the A matrix from the Q and R matrices.
+     *
+     * @return The A matrix.
+     */
+    @Override
+    public DenseMatrix64F getA() {
+        if( A.data.length < numRows*numCols ) {
+            A = new DenseMatrix64F(numRows,numCols);
+        }
+        A.reshape(numRows,numCols, false);
+        CommonOps.mult(Q,R,A);
+
+        return A;
+    }
+
+    @Override
+    public boolean addRowToA(double[] A_row , int rowIndex ) {
+        // see if it needs to grow the data structures
+        if( numRows + 1 > maxRows) {
+            // grow by 10%
+            int grow = maxRows / 10;
+            if( grow < 1 ) grow = 1;
+            maxRows = numRows + grow;
+            Q.reshape(maxRows,maxRows,true);
+            R.reshape(maxRows,maxCols,true);
+        }
+
+        update.addRow(Q,R,A_row,rowIndex,true);
+        numRows++;
+
+        return true;
+    }
+
+    @Override
+    public boolean removeRowFromA(int index) {
+        update.deleteRow(Q,R,index,true);
+        numRows--;
+        return true;
+    }
+
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/linsol/qr/BaseLinearSolverQrp_D64.java b/main/dense64/src/org/ejml/alg/dense/linsol/qr/BaseLinearSolverQrp_D64.java
new file mode 100644
index 0000000..35a4a96
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/linsol/qr/BaseLinearSolverQrp_D64.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.qr;
+
+import org.ejml.alg.dense.decomposition.TriangularSolver;
+import org.ejml.alg.dense.linsol.LinearSolverAbstract_D64;
+import org.ejml.alg.dense.linsol.LinearSolverSafe;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.LinearSolverFactory;
+import org.ejml.interfaces.decomposition.QRPDecomposition;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.SpecializedOps;
+
+/**
+ * <p>
+ * Base class for QR pivot based pseudo inverse classes.  It will return either the
+ * basic of minimal 2-norm solution. See [1] for details.  The minimal 2-norm solution refers to the solution
+ * 'x' whose 2-norm is the smallest making it unique, not some other error function.
+ * </p>
+ *
+ * <p>
+ * <pre>
+ * R = [ R12  R12 ] r      P^T*x = [ y ] r       Q^T*b = [ c ] r
+ *     [  0    0  ] m-r            [ z ] n -r            [ d ] m-r
+ *        r   n-r
+ *
+ * where r is the rank of the matrix and (m,n) is the dimension of the linear system.
+ * </pre>
+ * </p>
+ *
+ * <p>
+ * <pre>
+ * The solution 'x' is found by solving the system below.  The basic solution is found by setting z=0
+ *
+ *     [ R_11^-1*(c - R12*z) ]
+ * x = [          z          ]
+ * </pre>
+ * </p>
+ *
+ * <p>
+ * NOTE: The matrix rank is determined using the provided QR decomposition. [1] mentions that this will not always
+ * work and could cause some problems.
+ * </p>
+ *
+ * <p>
+ * [1] See page 258-259 in Gene H. Golub and Charles F. Van Loan "Matrix Computations" 3rd Ed, 1996
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public abstract class BaseLinearSolverQrp_D64 extends LinearSolverAbstract_D64 {
+
+    QRPDecomposition<DenseMatrix64F> decomposition;
+
+    // if true then only the basic solution will be found
+    protected boolean norm2Solution;
+
+    protected DenseMatrix64F Y = new DenseMatrix64F(1,1);
+    protected DenseMatrix64F R = new DenseMatrix64F(1,1);
+    
+    // stores sub-matrices inside the R matrix
+    protected DenseMatrix64F R11 = new DenseMatrix64F(1,1);
+    
+    // store an identity matrix for computing the inverse
+    protected DenseMatrix64F I = new DenseMatrix64F(1,1);
+
+    // rank of the system matrix
+    protected int rank;
+
+    protected LinearSolver<DenseMatrix64F> internalSolver = LinearSolverFactory.leastSquares(1, 1);
+
+    // used to compute optimal 2-norm solution
+    private DenseMatrix64F W = new DenseMatrix64F(1,1);
+
+    /**
+     * Configures internal parameters.
+     *
+     * @param decomposition Used to solve the linear system.
+     * @param norm2Solution If true then the optimal 2-norm solution will be computed for degenerate systems.
+     */
+    protected BaseLinearSolverQrp_D64(QRPDecomposition<DenseMatrix64F> decomposition,
+                                      boolean norm2Solution)
+    {
+        this.decomposition = decomposition;
+        this.norm2Solution = norm2Solution;
+
+        if( internalSolver.modifiesA() )
+            internalSolver = new LinearSolverSafe<DenseMatrix64F>(internalSolver);
+    }
+
+    @Override
+    public boolean setA(DenseMatrix64F A) {
+        _setA(A);
+
+        if( !decomposition.decompose(A) )
+            return false;
+
+        rank = decomposition.getRank();
+
+        R.reshape(numRows,numCols);
+        decomposition.getR(R,false);
+
+        // extract the r11 triangle sub matrix
+        R11.reshape(rank, rank);
+        CommonOps.extract(R, 0, rank, 0, rank, R11, 0, 0);
+
+        if( norm2Solution && rank < numCols ) {
+            // extract the R12 sub-matrix
+            W.reshape(rank,numCols - rank);
+            CommonOps.extract(R,0,rank,rank,numCols,W,0,0);
+
+            // W=inv(R11)*R12
+            TriangularSolver.solveU(R11.data, 0, R11.numCols, R11.numCols, W.data, 0, W.numCols, W.numCols);
+
+            // set the identity matrix in the upper portion
+            W.reshape(numCols, W.numCols,true);
+
+            for( int i = 0; i < numCols-rank; i++ ) {
+                for( int j = 0; j < numCols-rank; j++ ) {
+                    if( i == j )
+                        W.set(i+rank,j,-1);
+                    else
+                        W.set(i+rank,j,0);
+                }
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public double quality() {
+        return SpecializedOps.qualityTriangular(R);
+    }
+
+    /**
+     * <p>
+     * Upgrades the basic solution to the optimal 2-norm solution.
+     * </p>
+     *
+     * <pre>
+     * First solves for 'z'
+     *
+     *       || x_b - P*[ R_11^-1 * R_12 ] * z ||2
+     * min z ||         [ - I_{n-r}      ]     ||
+     *
+     * </pre>
+     *
+     * @param X basic solution, also output solution
+     */
+    protected void upgradeSolution( DenseMatrix64F X ) {
+        DenseMatrix64F z = Y; // recycle Y
+
+        // compute the z which will minimize the 2-norm of X
+        // because of the identity matrix tacked onto the end 'A' should never be singular
+        if( !internalSolver.setA(W) )
+            throw new RuntimeException("This should never happen.  Is input NaN?");
+        z.reshape(numCols-rank,1);
+        internalSolver.solve(X, z);
+
+        // compute X by tweaking the original
+        CommonOps.multAdd(-1, W, z, X);
+    }
+
+    @Override
+    public void invert(DenseMatrix64F A_inv) {
+        if( A_inv.numCols != numRows || A_inv.numRows != numCols )
+            throw new IllegalArgumentException("Unexpected dimensions for A_inv");
+
+        I.reshape(numRows, numRows);
+        CommonOps.setIdentity(I);
+
+        solve(I, A_inv);
+    }
+
+    public QRPDecomposition<DenseMatrix64F> getDecomposition() {
+        return decomposition;
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQrBlock64_D64.java b/main/dense64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQrBlock64_D64.java
new file mode 100644
index 0000000..87ae669
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQrBlock64_D64.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.qr;
+
+import org.ejml.alg.block.linsol.qr.BlockQrHouseHolderSolver;
+import org.ejml.alg.dense.linsol.LinearSolver_B64_to_D64;
+
+
+/**
+ * Wrapper around {@link BlockQrHouseHolderSolver} that allows it to process
+ * {@link org.ejml.data.DenseMatrix64F}.
+ *
+ * @author Peter Abeles
+ */
+public class LinearSolverQrBlock64_D64 extends LinearSolver_B64_to_D64 {
+
+    public LinearSolverQrBlock64_D64() {
+        super(new BlockQrHouseHolderSolver());
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQrHouseCol_D64.java b/main/dense64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQrHouseCol_D64.java
new file mode 100644
index 0000000..5771706
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQrHouseCol_D64.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.qr;
+
+import org.ejml.alg.dense.decomposition.TriangularSolver;
+import org.ejml.alg.dense.decomposition.qr.QRDecompositionHouseholderColumn_D64;
+import org.ejml.alg.dense.decomposition.qr.QrHelperFunctions_D64;
+import org.ejml.alg.dense.linsol.LinearSolverAbstract_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.QRDecomposition;
+import org.ejml.ops.SpecializedOps;
+
+
+/**
+ * <p>
+ * QR decomposition can be used to solve for systems.  However, this is not as computationally efficient
+ * as LU decomposition and costs about 3n<sup>2</sup> flops.
+ * </p>
+ * <p>
+ * It solve for x by first multiplying b by the transpose of Q then solving for the result.
+ * <br>
+ * QRx=b<br>
+ * Rx=Q^T b<br>
+ * </p>
+ *
+ * <p>
+ * A column major decomposition is used in this solver.
+ * <p>
+ *
+ * @author Peter Abeles
+ */
+public class LinearSolverQrHouseCol_D64 extends LinearSolverAbstract_D64 {
+
+    private QRDecompositionHouseholderColumn_D64 decomposer;
+
+    private DenseMatrix64F a = new DenseMatrix64F(1,1);
+    private DenseMatrix64F temp = new DenseMatrix64F(1,1);
+
+    protected int maxRows = -1;
+    protected int maxCols = -1;
+
+    private double[][] QR; // a column major QR matrix
+    private DenseMatrix64F R = new DenseMatrix64F(1,1);
+    private double gammas[];
+
+    /**
+     * Creates a linear solver that uses QR decomposition.
+     */
+    public LinearSolverQrHouseCol_D64() {
+        decomposer = new QRDecompositionHouseholderColumn_D64();
+    }
+
+    public void setMaxSize( int maxRows , int maxCols )
+    {
+        this.maxRows = maxRows; this.maxCols = maxCols;
+    }
+
+    /**
+     * Performs QR decomposition on A
+     *
+     * @param A not modified.
+     */
+    @Override
+    public boolean setA(DenseMatrix64F A) {
+        if( A.numRows < A.numCols )
+            throw new IllegalArgumentException("Can't solve for wide systems.  More variables than equations.");
+        if( A.numRows > maxRows || A.numCols > maxCols )
+            setMaxSize(A.numRows,A.numCols);
+
+        R.reshape(A.numCols,A.numCols);
+        a.reshape(A.numRows,1);
+        temp.reshape(A.numRows,1);
+
+        _setA(A);
+        if( !decomposer.decompose(A) )
+            return false;
+
+        gammas = decomposer.getGammas();
+        QR = decomposer.getQR();
+        decomposer.getR(R,true);
+        return true;
+    }
+
+    @Override
+    public double quality() {
+        return SpecializedOps.qualityTriangular(R);
+    }
+
+    /**
+     * Solves for X using the QR decomposition.
+     *
+     * @param B A matrix that is n by m.  Not modified.
+     * @param X An n by m matrix where the solution is written to.  Modified.
+     */
+    @Override
+    public void solve(DenseMatrix64F B, DenseMatrix64F X) {
+        if( X.numRows != numCols )
+            throw new IllegalArgumentException("Unexpected dimensions for X: X rows = "+X.numRows+" expected = "+numCols);
+        else if( B.numRows != numRows || B.numCols != X.numCols )
+            throw new IllegalArgumentException("Unexpected dimensions for B");
+
+        int BnumCols = B.numCols;
+        
+        // solve each column one by one
+        for( int colB = 0; colB < BnumCols; colB++ ) {
+
+            // make a copy of this column in the vector
+            for( int i = 0; i < numRows; i++ ) {
+                a.data[i] = B.data[i*BnumCols + colB];
+            }
+
+            // Solve Qa=b
+            // a = Q'b
+            // a = Q_{n-1}...Q_2*Q_1*b
+            //
+            // Q_n*b = (I-gamma*u*u^T)*b = b - u*(gamma*U^T*b)
+            for( int n = 0; n < numCols; n++ ) {
+                double []u = QR[n];
+
+                double vv = u[n];
+                u[n] = 1;
+                QrHelperFunctions_D64.rank1UpdateMultR(a, u, gammas[n], 0, n, numRows, temp.data);
+                u[n] = vv;
+            }
+
+            // solve for Rx = b using the standard upper triangular solver
+            TriangularSolver.solveU(R.data,a.data,numCols);
+
+            // save the results
+            for( int i = 0; i < numCols; i++ ) {
+                X.data[i*X.numCols+colB] = a.data[i];
+            }
+        }
+    }
+
+    @Override
+    public boolean modifiesA() {
+        return false;
+    }
+
+    @Override
+    public boolean modifiesB() {
+        return false;
+    }
+
+    @Override
+    public QRDecomposition<DenseMatrix64F> getDecomposition() {
+        return decomposer;
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQrHouseTran_D64.java b/main/dense64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQrHouseTran_D64.java
new file mode 100644
index 0000000..bbe6d7d
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQrHouseTran_D64.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.qr;
+
+import org.ejml.alg.dense.decomposition.TriangularSolver;
+import org.ejml.alg.dense.decomposition.qr.QRDecompositionHouseholderTran_D64;
+import org.ejml.alg.dense.linsol.LinearSolverAbstract_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.QRDecomposition;
+import org.ejml.ops.SpecializedOps;
+
+
+/**
+ * <p>
+ * QR decomposition can be used to solve for systems.  However, this is not as computationally efficient
+ * as LU decomposition and costs about 3n<sup>2</sup> flops.
+ * </p>
+ * <p>
+ * It solve for x by first multiplying b by the transpose of Q then solving for the result.
+ * <br>
+ * QRx=b<br>
+ * Rx=Q^T b<br>
+ * </p>
+ *
+ * <p>
+ * A column major decomposition is used in this solver.
+ * <p>
+ *
+ * @author Peter Abeles
+ */
+public class LinearSolverQrHouseTran_D64 extends LinearSolverAbstract_D64 {
+
+    private QRDecompositionHouseholderTran_D64 decomposer;
+
+    private double []a;
+
+    protected int maxRows = -1;
+    protected int maxCols = -1;
+
+    private DenseMatrix64F QR; // a column major QR matrix
+    private DenseMatrix64F U;
+
+    /**
+     * Creates a linear solver that uses QR decomposition.
+     */
+    public LinearSolverQrHouseTran_D64() {
+        decomposer = new QRDecompositionHouseholderTran_D64();
+    }
+
+    public void setMaxSize( int maxRows , int maxCols )
+    {
+        this.maxRows = maxRows; this.maxCols = maxCols;
+
+        a = new double[ maxRows ];
+    }
+
+    /**
+     * Performs QR decomposition on A
+     *
+     * @param A not modified.
+     */
+    @Override
+    public boolean setA(DenseMatrix64F A) {
+        if( A.numRows > maxRows || A.numCols > maxCols )
+            setMaxSize(A.numRows,A.numCols);
+
+        _setA(A);
+        if( !decomposer.decompose(A) )
+            return false;
+
+        QR = decomposer.getQR();
+        return true;
+    }
+
+    @Override
+    public double quality() {
+        // even those it is transposed the diagonal elements are at the same
+        // elements
+        return SpecializedOps.qualityTriangular(QR);
+    }
+
+    /**
+     * Solves for X using the QR decomposition.
+     *
+     * @param B A matrix that is n by m.  Not modified.
+     * @param X An n by m matrix where the solution is written to.  Modified.
+     */
+    @Override
+    public void solve(DenseMatrix64F B, DenseMatrix64F X) {
+        if( X.numRows != numCols )
+            throw new IllegalArgumentException("Unexpected dimensions for X: X rows = "+X.numRows+" expected = "+numCols);
+        else if( B.numRows != numRows || B.numCols != X.numCols )
+            throw new IllegalArgumentException("Unexpected dimensions for B");
+
+        U = decomposer.getR(U,true);
+        final double gammas[] = decomposer.getGammas();
+        final double dataQR[] = QR.data;
+
+        final int BnumCols = B.numCols;
+
+        // solve each column one by one
+        for( int colB = 0; colB < BnumCols; colB++ ) {
+
+            // make a copy of this column in the vector
+            for( int i = 0; i < numRows; i++ ) {
+                a[i] = B.data[i*BnumCols + colB];
+            }
+
+            // Solve Qa=b
+            // a = Q'b
+            // a = Q_{n-1}...Q_2*Q_1*b
+            //
+            // Q_n*b = (I-gamma*u*u^T)*b = b - u*(gamma*U^T*b)
+            for( int n = 0; n < numCols; n++ ) {
+                int indexU = n*numRows + n + 1;
+
+                double ub = a[n];
+                // U^T*b
+                for( int i = n+1; i < numRows; i++ , indexU++ ) {
+                    ub += dataQR[indexU]*a[i];
+                }
+
+                // gamma*U^T*b
+                ub *= gammas[n];
+
+                a[n] -= ub;
+                indexU = n*numRows + n + 1;
+                for( int i = n+1; i < numRows; i++ , indexU++) {
+                    a[i] -= dataQR[indexU]*ub;
+                }
+            }
+
+            // solve for Rx = b using the standard upper triangular solver
+            TriangularSolver.solveU(U.data,a,numCols);
+
+            // save the results
+            for( int i = 0; i < numCols; i++ ) {
+                X.data[i*X.numCols+colB] = a[i];
+            }
+        }
+    }
+
+    @Override
+    public boolean modifiesA() {
+        return decomposer.inputModified();
+    }
+
+    @Override
+    public boolean modifiesB() {
+        return false;
+    }
+
+    @Override
+    public QRDecomposition<DenseMatrix64F> getDecomposition() {
+        return decomposer;
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQrHouse_D64.java b/main/dense64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQrHouse_D64.java
new file mode 100644
index 0000000..1727d49
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQrHouse_D64.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.qr;
+
+import org.ejml.alg.dense.decomposition.TriangularSolver;
+import org.ejml.alg.dense.decomposition.qr.QRDecompositionHouseholder_D64;
+import org.ejml.alg.dense.linsol.LinearSolverAbstract_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.QRDecomposition;
+import org.ejml.ops.SpecializedOps;
+
+
+/**
+ * <p>
+ * QR decomposition can be used to solve for systems.  However, this is not as computationally efficient
+ * as LU decomposition and costs about 3n<sup>2</sup> flops.
+ * </p>
+ * <p>
+ * It solve for x by first multiplying b by the transpose of Q then solving for the result.
+ * <br>
+ * QRx=b<br>
+ * Rx=Q^T b<br>
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class LinearSolverQrHouse_D64 extends LinearSolverAbstract_D64 {
+
+    private QRDecompositionHouseholder_D64 decomposer;
+
+    private double []a,u;
+
+    private int maxRows = -1;
+
+    private DenseMatrix64F QR;
+    private double gammas[];
+
+    /**
+     * Creates a linear solver that uses QR decomposition.
+     */
+    public LinearSolverQrHouse_D64() {
+        decomposer = new QRDecompositionHouseholder_D64();
+
+
+    }
+
+    public void setMaxSize( int maxRows ) {
+        this.maxRows = maxRows;
+
+        a = new double[ maxRows ];
+        u = new double[ maxRows ];
+    }
+
+    /**
+     * Performs QR decomposition on A
+     *
+     * @param A not modified.
+     */
+    @Override
+    public boolean setA(DenseMatrix64F A) {
+        if( A.numRows > maxRows ) {
+            setMaxSize(A.numRows);
+        }
+
+        _setA(A);
+        if( !decomposer.decompose(A) )
+            return false;
+        
+        gammas = decomposer.getGammas();
+        QR = decomposer.getQR();
+
+        return true;
+    }
+
+    @Override
+    public double quality() {
+        return SpecializedOps.qualityTriangular(QR);
+    }
+
+    /**
+     * Solves for X using the QR decomposition.
+     *
+     * @param B A matrix that is n by m.  Not modified.
+     * @param X An n by m matrix where the solution is writen to.  Modified.
+     */
+    @Override
+    public void solve(DenseMatrix64F B, DenseMatrix64F X) {
+        if( X.numRows != numCols )
+            throw new IllegalArgumentException("Unexpected dimensions for X");
+        else if( B.numRows != numRows || B.numCols != X.numCols )
+            throw new IllegalArgumentException("Unexpected dimensions for B");
+
+        int BnumCols = B.numCols;
+
+        // solve each column one by one
+        for( int colB = 0; colB < BnumCols; colB++ ) {
+
+            // make a copy of this column in the vector
+            for( int i = 0; i < numRows; i++ ) {
+                a[i] = B.data[i*BnumCols + colB];
+            }
+
+            // Solve Qa=b
+            // a = Q'b
+            // a = Q_{n-1}...Q_2*Q_1*b
+            //
+            // Q_n*b = (I-gamma*u*u^T)*b = b - u*(gamma*U^T*b)
+            for( int n = 0; n < numCols; n++ ) {
+                u[n] = 1;
+                double ub = a[n];
+                // U^T*b
+                for( int i = n+1; i < numRows; i++ ) {
+                    ub += (u[i] = QR.unsafe_get(i,n))*a[i];
+                }
+
+                // gamma*U^T*b
+                ub *= gammas[n];
+ 
+                for( int i = n; i < numRows; i++ ) {
+                    a[i] -= u[i]*ub;
+                }
+            }
+
+            // solve for Rx = b using the standard upper triangular solver
+            TriangularSolver.solveU(QR.data,a,numCols);
+
+            // save the results
+            for( int i = 0; i < numCols; i++ ) {
+                X.data[i*X.numCols+colB] = a[i];
+            }
+        }
+    }
+
+    @Override
+    public boolean modifiesA() {
+        return false;
+    }
+
+    @Override
+    public boolean modifiesB() {
+        return false;
+    }
+
+    @Override
+    public QRDecomposition<DenseMatrix64F> getDecomposition() {
+        return decomposer;
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQr_D64.java b/main/dense64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQr_D64.java
new file mode 100644
index 0000000..0a607a1
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQr_D64.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.qr;
+
+import org.ejml.alg.dense.decomposition.TriangularSolver;
+import org.ejml.alg.dense.linsol.LinearSolverAbstract_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.QRDecomposition;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.SpecializedOps;
+
+
+/**
+ * <p>
+ * A solver for a generic QR decomposition algorithm.  This will in general be a bit slower than the
+ * specialized once since the full Q and R matrices need to be extracted.
+ * </p>
+ * <p>
+ * It solve for x by first multiplying b by the transpose of Q then solving for the result.
+ * <br>
+ * QRx=b<br>
+ * Rx=Q^T b<br>
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class LinearSolverQr_D64 extends LinearSolverAbstract_D64 {
+
+    private QRDecomposition<DenseMatrix64F> decomposer;
+
+    protected int maxRows = -1;
+    protected int maxCols = -1;
+
+    protected DenseMatrix64F Q;
+    protected DenseMatrix64F R;
+
+    private DenseMatrix64F Y,Z;
+
+    /**
+     * Creates a linear solver that uses QR decomposition.
+     *
+     */
+    public LinearSolverQr_D64(QRDecomposition<DenseMatrix64F> decomposer) {
+        this.decomposer = decomposer;
+    }
+
+    /**
+     * Changes the size of the matrix it can solve for
+     *
+     * @param maxRows Maximum number of rows in the matrix it will decompose.
+     * @param maxCols Maximum number of columns in the matrix it will decompose.
+     */
+    public void setMaxSize( int maxRows , int maxCols )
+    {
+        this.maxRows = maxRows; this.maxCols = maxCols;
+
+        Q = new DenseMatrix64F(maxRows,maxRows);
+        R = new DenseMatrix64F(maxRows,maxCols);
+
+        Y = new DenseMatrix64F(maxRows,1);
+        Z = new DenseMatrix64F(maxRows,1);
+    }
+
+    /**
+     * Performs QR decomposition on A
+     *
+     * @param A not modified.
+     */
+    @Override
+    public boolean setA(DenseMatrix64F A) {
+        if( A.numRows > maxRows || A.numCols > maxCols ) {
+            setMaxSize(A.numRows,A.numCols);
+        }
+
+        _setA(A);
+        if( !decomposer.decompose(A) )
+            return false;
+
+        Q.reshape(numRows,numRows, false);
+        R.reshape(numRows,numCols, false);
+        decomposer.getQ(Q,false);
+        decomposer.getR(R,false);
+
+        return true;
+    }
+
+    @Override
+    public double quality() {
+        return SpecializedOps.qualityTriangular(R);
+    }
+
+    /**
+     * Solves for X using the QR decomposition.
+     *
+     * @param B A matrix that is n by m.  Not modified.
+     * @param X An n by m matrix where the solution is written to.  Modified.
+     */
+    @Override
+    public void solve(DenseMatrix64F B, DenseMatrix64F X) {
+        if( X.numRows != numCols )
+            throw new IllegalArgumentException("Unexpected dimensions for X");
+        else if( B.numRows != numRows || B.numCols != X.numCols )
+            throw new IllegalArgumentException("Unexpected dimensions for B");
+
+        int BnumCols = B.numCols;
+
+        Y.reshape(numRows,1, false);
+        Z.reshape(numRows,1, false);
+
+        // solve each column one by one
+        for( int colB = 0; colB < BnumCols; colB++ ) {
+
+            // make a copy of this column in the vector
+            for( int i = 0; i < numRows; i++ ) {
+                Y.data[i] = B.get(i,colB);
+            }
+
+            // Solve Qa=b
+            // a = Q'b
+            CommonOps.multTransA(Q,Y,Z);
+
+            // solve for Rx = b using the standard upper triangular solver
+            TriangularSolver.solveU(R.data,Z.data,numCols);
+
+            // save the results
+            for( int i = 0; i < numCols; i++ ) {
+                X.set(i,colB,Z.data[i]);
+            }
+        }
+    }
+
+    @Override
+    public boolean modifiesA() {
+        return decomposer.inputModified();
+    }
+
+    @Override
+    public boolean modifiesB() {
+        return false;
+    }
+
+    @Override
+    public QRDecomposition<DenseMatrix64F> getDecomposition() {
+        return decomposer;
+    }
+
+    public QRDecomposition<DenseMatrix64F> getDecomposer() {
+        return decomposer;
+    }
+
+    public DenseMatrix64F getQ() {
+        return Q;
+    }
+
+    public DenseMatrix64F getR() {
+        return R;
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQrpHouseCol_D64.java b/main/dense64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQrpHouseCol_D64.java
new file mode 100644
index 0000000..ccd4b20
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQrpHouseCol_D64.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.qr;
+
+import org.ejml.alg.dense.decomposition.TriangularSolver;
+import org.ejml.alg.dense.decomposition.qr.QRColPivDecompositionHouseholderColumn_D64;
+import org.ejml.alg.dense.decomposition.qr.QrHelperFunctions_D64;
+import org.ejml.data.DenseMatrix64F;
+
+/**
+ * <p>
+ * Performs a pseudo inverse solver using the {@link org.ejml.alg.dense.decomposition.qr.QRColPivDecompositionHouseholderColumn_D64} decomposition
+ * directly.  For details on how the pseudo inverse is computed see {@link BaseLinearSolverQrp_D64}.
+ * </p>
+ * 
+ * @author Peter Abeles
+ */
+public class LinearSolverQrpHouseCol_D64 extends BaseLinearSolverQrp_D64 {
+
+    // Computes the QR decomposition
+    private QRColPivDecompositionHouseholderColumn_D64 decomposition;
+
+    // storage for basic solution
+    private DenseMatrix64F x_basic = new DenseMatrix64F(1,1);
+
+    public LinearSolverQrpHouseCol_D64(QRColPivDecompositionHouseholderColumn_D64 decomposition,
+                                       boolean norm2Solution)
+    {
+        super(decomposition,norm2Solution);
+        this.decomposition = decomposition;
+    }
+
+    @Override
+    public void solve(DenseMatrix64F B, DenseMatrix64F X) {
+        if( X.numRows != numCols )
+            throw new IllegalArgumentException("Unexpected dimensions for X");
+        else if( B.numRows != numRows || B.numCols != X.numCols )
+            throw new IllegalArgumentException("Unexpected dimensions for B");
+
+        int BnumCols = B.numCols;
+
+        // get the pivots and transpose them
+        int pivots[] = decomposition.getPivots();
+        
+        double qr[][] = decomposition.getQR();
+        double gammas[] = decomposition.getGammas();
+
+        // solve each column one by one
+        for( int colB = 0; colB < BnumCols; colB++ ) {
+            x_basic.reshape(numRows, 1);
+            Y.reshape(numRows,1);
+
+            // make a copy of this column in the vector
+            for( int i = 0; i < numRows; i++ ) {
+                x_basic.data[i] = B.get(i,colB);
+            }
+
+            // Solve Q*x=b => x = Q'*b
+            // Q_n*b = (I-gamma*u*u^T)*b = b - u*(gamma*U^T*b)
+            for( int i = 0; i < rank; i++ ) {
+                double u[] = qr[i];
+
+                double vv = u[i];
+                u[i] = 1;
+                QrHelperFunctions_D64.rank1UpdateMultR(x_basic, u, gammas[i], 0, i, numRows, Y.data);
+                u[i] = vv;
+            }
+
+            // solve for Rx = b using the standard upper triangular solver
+            TriangularSolver.solveU(R11.data, x_basic.data, rank);
+
+            // finish the basic solution by filling in zeros
+            x_basic.reshape(numCols, 1, true);
+            for( int i = rank; i < numCols; i++)
+                x_basic.data[i] = 0;
+
+            if( norm2Solution && rank < numCols )
+                upgradeSolution(x_basic);
+
+            // save the results
+            for( int i = 0; i < numCols; i++ ) {
+                X.set(pivots[i],colB,x_basic.data[i]);
+            }
+        }
+    }
+
+    @Override
+    public boolean modifiesA() {
+        return decomposition.inputModified();
+    }
+
+    @Override
+    public boolean modifiesB() {
+        return false;
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/linsol/qr/SolvePseudoInverseQrp_D64.java b/main/dense64/src/org/ejml/alg/dense/linsol/qr/SolvePseudoInverseQrp_D64.java
new file mode 100644
index 0000000..04735b1
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/linsol/qr/SolvePseudoInverseQrp_D64.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.qr;
+
+import org.ejml.alg.dense.decomposition.TriangularSolver;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.QRPDecomposition;
+import org.ejml.ops.CommonOps;
+
+/**
+ * <p>
+ * A pseudo inverse solver for a generic QR column pivot decomposition algorithm.  See
+ * {@link BaseLinearSolverQrp_D64} for technical details on the algorithm.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class SolvePseudoInverseQrp_D64 extends BaseLinearSolverQrp_D64 {
+
+    // stores the orthogonal Q matrix from QR decomposition
+    private DenseMatrix64F Q=new DenseMatrix64F(1,1);
+
+    // storage for basic solution
+    private DenseMatrix64F x_basic =new DenseMatrix64F(1,1);
+
+    /**
+     * Configure and provide decomposition
+     *
+     * @param decomposition Decomposition used.
+     * @param norm2Solution If true the basic solution will be returned, false the minimal 2-norm solution.
+     */
+    public SolvePseudoInverseQrp_D64(QRPDecomposition<DenseMatrix64F> decomposition,
+                                     boolean norm2Solution) {
+        super(decomposition,norm2Solution);
+    }
+
+    @Override
+    public boolean setA(DenseMatrix64F A) {
+        if( !super.setA(A))
+            return false;
+
+        Q.reshape(A.numRows, A.numRows);
+
+        decomposition.getQ(Q, false);
+
+        return true;
+    }
+
+    @Override
+    public void solve(DenseMatrix64F B, DenseMatrix64F X) {
+        if( X.numRows != numCols )
+            throw new IllegalArgumentException("Unexpected dimensions for X");
+        else if( B.numRows != numRows || B.numCols != X.numCols )
+            throw new IllegalArgumentException("Unexpected dimensions for B");
+
+        int BnumCols = B.numCols;
+
+        // get the pivots and transpose them
+        int pivots[] = decomposition.getPivots();
+        
+        // solve each column one by one
+        for( int colB = 0; colB < BnumCols; colB++ ) {
+            x_basic.reshape(numRows, 1);
+            Y.reshape(numRows,1);
+
+            // make a copy of this column in the vector
+            for( int i = 0; i < numRows; i++ ) {
+                Y.data[i] = B.get(i,colB);
+            }
+
+            // Solve Q*a=b => a = Q'*b
+            CommonOps.multTransA(Q, Y, x_basic);
+
+            // solve for Rx = b using the standard upper triangular solver
+            TriangularSolver.solveU(R11.data, x_basic.data, rank);
+
+            // finish the basic solution by filling in zeros
+            x_basic.reshape(numCols, 1, true);
+            for( int i = rank; i < numCols; i++)
+                x_basic.data[i] = 0;
+            
+            if( norm2Solution && rank < numCols )
+                upgradeSolution(x_basic);
+            
+            // save the results
+            for( int i = 0; i < numCols; i++ ) {
+                X.set(pivots[i],colB,x_basic.data[i]);
+            }
+        }
+    }
+
+    @Override
+    public boolean modifiesA() {
+        return decomposition.inputModified();
+    }
+
+    @Override
+    public boolean modifiesB() {
+        return false;
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/linsol/svd/SolvePseudoInverseSvd.java b/main/dense64/src/org/ejml/alg/dense/linsol/svd/SolvePseudoInverseSvd.java
new file mode 100644
index 0000000..504a22a
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/linsol/svd/SolvePseudoInverseSvd.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.svd;
+
+import org.ejml.UtilEjml;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.DecompositionFactory;
+import org.ejml.interfaces.decomposition.SingularValueDecomposition;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.ejml.ops.CommonOps;
+
+
+/**
+ * <p>
+ * The pseudo-inverse is typically used to solve over determined system for which there is no unique solution.<br>
+ * x=inv(A<sup>T</sup>A)A<sup>T</sup>b<br>
+ * where A ∈ ℜ <sup>m × n</sup> and m ≥ n.
+ * </p>
+ *
+ * <p>
+ * This class implements the Moore-Penrose pseudo-inverse using SVD and should never fail.  Alternative implementations
+ * can use Cholesky decomposition, but those will fail if the A<sup>T</sup>A matrix is singular.
+ * However the Cholesky implementation is much faster.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class SolvePseudoInverseSvd implements LinearSolver<DenseMatrix64F> {
+
+    // Used to compute pseudo inverse
+    private SingularValueDecomposition<DenseMatrix64F> svd;
+
+    // the results of the pseudo-inverse
+    private DenseMatrix64F pinv = new DenseMatrix64F(1,1);
+
+    // relative threshold used to select singular values
+    private double threshold = UtilEjml.EPS;
+
+    /**
+     * Creates a new solver targeted at the specified matrix size.
+     *
+     * @param maxRows The expected largest matrix it might have to process.  Can be larger.
+     * @param maxCols The expected largest matrix it might have to process.  Can be larger.
+     */
+    public SolvePseudoInverseSvd(int maxRows, int maxCols) {
+
+        svd = DecompositionFactory.svd(maxRows,maxCols,true,true,true);
+    }
+
+    /**
+     * Creates a solver targeted at matrices around 100x100
+     */
+    public SolvePseudoInverseSvd() {
+        this(100,100);
+    }
+
+    @Override
+    public boolean setA(DenseMatrix64F A) {
+        pinv.reshape(A.numCols,A.numRows,false);
+
+        if( !svd.decompose(A) )
+            return false;
+
+        DenseMatrix64F U_t = svd.getU(null,true);
+        DenseMatrix64F V = svd.getV(null,false);
+        double []S = svd.getSingularValues();
+        int N = Math.min(A.numRows,A.numCols);
+
+        // compute the threshold for singular values which are to be zeroed
+        double maxSingular = 0;
+        for( int i = 0; i < N; i++ ) {
+            if( S[i] > maxSingular )
+                maxSingular = S[i];
+        }
+
+        double tau = threshold*Math.max(A.numCols,A.numRows)*maxSingular;
+
+        // computer the pseudo inverse of A
+        if( maxSingular != 0.0 ) {
+            for (int i = 0; i < N; i++) {
+                double s = S[i];
+                if (s < tau)
+                    S[i] = 0;
+                else
+                    S[i] = 1.0 / S[i];
+            }
+        }
+
+        // V*W
+        for( int i = 0; i < V.numRows; i++ ) {
+            int index = i*V.numCols;
+            for( int j = 0; j < V.numCols; j++ ) {
+                V.data[index++] *= S[j];
+            }
+        }
+
+        // V*W*U^T
+        CommonOps.mult(V,U_t, pinv);
+
+        return true;
+    }
+
+    @Override
+    public double quality() {
+        throw new IllegalArgumentException("Not supported by this solver.");
+    }
+
+    @Override
+    public void solve( DenseMatrix64F b, DenseMatrix64F x) {
+        CommonOps.mult(pinv,b,x);
+    }
+
+    @Override
+    public void invert(DenseMatrix64F A_inv) {
+        A_inv.set(pinv);
+    }
+
+    @Override
+    public boolean modifiesA() {
+        return svd.inputModified();
+    }
+
+    @Override
+    public boolean modifiesB() {
+        return false;
+    }
+
+    @Override
+    public SingularValueDecomposition<DenseMatrix64F> getDecomposition() {
+        return svd;
+    }
+
+    /**
+     * Specify the relative threshold used to select singular values.  By default it's UtilEjml.EPS.
+     * @param threshold The singular value threshold
+     */
+    public void setThreshold(double threshold) {
+        this.threshold = threshold;
+    }
+
+    public SingularValueDecomposition<DenseMatrix64F> getDecomposer() {
+        return svd;
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/misc/DeterminantFromMinor.java b/main/dense64/src/org/ejml/alg/dense/misc/DeterminantFromMinor.java
new file mode 100644
index 0000000..998a43a
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/misc/DeterminantFromMinor.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.misc;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.data.RowD1Matrix64F;
+
+
+/**
+ * <p>
+ * Computes the determinant of a matrix using Laplace expansion.  This is done
+ * using minor matrices as is shown below:<br>
+ * <br>
+ * |A| = Sum{ i=1:k ; a<sub>ij</sub> C<sub>ij</sub> }<br>
+ * <br>
+ * C<sub>ij</sub> = (-1)<sup>i+j</sup> M<sub>ij</sub><br>
+ * <br>
+ * Where M_ij is the minor of matrix A formed by eliminating row i and column j from A.
+ * </p>
+ *
+ * <p>
+ * This is significantly more computationally expensive than using LU decomposition, but
+ * its computation has the advantage being independent of the matrices value.
+ * </p>
+ *
+ * @see org.ejml.alg.dense.decomposition.lu.LUDecompositionAlt_D64
+ *
+ * @author Peter Abeles
+ */
+public class DeterminantFromMinor {
+
+    // how wide the square matrix is
+    private int width;
+
+    // used to decide at which point it uses a direct algorithm to compute the determinant
+    private int minWidth;
+
+    // used to keep track of which submatrix it is computing the results for
+    private int []levelIndexes;
+    // the results at different levels of minor matrices
+    private double []levelResults;
+    // which columns where removed at what level
+    private int []levelRemoved;
+
+    // columns that are currently open
+    private int open[];
+    private int numOpen;
+    // a minor matrix which is created at the lowest level
+    private DenseMatrix64F tempMat;
+
+    private boolean dirty = false;
+
+    /**
+     *
+     * @param width The width of the matrices that it will be computing the determinant for
+     */
+    public DeterminantFromMinor( int width )
+    {
+        this(width,5);
+    }
+
+    /**
+     *
+     * @param width The width of the matrices that it will be computing the determinant for
+     * @param minWidth At which point should it use a predefined function to compute the determinant.
+     */
+    public DeterminantFromMinor( int width , int minWidth )
+    {
+        if( minWidth > 5 || minWidth < 2 ) {
+            throw new IllegalArgumentException("No direct function for that width");
+        }
+
+        if( width < minWidth )
+            minWidth = width;
+
+        this.minWidth = minWidth;
+        this.width = width;
+
+        int numLevels = width-(minWidth-2);
+
+        levelResults = new double[numLevels];
+        levelRemoved = new int[numLevels];
+        levelIndexes = new int[numLevels];
+
+        open = new int[ width ];
+
+        tempMat = new DenseMatrix64F(minWidth-1,minWidth-1);
+    }
+
+    /**
+     * Computes the determinant for the specified matrix.  It must be square and have
+     * the same width and height as what was specified in the constructor.
+     *
+     * @param mat The matrix whose determinant is to be computed.
+     * @return The determinant.
+     */
+    public double compute( RowD1Matrix64F mat ) {
+        if( width != mat.numCols || width != mat.numRows ) {
+            throw new RuntimeException("Unexpected matrix dimension");
+        }
+
+        // make sure everything is in the proper state before it starts
+        initStructures();
+
+//        System.arraycopy(mat.data,0,minorMatrix[0],0,mat.data.length);
+
+        int level = 0;
+        while( true ) {
+            int levelWidth = width-level;
+            int levelIndex = levelIndexes[level];
+
+            if( levelIndex == levelWidth ) {
+                if( level == 0 ) {
+                    return levelResults[0];
+                }
+                int prevLevelIndex = levelIndexes[level-1]++;
+
+                double val = mat.get((level-1)*width+levelRemoved[level-1]);
+                if( prevLevelIndex % 2 == 0 ) {
+                    levelResults[level-1] += val * levelResults[level];
+                } else {
+                    levelResults[level-1] -= val * levelResults[level];
+                }
+
+                putIntoOpen(level-1);
+
+                levelResults[level] = 0;
+                levelIndexes[level] = 0;
+                level--;
+            } else {
+                int excluded = openRemove( levelIndex );
+
+                levelRemoved[level] = excluded;
+
+                if( levelWidth == minWidth ) {
+                    createMinor(mat);
+                    double subresult = mat.get(level*width+levelRemoved[level]);
+
+                    subresult *= UnrolledDeterminantFromMinor.det(tempMat);
+
+                    if( levelIndex % 2 == 0 ) {
+                        levelResults[level] += subresult;
+                    } else {
+                        levelResults[level] -= subresult;
+                    }
+
+                    // put it back into the list
+                    putIntoOpen(level);
+                    levelIndexes[level]++;
+                } else {
+                    level++;
+                }
+            }
+        }
+    }
+
+    private void initStructures() {
+        for( int i = 0; i < width; i++ ) {
+            open[i] = i;
+        }
+        numOpen = width;
+
+        if( dirty ) {
+            for( int i = 0; i < levelIndexes.length; i++ ) {
+                levelIndexes[i] = 0;
+                levelResults[i] = 0;
+                levelRemoved[i] = 0;
+            }
+        }
+        dirty = true;
+    }
+
+    private int openRemove( int where ) {
+        int val = open[where];
+
+        System.arraycopy(open,where+1,open,where,(numOpen-where-1));
+        numOpen--;
+
+        return val;
+    }
+
+    private void openAdd( int where, int val )
+    {
+        for( int i = numOpen; i > where; i-- ) {
+            open[i] = open[i-1];
+        }
+        numOpen++;
+        open[where] = val;
+    }
+
+    private void openAdd( int val ) {
+        open[numOpen++] = val;
+    }
+
+    private void putIntoOpen(int level) {
+        boolean added = false;
+        for( int i = 0; i < numOpen; i++ ) {
+            if( open[i] > levelRemoved[level]) {
+                added = true;
+                openAdd(i,levelRemoved[level]);
+                break;
+            }
+        }
+        if( !added ) {
+            openAdd(levelRemoved[level]);
+        }
+    }
+
+    private void createMinor( RowD1Matrix64F mat ) {
+
+        int w = minWidth-1;
+        int firstRow = (width-w)*width;
+
+        for( int i = 0; i < numOpen; i++ ) {
+            int col = open[i];
+            int srcIndex = firstRow + col;
+            int dstIndex = i;
+
+            for( int j = 0; j < w; j++ ) {
+                tempMat.set( dstIndex , mat.get( srcIndex ) );
+                dstIndex += w;
+                srcIndex += width;
+            }
+        }
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/misc/ImplCommonOps_DenseMatrix64F.java b/main/dense64/src/org/ejml/alg/dense/misc/ImplCommonOps_DenseMatrix64F.java
new file mode 100644
index 0000000..e484d33
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/misc/ImplCommonOps_DenseMatrix64F.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.misc;
+
+import org.ejml.data.DenseMatrix64F;
+
+/**
+ * Implementations of common ops routines for {@link org.ejml.data.DenseMatrix64F}.  In general
+ * there is no need to directly invoke these functions.
+ *
+ * @author Peter Abeles
+ */
+public class ImplCommonOps_DenseMatrix64F {
+    public static void extract(DenseMatrix64F src,
+                               int srcY0, int srcX0,
+                               DenseMatrix64F dst,
+                               int dstY0, int dstX0,
+                               int numRows, int numCols)
+    {
+         for( int y = 0; y < numRows; y++ ) {
+             int indexSrc = src.getIndex(y+srcY0,srcX0);
+             int indexDst = dst.getIndex(y+dstY0,dstX0);
+             System.arraycopy(src.data,indexSrc,dst.data,indexDst, numCols);
+         }
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/misc/ImplCommonOps_Matrix64F.java b/main/dense64/src/org/ejml/alg/dense/misc/ImplCommonOps_Matrix64F.java
new file mode 100644
index 0000000..28ef08a
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/misc/ImplCommonOps_Matrix64F.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.misc;
+
+import org.ejml.data.RealMatrix64F;
+
+/**
+ * Implementations of common ops routines for {@link org.ejml.data.DenseMatrix64F}.  In general
+ * there is no need to directly invoke these functions.
+ *
+ * @author Peter Abeles
+ */
+public class ImplCommonOps_Matrix64F {
+    public static void extract(RealMatrix64F src,
+                               int srcY0, int srcX0,
+                               RealMatrix64F dst,
+                               int dstY0, int dstX0,
+                               int numRows, int numCols )
+    {
+        for( int y = 0; y < numRows; y++ ) {
+            for( int x = 0; x < numCols; x++ ) {
+                double v = src.get(y+srcY0,x+srcX0);
+                dst.set(dstY0+y , dstX0 +x, v);
+            }
+        }
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/misc/RrefGaussJordanRowPivot.java b/main/dense64/src/org/ejml/alg/dense/misc/RrefGaussJordanRowPivot.java
new file mode 100644
index 0000000..9f92548
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/misc/RrefGaussJordanRowPivot.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.misc;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.linsol.ReducedRowEchelonForm;
+
+/**
+ * Reduction to RREF using Gauss-Jordan elimination with row (partial) pivots.
+ *
+ * @author Peter Abeles
+ */
+public class RrefGaussJordanRowPivot implements ReducedRowEchelonForm<DenseMatrix64F> {
+
+    // tolerance for singular matrix
+    double tol;
+
+    @Override
+    public void setTolerance(double tol) {
+        this.tol = tol;
+    }
+
+    @Override
+    public void reduce( DenseMatrix64F A , int coefficientColumns) {
+        if( A.numCols < coefficientColumns)
+            throw new IllegalArgumentException("The system must be at least as wide as A");
+
+        // number of leading ones which have been found
+        int leadIndex = 0;
+        // compute the decomposition
+        for( int i = 0; i < coefficientColumns; i++ ) {
+
+            // select the row to pivot by finding the row with the largest column in 'i'
+            int pivotRow = -1;
+            double maxValue = tol;
+
+            for( int row = leadIndex; row < A.numRows; row++ ) {
+                double v = Math.abs(A.data[row*A.numCols + i]);
+
+                if( v > maxValue ) {
+                    maxValue = v;
+                    pivotRow = row;
+                }
+            }
+
+            if( pivotRow == -1 )
+                continue;
+
+            // perform the row pivot
+            // NOTE: performance could be improved by delaying the physical swap of rows until the end
+            //       and using a technique which does the minimal number of swaps
+            if( leadIndex != pivotRow)
+                swapRows(A,leadIndex,pivotRow);
+
+            // zero column 'i' in all but the pivot row
+            for( int row = 0; row < A.numRows; row++ ) {
+                if( row == leadIndex ) continue;
+
+                int indexPivot = leadIndex*A.numCols+i;
+                int indexTarget = row*A.numCols+i;
+
+                double alpha = A.data[indexTarget]/A.data[indexPivot++];
+                A.data[indexTarget++] = 0;
+                for( int col = i+1; col < A.numCols; col++ ) {
+                    A.data[indexTarget++] -= A.data[indexPivot++]*alpha;
+                }
+            }
+
+            // update the pivot row
+            int indexPivot = leadIndex*A.numCols+i;
+            double alpha = 1.0/A.data[indexPivot];
+            A.data[indexPivot++] = 1;
+            for( int col = i+1; col < A.numCols; col++ ) {
+                A.data[indexPivot++] *= alpha;
+            }
+            leadIndex++;
+        }
+    }
+
+    protected static void swapRows( DenseMatrix64F A , int rowA , int rowB ) {
+        int indexA = rowA*A.numCols;
+        int indexB = rowB*A.numCols;
+
+        for( int i = 0; i < A.numCols; i++ , indexA++,indexB++) {
+            double temp = A.data[indexA];
+            A.data[indexA] = A.data[indexB];
+            A.data[indexB] = temp;
+        }
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/misc/TransposeAlgs.java b/main/dense64/src/org/ejml/alg/dense/misc/TransposeAlgs.java
new file mode 100644
index 0000000..b42be2b
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/misc/TransposeAlgs.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.misc;
+
+import org.ejml.data.RowD1Matrix64F;
+
+
+/**
+ * Low level transpose algorithms.  No sanity checks are performed.    Take a look at BenchmarkTranspose to
+ * see which one is faster on your computer.
+ *
+ * @author Peter Abeles
+ */
+public class TransposeAlgs {
+
+    /**
+     * In-place transpose for a square matrix.  On most architectures it is faster than the standard transpose
+     * algorithm, but on most modern computers it's slower than block transpose.
+     *
+     * @param mat The matrix that is transposed in-place.  Modified.
+     */
+    public static void square( RowD1Matrix64F mat )
+    {
+        int index = 1;
+        int indexEnd = mat.numCols;
+        for( int i = 0; i < mat.numRows;
+             i++ , index += i+1 , indexEnd += mat.numCols ) {
+            int indexOther = (i+1)*mat.numCols + i;
+            for( ; index < indexEnd; index++, indexOther += mat.numCols) {
+                double val = mat.data[ index ];
+                mat.data[ index ] = mat.data[ indexOther ];
+                mat.data[indexOther] = val;
+            }
+        }
+    }
+
+    /**
+     * Performs a transpose across block sub-matrices.  Reduces
+     * the number of cache misses on larger matrices.
+     *
+     * *NOTE* If this is beneficial is highly dependent on the computer it is run on. e.g:
+     * - Q6600 Almost twice as fast as standard.
+     * - Pentium-M Same speed and some times a bit slower than standard.
+     *
+     * @param A Original matrix.  Not modified.
+     * @param A_tran Transposed matrix.  Modified.
+     * @param blockLength Length of a block.
+     */
+    public static void block( RowD1Matrix64F A , RowD1Matrix64F A_tran ,
+                              final int blockLength )
+    {
+        for( int i = 0; i < A.numRows; i += blockLength ) {
+            int blockHeight = Math.min( blockLength , A.numRows - i);
+
+            int indexSrc = i*A.numCols;
+            int indexDst = i;
+
+            for( int j = 0; j < A.numCols; j += blockLength ) {
+                int blockWidth = Math.min( blockLength , A.numCols - j);
+
+//                int indexSrc = i*A.numCols + j;
+//                int indexDst = j*A_tran.numCols + i;
+
+                int indexSrcEnd = indexSrc + blockWidth;
+//                for( int l = 0; l < blockWidth; l++ , indexSrc++ ) {
+                for( ; indexSrc < indexSrcEnd;  indexSrc++ ) {
+                    int rowSrc = indexSrc;
+                    int rowDst = indexDst;
+                    int end = rowDst + blockHeight;
+//                    for( int k = 0; k < blockHeight; k++ , rowSrc += A.numCols ) {
+                    for( ; rowDst < end; rowSrc += A.numCols ) {
+                        // faster to write in sequence than to read in sequence
+                        A_tran.data[ rowDst++ ] = A.data[ rowSrc ];
+                    }
+                    indexDst += A_tran.numCols;
+                }
+            }
+        }
+    }
+
+    /**
+     * A straight forward transpose.  Good for small non-square matrices.
+     *
+     * @param A Original matrix.  Not modified.
+     * @param A_tran Transposed matrix.  Modified.
+     */
+    public static void standard( RowD1Matrix64F A, RowD1Matrix64F A_tran)
+    {
+        int index = 0;
+        for( int i = 0; i < A_tran.numRows; i++ ) {
+            int index2 = i;
+
+            int end = index + A_tran.numCols;
+            while( index < end ) {
+                A_tran.data[index++ ] = A.data[ index2 ];
+                index2 += A.numCols;
+            }
+        }
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/misc/UnrolledDeterminantFromMinor.java b/main/dense64/src/org/ejml/alg/dense/misc/UnrolledDeterminantFromMinor.java
new file mode 100644
index 0000000..c438979
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/misc/UnrolledDeterminantFromMinor.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.misc;
+
+import org.ejml.data.RowD1Matrix64F;
+
+
+/**
+ * This code was auto generated by  {@link GenerateDeterminantFromMinor} and should not be modified
+ * directly.  
+ * 
+ * @author Peter Abeles
+ */
+public class UnrolledDeterminantFromMinor {
+    
+    public static final int MAX = 6;
+    
+    public static double det( RowD1Matrix64F mat ) {
+        switch( mat.numRows ) {
+            case 2: return det2(mat);
+            case 3: return det3(mat);
+            case 4: return det4(mat);
+            case 5: return det5(mat);
+            case 6: return det6(mat);
+            default: throw new IllegalArgumentException("Not supported");
+        }
+    }
+
+    public static double det2( RowD1Matrix64F mat )
+    {
+        double m[] = mat.data;
+
+        return m[0]*m[3] - m[1]*m[2];
+    }
+
+    public static double det3( RowD1Matrix64F mat )
+    {
+        double m[] = mat.data;
+
+        double a11 = m[0];
+        double a12 = m[1];
+        double a13 = m[2];
+        double a21 = m[3];
+        double a22 = m[4];
+        double a23 = m[5];
+        double a31 = m[6];
+        double a32 = m[7];
+        double a33 = m[8];
+
+        double a = a11*(a22*a33 - a23*a32);
+        double b = a12*(a21*a33 - a23*a31);
+        double c = a13*(a21*a32 - a31*a22);
+
+        return a-b+c;
+    }
+
+    public static double det4( RowD1Matrix64F mat )
+    {
+        double []data = mat.data;
+
+        double  a11 = data[ 5 ]; double  a12 = data[ 6 ]; double  a13 = data[ 7 ];
+        double  a21 = data[ 9 ]; double  a22 = data[ 10 ];double  a23 = data[ 11 ];
+        double  a31 = data[ 13 ];double  a32 = data[ 14 ];double  a33 = data[ 15 ];
+
+        double ret = 0;
+        ret += data[ 0 ] * ( + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31));
+        a11 = data[ 4 ];
+        a21 = data[ 8 ];
+        a31 = data[ 12 ];
+        ret -= data[ 1 ] * ( + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31));
+        a12 = data[ 5 ];
+        a22 = data[ 9 ];
+        a32 = data[ 13 ];
+        ret += data[ 2 ] * ( + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31));
+        a13 = data[ 6 ];
+        a23 = data[ 10 ];
+        a33 = data[ 14 ];
+        ret -= data[ 3 ] * ( + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31));
+        return ret;
+    }
+
+    public static double det5( RowD1Matrix64F mat )
+    {
+        double []data = mat.data;
+
+        double  a11 = data[ 6 ];
+        double  a12 = data[ 7 ];
+        double  a13 = data[ 8 ];
+        double  a14 = data[ 9 ];
+        double  a21 = data[ 11 ];
+        double  a22 = data[ 12 ];
+        double  a23 = data[ 13 ];
+        double  a24 = data[ 14 ];
+        double  a31 = data[ 16 ];
+        double  a32 = data[ 17 ];
+        double  a33 = data[ 18 ];
+        double  a34 = data[ 19 ];
+        double  a41 = data[ 21 ];
+        double  a42 = data[ 22 ];
+        double  a43 = data[ 23 ];
+        double  a44 = data[ 24 ];
+
+        double ret = 0;
+        ret += data[ 0 ] * ( + a11*( + a22*(a33*a44 - a34*a43) - a23*(a32*a44 - a34*a42) + a24*(a32*a43 - a33*a42)) - a12*( + a21*(a33*a44 - a34*a43) - a23*(a31*a44 - a34*a41) + a24*(a31*a43 - a33*a41)) + a13*( + a21*(a32*a44 - a34*a42) - a22*(a31*a44 - a34*a41) + a24*(a31*a42 - a32*a41)) - a14*( + a21*(a32*a43 - a33*a42) - a22*(a31*a43 - a33*a41) + a23*(a31*a42 - a32*a41)));
+        a11 = data[ 5 ];
+        a21 = data[ 10 ];
+        a31 = data[ 15 ];
+        a41 = data[ 20 ];
+        ret -= data[ 1 ] * ( + a11*( + a22*(a33*a44 - a34*a43) - a23*(a32*a44 - a34*a42) + a24*(a32*a43 - a33*a42)) - a12*( + a21*(a33*a44 - a34*a43) - a23*(a31*a44 - a34*a41) + a24*(a31*a43 - a33*a41)) + a13*( + a21*(a32*a44 - a34*a42) - a22*(a31*a44 - a34*a41) + a24*(a31*a42 - a32*a41)) - a14*( + a21*(a32*a43 - a33*a42) - a22*(a31*a43 - a33*a41) + a23*(a31*a42 - a32*a41)));
+        a12 = data[ 6 ];
+        a22 = data[ 11 ];
+        a32 = data[ 16 ];
+        a42 = data[ 21 ];
+        ret += data[ 2 ] * ( + a11*( + a22*(a33*a44 - a34*a43) - a23*(a32*a44 - a34*a42) + a24*(a32*a43 - a33*a42)) - a12*( + a21*(a33*a44 - a34*a43) - a23*(a31*a44 - a34*a41) + a24*(a31*a43 - a33*a41)) + a13*( + a21*(a32*a44 - a34*a42) - a22*(a31*a44 - a34*a41) + a24*(a31*a42 - a32*a41)) - a14*( + a21*(a32*a43 - a33*a42) - a22*(a31*a43 - a33*a41) + a23*(a31*a42 - a32*a41)));
+        a13 = data[ 7 ];
+        a23 = data[ 12 ];
+        a33 = data[ 17 ];
+        a43 = data[ 22 ];
+        ret -= data[ 3 ] * ( + a11*( + a22*(a33*a44 - a34*a43) - a23*(a32*a44 - a34*a42) + a24*(a32*a43 - a33*a42)) - a12*( + a21*(a33*a44 - a34*a43) - a23*(a31*a44 - a34*a41) + a24*(a31*a43 - a33*a41)) + a13*( + a21*(a32*a44 - a34*a42) - a22*(a31*a44 - a34*a41) + a24*(a31*a42 - a32*a41)) - a14*( + a21*(a32*a43 - a33*a42) - a22*(a31*a43 - a33*a41) + a23*(a31*a42 - a32*a41)));
+        a14 = data[ 8 ];
+        a24 = data[ 13 ];
+        a34 = data[ 18 ];
+        a44 = data[ 23 ];
+        ret += data[ 4 ] * ( + a11*( + a22*(a33*a44 - a34*a43) - a23*(a32*a44 - a34*a42) + a24*(a32*a43 - a33*a42)) - a12*( + a21*(a33*a44 - a34*a43) - a23*(a31*a44 - a34*a41) + a24*(a31*a43 - a33*a41)) + a13*( + a21*(a32*a44 - a34*a42) - a22*(a31*a44 - a34*a41) + a24*(a31*a42 - a32*a41)) - a14*( + a21*(a32*a43 - a33*a42) - a22*(a31*a43 - a33*a41) + a23*(a31*a42 - a32*a41)));
+        return ret;
+    }
+
+    public static double det6( RowD1Matrix64F mat )
+    {
+        double []data = mat.data;
+
+        double  a11 = data[ 7 ];
+        double  a12 = data[ 8 ];
+        double  a13 = data[ 9 ];
+        double  a14 = data[ 10 ];
+        double  a15 = data[ 11 ];
+        double  a21 = data[ 13 ];
+        double  a22 = data[ 14 ];
+        double  a23 = data[ 15 ];
+        double  a24 = data[ 16 ];
+        double  a25 = data[ 17 ];
+        double  a31 = data[ 19 ];
+        double  a32 = data[ 20 ];
+        double  a33 = data[ 21 ];
+        double  a34 = data[ 22 ];
+        double  a35 = data[ 23 ];
+        double  a41 = data[ 25 ];
+        double  a42 = data[ 26 ];
+        double  a43 = data[ 27 ];
+        double  a44 = data[ 28 ];
+        double  a45 = data[ 29 ];
+        double  a51 = data[ 31 ];
+        double  a52 = data[ 32 ];
+        double  a53 = data[ 33 ];
+        double  a54 = data[ 34 ];
+        double  a55 = data[ 35 ];
+
+        double ret = 0;
+        ret += data[ 0 ] * ( + a11*( + a22*( + a33*(a44*a55 - a45*a54) - a34*(a43*a55 - a45*a53) + a35*(a43*a54 - a44*a53)) - a23*( + a32*(a44*a55 - a45*a54) - a34*(a42*a55 - a45*a52) + a35*(a42*a54 - a44*a52)) + a24*( + a32*(a43*a55 - a45*a53) - a33*(a42*a55 - a45*a52) + a35*(a42*a53 - a43*a52)) - a25*( + a32*(a43*a54 - a44*a53) - a33*(a42*a54 - a44*a52) + a34*(a42*a53 - a43*a52))) - a12*( + a21*( + a33*(a44*a55 - a45*a54) - a34*(a43*a55 - a45*a53) + a35*(a43*a54 - a44*a53)) - a23*( + a [...]
+        a11 = data[ 6 ];
+        a21 = data[ 12 ];
+        a31 = data[ 18 ];
+        a41 = data[ 24 ];
+        a51 = data[ 30 ];
+        ret -= data[ 1 ] * ( + a11*( + a22*( + a33*(a44*a55 - a45*a54) - a34*(a43*a55 - a45*a53) + a35*(a43*a54 - a44*a53)) - a23*( + a32*(a44*a55 - a45*a54) - a34*(a42*a55 - a45*a52) + a35*(a42*a54 - a44*a52)) + a24*( + a32*(a43*a55 - a45*a53) - a33*(a42*a55 - a45*a52) + a35*(a42*a53 - a43*a52)) - a25*( + a32*(a43*a54 - a44*a53) - a33*(a42*a54 - a44*a52) + a34*(a42*a53 - a43*a52))) - a12*( + a21*( + a33*(a44*a55 - a45*a54) - a34*(a43*a55 - a45*a53) + a35*(a43*a54 - a44*a53)) - a23*( + a [...]
+        a12 = data[ 7 ];
+        a22 = data[ 13 ];
+        a32 = data[ 19 ];
+        a42 = data[ 25 ];
+        a52 = data[ 31 ];
+        ret += data[ 2 ] * ( + a11*( + a22*( + a33*(a44*a55 - a45*a54) - a34*(a43*a55 - a45*a53) + a35*(a43*a54 - a44*a53)) - a23*( + a32*(a44*a55 - a45*a54) - a34*(a42*a55 - a45*a52) + a35*(a42*a54 - a44*a52)) + a24*( + a32*(a43*a55 - a45*a53) - a33*(a42*a55 - a45*a52) + a35*(a42*a53 - a43*a52)) - a25*( + a32*(a43*a54 - a44*a53) - a33*(a42*a54 - a44*a52) + a34*(a42*a53 - a43*a52))) - a12*( + a21*( + a33*(a44*a55 - a45*a54) - a34*(a43*a55 - a45*a53) + a35*(a43*a54 - a44*a53)) - a23*( + a [...]
+        a13 = data[ 8 ];
+        a23 = data[ 14 ];
+        a33 = data[ 20 ];
+        a43 = data[ 26 ];
+        a53 = data[ 32 ];
+        ret -= data[ 3 ] * ( + a11*( + a22*( + a33*(a44*a55 - a45*a54) - a34*(a43*a55 - a45*a53) + a35*(a43*a54 - a44*a53)) - a23*( + a32*(a44*a55 - a45*a54) - a34*(a42*a55 - a45*a52) + a35*(a42*a54 - a44*a52)) + a24*( + a32*(a43*a55 - a45*a53) - a33*(a42*a55 - a45*a52) + a35*(a42*a53 - a43*a52)) - a25*( + a32*(a43*a54 - a44*a53) - a33*(a42*a54 - a44*a52) + a34*(a42*a53 - a43*a52))) - a12*( + a21*( + a33*(a44*a55 - a45*a54) - a34*(a43*a55 - a45*a53) + a35*(a43*a54 - a44*a53)) - a23*( + a [...]
+        a14 = data[ 9 ];
+        a24 = data[ 15 ];
+        a34 = data[ 21 ];
+        a44 = data[ 27 ];
+        a54 = data[ 33 ];
+        ret += data[ 4 ] * ( + a11*( + a22*( + a33*(a44*a55 - a45*a54) - a34*(a43*a55 - a45*a53) + a35*(a43*a54 - a44*a53)) - a23*( + a32*(a44*a55 - a45*a54) - a34*(a42*a55 - a45*a52) + a35*(a42*a54 - a44*a52)) + a24*( + a32*(a43*a55 - a45*a53) - a33*(a42*a55 - a45*a52) + a35*(a42*a53 - a43*a52)) - a25*( + a32*(a43*a54 - a44*a53) - a33*(a42*a54 - a44*a52) + a34*(a42*a53 - a43*a52))) - a12*( + a21*( + a33*(a44*a55 - a45*a54) - a34*(a43*a55 - a45*a53) + a35*(a43*a54 - a44*a53)) - a23*( + a [...]
+        a15 = data[ 10 ];
+        a25 = data[ 16 ];
+        a35 = data[ 22 ];
+        a45 = data[ 28 ];
+        a55 = data[ 34 ];
+        ret -= data[ 5 ] * ( + a11*( + a22*( + a33*(a44*a55 - a45*a54) - a34*(a43*a55 - a45*a53) + a35*(a43*a54 - a44*a53)) - a23*( + a32*(a44*a55 - a45*a54) - a34*(a42*a55 - a45*a52) + a35*(a42*a54 - a44*a52)) + a24*( + a32*(a43*a55 - a45*a53) - a33*(a42*a55 - a45*a52) + a35*(a42*a53 - a43*a52)) - a25*( + a32*(a43*a54 - a44*a53) - a33*(a42*a54 - a44*a52) + a34*(a42*a53 - a43*a52))) - a12*( + a21*( + a33*(a44*a55 - a45*a54) - a34*(a43*a55 - a45*a53) + a35*(a43*a54 - a44*a53)) - a23*( + a [...]
+        return ret;
+    }
+
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/misc/UnrolledInverseFromMinor.java b/main/dense64/src/org/ejml/alg/dense/misc/UnrolledInverseFromMinor.java
new file mode 100644
index 0000000..e2175bd
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/misc/UnrolledInverseFromMinor.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.misc;
+
+import org.ejml.data.DenseMatrix64F;
+
+
+/**
+ * This code was auto generated by  {@link GenerateInverseFromMinor} and should not be modified
+ * directly.  The input matrix is scaled make it much less prone to overflow and underflow issues.
+ * 
+ * @author Peter Abeles
+ */
+public class UnrolledInverseFromMinor {
+    
+    public static final int MAX = 5;
+    
+    public static void inv( DenseMatrix64F mat , DenseMatrix64F inv ) {
+        double max = Math.abs(mat.data[0]);
+        int N = mat.getNumElements();
+        
+        for( int i = 1; i < N; i++ ) {
+            double a = Math.abs(mat.data[i]);
+            if( a > max ) max = a;
+        }
+
+        switch( mat.numRows ) {
+            case 2: inv2(mat,inv,1.0/max); break;
+            case 3: inv3(mat,inv,1.0/max); break;
+            case 4: inv4(mat,inv,1.0/max); break;
+            case 5: inv5(mat,inv,1.0/max); break;
+            default: throw new IllegalArgumentException("Not supported");
+        }
+    }
+
+    public static void inv2( DenseMatrix64F mat , DenseMatrix64F inv , double scale )
+    {
+        double []data = mat.data;
+
+        double a11 = data[ 0 ]*scale;
+        double a12 = data[ 1 ]*scale;
+        double a21 = data[ 2 ]*scale;
+        double a22 = data[ 3 ]*scale;
+
+        double m11 = a22;
+        double m12 = -( a21);
+        double m21 = -( a12);
+        double m22 = a11;
+
+        double det = (a11*m11 + a12*m12)/scale;
+
+        data = inv.data;
+        data[0] = m11 / det;
+        data[1] = m21 / det;
+        data[2] = m12 / det;
+        data[3] = m22 / det;
+
+    }
+
+    public static void inv3( DenseMatrix64F mat , DenseMatrix64F inv , double scale )
+    {
+        double []data = mat.data;
+
+        double a11 = data[ 0 ]*scale;
+        double a12 = data[ 1 ]*scale;
+        double a13 = data[ 2 ]*scale;
+        double a21 = data[ 3 ]*scale;
+        double a22 = data[ 4 ]*scale;
+        double a23 = data[ 5 ]*scale;
+        double a31 = data[ 6 ]*scale;
+        double a32 = data[ 7 ]*scale;
+        double a33 = data[ 8 ]*scale;
+
+        double m11 = a22*a33 - a23*a32;
+        double m12 = -( a21*a33 - a23*a31);
+        double m13 = a21*a32 - a22*a31;
+        double m21 = -( a12*a33 - a13*a32);
+        double m22 = a11*a33 - a13*a31;
+        double m23 = -( a11*a32 - a12*a31);
+        double m31 = a12*a23 - a13*a22;
+        double m32 = -( a11*a23 - a13*a21);
+        double m33 = a11*a22 - a12*a21;
+
+        double det = (a11*m11 + a12*m12 + a13*m13)/scale;
+
+        data = inv.data;
+        data[0] = m11 / det;
+        data[1] = m21 / det;
+        data[2] = m31 / det;
+        data[3] = m12 / det;
+        data[4] = m22 / det;
+        data[5] = m32 / det;
+        data[6] = m13 / det;
+        data[7] = m23 / det;
+        data[8] = m33 / det;
+
+    }
+
+    public static void inv4( DenseMatrix64F mat , DenseMatrix64F inv , double scale )
+    {
+        double []data = mat.data;
+
+        double a11 = data[ 0 ]*scale;
+        double a12 = data[ 1 ]*scale;
+        double a13 = data[ 2 ]*scale;
+        double a14 = data[ 3 ]*scale;
+        double a21 = data[ 4 ]*scale;
+        double a22 = data[ 5 ]*scale;
+        double a23 = data[ 6 ]*scale;
+        double a24 = data[ 7 ]*scale;
+        double a31 = data[ 8 ]*scale;
+        double a32 = data[ 9 ]*scale;
+        double a33 = data[ 10 ]*scale;
+        double a34 = data[ 11 ]*scale;
+        double a41 = data[ 12 ]*scale;
+        double a42 = data[ 13 ]*scale;
+        double a43 = data[ 14 ]*scale;
+        double a44 = data[ 15 ]*scale;
+
+        double m11 =  + a22*(a33*a44 - a34*a43) - a23*(a32*a44 - a34*a42) + a24*(a32*a43 - a33*a42);
+        double m12 = -(  + a21*(a33*a44 - a34*a43) - a23*(a31*a44 - a34*a41) + a24*(a31*a43 - a33*a41));
+        double m13 =  + a21*(a32*a44 - a34*a42) - a22*(a31*a44 - a34*a41) + a24*(a31*a42 - a32*a41);
+        double m14 = -(  + a21*(a32*a43 - a33*a42) - a22*(a31*a43 - a33*a41) + a23*(a31*a42 - a32*a41));
+        double m21 = -(  + a12*(a33*a44 - a34*a43) - a13*(a32*a44 - a34*a42) + a14*(a32*a43 - a33*a42));
+        double m22 =  + a11*(a33*a44 - a34*a43) - a13*(a31*a44 - a34*a41) + a14*(a31*a43 - a33*a41);
+        double m23 = -(  + a11*(a32*a44 - a34*a42) - a12*(a31*a44 - a34*a41) + a14*(a31*a42 - a32*a41));
+        double m24 =  + a11*(a32*a43 - a33*a42) - a12*(a31*a43 - a33*a41) + a13*(a31*a42 - a32*a41);
+        double m31 =  + a12*(a23*a44 - a24*a43) - a13*(a22*a44 - a24*a42) + a14*(a22*a43 - a23*a42);
+        double m32 = -(  + a11*(a23*a44 - a24*a43) - a13*(a21*a44 - a24*a41) + a14*(a21*a43 - a23*a41));
+        double m33 =  + a11*(a22*a44 - a24*a42) - a12*(a21*a44 - a24*a41) + a14*(a21*a42 - a22*a41);
+        double m34 = -(  + a11*(a22*a43 - a23*a42) - a12*(a21*a43 - a23*a41) + a13*(a21*a42 - a22*a41));
+        double m41 = -(  + a12*(a23*a34 - a24*a33) - a13*(a22*a34 - a24*a32) + a14*(a22*a33 - a23*a32));
+        double m42 =  + a11*(a23*a34 - a24*a33) - a13*(a21*a34 - a24*a31) + a14*(a21*a33 - a23*a31);
+        double m43 = -(  + a11*(a22*a34 - a24*a32) - a12*(a21*a34 - a24*a31) + a14*(a21*a32 - a22*a31));
+        double m44 =  + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31);
+
+        double det = (a11*m11 + a12*m12 + a13*m13 + a14*m14)/scale;
+
+        data = inv.data;
+        data[0] = m11 / det;
+        data[1] = m21 / det;
+        data[2] = m31 / det;
+        data[3] = m41 / det;
+        data[4] = m12 / det;
+        data[5] = m22 / det;
+        data[6] = m32 / det;
+        data[7] = m42 / det;
+        data[8] = m13 / det;
+        data[9] = m23 / det;
+        data[10] = m33 / det;
+        data[11] = m43 / det;
+        data[12] = m14 / det;
+        data[13] = m24 / det;
+        data[14] = m34 / det;
+        data[15] = m44 / det;
+
+    }
+
+    public static void inv5( DenseMatrix64F mat , DenseMatrix64F inv , double scale )
+    {
+        double []data = mat.data;
+
+        double a11 = data[ 0 ]*scale;
+        double a12 = data[ 1 ]*scale;
+        double a13 = data[ 2 ]*scale;
+        double a14 = data[ 3 ]*scale;
+        double a15 = data[ 4 ]*scale;
+        double a21 = data[ 5 ]*scale;
+        double a22 = data[ 6 ]*scale;
+        double a23 = data[ 7 ]*scale;
+        double a24 = data[ 8 ]*scale;
+        double a25 = data[ 9 ]*scale;
+        double a31 = data[ 10 ]*scale;
+        double a32 = data[ 11 ]*scale;
+        double a33 = data[ 12 ]*scale;
+        double a34 = data[ 13 ]*scale;
+        double a35 = data[ 14 ]*scale;
+        double a41 = data[ 15 ]*scale;
+        double a42 = data[ 16 ]*scale;
+        double a43 = data[ 17 ]*scale;
+        double a44 = data[ 18 ]*scale;
+        double a45 = data[ 19 ]*scale;
+        double a51 = data[ 20 ]*scale;
+        double a52 = data[ 21 ]*scale;
+        double a53 = data[ 22 ]*scale;
+        double a54 = data[ 23 ]*scale;
+        double a55 = data[ 24 ]*scale;
+
+        double m11 =  + a22*( + a33*(a44*a55 - a45*a54) - a34*(a43*a55 - a45*a53) + a35*(a43*a54 - a44*a53)) - a23*( + a32*(a44*a55 - a45*a54) - a34*(a42*a55 - a45*a52) + a35*(a42*a54 - a44*a52)) + a24*( + a32*(a43*a55 - a45*a53) - a33*(a42*a55 - a45*a52) + a35*(a42*a53 - a43*a52)) - a25*( + a32*(a43*a54 - a44*a53) - a33*(a42*a54 - a44*a52) + a34*(a42*a53 - a43*a52));
+        double m12 = -(  + a21*( + a33*(a44*a55 - a45*a54) - a34*(a43*a55 - a45*a53) + a35*(a43*a54 - a44*a53)) - a23*( + a31*(a44*a55 - a45*a54) - a34*(a41*a55 - a45*a51) + a35*(a41*a54 - a44*a51)) + a24*( + a31*(a43*a55 - a45*a53) - a33*(a41*a55 - a45*a51) + a35*(a41*a53 - a43*a51)) - a25*( + a31*(a43*a54 - a44*a53) - a33*(a41*a54 - a44*a51) + a34*(a41*a53 - a43*a51)));
+        double m13 =  + a21*( + a32*(a44*a55 - a45*a54) - a34*(a42*a55 - a45*a52) + a35*(a42*a54 - a44*a52)) - a22*( + a31*(a44*a55 - a45*a54) - a34*(a41*a55 - a45*a51) + a35*(a41*a54 - a44*a51)) + a24*( + a31*(a42*a55 - a45*a52) - a32*(a41*a55 - a45*a51) + a35*(a41*a52 - a42*a51)) - a25*( + a31*(a42*a54 - a44*a52) - a32*(a41*a54 - a44*a51) + a34*(a41*a52 - a42*a51));
+        double m14 = -(  + a21*( + a32*(a43*a55 - a45*a53) - a33*(a42*a55 - a45*a52) + a35*(a42*a53 - a43*a52)) - a22*( + a31*(a43*a55 - a45*a53) - a33*(a41*a55 - a45*a51) + a35*(a41*a53 - a43*a51)) + a23*( + a31*(a42*a55 - a45*a52) - a32*(a41*a55 - a45*a51) + a35*(a41*a52 - a42*a51)) - a25*( + a31*(a42*a53 - a43*a52) - a32*(a41*a53 - a43*a51) + a33*(a41*a52 - a42*a51)));
+        double m15 =  + a21*( + a32*(a43*a54 - a44*a53) - a33*(a42*a54 - a44*a52) + a34*(a42*a53 - a43*a52)) - a22*( + a31*(a43*a54 - a44*a53) - a33*(a41*a54 - a44*a51) + a34*(a41*a53 - a43*a51)) + a23*( + a31*(a42*a54 - a44*a52) - a32*(a41*a54 - a44*a51) + a34*(a41*a52 - a42*a51)) - a24*( + a31*(a42*a53 - a43*a52) - a32*(a41*a53 - a43*a51) + a33*(a41*a52 - a42*a51));
+        double m21 = -(  + a12*( + a33*(a44*a55 - a45*a54) - a34*(a43*a55 - a45*a53) + a35*(a43*a54 - a44*a53)) - a13*( + a32*(a44*a55 - a45*a54) - a34*(a42*a55 - a45*a52) + a35*(a42*a54 - a44*a52)) + a14*( + a32*(a43*a55 - a45*a53) - a33*(a42*a55 - a45*a52) + a35*(a42*a53 - a43*a52)) - a15*( + a32*(a43*a54 - a44*a53) - a33*(a42*a54 - a44*a52) + a34*(a42*a53 - a43*a52)));
+        double m22 =  + a11*( + a33*(a44*a55 - a45*a54) - a34*(a43*a55 - a45*a53) + a35*(a43*a54 - a44*a53)) - a13*( + a31*(a44*a55 - a45*a54) - a34*(a41*a55 - a45*a51) + a35*(a41*a54 - a44*a51)) + a14*( + a31*(a43*a55 - a45*a53) - a33*(a41*a55 - a45*a51) + a35*(a41*a53 - a43*a51)) - a15*( + a31*(a43*a54 - a44*a53) - a33*(a41*a54 - a44*a51) + a34*(a41*a53 - a43*a51));
+        double m23 = -(  + a11*( + a32*(a44*a55 - a45*a54) - a34*(a42*a55 - a45*a52) + a35*(a42*a54 - a44*a52)) - a12*( + a31*(a44*a55 - a45*a54) - a34*(a41*a55 - a45*a51) + a35*(a41*a54 - a44*a51)) + a14*( + a31*(a42*a55 - a45*a52) - a32*(a41*a55 - a45*a51) + a35*(a41*a52 - a42*a51)) - a15*( + a31*(a42*a54 - a44*a52) - a32*(a41*a54 - a44*a51) + a34*(a41*a52 - a42*a51)));
+        double m24 =  + a11*( + a32*(a43*a55 - a45*a53) - a33*(a42*a55 - a45*a52) + a35*(a42*a53 - a43*a52)) - a12*( + a31*(a43*a55 - a45*a53) - a33*(a41*a55 - a45*a51) + a35*(a41*a53 - a43*a51)) + a13*( + a31*(a42*a55 - a45*a52) - a32*(a41*a55 - a45*a51) + a35*(a41*a52 - a42*a51)) - a15*( + a31*(a42*a53 - a43*a52) - a32*(a41*a53 - a43*a51) + a33*(a41*a52 - a42*a51));
+        double m25 = -(  + a11*( + a32*(a43*a54 - a44*a53) - a33*(a42*a54 - a44*a52) + a34*(a42*a53 - a43*a52)) - a12*( + a31*(a43*a54 - a44*a53) - a33*(a41*a54 - a44*a51) + a34*(a41*a53 - a43*a51)) + a13*( + a31*(a42*a54 - a44*a52) - a32*(a41*a54 - a44*a51) + a34*(a41*a52 - a42*a51)) - a14*( + a31*(a42*a53 - a43*a52) - a32*(a41*a53 - a43*a51) + a33*(a41*a52 - a42*a51)));
+        double m31 =  + a12*( + a23*(a44*a55 - a45*a54) - a24*(a43*a55 - a45*a53) + a25*(a43*a54 - a44*a53)) - a13*( + a22*(a44*a55 - a45*a54) - a24*(a42*a55 - a45*a52) + a25*(a42*a54 - a44*a52)) + a14*( + a22*(a43*a55 - a45*a53) - a23*(a42*a55 - a45*a52) + a25*(a42*a53 - a43*a52)) - a15*( + a22*(a43*a54 - a44*a53) - a23*(a42*a54 - a44*a52) + a24*(a42*a53 - a43*a52));
+        double m32 = -(  + a11*( + a23*(a44*a55 - a45*a54) - a24*(a43*a55 - a45*a53) + a25*(a43*a54 - a44*a53)) - a13*( + a21*(a44*a55 - a45*a54) - a24*(a41*a55 - a45*a51) + a25*(a41*a54 - a44*a51)) + a14*( + a21*(a43*a55 - a45*a53) - a23*(a41*a55 - a45*a51) + a25*(a41*a53 - a43*a51)) - a15*( + a21*(a43*a54 - a44*a53) - a23*(a41*a54 - a44*a51) + a24*(a41*a53 - a43*a51)));
+        double m33 =  + a11*( + a22*(a44*a55 - a45*a54) - a24*(a42*a55 - a45*a52) + a25*(a42*a54 - a44*a52)) - a12*( + a21*(a44*a55 - a45*a54) - a24*(a41*a55 - a45*a51) + a25*(a41*a54 - a44*a51)) + a14*( + a21*(a42*a55 - a45*a52) - a22*(a41*a55 - a45*a51) + a25*(a41*a52 - a42*a51)) - a15*( + a21*(a42*a54 - a44*a52) - a22*(a41*a54 - a44*a51) + a24*(a41*a52 - a42*a51));
+        double m34 = -(  + a11*( + a22*(a43*a55 - a45*a53) - a23*(a42*a55 - a45*a52) + a25*(a42*a53 - a43*a52)) - a12*( + a21*(a43*a55 - a45*a53) - a23*(a41*a55 - a45*a51) + a25*(a41*a53 - a43*a51)) + a13*( + a21*(a42*a55 - a45*a52) - a22*(a41*a55 - a45*a51) + a25*(a41*a52 - a42*a51)) - a15*( + a21*(a42*a53 - a43*a52) - a22*(a41*a53 - a43*a51) + a23*(a41*a52 - a42*a51)));
+        double m35 =  + a11*( + a22*(a43*a54 - a44*a53) - a23*(a42*a54 - a44*a52) + a24*(a42*a53 - a43*a52)) - a12*( + a21*(a43*a54 - a44*a53) - a23*(a41*a54 - a44*a51) + a24*(a41*a53 - a43*a51)) + a13*( + a21*(a42*a54 - a44*a52) - a22*(a41*a54 - a44*a51) + a24*(a41*a52 - a42*a51)) - a14*( + a21*(a42*a53 - a43*a52) - a22*(a41*a53 - a43*a51) + a23*(a41*a52 - a42*a51));
+        double m41 = -(  + a12*( + a23*(a34*a55 - a35*a54) - a24*(a33*a55 - a35*a53) + a25*(a33*a54 - a34*a53)) - a13*( + a22*(a34*a55 - a35*a54) - a24*(a32*a55 - a35*a52) + a25*(a32*a54 - a34*a52)) + a14*( + a22*(a33*a55 - a35*a53) - a23*(a32*a55 - a35*a52) + a25*(a32*a53 - a33*a52)) - a15*( + a22*(a33*a54 - a34*a53) - a23*(a32*a54 - a34*a52) + a24*(a32*a53 - a33*a52)));
+        double m42 =  + a11*( + a23*(a34*a55 - a35*a54) - a24*(a33*a55 - a35*a53) + a25*(a33*a54 - a34*a53)) - a13*( + a21*(a34*a55 - a35*a54) - a24*(a31*a55 - a35*a51) + a25*(a31*a54 - a34*a51)) + a14*( + a21*(a33*a55 - a35*a53) - a23*(a31*a55 - a35*a51) + a25*(a31*a53 - a33*a51)) - a15*( + a21*(a33*a54 - a34*a53) - a23*(a31*a54 - a34*a51) + a24*(a31*a53 - a33*a51));
+        double m43 = -(  + a11*( + a22*(a34*a55 - a35*a54) - a24*(a32*a55 - a35*a52) + a25*(a32*a54 - a34*a52)) - a12*( + a21*(a34*a55 - a35*a54) - a24*(a31*a55 - a35*a51) + a25*(a31*a54 - a34*a51)) + a14*( + a21*(a32*a55 - a35*a52) - a22*(a31*a55 - a35*a51) + a25*(a31*a52 - a32*a51)) - a15*( + a21*(a32*a54 - a34*a52) - a22*(a31*a54 - a34*a51) + a24*(a31*a52 - a32*a51)));
+        double m44 =  + a11*( + a22*(a33*a55 - a35*a53) - a23*(a32*a55 - a35*a52) + a25*(a32*a53 - a33*a52)) - a12*( + a21*(a33*a55 - a35*a53) - a23*(a31*a55 - a35*a51) + a25*(a31*a53 - a33*a51)) + a13*( + a21*(a32*a55 - a35*a52) - a22*(a31*a55 - a35*a51) + a25*(a31*a52 - a32*a51)) - a15*( + a21*(a32*a53 - a33*a52) - a22*(a31*a53 - a33*a51) + a23*(a31*a52 - a32*a51));
+        double m45 = -(  + a11*( + a22*(a33*a54 - a34*a53) - a23*(a32*a54 - a34*a52) + a24*(a32*a53 - a33*a52)) - a12*( + a21*(a33*a54 - a34*a53) - a23*(a31*a54 - a34*a51) + a24*(a31*a53 - a33*a51)) + a13*( + a21*(a32*a54 - a34*a52) - a22*(a31*a54 - a34*a51) + a24*(a31*a52 - a32*a51)) - a14*( + a21*(a32*a53 - a33*a52) - a22*(a31*a53 - a33*a51) + a23*(a31*a52 - a32*a51)));
+        double m51 =  + a12*( + a23*(a34*a45 - a35*a44) - a24*(a33*a45 - a35*a43) + a25*(a33*a44 - a34*a43)) - a13*( + a22*(a34*a45 - a35*a44) - a24*(a32*a45 - a35*a42) + a25*(a32*a44 - a34*a42)) + a14*( + a22*(a33*a45 - a35*a43) - a23*(a32*a45 - a35*a42) + a25*(a32*a43 - a33*a42)) - a15*( + a22*(a33*a44 - a34*a43) - a23*(a32*a44 - a34*a42) + a24*(a32*a43 - a33*a42));
+        double m52 = -(  + a11*( + a23*(a34*a45 - a35*a44) - a24*(a33*a45 - a35*a43) + a25*(a33*a44 - a34*a43)) - a13*( + a21*(a34*a45 - a35*a44) - a24*(a31*a45 - a35*a41) + a25*(a31*a44 - a34*a41)) + a14*( + a21*(a33*a45 - a35*a43) - a23*(a31*a45 - a35*a41) + a25*(a31*a43 - a33*a41)) - a15*( + a21*(a33*a44 - a34*a43) - a23*(a31*a44 - a34*a41) + a24*(a31*a43 - a33*a41)));
+        double m53 =  + a11*( + a22*(a34*a45 - a35*a44) - a24*(a32*a45 - a35*a42) + a25*(a32*a44 - a34*a42)) - a12*( + a21*(a34*a45 - a35*a44) - a24*(a31*a45 - a35*a41) + a25*(a31*a44 - a34*a41)) + a14*( + a21*(a32*a45 - a35*a42) - a22*(a31*a45 - a35*a41) + a25*(a31*a42 - a32*a41)) - a15*( + a21*(a32*a44 - a34*a42) - a22*(a31*a44 - a34*a41) + a24*(a31*a42 - a32*a41));
+        double m54 = -(  + a11*( + a22*(a33*a45 - a35*a43) - a23*(a32*a45 - a35*a42) + a25*(a32*a43 - a33*a42)) - a12*( + a21*(a33*a45 - a35*a43) - a23*(a31*a45 - a35*a41) + a25*(a31*a43 - a33*a41)) + a13*( + a21*(a32*a45 - a35*a42) - a22*(a31*a45 - a35*a41) + a25*(a31*a42 - a32*a41)) - a15*( + a21*(a32*a43 - a33*a42) - a22*(a31*a43 - a33*a41) + a23*(a31*a42 - a32*a41)));
+        double m55 =  + a11*( + a22*(a33*a44 - a34*a43) - a23*(a32*a44 - a34*a42) + a24*(a32*a43 - a33*a42)) - a12*( + a21*(a33*a44 - a34*a43) - a23*(a31*a44 - a34*a41) + a24*(a31*a43 - a33*a41)) + a13*( + a21*(a32*a44 - a34*a42) - a22*(a31*a44 - a34*a41) + a24*(a31*a42 - a32*a41)) - a14*( + a21*(a32*a43 - a33*a42) - a22*(a31*a43 - a33*a41) + a23*(a31*a42 - a32*a41));
+
+        double det = (a11*m11 + a12*m12 + a13*m13 + a14*m14 + a15*m15)/scale;
+
+        data = inv.data;
+        data[0] = m11 / det;
+        data[1] = m21 / det;
+        data[2] = m31 / det;
+        data[3] = m41 / det;
+        data[4] = m51 / det;
+        data[5] = m12 / det;
+        data[6] = m22 / det;
+        data[7] = m32 / det;
+        data[8] = m42 / det;
+        data[9] = m52 / det;
+        data[10] = m13 / det;
+        data[11] = m23 / det;
+        data[12] = m33 / det;
+        data[13] = m43 / det;
+        data[14] = m53 / det;
+        data[15] = m14 / det;
+        data[16] = m24 / det;
+        data[17] = m34 / det;
+        data[18] = m44 / det;
+        data[19] = m54 / det;
+        data[20] = m15 / det;
+        data[21] = m25 / det;
+        data[22] = m35 / det;
+        data[23] = m45 / det;
+        data[24] = m55 / det;
+
+    }
+
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/mult/MatrixMatrixMult.java b/main/dense64/src/org/ejml/alg/dense/mult/MatrixMatrixMult.java
new file mode 100644
index 0000000..e07cdb5
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/mult/MatrixMatrixMult.java
@@ -0,0 +1,1239 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.mult;
+
+import org.ejml.data.RowD1Matrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.MatrixDimensionException;
+
+/**
+ * <p>
+ * This class contains various types of matrix matrix multiplication operations for {@link RowD1Matrix64F}.
+ * </p>
+ * <p>
+ * Two algorithms that are equivalent can often have very different runtime performance.
+ * This is because of how modern computers uses fast memory caches to speed up reading/writing to data.
+ * Depending on the order in which variables are processed different algorithms can run much faster than others,
+ * even if the number of operations is the same.
+ * </p>
+ *
+ * <p>
+ * Algorithms that are labeled as 'reorder' are designed to avoid caching jumping issues, some times at the cost
+ * of increasing the number of operations.  This is important for large matrices.  The straight forward 
+ * implementation seems to be faster for small matrices.
+ * </p>
+ * 
+ * <p>
+ * Algorithms that are labeled as 'aux' use an auxiliary array of length n.  This array is used to create
+ * a copy of an out of sequence column vector that is referenced several times.  This reduces the number
+ * of cache misses.  If the 'aux' parameter passed in is null then the array is declared internally.
+ * </p>
+ *
+ * <p>
+ * Typically the straight forward implementation runs about 30% faster on smaller matrices and
+ * about 5 times slower on larger matrices.  This is all computer architecture and matrix shape/size specific.
+ * </p>
+ * 
+ * <p>
+ * <center>******** IMPORTANT **********</center>
+ * This class was auto generated using {@link org.ejml.alg.dense.mult.GeneratorMatrixMatrixMult}
+ * </p>
+ * 
+ * @author Peter Abeles
+ */
+public class MatrixMatrixMult {
+    /**
+     * @see org.ejml.ops.CommonOps#mult( org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void mult_reorder( RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numCols != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numRows != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        if( a.numCols == 0 || a.numRows == 0 ) {
+            CommonOps.fill(c,0);
+            return;
+        }
+        double valA;
+        int indexCbase= 0;
+        int endOfKLoop = b.numRows*b.numCols;
+
+        for( int i = 0; i < a.numRows; i++ ) {
+            int indexA = i*a.numCols;
+
+            // need to assign c.data to a value initially
+            int indexB = 0;
+            int indexC = indexCbase;
+            int end = indexB + b.numCols;
+
+            valA = a.get(indexA++);
+
+            while( indexB < end ) {
+                c.set(indexC++ , valA*b.get(indexB++));
+            }
+
+            // now add to it
+            while( indexB != endOfKLoop ) { // k loop
+                indexC = indexCbase;
+                end = indexB + b.numCols;
+
+                valA = a.get(indexA++);
+
+                while( indexB < end ) { // j loop
+                    c.plus(indexC++ , valA*b.get(indexB++));
+                }
+            }
+            indexCbase += c.numCols;
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#mult( org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void mult_small( RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numCols != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numRows != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        int aIndexStart = 0;
+        int cIndex = 0;
+
+        for( int i = 0; i < a.numRows; i++ ) {
+            for( int j = 0; j < b.numCols; j++ ) {
+                double total = 0;
+
+                int indexA = aIndexStart;
+                int indexB = j;
+                int end = indexA + b.numRows;
+                while( indexA < end ) {
+                    total += a.get(indexA++) * b.get(indexB);
+                    indexB += b.numCols;
+                }
+
+                c.set( cIndex++ , total );
+            }
+            aIndexStart += a.numCols;
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#mult( org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void mult_aux( RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c , double []aux )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numCols != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numRows != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        if( aux == null ) aux = new double[ b.numRows ];
+
+        for( int j = 0; j < b.numCols; j++ ) {
+            // create a copy of the column in B to avoid cache issues
+            for( int k = 0; k < b.numRows; k++ ) {
+                aux[k] = b.unsafe_get(k,j);
+            }
+
+            int indexA = 0;
+            for( int i = 0; i < a.numRows; i++ ) {
+                double total = 0;
+                for( int k = 0; k < b.numRows; ) {
+                    total += a.get(indexA++)*aux[k++];
+                }
+                c.set( i*c.numCols+j , total );
+            }
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#multTransA( org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void multTransA_reorder( RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numRows != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numCols != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        if( a.numCols == 0 || a.numRows == 0 ) {
+            CommonOps.fill(c,0);
+            return;
+        }
+        double valA;
+
+        for( int i = 0; i < a.numCols; i++ ) {
+            int indexC_start = i*c.numCols;
+
+            // first assign R
+            valA = a.get(i);
+            int indexB = 0;
+            int end = indexB+b.numCols;
+            int indexC = indexC_start;
+            while( indexB<end ) {
+                c.set( indexC++ , valA*b.get(indexB++));
+            }
+            // now increment it
+            for( int k = 1; k < a.numRows; k++ ) {
+                valA = a.unsafe_get(k,i);
+                end = indexB+b.numCols;
+                indexC = indexC_start;
+                // this is the loop for j
+                while( indexB<end ) {
+                    c.plus( indexC++ , valA*b.get(indexB++));
+                }
+            }
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#multTransA( org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void multTransA_small( RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numRows != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numCols != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        int cIndex = 0;
+
+        for( int i = 0; i < a.numCols; i++ ) {
+            for( int j = 0; j < b.numCols; j++ ) {
+                int indexA = i;
+                int indexB = j;
+                int end = indexB + b.numRows*b.numCols;
+
+                double total = 0;
+
+                // loop for k
+                for(; indexB < end; indexB += b.numCols ) {
+                    total += a.get(indexA) * b.get(indexB);
+                    indexA += a.numCols;
+                }
+
+                c.set( cIndex++ , total );
+            }
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#multTransAB( org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void multTransAB( RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numRows != b.numCols ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numCols != c.numRows || b.numRows != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        int cIndex = 0;
+
+        for( int i = 0; i < a.numCols; i++ ) {
+            int indexB = 0;
+            for( int j = 0; j < b.numRows; j++ ) {
+                int indexA = i;
+                int end = indexB + b.numCols;
+
+                double total = 0;
+
+                for( ;indexB<end; ) {
+                    total += a.get(indexA) * b.get(indexB++);
+                    indexA += a.numCols;
+                }
+
+                c.set( cIndex++ , total );
+            }
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#multTransAB( org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void multTransAB_aux( RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c , double []aux )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numRows != b.numCols ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numCols != c.numRows || b.numRows != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        if( aux == null ) aux = new double[ a.numRows ];
+
+        if( a.numCols == 0 || a.numRows == 0 ) {
+            CommonOps.fill(c,0);
+            return;
+        }
+        int indexC = 0;
+        for( int i = 0; i < a.numCols; i++ ) {
+            for( int k = 0; k < b.numCols; k++ ) {
+                aux[k] = a.unsafe_get(k,i);
+            }
+
+            for( int j = 0; j < b.numRows; j++ ) {
+                double total = 0;
+
+                for( int k = 0; k < b.numCols; k++ ) {
+                    total += aux[k] * b.unsafe_get(j,k);
+                }
+                c.set( indexC++ , total );
+            }
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#multTransB( org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void multTransB( RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numCols != b.numCols ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numRows != c.numRows || b.numRows != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        int cIndex = 0;
+        int aIndexStart = 0;
+
+        for( int xA = 0; xA < a.numRows; xA++ ) {
+            int end = aIndexStart + b.numCols;
+            int indexB = 0;
+            for( int xB = 0; xB < b.numRows; xB++ ) {
+                int indexA = aIndexStart;
+
+                double total = 0;
+
+                while( indexA<end ) {
+                    total += a.get(indexA++) * b.get(indexB++);
+                }
+
+                c.set( cIndex++ , total );
+            }
+            aIndexStart += a.numCols;
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#multAdd( org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void multAdd_reorder( RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numCols != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numRows != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        if( a.numCols == 0 || a.numRows == 0 ) {
+            return;
+        }
+        double valA;
+        int indexCbase= 0;
+        int endOfKLoop = b.numRows*b.numCols;
+
+        for( int i = 0; i < a.numRows; i++ ) {
+            int indexA = i*a.numCols;
+
+            // need to assign c.data to a value initially
+            int indexB = 0;
+            int indexC = indexCbase;
+            int end = indexB + b.numCols;
+
+            valA = a.get(indexA++);
+
+            while( indexB < end ) {
+                c.plus(indexC++ , valA*b.get(indexB++));
+            }
+
+            // now add to it
+            while( indexB != endOfKLoop ) { // k loop
+                indexC = indexCbase;
+                end = indexB + b.numCols;
+
+                valA = a.get(indexA++);
+
+                while( indexB < end ) { // j loop
+                    c.plus(indexC++ , valA*b.get(indexB++));
+                }
+            }
+            indexCbase += c.numCols;
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#multAdd( org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void multAdd_small( RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numCols != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numRows != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        int aIndexStart = 0;
+        int cIndex = 0;
+
+        for( int i = 0; i < a.numRows; i++ ) {
+            for( int j = 0; j < b.numCols; j++ ) {
+                double total = 0;
+
+                int indexA = aIndexStart;
+                int indexB = j;
+                int end = indexA + b.numRows;
+                while( indexA < end ) {
+                    total += a.get(indexA++) * b.get(indexB);
+                    indexB += b.numCols;
+                }
+
+                c.plus( cIndex++ , total );
+            }
+            aIndexStart += a.numCols;
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#multAdd( org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void multAdd_aux( RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c , double []aux )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numCols != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numRows != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        if( aux == null ) aux = new double[ b.numRows ];
+
+        for( int j = 0; j < b.numCols; j++ ) {
+            // create a copy of the column in B to avoid cache issues
+            for( int k = 0; k < b.numRows; k++ ) {
+                aux[k] = b.unsafe_get(k,j);
+            }
+
+            int indexA = 0;
+            for( int i = 0; i < a.numRows; i++ ) {
+                double total = 0;
+                for( int k = 0; k < b.numRows; ) {
+                    total += a.get(indexA++)*aux[k++];
+                }
+                c.plus( i*c.numCols+j , total );
+            }
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#multAddTransA( org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void multAddTransA_reorder( RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numRows != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numCols != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        if( a.numCols == 0 || a.numRows == 0 ) {
+            return;
+        }
+        double valA;
+
+        for( int i = 0; i < a.numCols; i++ ) {
+            int indexC_start = i*c.numCols;
+
+            // first assign R
+            valA = a.get(i);
+            int indexB = 0;
+            int end = indexB+b.numCols;
+            int indexC = indexC_start;
+            while( indexB<end ) {
+                c.plus( indexC++ , valA*b.get(indexB++));
+            }
+            // now increment it
+            for( int k = 1; k < a.numRows; k++ ) {
+                valA = a.unsafe_get(k,i);
+                end = indexB+b.numCols;
+                indexC = indexC_start;
+                // this is the loop for j
+                while( indexB<end ) {
+                    c.plus( indexC++ , valA*b.get(indexB++));
+                }
+            }
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#multAddTransA( org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void multAddTransA_small( RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numRows != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numCols != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        int cIndex = 0;
+
+        for( int i = 0; i < a.numCols; i++ ) {
+            for( int j = 0; j < b.numCols; j++ ) {
+                int indexA = i;
+                int indexB = j;
+                int end = indexB + b.numRows*b.numCols;
+
+                double total = 0;
+
+                // loop for k
+                for(; indexB < end; indexB += b.numCols ) {
+                    total += a.get(indexA) * b.get(indexB);
+                    indexA += a.numCols;
+                }
+
+                c.plus( cIndex++ , total );
+            }
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#multAddTransAB( org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void multAddTransAB( RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numRows != b.numCols ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numCols != c.numRows || b.numRows != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        int cIndex = 0;
+
+        for( int i = 0; i < a.numCols; i++ ) {
+            int indexB = 0;
+            for( int j = 0; j < b.numRows; j++ ) {
+                int indexA = i;
+                int end = indexB + b.numCols;
+
+                double total = 0;
+
+                for( ;indexB<end; ) {
+                    total += a.get(indexA) * b.get(indexB++);
+                    indexA += a.numCols;
+                }
+
+                c.plus( cIndex++ , total );
+            }
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#multAddTransAB( org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void multAddTransAB_aux( RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c , double []aux )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numRows != b.numCols ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numCols != c.numRows || b.numRows != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        if( aux == null ) aux = new double[ a.numRows ];
+
+        if( a.numCols == 0 || a.numRows == 0 ) {
+            return;
+        }
+        int indexC = 0;
+        for( int i = 0; i < a.numCols; i++ ) {
+            for( int k = 0; k < b.numCols; k++ ) {
+                aux[k] = a.unsafe_get(k,i);
+            }
+
+            for( int j = 0; j < b.numRows; j++ ) {
+                double total = 0;
+
+                for( int k = 0; k < b.numCols; k++ ) {
+                    total += aux[k] * b.unsafe_get(j,k);
+                }
+                c.plus( indexC++ , total );
+            }
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#multAddTransB( org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void multAddTransB( RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numCols != b.numCols ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numRows != c.numRows || b.numRows != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        int cIndex = 0;
+        int aIndexStart = 0;
+
+        for( int xA = 0; xA < a.numRows; xA++ ) {
+            int end = aIndexStart + b.numCols;
+            int indexB = 0;
+            for( int xB = 0; xB < b.numRows; xB++ ) {
+                int indexA = aIndexStart;
+
+                double total = 0;
+
+                while( indexA<end ) {
+                    total += a.get(indexA++) * b.get(indexB++);
+                }
+
+                c.plus( cIndex++ , total );
+            }
+            aIndexStart += a.numCols;
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#mult(double,  org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void mult_reorder( double alpha , RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numCols != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numRows != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        if( a.numCols == 0 || a.numRows == 0 ) {
+            CommonOps.fill(c,0);
+            return;
+        }
+        double valA;
+        int indexCbase= 0;
+        int endOfKLoop = b.numRows*b.numCols;
+
+        for( int i = 0; i < a.numRows; i++ ) {
+            int indexA = i*a.numCols;
+
+            // need to assign c.data to a value initially
+            int indexB = 0;
+            int indexC = indexCbase;
+            int end = indexB + b.numCols;
+
+            valA = alpha*a.get(indexA++);
+
+            while( indexB < end ) {
+                c.set(indexC++ , valA*b.get(indexB++));
+            }
+
+            // now add to it
+            while( indexB != endOfKLoop ) { // k loop
+                indexC = indexCbase;
+                end = indexB + b.numCols;
+
+                valA = alpha*a.get(indexA++);
+
+                while( indexB < end ) { // j loop
+                    c.plus(indexC++ , valA*b.get(indexB++));
+                }
+            }
+            indexCbase += c.numCols;
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#mult(double,  org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void mult_small( double alpha , RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numCols != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numRows != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        int aIndexStart = 0;
+        int cIndex = 0;
+
+        for( int i = 0; i < a.numRows; i++ ) {
+            for( int j = 0; j < b.numCols; j++ ) {
+                double total = 0;
+
+                int indexA = aIndexStart;
+                int indexB = j;
+                int end = indexA + b.numRows;
+                while( indexA < end ) {
+                    total += a.get(indexA++) * b.get(indexB);
+                    indexB += b.numCols;
+                }
+
+                c.set( cIndex++ , alpha*total );
+            }
+            aIndexStart += a.numCols;
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#mult(double,  org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void mult_aux( double alpha , RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c , double []aux )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numCols != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numRows != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        if( aux == null ) aux = new double[ b.numRows ];
+
+        for( int j = 0; j < b.numCols; j++ ) {
+            // create a copy of the column in B to avoid cache issues
+            for( int k = 0; k < b.numRows; k++ ) {
+                aux[k] = b.unsafe_get(k,j);
+            }
+
+            int indexA = 0;
+            for( int i = 0; i < a.numRows; i++ ) {
+                double total = 0;
+                for( int k = 0; k < b.numRows; ) {
+                    total += a.get(indexA++)*aux[k++];
+                }
+                c.set( i*c.numCols+j , alpha*total );
+            }
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#multTransA(double,  org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void multTransA_reorder( double alpha , RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numRows != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numCols != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        if( a.numCols == 0 || a.numRows == 0 ) {
+            CommonOps.fill(c,0);
+            return;
+        }
+        double valA;
+
+        for( int i = 0; i < a.numCols; i++ ) {
+            int indexC_start = i*c.numCols;
+
+            // first assign R
+            valA = alpha*a.get(i);
+            int indexB = 0;
+            int end = indexB+b.numCols;
+            int indexC = indexC_start;
+            while( indexB<end ) {
+                c.set( indexC++ , valA*b.get(indexB++));
+            }
+            // now increment it
+            for( int k = 1; k < a.numRows; k++ ) {
+                valA = alpha*a.unsafe_get(k,i);
+                end = indexB+b.numCols;
+                indexC = indexC_start;
+                // this is the loop for j
+                while( indexB<end ) {
+                    c.plus( indexC++ , valA*b.get(indexB++));
+                }
+            }
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#multTransA(double,  org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void multTransA_small( double alpha , RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numRows != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numCols != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        int cIndex = 0;
+
+        for( int i = 0; i < a.numCols; i++ ) {
+            for( int j = 0; j < b.numCols; j++ ) {
+                int indexA = i;
+                int indexB = j;
+                int end = indexB + b.numRows*b.numCols;
+
+                double total = 0;
+
+                // loop for k
+                for(; indexB < end; indexB += b.numCols ) {
+                    total += a.get(indexA) * b.get(indexB);
+                    indexA += a.numCols;
+                }
+
+                c.set( cIndex++ , alpha*total );
+            }
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#multTransAB(double,  org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void multTransAB( double alpha , RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numRows != b.numCols ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numCols != c.numRows || b.numRows != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        int cIndex = 0;
+
+        for( int i = 0; i < a.numCols; i++ ) {
+            int indexB = 0;
+            for( int j = 0; j < b.numRows; j++ ) {
+                int indexA = i;
+                int end = indexB + b.numCols;
+
+                double total = 0;
+
+                for( ;indexB<end; ) {
+                    total += a.get(indexA) * b.get(indexB++);
+                    indexA += a.numCols;
+                }
+
+                c.set( cIndex++ , alpha*total );
+            }
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#multTransAB(double,  org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void multTransAB_aux( double alpha , RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c , double []aux )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numRows != b.numCols ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numCols != c.numRows || b.numRows != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        if( aux == null ) aux = new double[ a.numRows ];
+
+        if( a.numCols == 0 || a.numRows == 0 ) {
+            CommonOps.fill(c,0);
+            return;
+        }
+        int indexC = 0;
+        for( int i = 0; i < a.numCols; i++ ) {
+            for( int k = 0; k < b.numCols; k++ ) {
+                aux[k] = a.unsafe_get(k,i);
+            }
+
+            for( int j = 0; j < b.numRows; j++ ) {
+                double total = 0;
+
+                for( int k = 0; k < b.numCols; k++ ) {
+                    total += aux[k] * b.unsafe_get(j,k);
+                }
+                c.set( indexC++ , alpha*total );
+            }
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#multTransB(double,  org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void multTransB( double alpha , RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numCols != b.numCols ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numRows != c.numRows || b.numRows != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        int cIndex = 0;
+        int aIndexStart = 0;
+
+        for( int xA = 0; xA < a.numRows; xA++ ) {
+            int end = aIndexStart + b.numCols;
+            int indexB = 0;
+            for( int xB = 0; xB < b.numRows; xB++ ) {
+                int indexA = aIndexStart;
+
+                double total = 0;
+
+                while( indexA<end ) {
+                    total += a.get(indexA++) * b.get(indexB++);
+                }
+
+                c.set( cIndex++ , alpha*total );
+            }
+            aIndexStart += a.numCols;
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#multAdd(double,  org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void multAdd_reorder( double alpha , RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numCols != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numRows != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        if( a.numCols == 0 || a.numRows == 0 ) {
+            return;
+        }
+        double valA;
+        int indexCbase= 0;
+        int endOfKLoop = b.numRows*b.numCols;
+
+        for( int i = 0; i < a.numRows; i++ ) {
+            int indexA = i*a.numCols;
+
+            // need to assign c.data to a value initially
+            int indexB = 0;
+            int indexC = indexCbase;
+            int end = indexB + b.numCols;
+
+            valA = alpha*a.get(indexA++);
+
+            while( indexB < end ) {
+                c.plus(indexC++ , valA*b.get(indexB++));
+            }
+
+            // now add to it
+            while( indexB != endOfKLoop ) { // k loop
+                indexC = indexCbase;
+                end = indexB + b.numCols;
+
+                valA = alpha*a.get(indexA++);
+
+                while( indexB < end ) { // j loop
+                    c.plus(indexC++ , valA*b.get(indexB++));
+                }
+            }
+            indexCbase += c.numCols;
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#multAdd(double,  org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void multAdd_small( double alpha , RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numCols != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numRows != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        int aIndexStart = 0;
+        int cIndex = 0;
+
+        for( int i = 0; i < a.numRows; i++ ) {
+            for( int j = 0; j < b.numCols; j++ ) {
+                double total = 0;
+
+                int indexA = aIndexStart;
+                int indexB = j;
+                int end = indexA + b.numRows;
+                while( indexA < end ) {
+                    total += a.get(indexA++) * b.get(indexB);
+                    indexB += b.numCols;
+                }
+
+                c.plus( cIndex++ , alpha*total );
+            }
+            aIndexStart += a.numCols;
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#multAdd(double,  org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void multAdd_aux( double alpha , RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c , double []aux )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numCols != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numRows != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        if( aux == null ) aux = new double[ b.numRows ];
+
+        for( int j = 0; j < b.numCols; j++ ) {
+            // create a copy of the column in B to avoid cache issues
+            for( int k = 0; k < b.numRows; k++ ) {
+                aux[k] = b.unsafe_get(k,j);
+            }
+
+            int indexA = 0;
+            for( int i = 0; i < a.numRows; i++ ) {
+                double total = 0;
+                for( int k = 0; k < b.numRows; ) {
+                    total += a.get(indexA++)*aux[k++];
+                }
+                c.plus( i*c.numCols+j , alpha*total );
+            }
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#multAddTransA(double,  org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void multAddTransA_reorder( double alpha , RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numRows != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numCols != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        if( a.numCols == 0 || a.numRows == 0 ) {
+            return;
+        }
+        double valA;
+
+        for( int i = 0; i < a.numCols; i++ ) {
+            int indexC_start = i*c.numCols;
+
+            // first assign R
+            valA = alpha*a.get(i);
+            int indexB = 0;
+            int end = indexB+b.numCols;
+            int indexC = indexC_start;
+            while( indexB<end ) {
+                c.plus( indexC++ , valA*b.get(indexB++));
+            }
+            // now increment it
+            for( int k = 1; k < a.numRows; k++ ) {
+                valA = alpha*a.unsafe_get(k,i);
+                end = indexB+b.numCols;
+                indexC = indexC_start;
+                // this is the loop for j
+                while( indexB<end ) {
+                    c.plus( indexC++ , valA*b.get(indexB++));
+                }
+            }
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#multAddTransA(double,  org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void multAddTransA_small( double alpha , RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numRows != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numCols != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        int cIndex = 0;
+
+        for( int i = 0; i < a.numCols; i++ ) {
+            for( int j = 0; j < b.numCols; j++ ) {
+                int indexA = i;
+                int indexB = j;
+                int end = indexB + b.numRows*b.numCols;
+
+                double total = 0;
+
+                // loop for k
+                for(; indexB < end; indexB += b.numCols ) {
+                    total += a.get(indexA) * b.get(indexB);
+                    indexA += a.numCols;
+                }
+
+                c.plus( cIndex++ , alpha*total );
+            }
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#multAddTransAB(double,  org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void multAddTransAB( double alpha , RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numRows != b.numCols ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numCols != c.numRows || b.numRows != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        int cIndex = 0;
+
+        for( int i = 0; i < a.numCols; i++ ) {
+            int indexB = 0;
+            for( int j = 0; j < b.numRows; j++ ) {
+                int indexA = i;
+                int end = indexB + b.numCols;
+
+                double total = 0;
+
+                for( ;indexB<end; ) {
+                    total += a.get(indexA) * b.get(indexB++);
+                    indexA += a.numCols;
+                }
+
+                c.plus( cIndex++ , alpha*total );
+            }
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#multAddTransAB(double,  org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void multAddTransAB_aux( double alpha , RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c , double []aux )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numRows != b.numCols ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numCols != c.numRows || b.numRows != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        if( aux == null ) aux = new double[ a.numRows ];
+
+        if( a.numCols == 0 || a.numRows == 0 ) {
+            return;
+        }
+        int indexC = 0;
+        for( int i = 0; i < a.numCols; i++ ) {
+            for( int k = 0; k < b.numCols; k++ ) {
+                aux[k] = a.unsafe_get(k,i);
+            }
+
+            for( int j = 0; j < b.numRows; j++ ) {
+                double total = 0;
+
+                for( int k = 0; k < b.numCols; k++ ) {
+                    total += aux[k] * b.unsafe_get(j,k);
+                }
+                c.plus( indexC++ , alpha*total );
+            }
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#multAddTransB(double,  org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void multAddTransB( double alpha , RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numCols != b.numCols ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numRows != c.numRows || b.numRows != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        int cIndex = 0;
+        int aIndexStart = 0;
+
+        for( int xA = 0; xA < a.numRows; xA++ ) {
+            int end = aIndexStart + b.numCols;
+            int indexB = 0;
+            for( int xB = 0; xB < b.numRows; xB++ ) {
+                int indexA = aIndexStart;
+
+                double total = 0;
+
+                while( indexA<end ) {
+                    total += a.get(indexA++) * b.get(indexB++);
+                }
+
+                c.plus( cIndex++ , alpha*total );
+            }
+            aIndexStart += a.numCols;
+        }
+    }
+
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/mult/MatrixMultProduct.java b/main/dense64/src/org/ejml/alg/dense/mult/MatrixMultProduct.java
new file mode 100644
index 0000000..0a0e99b
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/mult/MatrixMultProduct.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.mult;
+
+import org.ejml.data.RowD1Matrix64F;
+
+/**
+ * <p>
+ * Specialized operations for performing inner and outer products for matrices.
+ * </p>
+ * 
+ * <p>
+ * inner product: B=A<sup>T</sup>*A<br>
+ * outer product: B=A*A<sup>T</sup>
+ * </p>
+ * 
+ * @author Peter Abeles
+ */
+public class MatrixMultProduct {
+
+    public static void outer(RowD1Matrix64F a, RowD1Matrix64F c) {
+        for( int i = 0; i < a.numRows; i++ ) {
+            int indexC1 = i*c.numCols+i;
+            int indexC2 = indexC1;
+            for( int j = i; j < a.numRows; j++ , indexC2 += c.numCols) {
+                int indexA = i*a.numCols;
+                int indexB = j*a.numCols;
+                double sum = 0;
+                int end = indexA + a.numCols;
+                for( ; indexA < end; indexA++,indexB++ ) {
+                    sum += a.data[indexA]*a.data[indexB];
+                }
+                c.data[indexC2] = c.data[indexC1++] = sum;
+            }
+        }
+//        for( int i = 0; i < a.numRows; i++ ) {
+//            for( int j = 0; j < a.numRows; j++ ) {
+//                double sum = 0;
+//                for( int k = 0; k < a.numCols; k++ ) {
+//                    sum += a.get(i,k)*a.get(j,k);
+//                }
+//                c.set(i,j,sum);
+//            }
+//        }
+    }
+    
+    public static void inner_small(RowD1Matrix64F a, RowD1Matrix64F c) {
+
+        for( int i = 0; i < a.numCols; i++ ) {
+            for( int j = i; j < a.numCols; j++ ) {
+                int indexC1 = i*c.numCols+j;
+                int indexC2 = j*c.numCols+i;
+                int indexA = i;
+                int indexB = j;
+                double sum = 0;
+                int end = indexA + a.numRows*a.numCols;
+                for( ; indexA < end; indexA += a.numCols, indexB += a.numCols ) {
+                    sum += a.data[indexA]*a.data[indexB];
+                }
+                c.data[indexC1] = c.data[indexC2] = sum;
+            }
+        }
+//        for( int i = 0; i < a.numCols; i++ ) {
+//            for( int j = i; j < a.numCols; j++ ) {
+//                double sum = 0;
+//                for( int k = 0; k < a.numRows; k++ ) {
+//                    sum += a.get(k,i)*a.get(k,j);
+//                }
+//                c.set(i,j,sum);
+//                c.set(j,i,sum);
+//            }
+//        }
+    }
+
+    public static void inner_reorder(RowD1Matrix64F a, RowD1Matrix64F c) {
+
+        for( int i = 0; i < a.numCols; i++ ) {
+            int indexC = i*c.numCols+i;
+            double valAi = a.data[i];
+            for( int j = i; j < a.numCols; j++ ) {
+                c.data[indexC++] =  valAi*a.data[j];
+            }
+
+            for( int k = 1; k < a.numRows; k++ ) {
+                indexC = i*c.numCols+i;
+                int indexB = k*a.numCols+i;
+                valAi = a.data[indexB];
+                for( int j = i; j < a.numCols; j++ ) {
+                    c.data[indexC++] +=  valAi*a.data[indexB++];
+                }
+            }
+
+            indexC = i*c.numCols+i;
+            int indexC2 = indexC;
+            for( int j = i; j < a.numCols; j++ , indexC2 += c.numCols) {
+                c.data[indexC2] = c.data[indexC++];
+            }
+        }
+
+//        for( int i = 0; i < a.numCols; i++ ) {
+//            for( int j = i; j < a.numCols; j++ ) {
+//                c.set(i,j,a.get(0,i)*a.get(0,j));
+//            }
+//
+//            for( int k = 1; k < a.numRows; k++ ) {
+//                for( int j = i; j < a.numCols; j++ ) {
+//                    c.set(i,j, c.get(i,j)+ a.get(k,i)*a.get(k,j));
+//                }
+//            }
+//            for( int j = i; j < a.numCols; j++ ) {
+//                c.set(j,i,c.get(i,j));
+//            }
+//        }
+    }
+
+    public static void inner_reorder_upper(RowD1Matrix64F a, RowD1Matrix64F c) {
+        for( int i = 0; i < a.numCols; i++ ) {
+            int indexC = i*c.numCols+i;
+            double valAi = a.data[i];
+            for( int j = i; j < a.numCols; j++ ) {
+                c.data[indexC++] =  valAi*a.data[j];
+            }
+
+            for( int k = 1; k < a.numRows; k++ ) {
+                indexC = i*c.numCols+i;
+                int indexB = k*a.numCols+i;
+                valAi = a.data[indexB];
+                for( int j = i; j < a.numCols; j++ ) {
+                    c.data[indexC++] +=  valAi*a.data[indexB++];
+                }
+            }
+        }
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/mult/MatrixVectorMult.java b/main/dense64/src/org/ejml/alg/dense/mult/MatrixVectorMult.java
new file mode 100644
index 0000000..1a1f364
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/mult/MatrixVectorMult.java
@@ -0,0 +1,351 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.mult;
+
+import org.ejml.data.D1Matrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.data.RowD1Matrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.MatrixDimensionException;
+
+
+/**
+ * <p>
+ * This class contains various types of matrix vector multiplcation operations for {@link DenseMatrix64F}.
+ * </p>
+ * <p>
+ * If a matrix has only one column or row then it is a vector.  There are faster algorithms
+ * that can be used to multiply matrices by vectors.  Strangely, even though the operations
+ * count smaller, the difference between this and a regular matrix multiply is insignificant
+ * for large matrices.  The smaller matrices there is about a 40% speed improvement.  In
+ * practice the speed improvement for smaller matrices is not noticeable unless 10s of millions
+ * of matrix multiplications are being performed.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+ at SuppressWarnings({"ForLoopReplaceableByForEach"})
+public class MatrixVectorMult {
+
+    /**
+     * <p>
+     * Performs a matrix vector multiply.<br>
+     * <br>
+     * c = A * b <br>
+     * and<br>
+     * c = A * b<sup>T</sup> <br>
+     * <br>
+     * c<sub>i</sub> = Sum{ j=1:n, a<sub>ij</sub> * b<sub>j</sub>}<br>
+     * <br>
+     * where A is a matrix, b is a column or transposed row vector, and c is a column vector.
+     * </p>
+     *
+     * @param A A matrix that is m by n. Not modified.
+     * @param B A vector that has length n. Not modified.
+     * @param C A column vector that has length m. Modified.
+     */
+    public static void mult( RowD1Matrix64F A, D1Matrix64F B, D1Matrix64F C)
+    {
+        if( C.numCols != 1 ) {
+            throw new MatrixDimensionException("C is not a column vector");
+        } else if( C.numRows != A.numRows ) {
+            throw new MatrixDimensionException("C is not the expected length");
+        }
+        
+        if( B.numRows == 1 ) {
+            if( A.numCols != B.numCols ) {
+                throw new MatrixDimensionException("A and B are not compatible");
+            }
+        } else if( B.numCols == 1 ) {
+            if( A.numCols != B.numRows ) {
+                throw new MatrixDimensionException("A and B are not compatible");
+            }
+        } else {
+            throw new MatrixDimensionException("B is not a vector");
+        }
+
+        if( A.numCols == 0 ) {
+            CommonOps.fill(C,0);
+            return;
+        }
+
+        int indexA = 0;
+        int cIndex = 0;
+        double b0 = B.get(0);
+        for( int i = 0; i < A.numRows; i++ ) {
+            double total = A.get(indexA++) * b0;
+
+            for( int j = 1; j < A.numCols; j++ ) {
+                total += A.get(indexA++) * B.get(j);
+            }
+
+            C.set(cIndex++, total);
+        }
+    }
+
+    /**
+     * <p>
+     * Performs a matrix vector multiply.<br>
+     * <br>
+     * C = C + A * B <br>
+     * or<br>
+     * C = C + A * B<sup>T</sup> <br>
+     * <br>
+     * c<sub>i</sub> = Sum{ j=1:n, c<sub>i</sub> + a<sub>ij</sub> * b<sub>j</sub>}<br>
+     * <br>
+     * where A is a matrix, B is a column or transposed row vector, and C is a column vector.
+     * </p>
+     *
+     * @param A A matrix that is m by n. Not modified.
+     * @param B A vector that has length n. Not modified.
+     * @param C A column vector that has length m. Modified.
+     */
+    public static void multAdd( RowD1Matrix64F A , D1Matrix64F B , D1Matrix64F C )
+    {
+
+        if( C.numCols != 1 ) {
+            throw new MatrixDimensionException("C is not a column vector");
+        } else if( C.numRows != A.numRows ) {
+            throw new MatrixDimensionException("C is not the expected length");
+        }
+        if( B.numRows == 1 ) {
+            if( A.numCols != B.numCols ) {
+                throw new MatrixDimensionException("A and B are not compatible");
+            }
+        } else if( B.numCols == 1 ) {
+            if( A.numCols != B.numRows ) {
+                throw new MatrixDimensionException("A and B are not compatible");
+            }
+        } else {
+            throw new MatrixDimensionException("B is not a vector");
+        }
+
+        if( A.numCols == 0 ) {
+            return;
+        }
+
+        int indexA = 0;
+        int cIndex = 0;
+        for( int i = 0; i < A.numRows; i++ ) {
+            double total = A.get(indexA++) * B.get(0);
+
+            for( int j = 1; j < A.numCols; j++ ) {
+                total += A.get(indexA++) * B.get(j);
+            }
+
+            C.plus(cIndex++ , total );
+        }
+    }
+
+    /**
+     * <p>
+     * Performs a matrix vector multiply.<br>
+     * <br>
+     * C = A<sup>T</sup> * B <br>
+     * where B is a column vector.<br>
+     * or<br>
+     * C = A<sup>T</sup> * B<sup>T</sup> <br>
+     * where B is a row vector. <br>
+     * <br>
+     * c<sub>i</sub> = Sum{ j=1:n, a<sub>ji</sub> * b<sub>j</sub>}<br>
+     * <br>
+     * where A is a matrix, B is a column or transposed row vector, and C is a column vector.
+     * </p>
+     * <p>
+     * This implementation is optimal for small matrices.  There is a huge performance hit when
+     * used on large matrices due to CPU cache issues.
+     * </p>
+     *
+     * @param A A matrix that is m by n. Not modified.
+     * @param B A that has length m and is a column. Not modified.
+     * @param C A column vector that has length n. Modified.
+     */
+    public static void multTransA_small( RowD1Matrix64F A , D1Matrix64F B , D1Matrix64F C )
+    {
+        if( C.numCols != 1 ) {
+            throw new MatrixDimensionException("C is not a column vector");
+        } else if( C.numRows != A.numCols ) {
+            throw new MatrixDimensionException("C is not the expected length");
+        }
+        if( B.numRows == 1 ) {
+            if( A.numRows != B.numCols ) {
+                throw new MatrixDimensionException("A and B are not compatible");
+            }
+        } else if( B.numCols == 1 ) {
+            if( A.numRows != B.numRows ) {
+                throw new MatrixDimensionException("A and B are not compatible");
+            }
+        } else {
+            throw new MatrixDimensionException("B is not a vector");
+        }
+
+        int cIndex = 0;
+        for( int i = 0; i < A.numCols; i++ ) {
+            double total = 0.0;
+
+            int indexA = i;
+            for( int j = 0; j < A.numRows; j++ ) {
+                total += A.get(indexA) * B.get(j);
+                indexA += A.numCols;
+            }
+
+            C.set(cIndex++ , total);
+        }
+    }
+
+    /**
+     * An alternative implementation of {@link #multTransA_small} that performs well on large
+     * matrices.  There is a relative performance hit when used on small matrices.
+     *
+     * @param A A matrix that is m by n. Not modified.
+     * @param B A Vector that has length m. Not modified.
+     * @param C A column vector that has length n. Modified.
+     */
+    public static void multTransA_reorder( RowD1Matrix64F A , D1Matrix64F B , D1Matrix64F C )
+    {
+        if( C.numCols != 1 ) {
+            throw new MatrixDimensionException("C is not a column vector");
+        } else if( C.numRows != A.numCols ) {
+            throw new MatrixDimensionException("C is not the expected length");
+        }
+        if( B.numRows == 1 ) {
+            if( A.numRows != B.numCols ) {
+                throw new MatrixDimensionException("A and B are not compatible");
+            }
+        } else if( B.numCols == 1 ) {
+            if( A.numRows != B.numRows ) {
+                throw new MatrixDimensionException("A and B are not compatible");
+            }
+        } else {
+            throw new MatrixDimensionException("B is not a vector");
+        }
+
+        if( A.numRows == 0 ) {
+            CommonOps.fill(C,0);
+            return;
+        }
+
+        double B_val = B.get(0);
+        for( int i = 0; i < A.numCols; i++ ) {
+            C.set( i , A.get(i) * B_val );
+        }
+
+        int indexA = A.numCols;
+        for( int i = 1; i < A.numRows; i++ ) {
+            B_val = B.get(i);
+            for( int j = 0; j < A.numCols; j++ ) {
+                C.plus(  j , A.get(indexA++) * B_val );
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs a matrix vector multiply.<br>
+     * <br>
+     * C = C + A<sup>T</sup> * B <br>
+     * or<br>
+     * C = C<sup>T</sup> + A<sup>T</sup> * B<sup>T</sup> <br>
+     * <br>
+     * c<sub>i</sub> = Sum{ j=1:n, c<sub>i</sub> + a<sub>ji</sub> * b<sub>j</sub>}<br>
+     * <br>
+     * where A is a matrix, B is a column or transposed row vector, and C is a column vector.
+     * </p>
+     * <p>
+     * This implementation is optimal for small matrices.  There is a huge performance hit when
+     * used on large matrices due to CPU cache issues.
+     * </p>
+     *
+     * @param A A matrix that is m by n. Not modified.
+     * @param B A vector that has length m. Not modified.
+     * @param C A column vector that has length n. Modified.
+     */
+    public static void multAddTransA_small( RowD1Matrix64F A , D1Matrix64F B , D1Matrix64F C )
+    {
+        if( C.numCols != 1 ) {
+            throw new MatrixDimensionException("C is not a column vector");
+        } else if( C.numRows != A.numCols ) {
+            throw new MatrixDimensionException("C is not the expected length");
+        }
+        if( B.numRows == 1 ) {
+            if( A.numRows != B.numCols ) {
+                throw new MatrixDimensionException("A and B are not compatible");
+            }
+        } else if( B.numCols == 1 ) {
+            if( A.numRows != B.numRows ) {
+                throw new MatrixDimensionException("A and B are not compatible");
+            }
+        } else {
+            throw new MatrixDimensionException("B is not a vector");
+        }
+
+        int cIndex = 0;
+        for( int i = 0; i < A.numCols; i++ ) {
+            double total = 0.0;
+
+            int indexA = i;
+            for( int j = 0; j < A.numRows; j++ ) {
+                total += A.get(indexA) * B.get(j);
+                indexA += A.numCols;
+            }
+
+            C.plus( cIndex++ , total );
+        }
+    }
+
+    /**
+     * An alternative implementation of {@link #multAddTransA_small} that performs well on large
+     * matrices.  There is a relative performance hit when used on small matrices.
+     *
+     * @param A A matrix that is m by n. Not modified.
+     * @param B A vector that has length m. Not modified.
+     * @param C A column vector that has length n. Modified.
+     */
+    public static void multAddTransA_reorder( RowD1Matrix64F A , D1Matrix64F B , D1Matrix64F C )
+    {
+        if( C.numCols != 1 ) {
+            throw new MatrixDimensionException("C is not a column vector");
+        } else if( C.numRows != A.numCols ) {
+            throw new MatrixDimensionException("C is not the expected length");
+        }
+        if( B.numRows == 1 ) {
+            if( A.numRows != B.numCols ) {
+                throw new MatrixDimensionException("A and B are not compatible");
+            }
+        } else if( B.numCols == 1 ) {
+            if( A.numRows != B.numRows ) {
+                throw new MatrixDimensionException("A and B are not compatible");
+            }
+        } else {
+            throw new MatrixDimensionException("B is not a vector");
+        }
+
+        if( A.numRows == 0 ) {
+            return;
+        }
+
+        int indexA = 0;
+        for( int j = 0; j < A.numRows; j++ ) {
+            double B_val = B.get(j);
+            for( int i = 0; i < A.numCols; i++ ) {
+                C.plus( i , A.get(indexA++) * B_val );
+            }
+        }
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/mult/SubmatrixOps.java b/main/dense64/src/org/ejml/alg/dense/mult/SubmatrixOps.java
new file mode 100644
index 0000000..5de9289
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/mult/SubmatrixOps.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.mult;
+
+import org.ejml.data.RowD1Matrix64F;
+
+
+/**
+ * Operations that are performed on a submatrix inside a larger matrix.
+ *
+ * @author Peter Abeles
+ */
+public class SubmatrixOps {
+
+    public static void setSubMatrix( RowD1Matrix64F src , RowD1Matrix64F dst ,
+                                     int srcRow , int srcCol , int dstRow , int dstCol ,
+                                     int numSubRows, int numSubCols )
+    {
+        for( int i = 0; i < numSubRows; i++ ) {
+            for( int j = 0; j < numSubCols; j++ ) {
+                double val = src.get(i+srcRow,j+srcCol);
+                dst.set(i+dstRow,j+dstCol,val);
+            }
+        }
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/dense/mult/VectorVectorMult.java b/main/dense64/src/org/ejml/alg/dense/mult/VectorVectorMult.java
new file mode 100644
index 0000000..025a0a2
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/dense/mult/VectorVectorMult.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.mult;
+
+import org.ejml.data.D1Matrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.data.RowD1Matrix64F;
+
+
+/**
+ * Operations that involve multiplication of two vectors.
+ *
+ * @author Peter Abeles
+ */
+public class VectorVectorMult {
+
+    /**
+     * <p>
+     * Computes the inner product of the two vectors.  In geometry this is known as the dot product.<br>
+     * <br>
+     * ∑<sub>k=1:n</sub> x<sub>k</sub> * y<sub>k</sub><br>
+     * where x and y are vectors with n elements.
+     * </p>
+     *
+     * <p>
+     * These functions are often used inside of highly optimized code and therefor sanity checks are
+     * kept to a minimum.  It is not recommended that any of these functions be used directly.
+     * </p>
+     *
+     * @param x A vector with n elements. Not modified.
+     * @param y A vector with n elements. Not modified.
+     * @return The inner product of the two vectors.
+     */
+    public static double innerProd( D1Matrix64F x, D1Matrix64F y )
+    {
+        int m = x.getNumElements();
+
+        double total = 0;
+        for( int i = 0; i < m; i++ ) {
+            total += x.get(i) * y.get(i);
+        }
+
+        return total;
+    }
+
+    /**
+     * <p>
+     * return = x<sup>T</sup>*A*y
+     * </p>
+     *
+     * @param x  A vector with n elements. Not modified.
+     * @param A  A matrix with n by m elements.  Not modified.
+     * @param y  A vector with m elements. Not modified.
+     * @return  The results.
+     */
+    public static double innerProdA( D1Matrix64F x, D1Matrix64F A , D1Matrix64F y )
+    {
+        int n = A.numRows;
+        int m = A.numCols;
+
+        if( x.getNumElements() != n )
+            throw new IllegalArgumentException("Unexpected number of elements in x");
+        if( y.getNumElements() != m )
+            throw new IllegalArgumentException("Unexpected number of elements in y");
+
+        double result = 0;
+
+        for( int i = 0; i < m; i++ ) {
+            double total = 0;
+
+            for( int j = 0; j < n; j++ ) {
+                total += x.get(j)*A.unsafe_get(j,i);
+            }
+
+            result += total*y.get(i);
+        }
+
+        return result;
+    }
+
+
+    /**
+     * <p>
+     * x<sup>T</sup>A<sup>T</sup>y
+     * </p>
+     *
+     * @param x  A vector with n elements. Not modified.
+     * @param A  A matrix with n by n elements.  Not modified.
+     * @param y  A vector with n elements. Not modified.
+     * @return  The results.
+     */
+    // TODO better name for this
+    public static double innerProdTranA( D1Matrix64F x, D1Matrix64F A , D1Matrix64F y )
+    {
+        int n = A.numRows;
+
+        if( n != A.numCols)
+            throw new IllegalArgumentException("A must be square");
+
+        if( x.getNumElements() != n )
+            throw new IllegalArgumentException("Unexpected number of elements in x");
+        if( y.getNumElements() != n )
+            throw new IllegalArgumentException("Unexpected number of elements in y");
+
+        double result = 0;
+
+        for( int i = 0; i < n; i++ ) {
+            double total = 0;
+
+            for( int j = 0; j < n; j++ ) {
+                total += x.get(j)*A.unsafe_get(i,j);
+            }
+
+            result += total*y.get(i);
+        }
+
+        return result;
+    }
+
+    /**
+     * <p>
+     * Sets A ∈ ℜ <sup>m × n</sup> equal to an outer product multiplication of the two
+     * vectors.  This is also known as a rank-1 operation.<br>
+     * <br>
+     * A = x * y'
+     * where x ∈ ℜ <sup>m</sup> and y ∈ ℜ <sup>n</sup> are vectors.
+     * </p>
+     * <p>
+     * Which is equivalent to: A<sub>ij</sub> = x<sub>i</sub>*y<sub>j</sub>
+     * </p>
+     *
+     * <p>
+     * These functions are often used inside of highly optimized code and therefor sanity checks are
+     * kept to a minimum.  It is not recommended that any of these functions be used directly.
+     * </p>
+     *
+     * @param x A vector with m elements. Not modified.
+     * @param y A vector with n elements. Not modified.
+     * @param A A Matrix with m by n elements. Modified.
+     */
+    public static void outerProd( D1Matrix64F x, D1Matrix64F y, RowD1Matrix64F A ) {
+        int m = A.numRows;
+        int n = A.numCols;
+
+        int index = 0;
+        for( int i = 0; i < m; i++ ) {
+            double xdat = x.get(i);
+            for( int j = 0; j < n; j++ ) {
+                A.set(index++ ,  xdat*y.get(j) );
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Adds to A ∈ ℜ <sup>m × n</sup> the results of an outer product multiplication
+     * of the two vectors.  This is also known as a rank 1 update.<br>
+     * <br>
+     * A = A + γ x * y<sup>T</sup>
+     * where x ∈ ℜ <sup>m</sup> and y ∈ ℜ <sup>n</sup> are vectors.
+     * </p>
+     * <p>
+     * Which is equivalent to: A<sub>ij</sub> = A<sub>ij</sub> + γ x<sub>i</sub>*y<sub>j</sub>
+     * </p>
+     *
+     * <p>
+     * These functions are often used inside of highly optimized code and therefor sanity checks are
+     * kept to a minimum.  It is not recommended that any of these functions be used directly.
+     * </p>
+     *
+     * @param gamma A multiplication factor for the outer product.
+     * @param x A vector with m elements. Not modified.
+     * @param y A vector with n elements. Not modified.
+     * @param A A Matrix with m by n elements. Modified.
+     */
+    public static void addOuterProd( double gamma , D1Matrix64F x, D1Matrix64F y, RowD1Matrix64F A ) {
+        int m = A.numRows;
+        int n = A.numCols;
+
+        int index = 0;
+        if( gamma == 1.0 ) {
+            for( int i = 0; i < m; i++ ) {
+                double xdat = x.get(i);
+                for( int j = 0; j < n; j++ ) {
+                    A.plus( index++ , xdat*y.get(j) );
+                }
+            }
+        } else {
+            for( int i = 0; i < m; i++ ) {
+                double xdat = x.get(i);
+                for( int j = 0; j < n; j++ ) {
+                    A.plus( index++ , gamma*xdat*y.get(j));
+                }
+            }
+        }
+    }
+
+
+    /**
+     * <p>
+     * Multiplies a householder reflection against a vector:<br>
+     * <br>
+     * y = (I + γ u u<sup>T</sup>)x<br>
+     * </p>
+     * <p>
+     * The Householder reflection is used in some implementations of QR decomposition.
+     * </p>
+     * @param u A vector. Not modified.
+     * @param x a vector. Not modified.
+     * @param y Vector where the result are written to.
+     */
+    public static void householder( double gamma,
+                                    D1Matrix64F u ,
+                                    D1Matrix64F x , D1Matrix64F y )
+    {
+        int n = u.getNumElements();
+
+        double sum = 0;
+        for( int i = 0; i < n; i++ ) {
+            sum += u.get(i)*x.get(i);
+        }
+        for( int i = 0; i < n; i++ ) {
+            y.set( i , x.get(i) + gamma*u.get(i)*sum);
+        }
+    }
+
+    /**
+     * <p>
+     * Performs a rank one update on matrix A using vectors u and w.  The results are stored in B.<br>
+     * <br>
+     * B = A + γ u w<sup>T</sup><br>
+     * </p>
+     * <p>
+     * This is called a rank1 update because the matrix u w<sup>T</sup> has a rank of 1.  Both A and B
+     * can be the same matrix instance, but there is a special rank1Update for that.
+     * </p>
+     *
+     * @param gamma A scalar.
+     * @param A A m by m matrix. Not modified.
+     * @param u A vector with m elements.  Not modified.
+     * @param w A vector with m elements.  Not modified.
+     * @param B A m by m matrix where the results are stored. Modified.
+     */
+    public static void rank1Update( double gamma,
+                                    DenseMatrix64F A ,
+                                    DenseMatrix64F u , DenseMatrix64F w ,
+                                    DenseMatrix64F B )
+    {
+        int n = u.getNumElements();
+
+        int matrixIndex = 0;
+        for( int i = 0; i < n; i++ ) {
+            double elementU = u.data[i];
+
+            for( int j = 0; j < n; j++ , matrixIndex++) {
+                B.data[matrixIndex] = A.data[matrixIndex] + gamma*elementU*w.data[j];
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs a rank one update on matrix A using vectors u and w.  The results are stored in A.<br>
+     * <br>
+     * A = A + γ u w<sup>T</sup><br>
+     * </p>
+     * <p>
+     * This is called a rank1 update because the matrix u w<sup>T</sup> has a rank of 1.
+     * </p>
+     *
+     * @param gamma A scalar.
+     * @param A A m by m matrix. Modified.
+     * @param u A vector with m elements.  Not modified.
+     */
+    public static void rank1Update( double gamma,
+                                    DenseMatrix64F A ,
+                                    DenseMatrix64F u ,
+                                    DenseMatrix64F w )
+    {
+        int n = u.getNumElements();
+
+        int matrixIndex = 0;
+        for( int i = 0; i < n; i++ ) {
+            double elementU = u.data[i];
+
+            for( int j = 0; j < n; j++ ) {
+                A.data[matrixIndex++] += gamma*elementU*w.data[j];
+            }
+        }
+    }
+}
diff --git a/main/dense64/src/org/ejml/alg/fixed/FixedOps2.java b/main/dense64/src/org/ejml/alg/fixed/FixedOps2.java
new file mode 100644
index 0000000..768ba29
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/fixed/FixedOps2.java
@@ -0,0 +1,989 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+package org.ejml.alg.fixed;
+
+import org.ejml.data.FixedMatrix2_64F;
+import org.ejml.data.FixedMatrix2x2_64F;
+
+/**
+ * <p>Common matrix operations for fixed sized matrices which are 2 x 2 or 2 element vectors.</p>
+ * <p>DO NOT MODIFY.  Automatically generated code created by GenerateFixedOps</p>
+ *
+ * @author Peter Abeles
+ */
+public class FixedOps2 {
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a + b <br>
+     * c<sub>ij</sub> = a<sub>ij</sub> + b<sub>ij</sub> <br>
+     * </p>
+     *
+     * <p>
+     * Matrix C can be the same instance as Matrix A and/or B.
+     * </p>
+     *
+     * @param a A Matrix. Not modified.
+     * @param b A Matrix. Not modified.
+     * @param c A Matrix where the results are stored. Modified.
+     */
+    public static void add( FixedMatrix2x2_64F a , FixedMatrix2x2_64F b , FixedMatrix2x2_64F c ) {
+        c.a11 = a.a11 + b.a11;
+        c.a12 = a.a12 + b.a12;
+        c.a21 = a.a21 + b.a21;
+        c.a22 = a.a22 + b.a22;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a + b <br>
+     * c<sub>i</sub> = a<sub>i</sub> + b<sub>i</sub> <br>
+     * </p>
+     *
+     * <p>
+     * Vector C can be the same instance as Vector A and/or B.
+     * </p>
+     *
+     * @param a A Vector. Not modified.
+     * @param b A Vector. Not modified.
+     * @param c A Vector where the results are stored. Modified.
+     */
+    public static void add( FixedMatrix2_64F a , FixedMatrix2_64F b , FixedMatrix2_64F c ) {
+        c.a1 = a.a1 + b.a1;
+        c.a2 = a.a2 + b.a2;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * a = a + b <br>
+     * a<sub>ij</sub> = a<sub>ij</sub> + b<sub>ij</sub> <br>
+     * </p>
+     *
+     * @param a A Matrix. Modified.
+     * @param b A Matrix. Not modified.
+     */
+    public static void addEquals( FixedMatrix2x2_64F a , FixedMatrix2x2_64F b ) {
+        a.a11 += b.a11;
+        a.a12 += b.a12;
+        a.a21 += b.a21;
+        a.a22 += b.a22;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * a = a + b <br>
+     * a<sub>i</sub> = a<sub>i</sub> + b<sub>i</sub> <br>
+     * </p>
+     *
+     * @param a A Vector. Modified.
+     * @param b A Vector. Not modified.
+     */
+    public static void addEquals( FixedMatrix2_64F a , FixedMatrix2_64F b ) {
+        a.a1 += b.a1;
+        a.a2 += b.a2;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a - b <br>
+     * c<sub>ij</sub> = a<sub>ij</sub> - b<sub>ij</sub> <br>
+     * </p>
+     *
+     * <p>
+     * Matrix C can be the same instance as Matrix A and/or B.
+     * </p>
+     *
+     * @param a A Matrix. Not modified.
+     * @param b A Matrix. Not modified.
+     * @param c A Matrix where the results are stored. Modified.
+     */
+    public static void subtract( FixedMatrix2x2_64F a , FixedMatrix2x2_64F b , FixedMatrix2x2_64F c ) {
+        c.a11 = a.a11 - b.a11;
+        c.a12 = a.a12 - b.a12;
+        c.a21 = a.a21 - b.a21;
+        c.a22 = a.a22 - b.a22;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a - b <br>
+     * c<sub>i</sub> = a<sub>i</sub> - b<sub>i</sub> <br>
+     * </p>
+     *
+     * <p>
+     * Vector C can be the same instance as Vector A and/or B.
+     * </p>
+     *
+     * @param a A Vector. Not modified.
+     * @param b A Vector. Not modified.
+     * @param c A Vector where the results are stored. Modified.
+     */
+    public static void subtract( FixedMatrix2_64F a , FixedMatrix2_64F b , FixedMatrix2_64F c ) {
+        c.a1 = a.a1 - b.a1;
+        c.a2 = a.a2 - b.a2;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * a = a - b <br>
+     * a<sub>ij</sub> = a<sub>ij</sub> - b<sub>ij</sub> <br>
+     * </p>
+     *
+     * @param a A Matrix. Modified.
+     * @param b A Matrix. Not modified.
+     */
+    public static void subtractEquals( FixedMatrix2x2_64F a , FixedMatrix2x2_64F b ) {
+        a.a11 -= b.a11;
+        a.a12 -= b.a12;
+        a.a21 -= b.a21;
+        a.a22 -= b.a22;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * a = a - b <br>
+     * a<sub>i</sub> = a<sub>i</sub> - b<sub>i</sub> <br>
+     * </p>
+     *
+     * @param a A Vector. Modified.
+     * @param b A Vector. Not modified.
+     */
+    public static void subtractEquals( FixedMatrix2_64F a , FixedMatrix2_64F b ) {
+        a.a1 -= b.a1;
+        a.a2 -= b.a2;
+    }
+
+    /**
+     * Performs an in-place transpose.  This algorithm is only efficient for square
+     * matrices.
+     *
+     * @param m The matrix that is to be transposed. Modified.
+     */
+    public static void transpose( FixedMatrix2x2_64F m ) {
+        double tmp;
+        tmp = m.a12; m.a12 = m.a21; m.a21 = tmp;
+    }
+
+    /**
+     * <p>
+     * Transposes matrix 'a' and stores the results in 'b':<br>
+     * <br>
+     * b<sub>ij</sub> = a<sub>ji</sub><br>
+     * where 'b' is the transpose of 'a'.
+     * </p>
+     *
+     * @param input The original matrix.  Not modified.
+     * @param output Where the transpose is stored. If null a new matrix is created. Modified.
+     * @return The transposed matrix.
+     */
+    public static FixedMatrix2x2_64F transpose( FixedMatrix2x2_64F input , FixedMatrix2x2_64F output ) {
+        if( input == null )
+            input = new FixedMatrix2x2_64F();
+
+        output.a11 = input.a11;
+        output.a12 = input.a21;
+        output.a21 = input.a12;
+        output.a22 = input.a22;
+
+        return output;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a * b <br>
+     * <br>
+     * c<sub>ij</sub> = ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void mult( FixedMatrix2x2_64F a , FixedMatrix2x2_64F b , FixedMatrix2x2_64F c) {
+        c.a11 = a.a11*b.a11 + a.a12*b.a21;
+        c.a12 = a.a11*b.a12 + a.a12*b.a22;
+        c.a21 = a.a21*b.a11 + a.a22*b.a21;
+        c.a22 = a.a21*b.a12 + a.a22*b.a22;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a<sup>T</sup> * b <br>
+     * <br>
+     * c<sub>ij</sub> = ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multTransA( FixedMatrix2x2_64F a , FixedMatrix2x2_64F b , FixedMatrix2x2_64F c) {
+        c.a11 = a.a11*b.a11 + a.a21*b.a21;
+        c.a12 = a.a11*b.a12 + a.a21*b.a22;
+        c.a21 = a.a12*b.a11 + a.a22*b.a21;
+        c.a22 = a.a12*b.a12 + a.a22*b.a22;
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c = a<sup>T</sup> * b<sup>T</sup><br>
+     * c<sub>ij</sub> = ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>jk</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multTransAB( FixedMatrix2x2_64F a , FixedMatrix2x2_64F b , FixedMatrix2x2_64F c) {
+        c.a11 = a.a11*b.a11 + a.a21*b.a12;
+        c.a12 = a.a11*b.a21 + a.a21*b.a22;
+        c.a21 = a.a12*b.a11 + a.a22*b.a12;
+        c.a22 = a.a12*b.a21 + a.a22*b.a22;
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c = a * b<sup>T</sup> <br>
+     * c<sub>ij</sub> = ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>jk</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multTransB( FixedMatrix2x2_64F a , FixedMatrix2x2_64F b , FixedMatrix2x2_64F c) {
+        c.a11 = a.a11*b.a11 + a.a12*b.a12;
+        c.a12 = a.a11*b.a21 + a.a12*b.a22;
+        c.a21 = a.a21*b.a11 + a.a22*b.a12;
+        c.a22 = a.a21*b.a21 + a.a22*b.a22;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c += a * b <br>
+     * <br>
+     * c<sub>ij</sub> += ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAdd( FixedMatrix2x2_64F a , FixedMatrix2x2_64F b , FixedMatrix2x2_64F c) {
+        c.a11 += a.a11*b.a11 + a.a12*b.a21;
+        c.a12 += a.a11*b.a12 + a.a12*b.a22;
+        c.a21 += a.a21*b.a11 + a.a22*b.a21;
+        c.a22 += a.a21*b.a12 + a.a22*b.a22;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c += a<sup>T</sup> * b <br>
+     * <br>
+     * c<sub>ij</sub> += ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAddTransA( FixedMatrix2x2_64F a , FixedMatrix2x2_64F b , FixedMatrix2x2_64F c) {
+        c.a11 += a.a11*b.a11 + a.a21*b.a21;
+        c.a12 += a.a11*b.a12 + a.a21*b.a22;
+        c.a21 += a.a12*b.a11 + a.a22*b.a21;
+        c.a22 += a.a12*b.a12 + a.a22*b.a22;
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c += a<sup>T</sup> * b<sup>T</sup><br>
+     * c<sub>ij</sub> += ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>jk</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAddTransAB( FixedMatrix2x2_64F a , FixedMatrix2x2_64F b , FixedMatrix2x2_64F c) {
+        c.a11 += a.a11*b.a11 + a.a21*b.a12;
+        c.a12 += a.a11*b.a21 + a.a21*b.a22;
+        c.a21 += a.a12*b.a11 + a.a22*b.a12;
+        c.a22 += a.a12*b.a21 + a.a22*b.a22;
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c += a * b<sup>T</sup> <br>
+     * c<sub>ij</sub> += ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>jk</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAddTransB( FixedMatrix2x2_64F a , FixedMatrix2x2_64F b , FixedMatrix2x2_64F c) {
+        c.a11 += a.a11*b.a11 + a.a12*b.a12;
+        c.a12 += a.a11*b.a21 + a.a12*b.a22;
+        c.a21 += a.a21*b.a11 + a.a22*b.a12;
+        c.a22 += a.a21*b.a21 + a.a22*b.a22;
+    }
+
+    /**
+     * <p>Performs matrix to vector multiplication:<br>
+     * <br>
+     * c = a * b <br>
+     * <br>
+     * c<sub>i</sub> = ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>k</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right vector in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void mult( FixedMatrix2x2_64F a , FixedMatrix2_64F b , FixedMatrix2_64F c) {
+        c.a1 = a.a11*b.a1 + a.a12*b.a2;
+        c.a2 = a.a21*b.a1 + a.a22*b.a2;
+    }
+
+    /**
+     * <p>Performs vector to matrix multiplication:<br>
+     * <br>
+     * c = a * b <br>
+     * <br>
+     * c<sub>j</sub> = ∑<sub>k=1:n</sub> { b<sub>k</sub> * a<sub>kj</sub> }
+     * </p>
+     *
+     * @param a The left vector in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void mult( FixedMatrix2_64F a , FixedMatrix2x2_64F b , FixedMatrix2_64F c) {
+        c.a1 = a.a1*b.a11 + a.a2*b.a21;
+        c.a2 = a.a1*b.a12 + a.a2*b.a22;
+    }
+
+    /**
+     * <p>Performs the vector dot product:<br>
+     * <br>
+     * c = a * b <br>
+     * <br>
+     * c> = ∑<sub>k=1:n</sub> { b<sub>k</sub> * a<sub>k</sub> }
+     * </p>
+     *
+     * @param a The left vector in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @return The dot product
+     */
+    public static double dot( FixedMatrix2_64F a , FixedMatrix2_64F b ) {
+        return a.a1*b.a1 + a.a2*b.a2;
+    }
+
+    /**
+     * Sets all the diagonal elements equal to one and everything else equal to zero.
+     * If this is a square matrix then it will be an identity matrix.
+     *
+     * @param a A matrix.
+     */
+    public static void setIdentity( FixedMatrix2x2_64F a ) {
+        a.a11 = 1; a.a21 = 0;
+        a.a12 = 0; a.a22 = 1;
+    }
+
+    /**
+     * Inverts matrix 'a' using minor matrices and stores the results in 'inv'.  Scaling is applied to improve
+     * stability against overflow and underflow.
+     *
+     * WARNING: Potentially less stable than using LU decomposition.
+     *
+     * @param a Input matrix. Not modified.
+     * @param inv Inverted output matrix.  Modified.
+     * @return true if it was successful or false if it failed.  Not always reliable.
+     */
+    public static boolean invert( FixedMatrix2x2_64F a , FixedMatrix2x2_64F inv ) {
+
+        double scale = 1.0/elementMaxAbs(a);
+
+        double a11 = a.a11*scale;
+        double a12 = a.a12*scale;
+        double a21 = a.a21*scale;
+        double a22 = a.a22*scale;
+
+        double m11 = a22;
+        double m12 = -( a21);
+        double m21 = -( a12);
+        double m22 = a11;
+
+        double det = (a11*m11 + a12*m12)/scale;
+
+        inv.a11 = m11/det;
+        inv.a12 = m21/det;
+        inv.a21 = m12/det;
+        inv.a22 = m22/det;
+
+        return !Double.isNaN(det) && !Double.isInfinite(det);
+    }
+
+    /**
+     * Computes the determinant using minor matrices.
+     * <p></p>
+     * WARNING: Potentially less stable than using LU decomposition.
+     *
+     * @param mat Input matrix.  Not modified.
+     * @return The determinant.
+     */
+    public static double det( FixedMatrix2x2_64F mat ) {
+
+        return mat.a11*mat.a22 - mat.a12*mat.a21;
+    }
+
+    /**
+     * <p>
+     * This computes the trace of the matrix:<br>
+     * <br>
+     * trace = ∑<sub>i=1:n</sub> { a<sub>ii</sub> }
+     * </p>
+     * <p>
+     * The trace is only defined for square matrices.
+     * </p>
+     *
+     * @param a A square matrix.  Not modified.
+     */
+    public static double trace( FixedMatrix2x2_64F a ) {
+        return a.a11 + a.a21;
+    }
+
+    /**
+     * <p>
+     * Extracts all diagonal elements from 'input' and places them inside the 'out' vector. Elements
+     * are in sequential order.
+     * </p>
+     *
+     *
+     * @param input Matrix.  Not modified.
+     * @param out Vector containing diagonal elements.  Modified.
+     */
+    public static void diag( FixedMatrix2x2_64F input , FixedMatrix2_64F out ) {
+        out.a1 = input.a11;
+        out.a2 = input.a22;
+    }
+
+    /**
+     * <p>
+     * Returns the value of the element in the matrix that has the largest value.<br>
+     * <br>
+     * Max{ a<sub>ij</sub> } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max element value of the matrix.
+     */
+    public static double elementMax( FixedMatrix2x2_64F a ) {
+        double max = a.a11;
+        max = Math.max(max,a.a12);
+        max = Math.max(max,a.a21);
+        max = Math.max(max,a.a22);
+
+        return max;
+    }
+
+    /**
+     * <p>
+     * Returns the value of the element in the vector that has the largest value.<br>
+     * <br>
+     * Max{ a<sub>i</sub> } for all i<br>
+     * </p>
+     *
+     * @param a A vector. Not modified.
+     * @return The max element value of the matrix.
+     */
+    public static double elementMax( FixedMatrix2_64F a ) {
+        double max = a.a1;
+        max = Math.max(max,a.a2);
+
+        return max;
+    }
+
+    /**
+     * <p>
+     * Returns the absolute value of the element in the matrix that has the largest absolute value.<br>
+     * <br>
+     * Max{ |a<sub>ij</sub>| } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max abs element value of the matrix.
+     */
+    public static double elementMaxAbs( FixedMatrix2x2_64F a ) {
+        double max = a.a11;
+        max = Math.max(max,Math.abs(a.a12));
+        max = Math.max(max,Math.abs(a.a21));
+        max = Math.max(max,Math.abs(a.a22));
+
+        return max;
+    }
+
+    /**
+     * <p>
+     * Returns the absolute value of the element in the vector that has the largest absolute value.<br>
+     * <br>
+     * Max{ |a<sub>i</sub>| } for all i<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max abs element value of the vector.
+     */
+    public static double elementMaxAbs( FixedMatrix2_64F a ) {
+        double max = a.a1;
+        max = Math.max(max,Math.abs(a.a2));
+
+        return max;
+    }
+
+    /**
+     * <p>
+     * Returns the value of the element in the matrix that has the minimum value.<br>
+     * <br>
+     * Min{ a<sub>ij</sub> } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The value of element in the matrix with the minimum value.
+     */
+    public static double elementMin( FixedMatrix2x2_64F a ) {
+        double min = a.a11;
+        min = Math.min(min, a.a12);
+        min = Math.min(min, a.a21);
+        min = Math.min(min, a.a22);
+
+        return min;
+    }
+
+    /**
+     * <p>
+     * Returns the value of the element in the vector that has the minimum value.<br>
+     * <br>
+     * Min{ a<sub>i</sub> } for all<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The value of element in the vector with the minimum value.
+     */
+    public static double elementMin( FixedMatrix2_64F a ) {
+        double min = a.a1;
+        min = Math.min(min, a.a2);
+
+        return min;
+    }
+
+    /**
+     * <p>
+     * Returns the absolute value of the element in the matrix that has the smallest absolute value.<br>
+     * <br>
+     * Min{ |a<sub>ij</sub>| } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max element value of the matrix.
+     */
+    public static double elementMinAbs( FixedMatrix2x2_64F a ) {
+        double min = a.a11;
+        min = Math.min(min,Math.abs(a.a12));
+        min = Math.min(min,Math.abs(a.a21));
+        min = Math.min(min,Math.abs(a.a22));
+
+        return min;
+    }
+
+    /**
+     * <p>
+     * Returns the absolute value of the element in the vector that has the smallest absolute value.<br>
+     * <br>
+     * Min{ |a<sub>i</sub>| } for all i<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max element value of the vector.
+     */
+    public static double elementMinAbs( FixedMatrix2_64F a ) {
+        double min = a.a1;
+        min = Math.min(min,Math.abs(a.a2));
+
+        return min;
+    }
+
+    /**
+     * <p>Performs an element by element multiplication operation:<br>
+     * <br>
+     * a<sub>ij</sub> = a<sub>ij</sub> * b<sub>ij</sub> <br>
+     * </p>
+     * @param a The left matrix in the multiplication operation. Modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     */
+    public static void elementMult( FixedMatrix2x2_64F a , FixedMatrix2x2_64F b) {
+        a.a11 *= b.a11; a.a12 *= b.a12;
+        a.a21 *= b.a21; a.a22 *= b.a22;
+    }
+
+    /**
+     * <p>Performs an element by element multiplication operation:<br>
+     * <br>
+     * a<sub>i</sub> = a<sub>i</sub> * b<sub>i</sub> <br>
+     * </p>
+     * @param a The left vector in the multiplication operation. Modified.
+     * @param b The right vector in the multiplication operation. Not modified.
+     */
+    public static void elementMult( FixedMatrix2_64F a , FixedMatrix2_64F b) {
+        a.a1 *= b.a1;
+        a.a2 *= b.a2;
+    }
+
+    /**
+     * <p>Performs an element by element multiplication operation:<br>
+     * <br>
+     * c<sub>ij</sub> = a<sub>ij</sub> * b<sub>ij</sub> <br>
+     * </p>
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void elementMult( FixedMatrix2x2_64F a , FixedMatrix2x2_64F b , FixedMatrix2x2_64F c ) {
+        c.a11 = a.a11*b.a11; c.a12 = a.a12*b.a12;
+        c.a21 = a.a21*b.a21; c.a22 = a.a22*b.a22;
+    }
+
+    /**
+     * <p>Performs an element by element multiplication operation:<br>
+     * <br>
+     * c<sub>i</sub> = a<sub>i</sub> * b<sub>j</sub> <br>
+     * </p>
+     * @param a The left vector in the multiplication operation. Not modified.
+     * @param b The right vector in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void elementMult( FixedMatrix2_64F a , FixedMatrix2_64F b , FixedMatrix2_64F c ) {
+        c.a1 = a.a1*b.a1;
+        c.a2 = a.a2*b.a2;
+    }
+
+    /**
+     * <p>Performs an element by element division operation:<br>
+     * <br>
+     * a<sub>ij</sub> = a<sub>ij</sub> / b<sub>ij</sub> <br>
+     * </p>
+     * @param a The left matrix in the division operation. Modified.
+     * @param b The right matrix in the division operation. Not modified.
+     */
+    public static void elementDiv( FixedMatrix2x2_64F a , FixedMatrix2x2_64F b) {
+        a.a11 /= b.a11; a.a12 /= b.a12;
+        a.a21 /= b.a21; a.a22 /= b.a22;
+    }
+
+    /**
+     * <p>Performs an element by element division operation:<br>
+     * <br>
+     * a<sub>i</sub> = a<sub>i</sub> / b<sub>i</sub> <br>
+     * </p>
+     * @param a The left vector in the division operation. Modified.
+     * @param b The right vector in the division operation. Not modified.
+     */
+    public static void elementDiv( FixedMatrix2_64F a , FixedMatrix2_64F b) {
+        a.a1 /= b.a1;
+        a.a2 /= b.a2;
+    }
+
+    /**
+     * <p>Performs an element by element division operation:<br>
+     * <br>
+     * c<sub>ij</sub> = a<sub>ij</sub> / b<sub>ij</sub> <br>
+     * </p>
+     * @param a The left matrix in the division operation. Not modified.
+     * @param b The right matrix in the division operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void elementDiv( FixedMatrix2x2_64F a , FixedMatrix2x2_64F b , FixedMatrix2x2_64F c ) {
+        c.a11 = a.a11/b.a11; c.a12 = a.a12/b.a12;
+        c.a21 = a.a21/b.a21; c.a22 = a.a22/b.a22;
+    }
+
+    /**
+     * <p>Performs an element by element division operation:<br>
+     * <br>
+     * c<sub>i</sub> = a<sub>i</sub> / b<sub>i</sub> <br>
+     * </p>
+     * @param a The left vector in the division operation. Not modified.
+     * @param b The right vector in the division operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void elementDiv( FixedMatrix2_64F a , FixedMatrix2_64F b , FixedMatrix2_64F c ) {
+        c.a1 = a.a1/b.a1;
+        c.a2 = a.a2/b.a2;
+    }
+
+    /**
+     * <p>
+     * Performs an in-place element by element scalar multiplication.<br>
+     * <br>
+     * a<sub>ij</sub> = α*a<sub>ij</sub>
+     * </p>
+     *
+     * @param a The matrix that is to be scaled.  Modified.
+     * @param alpha the amount each element is multiplied by.
+     */
+    public static void scale( double alpha , FixedMatrix2x2_64F a ) {
+        a.a11 *= alpha; a.a12 *= alpha;
+        a.a21 *= alpha; a.a22 *= alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an in-place element by element scalar multiplication.<br>
+     * <br>
+     * a<sub>ij</sub> = α*a<sub>ij</sub>
+     * </p>
+     *
+     * @param a The vector that is to be scaled.  Modified.
+     * @param alpha the amount each element is multiplied by.
+     */
+    public static void scale( double alpha , FixedMatrix2_64F a ) {
+        a.a1 *= alpha;
+        a.a2 *= alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an element by element scalar multiplication.<br>
+     * <br>
+     * b<sub>ij</sub> = α*a<sub>ij</sub>
+     * </p>
+     *
+     * @param alpha the amount each element is multiplied by.
+     * @param a The matrix that is to be scaled.  Not modified.
+     * @param b Where the scaled matrix is stored. Modified.
+     */
+    public static void scale( double alpha , FixedMatrix2x2_64F a , FixedMatrix2x2_64F b ) {
+        b.a11 = a.a11*alpha; b.a12 = a.a12*alpha;
+        b.a21 = a.a21*alpha; b.a22 = a.a22*alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an element by element scalar multiplication.<br>
+     * <br>
+     * b<sub>i</sub> = α*a<sub>i</sub>
+     * </p>
+     *
+     * @param alpha the amount each element is multiplied by.
+     * @param a The vector that is to be scaled.  Not modified.
+     * @param b Where the scaled matrix is stored. Modified.
+     */
+    public static void scale( double alpha , FixedMatrix2_64F a , FixedMatrix2_64F b ) {
+        b.a1 = a.a1*alpha;
+        b.a2 = a.a2*alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an in-place element by element scalar division. Scalar denominator.<br>
+     * <br>
+     * a<sub>ij</sub> = a<sub>ij</sub>/α
+     * </p>
+     *
+     * @param a The matrix whose elements are to be divided.  Modified.
+     * @param alpha the amount each element is divided by.
+     */
+    public static void divide( FixedMatrix2x2_64F a , double alpha ) {
+        a.a11 /= alpha; a.a12 /= alpha;
+        a.a21 /= alpha; a.a22 /= alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an in-place element by element scalar division. Scalar denominator.<br>
+     * <br>
+     * a<sub>i</sub> = a<sub>i</sub>/α
+     * </p>
+     *
+     * @param a The vector whose elements are to be divided.  Modified.
+     * @param alpha the amount each element is divided by.
+     */
+    public static void divide( FixedMatrix2_64F a , double alpha ) {
+        a.a1 /= alpha;
+        a.a2 /= alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an element by element scalar division.  Scalar denominator.<br>
+     * <br>
+     * b<sub>ij</sub> = a<sub>ij</sub> /α
+     * </p>
+     *
+     * @param alpha the amount each element is divided by.
+     * @param a The matrix whose elements are to be divided.  Not modified.
+     * @param b Where the results are stored. Modified.
+     */
+    public static void divide( FixedMatrix2x2_64F a , double alpha , FixedMatrix2x2_64F b ) {
+        b.a11 = a.a11/alpha; b.a12 = a.a12/alpha;
+        b.a21 = a.a21/alpha; b.a22 = a.a22/alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an element by element scalar division.  Scalar denominator.<br>
+     * <br>
+     * b<sub>i</sub> = a<sub>i</sub> /α
+     * </p>
+     *
+     * @param alpha the amount each element is divided by.
+     * @param a The vector whose elements are to be divided.  Not modified.
+     * @param b Where the results are stored. Modified.
+     */
+    public static void divide( FixedMatrix2_64F a , double alpha , FixedMatrix2_64F b ) {
+        b.a1 = a.a1/alpha;
+        b.a2 = a.a2/alpha;
+    }
+
+    /**
+     * <p>
+     * Changes the sign of every element in the matrix.<br>
+     * <br>
+     * a<sub>ij</sub> = -a<sub>ij</sub>
+     * </p>
+     *
+     * @param a A matrix. Modified.
+     */
+    public static void changeSign( FixedMatrix2x2_64F a )
+    {
+        a.a11 = -a.a11; a.a12 = -a.a12;
+        a.a21 = -a.a21; a.a22 = -a.a22;
+    }
+
+    /**
+     * <p>
+     * Changes the sign of every element in the vector.<br>
+     * <br>
+     * a<sub>i</sub> = -a<sub>i</sub>
+     * </p>
+     *
+     * @param a A vector. Modified.
+     */
+    public static void changeSign( FixedMatrix2_64F a )
+    {
+        a.a1 = -a.a1;
+        a.a2 = -a.a2;
+    }
+
+    /**
+     * <p>
+     * Sets every element in the matrix to the specified value.<br>
+     * <br>
+     * a<sub>ij</sub> = value
+     * <p>
+     *
+     * @param a A matrix whose elements are about to be set. Modified.
+     * @param v The value each element will have.
+     */
+    public static void fill( FixedMatrix2x2_64F a , double v  ) {
+        a.a11 = v; a.a12 = v;
+        a.a21 = v; a.a22 = v;
+    }
+
+    /**
+     * <p>
+     * Sets every element in the vector to the specified value.<br>
+     * <br>
+     * a<sub>i</sub> = value
+     * <p>
+     *
+     * @param a A vector whose elements are about to be set. Modified.
+     * @param v The value each element will have.
+     */
+    public static void fill( FixedMatrix2_64F a , double v  ) {
+        a.a1 = v;
+        a.a2 = v;
+    }
+
+    /**
+     * Extracts the row from the matrix a.
+     * @param a Input matrix
+     * @param row Which row is to be extracted
+     * @param out output. Storage for the extracted row. If null then a new vector will be returned.
+     * @return The extracted row.
+     */
+    public static FixedMatrix2_64F extractRow( FixedMatrix2x2_64F a , int row , FixedMatrix2_64F out ) {
+        if( out == null) out = new FixedMatrix2_64F();
+        switch( row ) {
+            case 0:
+                out.a1 = a.a11;
+                out.a2 = a.a12;
+            break;
+            case 1:
+                out.a1 = a.a21;
+                out.a2 = a.a22;
+            break;
+            default:
+                throw new IllegalArgumentException("Out of bounds row.  row = "+row);
+        }
+        return out;
+    }
+
+    /**
+     * Extracts the column from the matrix a.
+     * @param a Input matrix
+     * @param column Which column is to be extracted
+     * @param out output. Storage for the extracted column. If null then a new vector will be returned.
+     * @return The extracted column.
+     */
+    public static FixedMatrix2_64F extractColumn( FixedMatrix2x2_64F a , int column , FixedMatrix2_64F out ) {
+        if( out == null) out = new FixedMatrix2_64F();
+        switch( column ) {
+            case 0:
+                out.a1 = a.a11;
+                out.a2 = a.a21;
+            break;
+            case 1:
+                out.a1 = a.a12;
+                out.a2 = a.a22;
+            break;
+            default:
+                throw new IllegalArgumentException("Out of bounds column.  column = "+column);
+        }
+        return out;
+    }
+
+}
+
diff --git a/main/dense64/src/org/ejml/alg/fixed/FixedOps3.java b/main/dense64/src/org/ejml/alg/fixed/FixedOps3.java
new file mode 100644
index 0000000..cb3406c
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/fixed/FixedOps3.java
@@ -0,0 +1,1141 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+package org.ejml.alg.fixed;
+
+import org.ejml.data.FixedMatrix3_64F;
+import org.ejml.data.FixedMatrix3x3_64F;
+
+/**
+ * <p>Common matrix operations for fixed sized matrices which are 3 x 3 or 3 element vectors.</p>
+ * <p>DO NOT MODIFY.  Automatically generated code created by GenerateFixedOps</p>
+ *
+ * @author Peter Abeles
+ */
+public class FixedOps3 {
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a + b <br>
+     * c<sub>ij</sub> = a<sub>ij</sub> + b<sub>ij</sub> <br>
+     * </p>
+     *
+     * <p>
+     * Matrix C can be the same instance as Matrix A and/or B.
+     * </p>
+     *
+     * @param a A Matrix. Not modified.
+     * @param b A Matrix. Not modified.
+     * @param c A Matrix where the results are stored. Modified.
+     */
+    public static void add( FixedMatrix3x3_64F a , FixedMatrix3x3_64F b , FixedMatrix3x3_64F c ) {
+        c.a11 = a.a11 + b.a11;
+        c.a12 = a.a12 + b.a12;
+        c.a13 = a.a13 + b.a13;
+        c.a21 = a.a21 + b.a21;
+        c.a22 = a.a22 + b.a22;
+        c.a23 = a.a23 + b.a23;
+        c.a31 = a.a31 + b.a31;
+        c.a32 = a.a32 + b.a32;
+        c.a33 = a.a33 + b.a33;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a + b <br>
+     * c<sub>i</sub> = a<sub>i</sub> + b<sub>i</sub> <br>
+     * </p>
+     *
+     * <p>
+     * Vector C can be the same instance as Vector A and/or B.
+     * </p>
+     *
+     * @param a A Vector. Not modified.
+     * @param b A Vector. Not modified.
+     * @param c A Vector where the results are stored. Modified.
+     */
+    public static void add( FixedMatrix3_64F a , FixedMatrix3_64F b , FixedMatrix3_64F c ) {
+        c.a1 = a.a1 + b.a1;
+        c.a2 = a.a2 + b.a2;
+        c.a3 = a.a3 + b.a3;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * a = a + b <br>
+     * a<sub>ij</sub> = a<sub>ij</sub> + b<sub>ij</sub> <br>
+     * </p>
+     *
+     * @param a A Matrix. Modified.
+     * @param b A Matrix. Not modified.
+     */
+    public static void addEquals( FixedMatrix3x3_64F a , FixedMatrix3x3_64F b ) {
+        a.a11 += b.a11;
+        a.a12 += b.a12;
+        a.a13 += b.a13;
+        a.a21 += b.a21;
+        a.a22 += b.a22;
+        a.a23 += b.a23;
+        a.a31 += b.a31;
+        a.a32 += b.a32;
+        a.a33 += b.a33;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * a = a + b <br>
+     * a<sub>i</sub> = a<sub>i</sub> + b<sub>i</sub> <br>
+     * </p>
+     *
+     * @param a A Vector. Modified.
+     * @param b A Vector. Not modified.
+     */
+    public static void addEquals( FixedMatrix3_64F a , FixedMatrix3_64F b ) {
+        a.a1 += b.a1;
+        a.a2 += b.a2;
+        a.a3 += b.a3;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a - b <br>
+     * c<sub>ij</sub> = a<sub>ij</sub> - b<sub>ij</sub> <br>
+     * </p>
+     *
+     * <p>
+     * Matrix C can be the same instance as Matrix A and/or B.
+     * </p>
+     *
+     * @param a A Matrix. Not modified.
+     * @param b A Matrix. Not modified.
+     * @param c A Matrix where the results are stored. Modified.
+     */
+    public static void subtract( FixedMatrix3x3_64F a , FixedMatrix3x3_64F b , FixedMatrix3x3_64F c ) {
+        c.a11 = a.a11 - b.a11;
+        c.a12 = a.a12 - b.a12;
+        c.a13 = a.a13 - b.a13;
+        c.a21 = a.a21 - b.a21;
+        c.a22 = a.a22 - b.a22;
+        c.a23 = a.a23 - b.a23;
+        c.a31 = a.a31 - b.a31;
+        c.a32 = a.a32 - b.a32;
+        c.a33 = a.a33 - b.a33;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a - b <br>
+     * c<sub>i</sub> = a<sub>i</sub> - b<sub>i</sub> <br>
+     * </p>
+     *
+     * <p>
+     * Vector C can be the same instance as Vector A and/or B.
+     * </p>
+     *
+     * @param a A Vector. Not modified.
+     * @param b A Vector. Not modified.
+     * @param c A Vector where the results are stored. Modified.
+     */
+    public static void subtract( FixedMatrix3_64F a , FixedMatrix3_64F b , FixedMatrix3_64F c ) {
+        c.a1 = a.a1 - b.a1;
+        c.a2 = a.a2 - b.a2;
+        c.a3 = a.a3 - b.a3;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * a = a - b <br>
+     * a<sub>ij</sub> = a<sub>ij</sub> - b<sub>ij</sub> <br>
+     * </p>
+     *
+     * @param a A Matrix. Modified.
+     * @param b A Matrix. Not modified.
+     */
+    public static void subtractEquals( FixedMatrix3x3_64F a , FixedMatrix3x3_64F b ) {
+        a.a11 -= b.a11;
+        a.a12 -= b.a12;
+        a.a13 -= b.a13;
+        a.a21 -= b.a21;
+        a.a22 -= b.a22;
+        a.a23 -= b.a23;
+        a.a31 -= b.a31;
+        a.a32 -= b.a32;
+        a.a33 -= b.a33;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * a = a - b <br>
+     * a<sub>i</sub> = a<sub>i</sub> - b<sub>i</sub> <br>
+     * </p>
+     *
+     * @param a A Vector. Modified.
+     * @param b A Vector. Not modified.
+     */
+    public static void subtractEquals( FixedMatrix3_64F a , FixedMatrix3_64F b ) {
+        a.a1 -= b.a1;
+        a.a2 -= b.a2;
+        a.a3 -= b.a3;
+    }
+
+    /**
+     * Performs an in-place transpose.  This algorithm is only efficient for square
+     * matrices.
+     *
+     * @param m The matrix that is to be transposed. Modified.
+     */
+    public static void transpose( FixedMatrix3x3_64F m ) {
+        double tmp;
+        tmp = m.a12; m.a12 = m.a21; m.a21 = tmp;
+        tmp = m.a13; m.a13 = m.a31; m.a31 = tmp;
+        tmp = m.a23; m.a23 = m.a32; m.a32 = tmp;
+    }
+
+    /**
+     * <p>
+     * Transposes matrix 'a' and stores the results in 'b':<br>
+     * <br>
+     * b<sub>ij</sub> = a<sub>ji</sub><br>
+     * where 'b' is the transpose of 'a'.
+     * </p>
+     *
+     * @param input The original matrix.  Not modified.
+     * @param output Where the transpose is stored. If null a new matrix is created. Modified.
+     * @return The transposed matrix.
+     */
+    public static FixedMatrix3x3_64F transpose( FixedMatrix3x3_64F input , FixedMatrix3x3_64F output ) {
+        if( input == null )
+            input = new FixedMatrix3x3_64F();
+
+        output.a11 = input.a11;
+        output.a12 = input.a21;
+        output.a13 = input.a31;
+        output.a21 = input.a12;
+        output.a22 = input.a22;
+        output.a23 = input.a32;
+        output.a31 = input.a13;
+        output.a32 = input.a23;
+        output.a33 = input.a33;
+
+        return output;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a * b <br>
+     * <br>
+     * c<sub>ij</sub> = ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void mult( FixedMatrix3x3_64F a , FixedMatrix3x3_64F b , FixedMatrix3x3_64F c) {
+        c.a11 = a.a11*b.a11 + a.a12*b.a21 + a.a13*b.a31;
+        c.a12 = a.a11*b.a12 + a.a12*b.a22 + a.a13*b.a32;
+        c.a13 = a.a11*b.a13 + a.a12*b.a23 + a.a13*b.a33;
+        c.a21 = a.a21*b.a11 + a.a22*b.a21 + a.a23*b.a31;
+        c.a22 = a.a21*b.a12 + a.a22*b.a22 + a.a23*b.a32;
+        c.a23 = a.a21*b.a13 + a.a22*b.a23 + a.a23*b.a33;
+        c.a31 = a.a31*b.a11 + a.a32*b.a21 + a.a33*b.a31;
+        c.a32 = a.a31*b.a12 + a.a32*b.a22 + a.a33*b.a32;
+        c.a33 = a.a31*b.a13 + a.a32*b.a23 + a.a33*b.a33;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a<sup>T</sup> * b <br>
+     * <br>
+     * c<sub>ij</sub> = ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multTransA( FixedMatrix3x3_64F a , FixedMatrix3x3_64F b , FixedMatrix3x3_64F c) {
+        c.a11 = a.a11*b.a11 + a.a21*b.a21 + a.a31*b.a31;
+        c.a12 = a.a11*b.a12 + a.a21*b.a22 + a.a31*b.a32;
+        c.a13 = a.a11*b.a13 + a.a21*b.a23 + a.a31*b.a33;
+        c.a21 = a.a12*b.a11 + a.a22*b.a21 + a.a32*b.a31;
+        c.a22 = a.a12*b.a12 + a.a22*b.a22 + a.a32*b.a32;
+        c.a23 = a.a12*b.a13 + a.a22*b.a23 + a.a32*b.a33;
+        c.a31 = a.a13*b.a11 + a.a23*b.a21 + a.a33*b.a31;
+        c.a32 = a.a13*b.a12 + a.a23*b.a22 + a.a33*b.a32;
+        c.a33 = a.a13*b.a13 + a.a23*b.a23 + a.a33*b.a33;
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c = a<sup>T</sup> * b<sup>T</sup><br>
+     * c<sub>ij</sub> = ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>jk</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multTransAB( FixedMatrix3x3_64F a , FixedMatrix3x3_64F b , FixedMatrix3x3_64F c) {
+        c.a11 = a.a11*b.a11 + a.a21*b.a12 + a.a31*b.a13;
+        c.a12 = a.a11*b.a21 + a.a21*b.a22 + a.a31*b.a23;
+        c.a13 = a.a11*b.a31 + a.a21*b.a32 + a.a31*b.a33;
+        c.a21 = a.a12*b.a11 + a.a22*b.a12 + a.a32*b.a13;
+        c.a22 = a.a12*b.a21 + a.a22*b.a22 + a.a32*b.a23;
+        c.a23 = a.a12*b.a31 + a.a22*b.a32 + a.a32*b.a33;
+        c.a31 = a.a13*b.a11 + a.a23*b.a12 + a.a33*b.a13;
+        c.a32 = a.a13*b.a21 + a.a23*b.a22 + a.a33*b.a23;
+        c.a33 = a.a13*b.a31 + a.a23*b.a32 + a.a33*b.a33;
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c = a * b<sup>T</sup> <br>
+     * c<sub>ij</sub> = ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>jk</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multTransB( FixedMatrix3x3_64F a , FixedMatrix3x3_64F b , FixedMatrix3x3_64F c) {
+        c.a11 = a.a11*b.a11 + a.a12*b.a12 + a.a13*b.a13;
+        c.a12 = a.a11*b.a21 + a.a12*b.a22 + a.a13*b.a23;
+        c.a13 = a.a11*b.a31 + a.a12*b.a32 + a.a13*b.a33;
+        c.a21 = a.a21*b.a11 + a.a22*b.a12 + a.a23*b.a13;
+        c.a22 = a.a21*b.a21 + a.a22*b.a22 + a.a23*b.a23;
+        c.a23 = a.a21*b.a31 + a.a22*b.a32 + a.a23*b.a33;
+        c.a31 = a.a31*b.a11 + a.a32*b.a12 + a.a33*b.a13;
+        c.a32 = a.a31*b.a21 + a.a32*b.a22 + a.a33*b.a23;
+        c.a33 = a.a31*b.a31 + a.a32*b.a32 + a.a33*b.a33;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c += a * b <br>
+     * <br>
+     * c<sub>ij</sub> += ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAdd( FixedMatrix3x3_64F a , FixedMatrix3x3_64F b , FixedMatrix3x3_64F c) {
+        c.a11 += a.a11*b.a11 + a.a12*b.a21 + a.a13*b.a31;
+        c.a12 += a.a11*b.a12 + a.a12*b.a22 + a.a13*b.a32;
+        c.a13 += a.a11*b.a13 + a.a12*b.a23 + a.a13*b.a33;
+        c.a21 += a.a21*b.a11 + a.a22*b.a21 + a.a23*b.a31;
+        c.a22 += a.a21*b.a12 + a.a22*b.a22 + a.a23*b.a32;
+        c.a23 += a.a21*b.a13 + a.a22*b.a23 + a.a23*b.a33;
+        c.a31 += a.a31*b.a11 + a.a32*b.a21 + a.a33*b.a31;
+        c.a32 += a.a31*b.a12 + a.a32*b.a22 + a.a33*b.a32;
+        c.a33 += a.a31*b.a13 + a.a32*b.a23 + a.a33*b.a33;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c += a<sup>T</sup> * b <br>
+     * <br>
+     * c<sub>ij</sub> += ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAddTransA( FixedMatrix3x3_64F a , FixedMatrix3x3_64F b , FixedMatrix3x3_64F c) {
+        c.a11 += a.a11*b.a11 + a.a21*b.a21 + a.a31*b.a31;
+        c.a12 += a.a11*b.a12 + a.a21*b.a22 + a.a31*b.a32;
+        c.a13 += a.a11*b.a13 + a.a21*b.a23 + a.a31*b.a33;
+        c.a21 += a.a12*b.a11 + a.a22*b.a21 + a.a32*b.a31;
+        c.a22 += a.a12*b.a12 + a.a22*b.a22 + a.a32*b.a32;
+        c.a23 += a.a12*b.a13 + a.a22*b.a23 + a.a32*b.a33;
+        c.a31 += a.a13*b.a11 + a.a23*b.a21 + a.a33*b.a31;
+        c.a32 += a.a13*b.a12 + a.a23*b.a22 + a.a33*b.a32;
+        c.a33 += a.a13*b.a13 + a.a23*b.a23 + a.a33*b.a33;
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c += a<sup>T</sup> * b<sup>T</sup><br>
+     * c<sub>ij</sub> += ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>jk</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAddTransAB( FixedMatrix3x3_64F a , FixedMatrix3x3_64F b , FixedMatrix3x3_64F c) {
+        c.a11 += a.a11*b.a11 + a.a21*b.a12 + a.a31*b.a13;
+        c.a12 += a.a11*b.a21 + a.a21*b.a22 + a.a31*b.a23;
+        c.a13 += a.a11*b.a31 + a.a21*b.a32 + a.a31*b.a33;
+        c.a21 += a.a12*b.a11 + a.a22*b.a12 + a.a32*b.a13;
+        c.a22 += a.a12*b.a21 + a.a22*b.a22 + a.a32*b.a23;
+        c.a23 += a.a12*b.a31 + a.a22*b.a32 + a.a32*b.a33;
+        c.a31 += a.a13*b.a11 + a.a23*b.a12 + a.a33*b.a13;
+        c.a32 += a.a13*b.a21 + a.a23*b.a22 + a.a33*b.a23;
+        c.a33 += a.a13*b.a31 + a.a23*b.a32 + a.a33*b.a33;
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c += a * b<sup>T</sup> <br>
+     * c<sub>ij</sub> += ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>jk</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAddTransB( FixedMatrix3x3_64F a , FixedMatrix3x3_64F b , FixedMatrix3x3_64F c) {
+        c.a11 += a.a11*b.a11 + a.a12*b.a12 + a.a13*b.a13;
+        c.a12 += a.a11*b.a21 + a.a12*b.a22 + a.a13*b.a23;
+        c.a13 += a.a11*b.a31 + a.a12*b.a32 + a.a13*b.a33;
+        c.a21 += a.a21*b.a11 + a.a22*b.a12 + a.a23*b.a13;
+        c.a22 += a.a21*b.a21 + a.a22*b.a22 + a.a23*b.a23;
+        c.a23 += a.a21*b.a31 + a.a22*b.a32 + a.a23*b.a33;
+        c.a31 += a.a31*b.a11 + a.a32*b.a12 + a.a33*b.a13;
+        c.a32 += a.a31*b.a21 + a.a32*b.a22 + a.a33*b.a23;
+        c.a33 += a.a31*b.a31 + a.a32*b.a32 + a.a33*b.a33;
+    }
+
+    /**
+     * <p>Performs matrix to vector multiplication:<br>
+     * <br>
+     * c = a * b <br>
+     * <br>
+     * c<sub>i</sub> = ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>k</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right vector in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void mult( FixedMatrix3x3_64F a , FixedMatrix3_64F b , FixedMatrix3_64F c) {
+        c.a1 = a.a11*b.a1 + a.a12*b.a2 + a.a13*b.a3;
+        c.a2 = a.a21*b.a1 + a.a22*b.a2 + a.a23*b.a3;
+        c.a3 = a.a31*b.a1 + a.a32*b.a2 + a.a33*b.a3;
+    }
+
+    /**
+     * <p>Performs vector to matrix multiplication:<br>
+     * <br>
+     * c = a * b <br>
+     * <br>
+     * c<sub>j</sub> = ∑<sub>k=1:n</sub> { b<sub>k</sub> * a<sub>kj</sub> }
+     * </p>
+     *
+     * @param a The left vector in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void mult( FixedMatrix3_64F a , FixedMatrix3x3_64F b , FixedMatrix3_64F c) {
+        c.a1 = a.a1*b.a11 + a.a2*b.a21 + a.a3*b.a31;
+        c.a2 = a.a1*b.a12 + a.a2*b.a22 + a.a3*b.a32;
+        c.a3 = a.a1*b.a13 + a.a2*b.a23 + a.a3*b.a33;
+    }
+
+    /**
+     * <p>Performs the vector dot product:<br>
+     * <br>
+     * c = a * b <br>
+     * <br>
+     * c> = ∑<sub>k=1:n</sub> { b<sub>k</sub> * a<sub>k</sub> }
+     * </p>
+     *
+     * @param a The left vector in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @return The dot product
+     */
+    public static double dot( FixedMatrix3_64F a , FixedMatrix3_64F b ) {
+        return a.a1*b.a1 + a.a2*b.a2 + a.a3*b.a3;
+    }
+
+    /**
+     * Sets all the diagonal elements equal to one and everything else equal to zero.
+     * If this is a square matrix then it will be an identity matrix.
+     *
+     * @param a A matrix.
+     */
+    public static void setIdentity( FixedMatrix3x3_64F a ) {
+        a.a11 = 1; a.a21 = 0; a.a31 = 0;
+        a.a12 = 0; a.a22 = 1; a.a32 = 0;
+        a.a13 = 0; a.a23 = 0; a.a33 = 1;
+    }
+
+    /**
+     * Inverts matrix 'a' using minor matrices and stores the results in 'inv'.  Scaling is applied to improve
+     * stability against overflow and underflow.
+     *
+     * WARNING: Potentially less stable than using LU decomposition.
+     *
+     * @param a Input matrix. Not modified.
+     * @param inv Inverted output matrix.  Modified.
+     * @return true if it was successful or false if it failed.  Not always reliable.
+     */
+    public static boolean invert( FixedMatrix3x3_64F a , FixedMatrix3x3_64F inv ) {
+
+        double scale = 1.0/elementMaxAbs(a);
+
+        double a11 = a.a11*scale;
+        double a12 = a.a12*scale;
+        double a13 = a.a13*scale;
+        double a21 = a.a21*scale;
+        double a22 = a.a22*scale;
+        double a23 = a.a23*scale;
+        double a31 = a.a31*scale;
+        double a32 = a.a32*scale;
+        double a33 = a.a33*scale;
+
+        double m11 = a22*a33 - a23*a32;
+        double m12 = -( a21*a33 - a23*a31);
+        double m13 = a21*a32 - a22*a31;
+        double m21 = -( a12*a33 - a13*a32);
+        double m22 = a11*a33 - a13*a31;
+        double m23 = -( a11*a32 - a12*a31);
+        double m31 = a12*a23 - a13*a22;
+        double m32 = -( a11*a23 - a13*a21);
+        double m33 = a11*a22 - a12*a21;
+
+        double det = (a11*m11 + a12*m12 + a13*m13)/scale;
+
+        inv.a11 = m11/det;
+        inv.a12 = m21/det;
+        inv.a13 = m31/det;
+        inv.a21 = m12/det;
+        inv.a22 = m22/det;
+        inv.a23 = m32/det;
+        inv.a31 = m13/det;
+        inv.a32 = m23/det;
+        inv.a33 = m33/det;
+
+        return !Double.isNaN(det) && !Double.isInfinite(det);
+    }
+
+    /**
+     * Computes the determinant using minor matrices.
+     * <p></p>
+     * WARNING: Potentially less stable than using LU decomposition.
+     *
+     * @param mat Input matrix.  Not modified.
+     * @return The determinant.
+     */
+    public static double det( FixedMatrix3x3_64F mat ) {
+
+        double a = mat.a11*(mat.a22*mat.a33 - mat.a23*mat.a32);
+        double b = mat.a12*(mat.a21*mat.a33 - mat.a23*mat.a31);
+        double c = mat.a13*(mat.a21*mat.a32 - mat.a31*mat.a22);
+
+        return a-b+c;
+    }
+
+    /**
+     * <p>
+     * This computes the trace of the matrix:<br>
+     * <br>
+     * trace = ∑<sub>i=1:n</sub> { a<sub>ii</sub> }
+     * </p>
+     * <p>
+     * The trace is only defined for square matrices.
+     * </p>
+     *
+     * @param a A square matrix.  Not modified.
+     */
+    public static double trace( FixedMatrix3x3_64F a ) {
+        return a.a11 + a.a21 + a.a31;
+    }
+
+    /**
+     * <p>
+     * Extracts all diagonal elements from 'input' and places them inside the 'out' vector. Elements
+     * are in sequential order.
+     * </p>
+     *
+     *
+     * @param input Matrix.  Not modified.
+     * @param out Vector containing diagonal elements.  Modified.
+     */
+    public static void diag( FixedMatrix3x3_64F input , FixedMatrix3_64F out ) {
+        out.a1 = input.a11;
+        out.a2 = input.a22;
+        out.a3 = input.a33;
+    }
+
+    /**
+     * <p>
+     * Returns the value of the element in the matrix that has the largest value.<br>
+     * <br>
+     * Max{ a<sub>ij</sub> } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max element value of the matrix.
+     */
+    public static double elementMax( FixedMatrix3x3_64F a ) {
+        double max = a.a11;
+        max = Math.max(max,a.a12);
+        max = Math.max(max,a.a13);
+        max = Math.max(max,a.a21);
+        max = Math.max(max,a.a22);
+        max = Math.max(max,a.a23);
+        max = Math.max(max,a.a31);
+        max = Math.max(max,a.a32);
+        max = Math.max(max,a.a33);
+
+        return max;
+    }
+
+    /**
+     * <p>
+     * Returns the value of the element in the vector that has the largest value.<br>
+     * <br>
+     * Max{ a<sub>i</sub> } for all i<br>
+     * </p>
+     *
+     * @param a A vector. Not modified.
+     * @return The max element value of the matrix.
+     */
+    public static double elementMax( FixedMatrix3_64F a ) {
+        double max = a.a1;
+        max = Math.max(max,a.a2);
+        max = Math.max(max,a.a3);
+
+        return max;
+    }
+
+    /**
+     * <p>
+     * Returns the absolute value of the element in the matrix that has the largest absolute value.<br>
+     * <br>
+     * Max{ |a<sub>ij</sub>| } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max abs element value of the matrix.
+     */
+    public static double elementMaxAbs( FixedMatrix3x3_64F a ) {
+        double max = a.a11;
+        max = Math.max(max,Math.abs(a.a12));
+        max = Math.max(max,Math.abs(a.a13));
+        max = Math.max(max,Math.abs(a.a21));
+        max = Math.max(max,Math.abs(a.a22));
+        max = Math.max(max,Math.abs(a.a23));
+        max = Math.max(max,Math.abs(a.a31));
+        max = Math.max(max,Math.abs(a.a32));
+        max = Math.max(max,Math.abs(a.a33));
+
+        return max;
+    }
+
+    /**
+     * <p>
+     * Returns the absolute value of the element in the vector that has the largest absolute value.<br>
+     * <br>
+     * Max{ |a<sub>i</sub>| } for all i<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max abs element value of the vector.
+     */
+    public static double elementMaxAbs( FixedMatrix3_64F a ) {
+        double max = a.a1;
+        max = Math.max(max,Math.abs(a.a2));
+        max = Math.max(max,Math.abs(a.a3));
+
+        return max;
+    }
+
+    /**
+     * <p>
+     * Returns the value of the element in the matrix that has the minimum value.<br>
+     * <br>
+     * Min{ a<sub>ij</sub> } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The value of element in the matrix with the minimum value.
+     */
+    public static double elementMin( FixedMatrix3x3_64F a ) {
+        double min = a.a11;
+        min = Math.min(min, a.a12);
+        min = Math.min(min, a.a13);
+        min = Math.min(min, a.a21);
+        min = Math.min(min, a.a22);
+        min = Math.min(min, a.a23);
+        min = Math.min(min, a.a31);
+        min = Math.min(min, a.a32);
+        min = Math.min(min, a.a33);
+
+        return min;
+    }
+
+    /**
+     * <p>
+     * Returns the value of the element in the vector that has the minimum value.<br>
+     * <br>
+     * Min{ a<sub>i</sub> } for all<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The value of element in the vector with the minimum value.
+     */
+    public static double elementMin( FixedMatrix3_64F a ) {
+        double min = a.a1;
+        min = Math.min(min, a.a2);
+        min = Math.min(min, a.a3);
+
+        return min;
+    }
+
+    /**
+     * <p>
+     * Returns the absolute value of the element in the matrix that has the smallest absolute value.<br>
+     * <br>
+     * Min{ |a<sub>ij</sub>| } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max element value of the matrix.
+     */
+    public static double elementMinAbs( FixedMatrix3x3_64F a ) {
+        double min = a.a11;
+        min = Math.min(min,Math.abs(a.a12));
+        min = Math.min(min,Math.abs(a.a13));
+        min = Math.min(min,Math.abs(a.a21));
+        min = Math.min(min,Math.abs(a.a22));
+        min = Math.min(min,Math.abs(a.a23));
+        min = Math.min(min,Math.abs(a.a31));
+        min = Math.min(min,Math.abs(a.a32));
+        min = Math.min(min,Math.abs(a.a33));
+
+        return min;
+    }
+
+    /**
+     * <p>
+     * Returns the absolute value of the element in the vector that has the smallest absolute value.<br>
+     * <br>
+     * Min{ |a<sub>i</sub>| } for all i<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max element value of the vector.
+     */
+    public static double elementMinAbs( FixedMatrix3_64F a ) {
+        double min = a.a1;
+        min = Math.min(min,Math.abs(a.a2));
+        min = Math.min(min,Math.abs(a.a3));
+
+        return min;
+    }
+
+    /**
+     * <p>Performs an element by element multiplication operation:<br>
+     * <br>
+     * a<sub>ij</sub> = a<sub>ij</sub> * b<sub>ij</sub> <br>
+     * </p>
+     * @param a The left matrix in the multiplication operation. Modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     */
+    public static void elementMult( FixedMatrix3x3_64F a , FixedMatrix3x3_64F b) {
+        a.a11 *= b.a11; a.a12 *= b.a12; a.a13 *= b.a13;
+        a.a21 *= b.a21; a.a22 *= b.a22; a.a23 *= b.a23;
+        a.a31 *= b.a31; a.a32 *= b.a32; a.a33 *= b.a33;
+    }
+
+    /**
+     * <p>Performs an element by element multiplication operation:<br>
+     * <br>
+     * a<sub>i</sub> = a<sub>i</sub> * b<sub>i</sub> <br>
+     * </p>
+     * @param a The left vector in the multiplication operation. Modified.
+     * @param b The right vector in the multiplication operation. Not modified.
+     */
+    public static void elementMult( FixedMatrix3_64F a , FixedMatrix3_64F b) {
+        a.a1 *= b.a1;
+        a.a2 *= b.a2;
+        a.a3 *= b.a3;
+    }
+
+    /**
+     * <p>Performs an element by element multiplication operation:<br>
+     * <br>
+     * c<sub>ij</sub> = a<sub>ij</sub> * b<sub>ij</sub> <br>
+     * </p>
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void elementMult( FixedMatrix3x3_64F a , FixedMatrix3x3_64F b , FixedMatrix3x3_64F c ) {
+        c.a11 = a.a11*b.a11; c.a12 = a.a12*b.a12; c.a13 = a.a13*b.a13;
+        c.a21 = a.a21*b.a21; c.a22 = a.a22*b.a22; c.a23 = a.a23*b.a23;
+        c.a31 = a.a31*b.a31; c.a32 = a.a32*b.a32; c.a33 = a.a33*b.a33;
+    }
+
+    /**
+     * <p>Performs an element by element multiplication operation:<br>
+     * <br>
+     * c<sub>i</sub> = a<sub>i</sub> * b<sub>j</sub> <br>
+     * </p>
+     * @param a The left vector in the multiplication operation. Not modified.
+     * @param b The right vector in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void elementMult( FixedMatrix3_64F a , FixedMatrix3_64F b , FixedMatrix3_64F c ) {
+        c.a1 = a.a1*b.a1;
+        c.a2 = a.a2*b.a2;
+        c.a3 = a.a3*b.a3;
+    }
+
+    /**
+     * <p>Performs an element by element division operation:<br>
+     * <br>
+     * a<sub>ij</sub> = a<sub>ij</sub> / b<sub>ij</sub> <br>
+     * </p>
+     * @param a The left matrix in the division operation. Modified.
+     * @param b The right matrix in the division operation. Not modified.
+     */
+    public static void elementDiv( FixedMatrix3x3_64F a , FixedMatrix3x3_64F b) {
+        a.a11 /= b.a11; a.a12 /= b.a12; a.a13 /= b.a13;
+        a.a21 /= b.a21; a.a22 /= b.a22; a.a23 /= b.a23;
+        a.a31 /= b.a31; a.a32 /= b.a32; a.a33 /= b.a33;
+    }
+
+    /**
+     * <p>Performs an element by element division operation:<br>
+     * <br>
+     * a<sub>i</sub> = a<sub>i</sub> / b<sub>i</sub> <br>
+     * </p>
+     * @param a The left vector in the division operation. Modified.
+     * @param b The right vector in the division operation. Not modified.
+     */
+    public static void elementDiv( FixedMatrix3_64F a , FixedMatrix3_64F b) {
+        a.a1 /= b.a1;
+        a.a2 /= b.a2;
+        a.a3 /= b.a3;
+    }
+
+    /**
+     * <p>Performs an element by element division operation:<br>
+     * <br>
+     * c<sub>ij</sub> = a<sub>ij</sub> / b<sub>ij</sub> <br>
+     * </p>
+     * @param a The left matrix in the division operation. Not modified.
+     * @param b The right matrix in the division operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void elementDiv( FixedMatrix3x3_64F a , FixedMatrix3x3_64F b , FixedMatrix3x3_64F c ) {
+        c.a11 = a.a11/b.a11; c.a12 = a.a12/b.a12; c.a13 = a.a13/b.a13;
+        c.a21 = a.a21/b.a21; c.a22 = a.a22/b.a22; c.a23 = a.a23/b.a23;
+        c.a31 = a.a31/b.a31; c.a32 = a.a32/b.a32; c.a33 = a.a33/b.a33;
+    }
+
+    /**
+     * <p>Performs an element by element division operation:<br>
+     * <br>
+     * c<sub>i</sub> = a<sub>i</sub> / b<sub>i</sub> <br>
+     * </p>
+     * @param a The left vector in the division operation. Not modified.
+     * @param b The right vector in the division operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void elementDiv( FixedMatrix3_64F a , FixedMatrix3_64F b , FixedMatrix3_64F c ) {
+        c.a1 = a.a1/b.a1;
+        c.a2 = a.a2/b.a2;
+        c.a3 = a.a3/b.a3;
+    }
+
+    /**
+     * <p>
+     * Performs an in-place element by element scalar multiplication.<br>
+     * <br>
+     * a<sub>ij</sub> = α*a<sub>ij</sub>
+     * </p>
+     *
+     * @param a The matrix that is to be scaled.  Modified.
+     * @param alpha the amount each element is multiplied by.
+     */
+    public static void scale( double alpha , FixedMatrix3x3_64F a ) {
+        a.a11 *= alpha; a.a12 *= alpha; a.a13 *= alpha;
+        a.a21 *= alpha; a.a22 *= alpha; a.a23 *= alpha;
+        a.a31 *= alpha; a.a32 *= alpha; a.a33 *= alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an in-place element by element scalar multiplication.<br>
+     * <br>
+     * a<sub>ij</sub> = α*a<sub>ij</sub>
+     * </p>
+     *
+     * @param a The vector that is to be scaled.  Modified.
+     * @param alpha the amount each element is multiplied by.
+     */
+    public static void scale( double alpha , FixedMatrix3_64F a ) {
+        a.a1 *= alpha;
+        a.a2 *= alpha;
+        a.a3 *= alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an element by element scalar multiplication.<br>
+     * <br>
+     * b<sub>ij</sub> = α*a<sub>ij</sub>
+     * </p>
+     *
+     * @param alpha the amount each element is multiplied by.
+     * @param a The matrix that is to be scaled.  Not modified.
+     * @param b Where the scaled matrix is stored. Modified.
+     */
+    public static void scale( double alpha , FixedMatrix3x3_64F a , FixedMatrix3x3_64F b ) {
+        b.a11 = a.a11*alpha; b.a12 = a.a12*alpha; b.a13 = a.a13*alpha;
+        b.a21 = a.a21*alpha; b.a22 = a.a22*alpha; b.a23 = a.a23*alpha;
+        b.a31 = a.a31*alpha; b.a32 = a.a32*alpha; b.a33 = a.a33*alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an element by element scalar multiplication.<br>
+     * <br>
+     * b<sub>i</sub> = α*a<sub>i</sub>
+     * </p>
+     *
+     * @param alpha the amount each element is multiplied by.
+     * @param a The vector that is to be scaled.  Not modified.
+     * @param b Where the scaled matrix is stored. Modified.
+     */
+    public static void scale( double alpha , FixedMatrix3_64F a , FixedMatrix3_64F b ) {
+        b.a1 = a.a1*alpha;
+        b.a2 = a.a2*alpha;
+        b.a3 = a.a3*alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an in-place element by element scalar division. Scalar denominator.<br>
+     * <br>
+     * a<sub>ij</sub> = a<sub>ij</sub>/α
+     * </p>
+     *
+     * @param a The matrix whose elements are to be divided.  Modified.
+     * @param alpha the amount each element is divided by.
+     */
+    public static void divide( FixedMatrix3x3_64F a , double alpha ) {
+        a.a11 /= alpha; a.a12 /= alpha; a.a13 /= alpha;
+        a.a21 /= alpha; a.a22 /= alpha; a.a23 /= alpha;
+        a.a31 /= alpha; a.a32 /= alpha; a.a33 /= alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an in-place element by element scalar division. Scalar denominator.<br>
+     * <br>
+     * a<sub>i</sub> = a<sub>i</sub>/α
+     * </p>
+     *
+     * @param a The vector whose elements are to be divided.  Modified.
+     * @param alpha the amount each element is divided by.
+     */
+    public static void divide( FixedMatrix3_64F a , double alpha ) {
+        a.a1 /= alpha;
+        a.a2 /= alpha;
+        a.a3 /= alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an element by element scalar division.  Scalar denominator.<br>
+     * <br>
+     * b<sub>ij</sub> = a<sub>ij</sub> /α
+     * </p>
+     *
+     * @param alpha the amount each element is divided by.
+     * @param a The matrix whose elements are to be divided.  Not modified.
+     * @param b Where the results are stored. Modified.
+     */
+    public static void divide( FixedMatrix3x3_64F a , double alpha , FixedMatrix3x3_64F b ) {
+        b.a11 = a.a11/alpha; b.a12 = a.a12/alpha; b.a13 = a.a13/alpha;
+        b.a21 = a.a21/alpha; b.a22 = a.a22/alpha; b.a23 = a.a23/alpha;
+        b.a31 = a.a31/alpha; b.a32 = a.a32/alpha; b.a33 = a.a33/alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an element by element scalar division.  Scalar denominator.<br>
+     * <br>
+     * b<sub>i</sub> = a<sub>i</sub> /α
+     * </p>
+     *
+     * @param alpha the amount each element is divided by.
+     * @param a The vector whose elements are to be divided.  Not modified.
+     * @param b Where the results are stored. Modified.
+     */
+    public static void divide( FixedMatrix3_64F a , double alpha , FixedMatrix3_64F b ) {
+        b.a1 = a.a1/alpha;
+        b.a2 = a.a2/alpha;
+        b.a3 = a.a3/alpha;
+    }
+
+    /**
+     * <p>
+     * Changes the sign of every element in the matrix.<br>
+     * <br>
+     * a<sub>ij</sub> = -a<sub>ij</sub>
+     * </p>
+     *
+     * @param a A matrix. Modified.
+     */
+    public static void changeSign( FixedMatrix3x3_64F a )
+    {
+        a.a11 = -a.a11; a.a12 = -a.a12; a.a13 = -a.a13;
+        a.a21 = -a.a21; a.a22 = -a.a22; a.a23 = -a.a23;
+        a.a31 = -a.a31; a.a32 = -a.a32; a.a33 = -a.a33;
+    }
+
+    /**
+     * <p>
+     * Changes the sign of every element in the vector.<br>
+     * <br>
+     * a<sub>i</sub> = -a<sub>i</sub>
+     * </p>
+     *
+     * @param a A vector. Modified.
+     */
+    public static void changeSign( FixedMatrix3_64F a )
+    {
+        a.a1 = -a.a1;
+        a.a2 = -a.a2;
+        a.a3 = -a.a3;
+    }
+
+    /**
+     * <p>
+     * Sets every element in the matrix to the specified value.<br>
+     * <br>
+     * a<sub>ij</sub> = value
+     * <p>
+     *
+     * @param a A matrix whose elements are about to be set. Modified.
+     * @param v The value each element will have.
+     */
+    public static void fill( FixedMatrix3x3_64F a , double v  ) {
+        a.a11 = v; a.a12 = v; a.a13 = v;
+        a.a21 = v; a.a22 = v; a.a23 = v;
+        a.a31 = v; a.a32 = v; a.a33 = v;
+    }
+
+    /**
+     * <p>
+     * Sets every element in the vector to the specified value.<br>
+     * <br>
+     * a<sub>i</sub> = value
+     * <p>
+     *
+     * @param a A vector whose elements are about to be set. Modified.
+     * @param v The value each element will have.
+     */
+    public static void fill( FixedMatrix3_64F a , double v  ) {
+        a.a1 = v;
+        a.a2 = v;
+        a.a3 = v;
+    }
+
+    /**
+     * Extracts the row from the matrix a.
+     * @param a Input matrix
+     * @param row Which row is to be extracted
+     * @param out output. Storage for the extracted row. If null then a new vector will be returned.
+     * @return The extracted row.
+     */
+    public static FixedMatrix3_64F extractRow( FixedMatrix3x3_64F a , int row , FixedMatrix3_64F out ) {
+        if( out == null) out = new FixedMatrix3_64F();
+        switch( row ) {
+            case 0:
+                out.a1 = a.a11;
+                out.a2 = a.a12;
+                out.a3 = a.a13;
+            break;
+            case 1:
+                out.a1 = a.a21;
+                out.a2 = a.a22;
+                out.a3 = a.a23;
+            break;
+            case 2:
+                out.a1 = a.a31;
+                out.a2 = a.a32;
+                out.a3 = a.a33;
+            break;
+            default:
+                throw new IllegalArgumentException("Out of bounds row.  row = "+row);
+        }
+        return out;
+    }
+
+    /**
+     * Extracts the column from the matrix a.
+     * @param a Input matrix
+     * @param column Which column is to be extracted
+     * @param out output. Storage for the extracted column. If null then a new vector will be returned.
+     * @return The extracted column.
+     */
+    public static FixedMatrix3_64F extractColumn( FixedMatrix3x3_64F a , int column , FixedMatrix3_64F out ) {
+        if( out == null) out = new FixedMatrix3_64F();
+        switch( column ) {
+            case 0:
+                out.a1 = a.a11;
+                out.a2 = a.a21;
+                out.a3 = a.a31;
+            break;
+            case 1:
+                out.a1 = a.a12;
+                out.a2 = a.a22;
+                out.a3 = a.a32;
+            break;
+            case 2:
+                out.a1 = a.a13;
+                out.a2 = a.a23;
+                out.a3 = a.a33;
+            break;
+            default:
+                throw new IllegalArgumentException("Out of bounds column.  column = "+column);
+        }
+        return out;
+    }
+
+}
+
diff --git a/main/dense64/src/org/ejml/alg/fixed/FixedOps4.java b/main/dense64/src/org/ejml/alg/fixed/FixedOps4.java
new file mode 100644
index 0000000..b93d0cd
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/fixed/FixedOps4.java
@@ -0,0 +1,1355 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+package org.ejml.alg.fixed;
+
+import org.ejml.data.FixedMatrix4_64F;
+import org.ejml.data.FixedMatrix4x4_64F;
+
+/**
+ * <p>Common matrix operations for fixed sized matrices which are 4 x 4 or 4 element vectors.</p>
+ * <p>DO NOT MODIFY.  Automatically generated code created by GenerateFixedOps</p>
+ *
+ * @author Peter Abeles
+ */
+public class FixedOps4 {
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a + b <br>
+     * c<sub>ij</sub> = a<sub>ij</sub> + b<sub>ij</sub> <br>
+     * </p>
+     *
+     * <p>
+     * Matrix C can be the same instance as Matrix A and/or B.
+     * </p>
+     *
+     * @param a A Matrix. Not modified.
+     * @param b A Matrix. Not modified.
+     * @param c A Matrix where the results are stored. Modified.
+     */
+    public static void add( FixedMatrix4x4_64F a , FixedMatrix4x4_64F b , FixedMatrix4x4_64F c ) {
+        c.a11 = a.a11 + b.a11;
+        c.a12 = a.a12 + b.a12;
+        c.a13 = a.a13 + b.a13;
+        c.a14 = a.a14 + b.a14;
+        c.a21 = a.a21 + b.a21;
+        c.a22 = a.a22 + b.a22;
+        c.a23 = a.a23 + b.a23;
+        c.a24 = a.a24 + b.a24;
+        c.a31 = a.a31 + b.a31;
+        c.a32 = a.a32 + b.a32;
+        c.a33 = a.a33 + b.a33;
+        c.a34 = a.a34 + b.a34;
+        c.a41 = a.a41 + b.a41;
+        c.a42 = a.a42 + b.a42;
+        c.a43 = a.a43 + b.a43;
+        c.a44 = a.a44 + b.a44;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a + b <br>
+     * c<sub>i</sub> = a<sub>i</sub> + b<sub>i</sub> <br>
+     * </p>
+     *
+     * <p>
+     * Vector C can be the same instance as Vector A and/or B.
+     * </p>
+     *
+     * @param a A Vector. Not modified.
+     * @param b A Vector. Not modified.
+     * @param c A Vector where the results are stored. Modified.
+     */
+    public static void add( FixedMatrix4_64F a , FixedMatrix4_64F b , FixedMatrix4_64F c ) {
+        c.a1 = a.a1 + b.a1;
+        c.a2 = a.a2 + b.a2;
+        c.a3 = a.a3 + b.a3;
+        c.a4 = a.a4 + b.a4;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * a = a + b <br>
+     * a<sub>ij</sub> = a<sub>ij</sub> + b<sub>ij</sub> <br>
+     * </p>
+     *
+     * @param a A Matrix. Modified.
+     * @param b A Matrix. Not modified.
+     */
+    public static void addEquals( FixedMatrix4x4_64F a , FixedMatrix4x4_64F b ) {
+        a.a11 += b.a11;
+        a.a12 += b.a12;
+        a.a13 += b.a13;
+        a.a14 += b.a14;
+        a.a21 += b.a21;
+        a.a22 += b.a22;
+        a.a23 += b.a23;
+        a.a24 += b.a24;
+        a.a31 += b.a31;
+        a.a32 += b.a32;
+        a.a33 += b.a33;
+        a.a34 += b.a34;
+        a.a41 += b.a41;
+        a.a42 += b.a42;
+        a.a43 += b.a43;
+        a.a44 += b.a44;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * a = a + b <br>
+     * a<sub>i</sub> = a<sub>i</sub> + b<sub>i</sub> <br>
+     * </p>
+     *
+     * @param a A Vector. Modified.
+     * @param b A Vector. Not modified.
+     */
+    public static void addEquals( FixedMatrix4_64F a , FixedMatrix4_64F b ) {
+        a.a1 += b.a1;
+        a.a2 += b.a2;
+        a.a3 += b.a3;
+        a.a4 += b.a4;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a - b <br>
+     * c<sub>ij</sub> = a<sub>ij</sub> - b<sub>ij</sub> <br>
+     * </p>
+     *
+     * <p>
+     * Matrix C can be the same instance as Matrix A and/or B.
+     * </p>
+     *
+     * @param a A Matrix. Not modified.
+     * @param b A Matrix. Not modified.
+     * @param c A Matrix where the results are stored. Modified.
+     */
+    public static void subtract( FixedMatrix4x4_64F a , FixedMatrix4x4_64F b , FixedMatrix4x4_64F c ) {
+        c.a11 = a.a11 - b.a11;
+        c.a12 = a.a12 - b.a12;
+        c.a13 = a.a13 - b.a13;
+        c.a14 = a.a14 - b.a14;
+        c.a21 = a.a21 - b.a21;
+        c.a22 = a.a22 - b.a22;
+        c.a23 = a.a23 - b.a23;
+        c.a24 = a.a24 - b.a24;
+        c.a31 = a.a31 - b.a31;
+        c.a32 = a.a32 - b.a32;
+        c.a33 = a.a33 - b.a33;
+        c.a34 = a.a34 - b.a34;
+        c.a41 = a.a41 - b.a41;
+        c.a42 = a.a42 - b.a42;
+        c.a43 = a.a43 - b.a43;
+        c.a44 = a.a44 - b.a44;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a - b <br>
+     * c<sub>i</sub> = a<sub>i</sub> - b<sub>i</sub> <br>
+     * </p>
+     *
+     * <p>
+     * Vector C can be the same instance as Vector A and/or B.
+     * </p>
+     *
+     * @param a A Vector. Not modified.
+     * @param b A Vector. Not modified.
+     * @param c A Vector where the results are stored. Modified.
+     */
+    public static void subtract( FixedMatrix4_64F a , FixedMatrix4_64F b , FixedMatrix4_64F c ) {
+        c.a1 = a.a1 - b.a1;
+        c.a2 = a.a2 - b.a2;
+        c.a3 = a.a3 - b.a3;
+        c.a4 = a.a4 - b.a4;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * a = a - b <br>
+     * a<sub>ij</sub> = a<sub>ij</sub> - b<sub>ij</sub> <br>
+     * </p>
+     *
+     * @param a A Matrix. Modified.
+     * @param b A Matrix. Not modified.
+     */
+    public static void subtractEquals( FixedMatrix4x4_64F a , FixedMatrix4x4_64F b ) {
+        a.a11 -= b.a11;
+        a.a12 -= b.a12;
+        a.a13 -= b.a13;
+        a.a14 -= b.a14;
+        a.a21 -= b.a21;
+        a.a22 -= b.a22;
+        a.a23 -= b.a23;
+        a.a24 -= b.a24;
+        a.a31 -= b.a31;
+        a.a32 -= b.a32;
+        a.a33 -= b.a33;
+        a.a34 -= b.a34;
+        a.a41 -= b.a41;
+        a.a42 -= b.a42;
+        a.a43 -= b.a43;
+        a.a44 -= b.a44;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * a = a - b <br>
+     * a<sub>i</sub> = a<sub>i</sub> - b<sub>i</sub> <br>
+     * </p>
+     *
+     * @param a A Vector. Modified.
+     * @param b A Vector. Not modified.
+     */
+    public static void subtractEquals( FixedMatrix4_64F a , FixedMatrix4_64F b ) {
+        a.a1 -= b.a1;
+        a.a2 -= b.a2;
+        a.a3 -= b.a3;
+        a.a4 -= b.a4;
+    }
+
+    /**
+     * Performs an in-place transpose.  This algorithm is only efficient for square
+     * matrices.
+     *
+     * @param m The matrix that is to be transposed. Modified.
+     */
+    public static void transpose( FixedMatrix4x4_64F m ) {
+        double tmp;
+        tmp = m.a12; m.a12 = m.a21; m.a21 = tmp;
+        tmp = m.a13; m.a13 = m.a31; m.a31 = tmp;
+        tmp = m.a14; m.a14 = m.a41; m.a41 = tmp;
+        tmp = m.a23; m.a23 = m.a32; m.a32 = tmp;
+        tmp = m.a24; m.a24 = m.a42; m.a42 = tmp;
+        tmp = m.a34; m.a34 = m.a43; m.a43 = tmp;
+    }
+
+    /**
+     * <p>
+     * Transposes matrix 'a' and stores the results in 'b':<br>
+     * <br>
+     * b<sub>ij</sub> = a<sub>ji</sub><br>
+     * where 'b' is the transpose of 'a'.
+     * </p>
+     *
+     * @param input The original matrix.  Not modified.
+     * @param output Where the transpose is stored. If null a new matrix is created. Modified.
+     * @return The transposed matrix.
+     */
+    public static FixedMatrix4x4_64F transpose( FixedMatrix4x4_64F input , FixedMatrix4x4_64F output ) {
+        if( input == null )
+            input = new FixedMatrix4x4_64F();
+
+        output.a11 = input.a11;
+        output.a12 = input.a21;
+        output.a13 = input.a31;
+        output.a14 = input.a41;
+        output.a21 = input.a12;
+        output.a22 = input.a22;
+        output.a23 = input.a32;
+        output.a24 = input.a42;
+        output.a31 = input.a13;
+        output.a32 = input.a23;
+        output.a33 = input.a33;
+        output.a34 = input.a43;
+        output.a41 = input.a14;
+        output.a42 = input.a24;
+        output.a43 = input.a34;
+        output.a44 = input.a44;
+
+        return output;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a * b <br>
+     * <br>
+     * c<sub>ij</sub> = ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void mult( FixedMatrix4x4_64F a , FixedMatrix4x4_64F b , FixedMatrix4x4_64F c) {
+        c.a11 = a.a11*b.a11 + a.a12*b.a21 + a.a13*b.a31 + a.a14*b.a41;
+        c.a12 = a.a11*b.a12 + a.a12*b.a22 + a.a13*b.a32 + a.a14*b.a42;
+        c.a13 = a.a11*b.a13 + a.a12*b.a23 + a.a13*b.a33 + a.a14*b.a43;
+        c.a14 = a.a11*b.a14 + a.a12*b.a24 + a.a13*b.a34 + a.a14*b.a44;
+        c.a21 = a.a21*b.a11 + a.a22*b.a21 + a.a23*b.a31 + a.a24*b.a41;
+        c.a22 = a.a21*b.a12 + a.a22*b.a22 + a.a23*b.a32 + a.a24*b.a42;
+        c.a23 = a.a21*b.a13 + a.a22*b.a23 + a.a23*b.a33 + a.a24*b.a43;
+        c.a24 = a.a21*b.a14 + a.a22*b.a24 + a.a23*b.a34 + a.a24*b.a44;
+        c.a31 = a.a31*b.a11 + a.a32*b.a21 + a.a33*b.a31 + a.a34*b.a41;
+        c.a32 = a.a31*b.a12 + a.a32*b.a22 + a.a33*b.a32 + a.a34*b.a42;
+        c.a33 = a.a31*b.a13 + a.a32*b.a23 + a.a33*b.a33 + a.a34*b.a43;
+        c.a34 = a.a31*b.a14 + a.a32*b.a24 + a.a33*b.a34 + a.a34*b.a44;
+        c.a41 = a.a41*b.a11 + a.a42*b.a21 + a.a43*b.a31 + a.a44*b.a41;
+        c.a42 = a.a41*b.a12 + a.a42*b.a22 + a.a43*b.a32 + a.a44*b.a42;
+        c.a43 = a.a41*b.a13 + a.a42*b.a23 + a.a43*b.a33 + a.a44*b.a43;
+        c.a44 = a.a41*b.a14 + a.a42*b.a24 + a.a43*b.a34 + a.a44*b.a44;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a<sup>T</sup> * b <br>
+     * <br>
+     * c<sub>ij</sub> = ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multTransA( FixedMatrix4x4_64F a , FixedMatrix4x4_64F b , FixedMatrix4x4_64F c) {
+        c.a11 = a.a11*b.a11 + a.a21*b.a21 + a.a31*b.a31 + a.a41*b.a41;
+        c.a12 = a.a11*b.a12 + a.a21*b.a22 + a.a31*b.a32 + a.a41*b.a42;
+        c.a13 = a.a11*b.a13 + a.a21*b.a23 + a.a31*b.a33 + a.a41*b.a43;
+        c.a14 = a.a11*b.a14 + a.a21*b.a24 + a.a31*b.a34 + a.a41*b.a44;
+        c.a21 = a.a12*b.a11 + a.a22*b.a21 + a.a32*b.a31 + a.a42*b.a41;
+        c.a22 = a.a12*b.a12 + a.a22*b.a22 + a.a32*b.a32 + a.a42*b.a42;
+        c.a23 = a.a12*b.a13 + a.a22*b.a23 + a.a32*b.a33 + a.a42*b.a43;
+        c.a24 = a.a12*b.a14 + a.a22*b.a24 + a.a32*b.a34 + a.a42*b.a44;
+        c.a31 = a.a13*b.a11 + a.a23*b.a21 + a.a33*b.a31 + a.a43*b.a41;
+        c.a32 = a.a13*b.a12 + a.a23*b.a22 + a.a33*b.a32 + a.a43*b.a42;
+        c.a33 = a.a13*b.a13 + a.a23*b.a23 + a.a33*b.a33 + a.a43*b.a43;
+        c.a34 = a.a13*b.a14 + a.a23*b.a24 + a.a33*b.a34 + a.a43*b.a44;
+        c.a41 = a.a14*b.a11 + a.a24*b.a21 + a.a34*b.a31 + a.a44*b.a41;
+        c.a42 = a.a14*b.a12 + a.a24*b.a22 + a.a34*b.a32 + a.a44*b.a42;
+        c.a43 = a.a14*b.a13 + a.a24*b.a23 + a.a34*b.a33 + a.a44*b.a43;
+        c.a44 = a.a14*b.a14 + a.a24*b.a24 + a.a34*b.a34 + a.a44*b.a44;
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c = a<sup>T</sup> * b<sup>T</sup><br>
+     * c<sub>ij</sub> = ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>jk</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multTransAB( FixedMatrix4x4_64F a , FixedMatrix4x4_64F b , FixedMatrix4x4_64F c) {
+        c.a11 = a.a11*b.a11 + a.a21*b.a12 + a.a31*b.a13 + a.a41*b.a14;
+        c.a12 = a.a11*b.a21 + a.a21*b.a22 + a.a31*b.a23 + a.a41*b.a24;
+        c.a13 = a.a11*b.a31 + a.a21*b.a32 + a.a31*b.a33 + a.a41*b.a34;
+        c.a14 = a.a11*b.a41 + a.a21*b.a42 + a.a31*b.a43 + a.a41*b.a44;
+        c.a21 = a.a12*b.a11 + a.a22*b.a12 + a.a32*b.a13 + a.a42*b.a14;
+        c.a22 = a.a12*b.a21 + a.a22*b.a22 + a.a32*b.a23 + a.a42*b.a24;
+        c.a23 = a.a12*b.a31 + a.a22*b.a32 + a.a32*b.a33 + a.a42*b.a34;
+        c.a24 = a.a12*b.a41 + a.a22*b.a42 + a.a32*b.a43 + a.a42*b.a44;
+        c.a31 = a.a13*b.a11 + a.a23*b.a12 + a.a33*b.a13 + a.a43*b.a14;
+        c.a32 = a.a13*b.a21 + a.a23*b.a22 + a.a33*b.a23 + a.a43*b.a24;
+        c.a33 = a.a13*b.a31 + a.a23*b.a32 + a.a33*b.a33 + a.a43*b.a34;
+        c.a34 = a.a13*b.a41 + a.a23*b.a42 + a.a33*b.a43 + a.a43*b.a44;
+        c.a41 = a.a14*b.a11 + a.a24*b.a12 + a.a34*b.a13 + a.a44*b.a14;
+        c.a42 = a.a14*b.a21 + a.a24*b.a22 + a.a34*b.a23 + a.a44*b.a24;
+        c.a43 = a.a14*b.a31 + a.a24*b.a32 + a.a34*b.a33 + a.a44*b.a34;
+        c.a44 = a.a14*b.a41 + a.a24*b.a42 + a.a34*b.a43 + a.a44*b.a44;
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c = a * b<sup>T</sup> <br>
+     * c<sub>ij</sub> = ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>jk</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multTransB( FixedMatrix4x4_64F a , FixedMatrix4x4_64F b , FixedMatrix4x4_64F c) {
+        c.a11 = a.a11*b.a11 + a.a12*b.a12 + a.a13*b.a13 + a.a14*b.a14;
+        c.a12 = a.a11*b.a21 + a.a12*b.a22 + a.a13*b.a23 + a.a14*b.a24;
+        c.a13 = a.a11*b.a31 + a.a12*b.a32 + a.a13*b.a33 + a.a14*b.a34;
+        c.a14 = a.a11*b.a41 + a.a12*b.a42 + a.a13*b.a43 + a.a14*b.a44;
+        c.a21 = a.a21*b.a11 + a.a22*b.a12 + a.a23*b.a13 + a.a24*b.a14;
+        c.a22 = a.a21*b.a21 + a.a22*b.a22 + a.a23*b.a23 + a.a24*b.a24;
+        c.a23 = a.a21*b.a31 + a.a22*b.a32 + a.a23*b.a33 + a.a24*b.a34;
+        c.a24 = a.a21*b.a41 + a.a22*b.a42 + a.a23*b.a43 + a.a24*b.a44;
+        c.a31 = a.a31*b.a11 + a.a32*b.a12 + a.a33*b.a13 + a.a34*b.a14;
+        c.a32 = a.a31*b.a21 + a.a32*b.a22 + a.a33*b.a23 + a.a34*b.a24;
+        c.a33 = a.a31*b.a31 + a.a32*b.a32 + a.a33*b.a33 + a.a34*b.a34;
+        c.a34 = a.a31*b.a41 + a.a32*b.a42 + a.a33*b.a43 + a.a34*b.a44;
+        c.a41 = a.a41*b.a11 + a.a42*b.a12 + a.a43*b.a13 + a.a44*b.a14;
+        c.a42 = a.a41*b.a21 + a.a42*b.a22 + a.a43*b.a23 + a.a44*b.a24;
+        c.a43 = a.a41*b.a31 + a.a42*b.a32 + a.a43*b.a33 + a.a44*b.a34;
+        c.a44 = a.a41*b.a41 + a.a42*b.a42 + a.a43*b.a43 + a.a44*b.a44;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c += a * b <br>
+     * <br>
+     * c<sub>ij</sub> += ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAdd( FixedMatrix4x4_64F a , FixedMatrix4x4_64F b , FixedMatrix4x4_64F c) {
+        c.a11 += a.a11*b.a11 + a.a12*b.a21 + a.a13*b.a31 + a.a14*b.a41;
+        c.a12 += a.a11*b.a12 + a.a12*b.a22 + a.a13*b.a32 + a.a14*b.a42;
+        c.a13 += a.a11*b.a13 + a.a12*b.a23 + a.a13*b.a33 + a.a14*b.a43;
+        c.a14 += a.a11*b.a14 + a.a12*b.a24 + a.a13*b.a34 + a.a14*b.a44;
+        c.a21 += a.a21*b.a11 + a.a22*b.a21 + a.a23*b.a31 + a.a24*b.a41;
+        c.a22 += a.a21*b.a12 + a.a22*b.a22 + a.a23*b.a32 + a.a24*b.a42;
+        c.a23 += a.a21*b.a13 + a.a22*b.a23 + a.a23*b.a33 + a.a24*b.a43;
+        c.a24 += a.a21*b.a14 + a.a22*b.a24 + a.a23*b.a34 + a.a24*b.a44;
+        c.a31 += a.a31*b.a11 + a.a32*b.a21 + a.a33*b.a31 + a.a34*b.a41;
+        c.a32 += a.a31*b.a12 + a.a32*b.a22 + a.a33*b.a32 + a.a34*b.a42;
+        c.a33 += a.a31*b.a13 + a.a32*b.a23 + a.a33*b.a33 + a.a34*b.a43;
+        c.a34 += a.a31*b.a14 + a.a32*b.a24 + a.a33*b.a34 + a.a34*b.a44;
+        c.a41 += a.a41*b.a11 + a.a42*b.a21 + a.a43*b.a31 + a.a44*b.a41;
+        c.a42 += a.a41*b.a12 + a.a42*b.a22 + a.a43*b.a32 + a.a44*b.a42;
+        c.a43 += a.a41*b.a13 + a.a42*b.a23 + a.a43*b.a33 + a.a44*b.a43;
+        c.a44 += a.a41*b.a14 + a.a42*b.a24 + a.a43*b.a34 + a.a44*b.a44;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c += a<sup>T</sup> * b <br>
+     * <br>
+     * c<sub>ij</sub> += ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAddTransA( FixedMatrix4x4_64F a , FixedMatrix4x4_64F b , FixedMatrix4x4_64F c) {
+        c.a11 += a.a11*b.a11 + a.a21*b.a21 + a.a31*b.a31 + a.a41*b.a41;
+        c.a12 += a.a11*b.a12 + a.a21*b.a22 + a.a31*b.a32 + a.a41*b.a42;
+        c.a13 += a.a11*b.a13 + a.a21*b.a23 + a.a31*b.a33 + a.a41*b.a43;
+        c.a14 += a.a11*b.a14 + a.a21*b.a24 + a.a31*b.a34 + a.a41*b.a44;
+        c.a21 += a.a12*b.a11 + a.a22*b.a21 + a.a32*b.a31 + a.a42*b.a41;
+        c.a22 += a.a12*b.a12 + a.a22*b.a22 + a.a32*b.a32 + a.a42*b.a42;
+        c.a23 += a.a12*b.a13 + a.a22*b.a23 + a.a32*b.a33 + a.a42*b.a43;
+        c.a24 += a.a12*b.a14 + a.a22*b.a24 + a.a32*b.a34 + a.a42*b.a44;
+        c.a31 += a.a13*b.a11 + a.a23*b.a21 + a.a33*b.a31 + a.a43*b.a41;
+        c.a32 += a.a13*b.a12 + a.a23*b.a22 + a.a33*b.a32 + a.a43*b.a42;
+        c.a33 += a.a13*b.a13 + a.a23*b.a23 + a.a33*b.a33 + a.a43*b.a43;
+        c.a34 += a.a13*b.a14 + a.a23*b.a24 + a.a33*b.a34 + a.a43*b.a44;
+        c.a41 += a.a14*b.a11 + a.a24*b.a21 + a.a34*b.a31 + a.a44*b.a41;
+        c.a42 += a.a14*b.a12 + a.a24*b.a22 + a.a34*b.a32 + a.a44*b.a42;
+        c.a43 += a.a14*b.a13 + a.a24*b.a23 + a.a34*b.a33 + a.a44*b.a43;
+        c.a44 += a.a14*b.a14 + a.a24*b.a24 + a.a34*b.a34 + a.a44*b.a44;
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c += a<sup>T</sup> * b<sup>T</sup><br>
+     * c<sub>ij</sub> += ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>jk</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAddTransAB( FixedMatrix4x4_64F a , FixedMatrix4x4_64F b , FixedMatrix4x4_64F c) {
+        c.a11 += a.a11*b.a11 + a.a21*b.a12 + a.a31*b.a13 + a.a41*b.a14;
+        c.a12 += a.a11*b.a21 + a.a21*b.a22 + a.a31*b.a23 + a.a41*b.a24;
+        c.a13 += a.a11*b.a31 + a.a21*b.a32 + a.a31*b.a33 + a.a41*b.a34;
+        c.a14 += a.a11*b.a41 + a.a21*b.a42 + a.a31*b.a43 + a.a41*b.a44;
+        c.a21 += a.a12*b.a11 + a.a22*b.a12 + a.a32*b.a13 + a.a42*b.a14;
+        c.a22 += a.a12*b.a21 + a.a22*b.a22 + a.a32*b.a23 + a.a42*b.a24;
+        c.a23 += a.a12*b.a31 + a.a22*b.a32 + a.a32*b.a33 + a.a42*b.a34;
+        c.a24 += a.a12*b.a41 + a.a22*b.a42 + a.a32*b.a43 + a.a42*b.a44;
+        c.a31 += a.a13*b.a11 + a.a23*b.a12 + a.a33*b.a13 + a.a43*b.a14;
+        c.a32 += a.a13*b.a21 + a.a23*b.a22 + a.a33*b.a23 + a.a43*b.a24;
+        c.a33 += a.a13*b.a31 + a.a23*b.a32 + a.a33*b.a33 + a.a43*b.a34;
+        c.a34 += a.a13*b.a41 + a.a23*b.a42 + a.a33*b.a43 + a.a43*b.a44;
+        c.a41 += a.a14*b.a11 + a.a24*b.a12 + a.a34*b.a13 + a.a44*b.a14;
+        c.a42 += a.a14*b.a21 + a.a24*b.a22 + a.a34*b.a23 + a.a44*b.a24;
+        c.a43 += a.a14*b.a31 + a.a24*b.a32 + a.a34*b.a33 + a.a44*b.a34;
+        c.a44 += a.a14*b.a41 + a.a24*b.a42 + a.a34*b.a43 + a.a44*b.a44;
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c += a * b<sup>T</sup> <br>
+     * c<sub>ij</sub> += ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>jk</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAddTransB( FixedMatrix4x4_64F a , FixedMatrix4x4_64F b , FixedMatrix4x4_64F c) {
+        c.a11 += a.a11*b.a11 + a.a12*b.a12 + a.a13*b.a13 + a.a14*b.a14;
+        c.a12 += a.a11*b.a21 + a.a12*b.a22 + a.a13*b.a23 + a.a14*b.a24;
+        c.a13 += a.a11*b.a31 + a.a12*b.a32 + a.a13*b.a33 + a.a14*b.a34;
+        c.a14 += a.a11*b.a41 + a.a12*b.a42 + a.a13*b.a43 + a.a14*b.a44;
+        c.a21 += a.a21*b.a11 + a.a22*b.a12 + a.a23*b.a13 + a.a24*b.a14;
+        c.a22 += a.a21*b.a21 + a.a22*b.a22 + a.a23*b.a23 + a.a24*b.a24;
+        c.a23 += a.a21*b.a31 + a.a22*b.a32 + a.a23*b.a33 + a.a24*b.a34;
+        c.a24 += a.a21*b.a41 + a.a22*b.a42 + a.a23*b.a43 + a.a24*b.a44;
+        c.a31 += a.a31*b.a11 + a.a32*b.a12 + a.a33*b.a13 + a.a34*b.a14;
+        c.a32 += a.a31*b.a21 + a.a32*b.a22 + a.a33*b.a23 + a.a34*b.a24;
+        c.a33 += a.a31*b.a31 + a.a32*b.a32 + a.a33*b.a33 + a.a34*b.a34;
+        c.a34 += a.a31*b.a41 + a.a32*b.a42 + a.a33*b.a43 + a.a34*b.a44;
+        c.a41 += a.a41*b.a11 + a.a42*b.a12 + a.a43*b.a13 + a.a44*b.a14;
+        c.a42 += a.a41*b.a21 + a.a42*b.a22 + a.a43*b.a23 + a.a44*b.a24;
+        c.a43 += a.a41*b.a31 + a.a42*b.a32 + a.a43*b.a33 + a.a44*b.a34;
+        c.a44 += a.a41*b.a41 + a.a42*b.a42 + a.a43*b.a43 + a.a44*b.a44;
+    }
+
+    /**
+     * <p>Performs matrix to vector multiplication:<br>
+     * <br>
+     * c = a * b <br>
+     * <br>
+     * c<sub>i</sub> = ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>k</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right vector in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void mult( FixedMatrix4x4_64F a , FixedMatrix4_64F b , FixedMatrix4_64F c) {
+        c.a1 = a.a11*b.a1 + a.a12*b.a2 + a.a13*b.a3 + a.a14*b.a4;
+        c.a2 = a.a21*b.a1 + a.a22*b.a2 + a.a23*b.a3 + a.a24*b.a4;
+        c.a3 = a.a31*b.a1 + a.a32*b.a2 + a.a33*b.a3 + a.a34*b.a4;
+        c.a4 = a.a41*b.a1 + a.a42*b.a2 + a.a43*b.a3 + a.a44*b.a4;
+    }
+
+    /**
+     * <p>Performs vector to matrix multiplication:<br>
+     * <br>
+     * c = a * b <br>
+     * <br>
+     * c<sub>j</sub> = ∑<sub>k=1:n</sub> { b<sub>k</sub> * a<sub>kj</sub> }
+     * </p>
+     *
+     * @param a The left vector in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void mult( FixedMatrix4_64F a , FixedMatrix4x4_64F b , FixedMatrix4_64F c) {
+        c.a1 = a.a1*b.a11 + a.a2*b.a21 + a.a3*b.a31 + a.a4*b.a41;
+        c.a2 = a.a1*b.a12 + a.a2*b.a22 + a.a3*b.a32 + a.a4*b.a42;
+        c.a3 = a.a1*b.a13 + a.a2*b.a23 + a.a3*b.a33 + a.a4*b.a43;
+        c.a4 = a.a1*b.a14 + a.a2*b.a24 + a.a3*b.a34 + a.a4*b.a44;
+    }
+
+    /**
+     * <p>Performs the vector dot product:<br>
+     * <br>
+     * c = a * b <br>
+     * <br>
+     * c> = ∑<sub>k=1:n</sub> { b<sub>k</sub> * a<sub>k</sub> }
+     * </p>
+     *
+     * @param a The left vector in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @return The dot product
+     */
+    public static double dot( FixedMatrix4_64F a , FixedMatrix4_64F b ) {
+        return a.a1*b.a1 + a.a2*b.a2 + a.a3*b.a3 + a.a4*b.a4;
+    }
+
+    /**
+     * Sets all the diagonal elements equal to one and everything else equal to zero.
+     * If this is a square matrix then it will be an identity matrix.
+     *
+     * @param a A matrix.
+     */
+    public static void setIdentity( FixedMatrix4x4_64F a ) {
+        a.a11 = 1; a.a21 = 0; a.a31 = 0; a.a41 = 0;
+        a.a12 = 0; a.a22 = 1; a.a32 = 0; a.a42 = 0;
+        a.a13 = 0; a.a23 = 0; a.a33 = 1; a.a43 = 0;
+        a.a14 = 0; a.a24 = 0; a.a34 = 0; a.a44 = 1;
+    }
+
+    /**
+     * Inverts matrix 'a' using minor matrices and stores the results in 'inv'.  Scaling is applied to improve
+     * stability against overflow and underflow.
+     *
+     * WARNING: Potentially less stable than using LU decomposition.
+     *
+     * @param a Input matrix. Not modified.
+     * @param inv Inverted output matrix.  Modified.
+     * @return true if it was successful or false if it failed.  Not always reliable.
+     */
+    public static boolean invert( FixedMatrix4x4_64F a , FixedMatrix4x4_64F inv ) {
+
+        double scale = 1.0/elementMaxAbs(a);
+
+        double a11 = a.a11*scale;
+        double a12 = a.a12*scale;
+        double a13 = a.a13*scale;
+        double a14 = a.a14*scale;
+        double a21 = a.a21*scale;
+        double a22 = a.a22*scale;
+        double a23 = a.a23*scale;
+        double a24 = a.a24*scale;
+        double a31 = a.a31*scale;
+        double a32 = a.a32*scale;
+        double a33 = a.a33*scale;
+        double a34 = a.a34*scale;
+        double a41 = a.a41*scale;
+        double a42 = a.a42*scale;
+        double a43 = a.a43*scale;
+        double a44 = a.a44*scale;
+
+        double m11 =  + a22*(a33*a44 - a34*a43) - a23*(a32*a44 - a34*a42) + a24*(a32*a43 - a33*a42);
+        double m12 = -(  + a21*(a33*a44 - a34*a43) - a23*(a31*a44 - a34*a41) + a24*(a31*a43 - a33*a41));
+        double m13 =  + a21*(a32*a44 - a34*a42) - a22*(a31*a44 - a34*a41) + a24*(a31*a42 - a32*a41);
+        double m14 = -(  + a21*(a32*a43 - a33*a42) - a22*(a31*a43 - a33*a41) + a23*(a31*a42 - a32*a41));
+        double m21 = -(  + a12*(a33*a44 - a34*a43) - a13*(a32*a44 - a34*a42) + a14*(a32*a43 - a33*a42));
+        double m22 =  + a11*(a33*a44 - a34*a43) - a13*(a31*a44 - a34*a41) + a14*(a31*a43 - a33*a41);
+        double m23 = -(  + a11*(a32*a44 - a34*a42) - a12*(a31*a44 - a34*a41) + a14*(a31*a42 - a32*a41));
+        double m24 =  + a11*(a32*a43 - a33*a42) - a12*(a31*a43 - a33*a41) + a13*(a31*a42 - a32*a41);
+        double m31 =  + a12*(a23*a44 - a24*a43) - a13*(a22*a44 - a24*a42) + a14*(a22*a43 - a23*a42);
+        double m32 = -(  + a11*(a23*a44 - a24*a43) - a13*(a21*a44 - a24*a41) + a14*(a21*a43 - a23*a41));
+        double m33 =  + a11*(a22*a44 - a24*a42) - a12*(a21*a44 - a24*a41) + a14*(a21*a42 - a22*a41);
+        double m34 = -(  + a11*(a22*a43 - a23*a42) - a12*(a21*a43 - a23*a41) + a13*(a21*a42 - a22*a41));
+        double m41 = -(  + a12*(a23*a34 - a24*a33) - a13*(a22*a34 - a24*a32) + a14*(a22*a33 - a23*a32));
+        double m42 =  + a11*(a23*a34 - a24*a33) - a13*(a21*a34 - a24*a31) + a14*(a21*a33 - a23*a31);
+        double m43 = -(  + a11*(a22*a34 - a24*a32) - a12*(a21*a34 - a24*a31) + a14*(a21*a32 - a22*a31));
+        double m44 =  + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31);
+
+        double det = (a11*m11 + a12*m12 + a13*m13 + a14*m14)/scale;
+
+        inv.a11 = m11/det;
+        inv.a12 = m21/det;
+        inv.a13 = m31/det;
+        inv.a14 = m41/det;
+        inv.a21 = m12/det;
+        inv.a22 = m22/det;
+        inv.a23 = m32/det;
+        inv.a24 = m42/det;
+        inv.a31 = m13/det;
+        inv.a32 = m23/det;
+        inv.a33 = m33/det;
+        inv.a34 = m43/det;
+        inv.a41 = m14/det;
+        inv.a42 = m24/det;
+        inv.a43 = m34/det;
+        inv.a44 = m44/det;
+
+        return !Double.isNaN(det) && !Double.isInfinite(det);
+    }
+
+    /**
+     * Computes the determinant using minor matrices.
+     * <p></p>
+     * WARNING: Potentially less stable than using LU decomposition.
+     *
+     * @param mat Input matrix.  Not modified.
+     * @return The determinant.
+     */
+    public static double det( FixedMatrix4x4_64F mat ) {
+
+        double  a11 = mat.a22;
+        double  a12 = mat.a23;
+        double  a13 = mat.a24;
+        double  a21 = mat.a32;
+        double  a22 = mat.a33;
+        double  a23 = mat.a34;
+        double  a31 = mat.a42;
+        double  a32 = mat.a43;
+        double  a33 = mat.a44;
+
+        double ret = 0;
+        ret += mat.a11 * ( + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31));
+        a11 = mat.a21;
+        a21 = mat.a31;
+        a31 = mat.a41;
+        ret -= mat.a12 * ( + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31));
+        a12 = mat.a22;
+        a22 = mat.a32;
+        a32 = mat.a42;
+        ret += mat.a13 * ( + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31));
+        a13 = mat.a23;
+        a23 = mat.a33;
+        a33 = mat.a43;
+        ret -= mat.a14 * ( + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31));
+
+        return ret;
+    }
+
+    /**
+     * <p>
+     * This computes the trace of the matrix:<br>
+     * <br>
+     * trace = ∑<sub>i=1:n</sub> { a<sub>ii</sub> }
+     * </p>
+     * <p>
+     * The trace is only defined for square matrices.
+     * </p>
+     *
+     * @param a A square matrix.  Not modified.
+     */
+    public static double trace( FixedMatrix4x4_64F a ) {
+        return a.a11 + a.a21 + a.a31 + a.a41;
+    }
+
+    /**
+     * <p>
+     * Extracts all diagonal elements from 'input' and places them inside the 'out' vector. Elements
+     * are in sequential order.
+     * </p>
+     *
+     *
+     * @param input Matrix.  Not modified.
+     * @param out Vector containing diagonal elements.  Modified.
+     */
+    public static void diag( FixedMatrix4x4_64F input , FixedMatrix4_64F out ) {
+        out.a1 = input.a11;
+        out.a2 = input.a22;
+        out.a3 = input.a33;
+        out.a4 = input.a44;
+    }
+
+    /**
+     * <p>
+     * Returns the value of the element in the matrix that has the largest value.<br>
+     * <br>
+     * Max{ a<sub>ij</sub> } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max element value of the matrix.
+     */
+    public static double elementMax( FixedMatrix4x4_64F a ) {
+        double max = a.a11;
+        max = Math.max(max,a.a12);
+        max = Math.max(max,a.a13);
+        max = Math.max(max,a.a14);
+        max = Math.max(max,a.a21);
+        max = Math.max(max,a.a22);
+        max = Math.max(max,a.a23);
+        max = Math.max(max,a.a24);
+        max = Math.max(max,a.a31);
+        max = Math.max(max,a.a32);
+        max = Math.max(max,a.a33);
+        max = Math.max(max,a.a34);
+        max = Math.max(max,a.a41);
+        max = Math.max(max,a.a42);
+        max = Math.max(max,a.a43);
+        max = Math.max(max,a.a44);
+
+        return max;
+    }
+
+    /**
+     * <p>
+     * Returns the value of the element in the vector that has the largest value.<br>
+     * <br>
+     * Max{ a<sub>i</sub> } for all i<br>
+     * </p>
+     *
+     * @param a A vector. Not modified.
+     * @return The max element value of the matrix.
+     */
+    public static double elementMax( FixedMatrix4_64F a ) {
+        double max = a.a1;
+        max = Math.max(max,a.a2);
+        max = Math.max(max,a.a3);
+        max = Math.max(max,a.a4);
+
+        return max;
+    }
+
+    /**
+     * <p>
+     * Returns the absolute value of the element in the matrix that has the largest absolute value.<br>
+     * <br>
+     * Max{ |a<sub>ij</sub>| } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max abs element value of the matrix.
+     */
+    public static double elementMaxAbs( FixedMatrix4x4_64F a ) {
+        double max = a.a11;
+        max = Math.max(max,Math.abs(a.a12));
+        max = Math.max(max,Math.abs(a.a13));
+        max = Math.max(max,Math.abs(a.a14));
+        max = Math.max(max,Math.abs(a.a21));
+        max = Math.max(max,Math.abs(a.a22));
+        max = Math.max(max,Math.abs(a.a23));
+        max = Math.max(max,Math.abs(a.a24));
+        max = Math.max(max,Math.abs(a.a31));
+        max = Math.max(max,Math.abs(a.a32));
+        max = Math.max(max,Math.abs(a.a33));
+        max = Math.max(max,Math.abs(a.a34));
+        max = Math.max(max,Math.abs(a.a41));
+        max = Math.max(max,Math.abs(a.a42));
+        max = Math.max(max,Math.abs(a.a43));
+        max = Math.max(max,Math.abs(a.a44));
+
+        return max;
+    }
+
+    /**
+     * <p>
+     * Returns the absolute value of the element in the vector that has the largest absolute value.<br>
+     * <br>
+     * Max{ |a<sub>i</sub>| } for all i<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max abs element value of the vector.
+     */
+    public static double elementMaxAbs( FixedMatrix4_64F a ) {
+        double max = a.a1;
+        max = Math.max(max,Math.abs(a.a2));
+        max = Math.max(max,Math.abs(a.a3));
+        max = Math.max(max,Math.abs(a.a4));
+
+        return max;
+    }
+
+    /**
+     * <p>
+     * Returns the value of the element in the matrix that has the minimum value.<br>
+     * <br>
+     * Min{ a<sub>ij</sub> } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The value of element in the matrix with the minimum value.
+     */
+    public static double elementMin( FixedMatrix4x4_64F a ) {
+        double min = a.a11;
+        min = Math.min(min, a.a12);
+        min = Math.min(min, a.a13);
+        min = Math.min(min, a.a14);
+        min = Math.min(min, a.a21);
+        min = Math.min(min, a.a22);
+        min = Math.min(min, a.a23);
+        min = Math.min(min, a.a24);
+        min = Math.min(min, a.a31);
+        min = Math.min(min, a.a32);
+        min = Math.min(min, a.a33);
+        min = Math.min(min, a.a34);
+        min = Math.min(min, a.a41);
+        min = Math.min(min, a.a42);
+        min = Math.min(min, a.a43);
+        min = Math.min(min, a.a44);
+
+        return min;
+    }
+
+    /**
+     * <p>
+     * Returns the value of the element in the vector that has the minimum value.<br>
+     * <br>
+     * Min{ a<sub>i</sub> } for all<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The value of element in the vector with the minimum value.
+     */
+    public static double elementMin( FixedMatrix4_64F a ) {
+        double min = a.a1;
+        min = Math.min(min, a.a2);
+        min = Math.min(min, a.a3);
+        min = Math.min(min, a.a4);
+
+        return min;
+    }
+
+    /**
+     * <p>
+     * Returns the absolute value of the element in the matrix that has the smallest absolute value.<br>
+     * <br>
+     * Min{ |a<sub>ij</sub>| } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max element value of the matrix.
+     */
+    public static double elementMinAbs( FixedMatrix4x4_64F a ) {
+        double min = a.a11;
+        min = Math.min(min,Math.abs(a.a12));
+        min = Math.min(min,Math.abs(a.a13));
+        min = Math.min(min,Math.abs(a.a14));
+        min = Math.min(min,Math.abs(a.a21));
+        min = Math.min(min,Math.abs(a.a22));
+        min = Math.min(min,Math.abs(a.a23));
+        min = Math.min(min,Math.abs(a.a24));
+        min = Math.min(min,Math.abs(a.a31));
+        min = Math.min(min,Math.abs(a.a32));
+        min = Math.min(min,Math.abs(a.a33));
+        min = Math.min(min,Math.abs(a.a34));
+        min = Math.min(min,Math.abs(a.a41));
+        min = Math.min(min,Math.abs(a.a42));
+        min = Math.min(min,Math.abs(a.a43));
+        min = Math.min(min,Math.abs(a.a44));
+
+        return min;
+    }
+
+    /**
+     * <p>
+     * Returns the absolute value of the element in the vector that has the smallest absolute value.<br>
+     * <br>
+     * Min{ |a<sub>i</sub>| } for all i<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max element value of the vector.
+     */
+    public static double elementMinAbs( FixedMatrix4_64F a ) {
+        double min = a.a1;
+        min = Math.min(min,Math.abs(a.a2));
+        min = Math.min(min,Math.abs(a.a3));
+        min = Math.min(min,Math.abs(a.a4));
+
+        return min;
+    }
+
+    /**
+     * <p>Performs an element by element multiplication operation:<br>
+     * <br>
+     * a<sub>ij</sub> = a<sub>ij</sub> * b<sub>ij</sub> <br>
+     * </p>
+     * @param a The left matrix in the multiplication operation. Modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     */
+    public static void elementMult( FixedMatrix4x4_64F a , FixedMatrix4x4_64F b) {
+        a.a11 *= b.a11; a.a12 *= b.a12; a.a13 *= b.a13; a.a14 *= b.a14;
+        a.a21 *= b.a21; a.a22 *= b.a22; a.a23 *= b.a23; a.a24 *= b.a24;
+        a.a31 *= b.a31; a.a32 *= b.a32; a.a33 *= b.a33; a.a34 *= b.a34;
+        a.a41 *= b.a41; a.a42 *= b.a42; a.a43 *= b.a43; a.a44 *= b.a44;
+    }
+
+    /**
+     * <p>Performs an element by element multiplication operation:<br>
+     * <br>
+     * a<sub>i</sub> = a<sub>i</sub> * b<sub>i</sub> <br>
+     * </p>
+     * @param a The left vector in the multiplication operation. Modified.
+     * @param b The right vector in the multiplication operation. Not modified.
+     */
+    public static void elementMult( FixedMatrix4_64F a , FixedMatrix4_64F b) {
+        a.a1 *= b.a1;
+        a.a2 *= b.a2;
+        a.a3 *= b.a3;
+        a.a4 *= b.a4;
+    }
+
+    /**
+     * <p>Performs an element by element multiplication operation:<br>
+     * <br>
+     * c<sub>ij</sub> = a<sub>ij</sub> * b<sub>ij</sub> <br>
+     * </p>
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void elementMult( FixedMatrix4x4_64F a , FixedMatrix4x4_64F b , FixedMatrix4x4_64F c ) {
+        c.a11 = a.a11*b.a11; c.a12 = a.a12*b.a12; c.a13 = a.a13*b.a13; c.a14 = a.a14*b.a14;
+        c.a21 = a.a21*b.a21; c.a22 = a.a22*b.a22; c.a23 = a.a23*b.a23; c.a24 = a.a24*b.a24;
+        c.a31 = a.a31*b.a31; c.a32 = a.a32*b.a32; c.a33 = a.a33*b.a33; c.a34 = a.a34*b.a34;
+        c.a41 = a.a41*b.a41; c.a42 = a.a42*b.a42; c.a43 = a.a43*b.a43; c.a44 = a.a44*b.a44;
+    }
+
+    /**
+     * <p>Performs an element by element multiplication operation:<br>
+     * <br>
+     * c<sub>i</sub> = a<sub>i</sub> * b<sub>j</sub> <br>
+     * </p>
+     * @param a The left vector in the multiplication operation. Not modified.
+     * @param b The right vector in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void elementMult( FixedMatrix4_64F a , FixedMatrix4_64F b , FixedMatrix4_64F c ) {
+        c.a1 = a.a1*b.a1;
+        c.a2 = a.a2*b.a2;
+        c.a3 = a.a3*b.a3;
+        c.a4 = a.a4*b.a4;
+    }
+
+    /**
+     * <p>Performs an element by element division operation:<br>
+     * <br>
+     * a<sub>ij</sub> = a<sub>ij</sub> / b<sub>ij</sub> <br>
+     * </p>
+     * @param a The left matrix in the division operation. Modified.
+     * @param b The right matrix in the division operation. Not modified.
+     */
+    public static void elementDiv( FixedMatrix4x4_64F a , FixedMatrix4x4_64F b) {
+        a.a11 /= b.a11; a.a12 /= b.a12; a.a13 /= b.a13; a.a14 /= b.a14;
+        a.a21 /= b.a21; a.a22 /= b.a22; a.a23 /= b.a23; a.a24 /= b.a24;
+        a.a31 /= b.a31; a.a32 /= b.a32; a.a33 /= b.a33; a.a34 /= b.a34;
+        a.a41 /= b.a41; a.a42 /= b.a42; a.a43 /= b.a43; a.a44 /= b.a44;
+    }
+
+    /**
+     * <p>Performs an element by element division operation:<br>
+     * <br>
+     * a<sub>i</sub> = a<sub>i</sub> / b<sub>i</sub> <br>
+     * </p>
+     * @param a The left vector in the division operation. Modified.
+     * @param b The right vector in the division operation. Not modified.
+     */
+    public static void elementDiv( FixedMatrix4_64F a , FixedMatrix4_64F b) {
+        a.a1 /= b.a1;
+        a.a2 /= b.a2;
+        a.a3 /= b.a3;
+        a.a4 /= b.a4;
+    }
+
+    /**
+     * <p>Performs an element by element division operation:<br>
+     * <br>
+     * c<sub>ij</sub> = a<sub>ij</sub> / b<sub>ij</sub> <br>
+     * </p>
+     * @param a The left matrix in the division operation. Not modified.
+     * @param b The right matrix in the division operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void elementDiv( FixedMatrix4x4_64F a , FixedMatrix4x4_64F b , FixedMatrix4x4_64F c ) {
+        c.a11 = a.a11/b.a11; c.a12 = a.a12/b.a12; c.a13 = a.a13/b.a13; c.a14 = a.a14/b.a14;
+        c.a21 = a.a21/b.a21; c.a22 = a.a22/b.a22; c.a23 = a.a23/b.a23; c.a24 = a.a24/b.a24;
+        c.a31 = a.a31/b.a31; c.a32 = a.a32/b.a32; c.a33 = a.a33/b.a33; c.a34 = a.a34/b.a34;
+        c.a41 = a.a41/b.a41; c.a42 = a.a42/b.a42; c.a43 = a.a43/b.a43; c.a44 = a.a44/b.a44;
+    }
+
+    /**
+     * <p>Performs an element by element division operation:<br>
+     * <br>
+     * c<sub>i</sub> = a<sub>i</sub> / b<sub>i</sub> <br>
+     * </p>
+     * @param a The left vector in the division operation. Not modified.
+     * @param b The right vector in the division operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void elementDiv( FixedMatrix4_64F a , FixedMatrix4_64F b , FixedMatrix4_64F c ) {
+        c.a1 = a.a1/b.a1;
+        c.a2 = a.a2/b.a2;
+        c.a3 = a.a3/b.a3;
+        c.a4 = a.a4/b.a4;
+    }
+
+    /**
+     * <p>
+     * Performs an in-place element by element scalar multiplication.<br>
+     * <br>
+     * a<sub>ij</sub> = α*a<sub>ij</sub>
+     * </p>
+     *
+     * @param a The matrix that is to be scaled.  Modified.
+     * @param alpha the amount each element is multiplied by.
+     */
+    public static void scale( double alpha , FixedMatrix4x4_64F a ) {
+        a.a11 *= alpha; a.a12 *= alpha; a.a13 *= alpha; a.a14 *= alpha;
+        a.a21 *= alpha; a.a22 *= alpha; a.a23 *= alpha; a.a24 *= alpha;
+        a.a31 *= alpha; a.a32 *= alpha; a.a33 *= alpha; a.a34 *= alpha;
+        a.a41 *= alpha; a.a42 *= alpha; a.a43 *= alpha; a.a44 *= alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an in-place element by element scalar multiplication.<br>
+     * <br>
+     * a<sub>ij</sub> = α*a<sub>ij</sub>
+     * </p>
+     *
+     * @param a The vector that is to be scaled.  Modified.
+     * @param alpha the amount each element is multiplied by.
+     */
+    public static void scale( double alpha , FixedMatrix4_64F a ) {
+        a.a1 *= alpha;
+        a.a2 *= alpha;
+        a.a3 *= alpha;
+        a.a4 *= alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an element by element scalar multiplication.<br>
+     * <br>
+     * b<sub>ij</sub> = α*a<sub>ij</sub>
+     * </p>
+     *
+     * @param alpha the amount each element is multiplied by.
+     * @param a The matrix that is to be scaled.  Not modified.
+     * @param b Where the scaled matrix is stored. Modified.
+     */
+    public static void scale( double alpha , FixedMatrix4x4_64F a , FixedMatrix4x4_64F b ) {
+        b.a11 = a.a11*alpha; b.a12 = a.a12*alpha; b.a13 = a.a13*alpha; b.a14 = a.a14*alpha;
+        b.a21 = a.a21*alpha; b.a22 = a.a22*alpha; b.a23 = a.a23*alpha; b.a24 = a.a24*alpha;
+        b.a31 = a.a31*alpha; b.a32 = a.a32*alpha; b.a33 = a.a33*alpha; b.a34 = a.a34*alpha;
+        b.a41 = a.a41*alpha; b.a42 = a.a42*alpha; b.a43 = a.a43*alpha; b.a44 = a.a44*alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an element by element scalar multiplication.<br>
+     * <br>
+     * b<sub>i</sub> = α*a<sub>i</sub>
+     * </p>
+     *
+     * @param alpha the amount each element is multiplied by.
+     * @param a The vector that is to be scaled.  Not modified.
+     * @param b Where the scaled matrix is stored. Modified.
+     */
+    public static void scale( double alpha , FixedMatrix4_64F a , FixedMatrix4_64F b ) {
+        b.a1 = a.a1*alpha;
+        b.a2 = a.a2*alpha;
+        b.a3 = a.a3*alpha;
+        b.a4 = a.a4*alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an in-place element by element scalar division. Scalar denominator.<br>
+     * <br>
+     * a<sub>ij</sub> = a<sub>ij</sub>/α
+     * </p>
+     *
+     * @param a The matrix whose elements are to be divided.  Modified.
+     * @param alpha the amount each element is divided by.
+     */
+    public static void divide( FixedMatrix4x4_64F a , double alpha ) {
+        a.a11 /= alpha; a.a12 /= alpha; a.a13 /= alpha; a.a14 /= alpha;
+        a.a21 /= alpha; a.a22 /= alpha; a.a23 /= alpha; a.a24 /= alpha;
+        a.a31 /= alpha; a.a32 /= alpha; a.a33 /= alpha; a.a34 /= alpha;
+        a.a41 /= alpha; a.a42 /= alpha; a.a43 /= alpha; a.a44 /= alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an in-place element by element scalar division. Scalar denominator.<br>
+     * <br>
+     * a<sub>i</sub> = a<sub>i</sub>/α
+     * </p>
+     *
+     * @param a The vector whose elements are to be divided.  Modified.
+     * @param alpha the amount each element is divided by.
+     */
+    public static void divide( FixedMatrix4_64F a , double alpha ) {
+        a.a1 /= alpha;
+        a.a2 /= alpha;
+        a.a3 /= alpha;
+        a.a4 /= alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an element by element scalar division.  Scalar denominator.<br>
+     * <br>
+     * b<sub>ij</sub> = a<sub>ij</sub> /α
+     * </p>
+     *
+     * @param alpha the amount each element is divided by.
+     * @param a The matrix whose elements are to be divided.  Not modified.
+     * @param b Where the results are stored. Modified.
+     */
+    public static void divide( FixedMatrix4x4_64F a , double alpha , FixedMatrix4x4_64F b ) {
+        b.a11 = a.a11/alpha; b.a12 = a.a12/alpha; b.a13 = a.a13/alpha; b.a14 = a.a14/alpha;
+        b.a21 = a.a21/alpha; b.a22 = a.a22/alpha; b.a23 = a.a23/alpha; b.a24 = a.a24/alpha;
+        b.a31 = a.a31/alpha; b.a32 = a.a32/alpha; b.a33 = a.a33/alpha; b.a34 = a.a34/alpha;
+        b.a41 = a.a41/alpha; b.a42 = a.a42/alpha; b.a43 = a.a43/alpha; b.a44 = a.a44/alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an element by element scalar division.  Scalar denominator.<br>
+     * <br>
+     * b<sub>i</sub> = a<sub>i</sub> /α
+     * </p>
+     *
+     * @param alpha the amount each element is divided by.
+     * @param a The vector whose elements are to be divided.  Not modified.
+     * @param b Where the results are stored. Modified.
+     */
+    public static void divide( FixedMatrix4_64F a , double alpha , FixedMatrix4_64F b ) {
+        b.a1 = a.a1/alpha;
+        b.a2 = a.a2/alpha;
+        b.a3 = a.a3/alpha;
+        b.a4 = a.a4/alpha;
+    }
+
+    /**
+     * <p>
+     * Changes the sign of every element in the matrix.<br>
+     * <br>
+     * a<sub>ij</sub> = -a<sub>ij</sub>
+     * </p>
+     *
+     * @param a A matrix. Modified.
+     */
+    public static void changeSign( FixedMatrix4x4_64F a )
+    {
+        a.a11 = -a.a11; a.a12 = -a.a12; a.a13 = -a.a13; a.a14 = -a.a14;
+        a.a21 = -a.a21; a.a22 = -a.a22; a.a23 = -a.a23; a.a24 = -a.a24;
+        a.a31 = -a.a31; a.a32 = -a.a32; a.a33 = -a.a33; a.a34 = -a.a34;
+        a.a41 = -a.a41; a.a42 = -a.a42; a.a43 = -a.a43; a.a44 = -a.a44;
+    }
+
+    /**
+     * <p>
+     * Changes the sign of every element in the vector.<br>
+     * <br>
+     * a<sub>i</sub> = -a<sub>i</sub>
+     * </p>
+     *
+     * @param a A vector. Modified.
+     */
+    public static void changeSign( FixedMatrix4_64F a )
+    {
+        a.a1 = -a.a1;
+        a.a2 = -a.a2;
+        a.a3 = -a.a3;
+        a.a4 = -a.a4;
+    }
+
+    /**
+     * <p>
+     * Sets every element in the matrix to the specified value.<br>
+     * <br>
+     * a<sub>ij</sub> = value
+     * <p>
+     *
+     * @param a A matrix whose elements are about to be set. Modified.
+     * @param v The value each element will have.
+     */
+    public static void fill( FixedMatrix4x4_64F a , double v  ) {
+        a.a11 = v; a.a12 = v; a.a13 = v; a.a14 = v;
+        a.a21 = v; a.a22 = v; a.a23 = v; a.a24 = v;
+        a.a31 = v; a.a32 = v; a.a33 = v; a.a34 = v;
+        a.a41 = v; a.a42 = v; a.a43 = v; a.a44 = v;
+    }
+
+    /**
+     * <p>
+     * Sets every element in the vector to the specified value.<br>
+     * <br>
+     * a<sub>i</sub> = value
+     * <p>
+     *
+     * @param a A vector whose elements are about to be set. Modified.
+     * @param v The value each element will have.
+     */
+    public static void fill( FixedMatrix4_64F a , double v  ) {
+        a.a1 = v;
+        a.a2 = v;
+        a.a3 = v;
+        a.a4 = v;
+    }
+
+    /**
+     * Extracts the row from the matrix a.
+     * @param a Input matrix
+     * @param row Which row is to be extracted
+     * @param out output. Storage for the extracted row. If null then a new vector will be returned.
+     * @return The extracted row.
+     */
+    public static FixedMatrix4_64F extractRow( FixedMatrix4x4_64F a , int row , FixedMatrix4_64F out ) {
+        if( out == null) out = new FixedMatrix4_64F();
+        switch( row ) {
+            case 0:
+                out.a1 = a.a11;
+                out.a2 = a.a12;
+                out.a3 = a.a13;
+                out.a4 = a.a14;
+            break;
+            case 1:
+                out.a1 = a.a21;
+                out.a2 = a.a22;
+                out.a3 = a.a23;
+                out.a4 = a.a24;
+            break;
+            case 2:
+                out.a1 = a.a31;
+                out.a2 = a.a32;
+                out.a3 = a.a33;
+                out.a4 = a.a34;
+            break;
+            case 3:
+                out.a1 = a.a41;
+                out.a2 = a.a42;
+                out.a3 = a.a43;
+                out.a4 = a.a44;
+            break;
+            default:
+                throw new IllegalArgumentException("Out of bounds row.  row = "+row);
+        }
+        return out;
+    }
+
+    /**
+     * Extracts the column from the matrix a.
+     * @param a Input matrix
+     * @param column Which column is to be extracted
+     * @param out output. Storage for the extracted column. If null then a new vector will be returned.
+     * @return The extracted column.
+     */
+    public static FixedMatrix4_64F extractColumn( FixedMatrix4x4_64F a , int column , FixedMatrix4_64F out ) {
+        if( out == null) out = new FixedMatrix4_64F();
+        switch( column ) {
+            case 0:
+                out.a1 = a.a11;
+                out.a2 = a.a21;
+                out.a3 = a.a31;
+                out.a4 = a.a41;
+            break;
+            case 1:
+                out.a1 = a.a12;
+                out.a2 = a.a22;
+                out.a3 = a.a32;
+                out.a4 = a.a42;
+            break;
+            case 2:
+                out.a1 = a.a13;
+                out.a2 = a.a23;
+                out.a3 = a.a33;
+                out.a4 = a.a43;
+            break;
+            case 3:
+                out.a1 = a.a14;
+                out.a2 = a.a24;
+                out.a3 = a.a34;
+                out.a4 = a.a44;
+            break;
+            default:
+                throw new IllegalArgumentException("Out of bounds column.  column = "+column);
+        }
+        return out;
+    }
+
+}
+
diff --git a/main/dense64/src/org/ejml/alg/fixed/FixedOps5.java b/main/dense64/src/org/ejml/alg/fixed/FixedOps5.java
new file mode 100644
index 0000000..0f1cb82
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/fixed/FixedOps5.java
@@ -0,0 +1,1608 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+package org.ejml.alg.fixed;
+
+import org.ejml.data.FixedMatrix5_64F;
+import org.ejml.data.FixedMatrix5x5_64F;
+
+/**
+ * <p>Common matrix operations for fixed sized matrices which are 5 x 5 or 5 element vectors.</p>
+ * <p>DO NOT MODIFY.  Automatically generated code created by GenerateFixedOps</p>
+ *
+ * @author Peter Abeles
+ */
+public class FixedOps5 {
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a + b <br>
+     * c<sub>ij</sub> = a<sub>ij</sub> + b<sub>ij</sub> <br>
+     * </p>
+     *
+     * <p>
+     * Matrix C can be the same instance as Matrix A and/or B.
+     * </p>
+     *
+     * @param a A Matrix. Not modified.
+     * @param b A Matrix. Not modified.
+     * @param c A Matrix where the results are stored. Modified.
+     */
+    public static void add( FixedMatrix5x5_64F a , FixedMatrix5x5_64F b , FixedMatrix5x5_64F c ) {
+        c.a11 = a.a11 + b.a11;
+        c.a12 = a.a12 + b.a12;
+        c.a13 = a.a13 + b.a13;
+        c.a14 = a.a14 + b.a14;
+        c.a15 = a.a15 + b.a15;
+        c.a21 = a.a21 + b.a21;
+        c.a22 = a.a22 + b.a22;
+        c.a23 = a.a23 + b.a23;
+        c.a24 = a.a24 + b.a24;
+        c.a25 = a.a25 + b.a25;
+        c.a31 = a.a31 + b.a31;
+        c.a32 = a.a32 + b.a32;
+        c.a33 = a.a33 + b.a33;
+        c.a34 = a.a34 + b.a34;
+        c.a35 = a.a35 + b.a35;
+        c.a41 = a.a41 + b.a41;
+        c.a42 = a.a42 + b.a42;
+        c.a43 = a.a43 + b.a43;
+        c.a44 = a.a44 + b.a44;
+        c.a45 = a.a45 + b.a45;
+        c.a51 = a.a51 + b.a51;
+        c.a52 = a.a52 + b.a52;
+        c.a53 = a.a53 + b.a53;
+        c.a54 = a.a54 + b.a54;
+        c.a55 = a.a55 + b.a55;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a + b <br>
+     * c<sub>i</sub> = a<sub>i</sub> + b<sub>i</sub> <br>
+     * </p>
+     *
+     * <p>
+     * Vector C can be the same instance as Vector A and/or B.
+     * </p>
+     *
+     * @param a A Vector. Not modified.
+     * @param b A Vector. Not modified.
+     * @param c A Vector where the results are stored. Modified.
+     */
+    public static void add( FixedMatrix5_64F a , FixedMatrix5_64F b , FixedMatrix5_64F c ) {
+        c.a1 = a.a1 + b.a1;
+        c.a2 = a.a2 + b.a2;
+        c.a3 = a.a3 + b.a3;
+        c.a4 = a.a4 + b.a4;
+        c.a5 = a.a5 + b.a5;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * a = a + b <br>
+     * a<sub>ij</sub> = a<sub>ij</sub> + b<sub>ij</sub> <br>
+     * </p>
+     *
+     * @param a A Matrix. Modified.
+     * @param b A Matrix. Not modified.
+     */
+    public static void addEquals( FixedMatrix5x5_64F a , FixedMatrix5x5_64F b ) {
+        a.a11 += b.a11;
+        a.a12 += b.a12;
+        a.a13 += b.a13;
+        a.a14 += b.a14;
+        a.a15 += b.a15;
+        a.a21 += b.a21;
+        a.a22 += b.a22;
+        a.a23 += b.a23;
+        a.a24 += b.a24;
+        a.a25 += b.a25;
+        a.a31 += b.a31;
+        a.a32 += b.a32;
+        a.a33 += b.a33;
+        a.a34 += b.a34;
+        a.a35 += b.a35;
+        a.a41 += b.a41;
+        a.a42 += b.a42;
+        a.a43 += b.a43;
+        a.a44 += b.a44;
+        a.a45 += b.a45;
+        a.a51 += b.a51;
+        a.a52 += b.a52;
+        a.a53 += b.a53;
+        a.a54 += b.a54;
+        a.a55 += b.a55;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * a = a + b <br>
+     * a<sub>i</sub> = a<sub>i</sub> + b<sub>i</sub> <br>
+     * </p>
+     *
+     * @param a A Vector. Modified.
+     * @param b A Vector. Not modified.
+     */
+    public static void addEquals( FixedMatrix5_64F a , FixedMatrix5_64F b ) {
+        a.a1 += b.a1;
+        a.a2 += b.a2;
+        a.a3 += b.a3;
+        a.a4 += b.a4;
+        a.a5 += b.a5;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a - b <br>
+     * c<sub>ij</sub> = a<sub>ij</sub> - b<sub>ij</sub> <br>
+     * </p>
+     *
+     * <p>
+     * Matrix C can be the same instance as Matrix A and/or B.
+     * </p>
+     *
+     * @param a A Matrix. Not modified.
+     * @param b A Matrix. Not modified.
+     * @param c A Matrix where the results are stored. Modified.
+     */
+    public static void subtract( FixedMatrix5x5_64F a , FixedMatrix5x5_64F b , FixedMatrix5x5_64F c ) {
+        c.a11 = a.a11 - b.a11;
+        c.a12 = a.a12 - b.a12;
+        c.a13 = a.a13 - b.a13;
+        c.a14 = a.a14 - b.a14;
+        c.a15 = a.a15 - b.a15;
+        c.a21 = a.a21 - b.a21;
+        c.a22 = a.a22 - b.a22;
+        c.a23 = a.a23 - b.a23;
+        c.a24 = a.a24 - b.a24;
+        c.a25 = a.a25 - b.a25;
+        c.a31 = a.a31 - b.a31;
+        c.a32 = a.a32 - b.a32;
+        c.a33 = a.a33 - b.a33;
+        c.a34 = a.a34 - b.a34;
+        c.a35 = a.a35 - b.a35;
+        c.a41 = a.a41 - b.a41;
+        c.a42 = a.a42 - b.a42;
+        c.a43 = a.a43 - b.a43;
+        c.a44 = a.a44 - b.a44;
+        c.a45 = a.a45 - b.a45;
+        c.a51 = a.a51 - b.a51;
+        c.a52 = a.a52 - b.a52;
+        c.a53 = a.a53 - b.a53;
+        c.a54 = a.a54 - b.a54;
+        c.a55 = a.a55 - b.a55;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a - b <br>
+     * c<sub>i</sub> = a<sub>i</sub> - b<sub>i</sub> <br>
+     * </p>
+     *
+     * <p>
+     * Vector C can be the same instance as Vector A and/or B.
+     * </p>
+     *
+     * @param a A Vector. Not modified.
+     * @param b A Vector. Not modified.
+     * @param c A Vector where the results are stored. Modified.
+     */
+    public static void subtract( FixedMatrix5_64F a , FixedMatrix5_64F b , FixedMatrix5_64F c ) {
+        c.a1 = a.a1 - b.a1;
+        c.a2 = a.a2 - b.a2;
+        c.a3 = a.a3 - b.a3;
+        c.a4 = a.a4 - b.a4;
+        c.a5 = a.a5 - b.a5;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * a = a - b <br>
+     * a<sub>ij</sub> = a<sub>ij</sub> - b<sub>ij</sub> <br>
+     * </p>
+     *
+     * @param a A Matrix. Modified.
+     * @param b A Matrix. Not modified.
+     */
+    public static void subtractEquals( FixedMatrix5x5_64F a , FixedMatrix5x5_64F b ) {
+        a.a11 -= b.a11;
+        a.a12 -= b.a12;
+        a.a13 -= b.a13;
+        a.a14 -= b.a14;
+        a.a15 -= b.a15;
+        a.a21 -= b.a21;
+        a.a22 -= b.a22;
+        a.a23 -= b.a23;
+        a.a24 -= b.a24;
+        a.a25 -= b.a25;
+        a.a31 -= b.a31;
+        a.a32 -= b.a32;
+        a.a33 -= b.a33;
+        a.a34 -= b.a34;
+        a.a35 -= b.a35;
+        a.a41 -= b.a41;
+        a.a42 -= b.a42;
+        a.a43 -= b.a43;
+        a.a44 -= b.a44;
+        a.a45 -= b.a45;
+        a.a51 -= b.a51;
+        a.a52 -= b.a52;
+        a.a53 -= b.a53;
+        a.a54 -= b.a54;
+        a.a55 -= b.a55;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * a = a - b <br>
+     * a<sub>i</sub> = a<sub>i</sub> - b<sub>i</sub> <br>
+     * </p>
+     *
+     * @param a A Vector. Modified.
+     * @param b A Vector. Not modified.
+     */
+    public static void subtractEquals( FixedMatrix5_64F a , FixedMatrix5_64F b ) {
+        a.a1 -= b.a1;
+        a.a2 -= b.a2;
+        a.a3 -= b.a3;
+        a.a4 -= b.a4;
+        a.a5 -= b.a5;
+    }
+
+    /**
+     * Performs an in-place transpose.  This algorithm is only efficient for square
+     * matrices.
+     *
+     * @param m The matrix that is to be transposed. Modified.
+     */
+    public static void transpose( FixedMatrix5x5_64F m ) {
+        double tmp;
+        tmp = m.a12; m.a12 = m.a21; m.a21 = tmp;
+        tmp = m.a13; m.a13 = m.a31; m.a31 = tmp;
+        tmp = m.a14; m.a14 = m.a41; m.a41 = tmp;
+        tmp = m.a15; m.a15 = m.a51; m.a51 = tmp;
+        tmp = m.a23; m.a23 = m.a32; m.a32 = tmp;
+        tmp = m.a24; m.a24 = m.a42; m.a42 = tmp;
+        tmp = m.a25; m.a25 = m.a52; m.a52 = tmp;
+        tmp = m.a34; m.a34 = m.a43; m.a43 = tmp;
+        tmp = m.a35; m.a35 = m.a53; m.a53 = tmp;
+        tmp = m.a45; m.a45 = m.a54; m.a54 = tmp;
+    }
+
+    /**
+     * <p>
+     * Transposes matrix 'a' and stores the results in 'b':<br>
+     * <br>
+     * b<sub>ij</sub> = a<sub>ji</sub><br>
+     * where 'b' is the transpose of 'a'.
+     * </p>
+     *
+     * @param input The original matrix.  Not modified.
+     * @param output Where the transpose is stored. If null a new matrix is created. Modified.
+     * @return The transposed matrix.
+     */
+    public static FixedMatrix5x5_64F transpose( FixedMatrix5x5_64F input , FixedMatrix5x5_64F output ) {
+        if( input == null )
+            input = new FixedMatrix5x5_64F();
+
+        output.a11 = input.a11;
+        output.a12 = input.a21;
+        output.a13 = input.a31;
+        output.a14 = input.a41;
+        output.a15 = input.a51;
+        output.a21 = input.a12;
+        output.a22 = input.a22;
+        output.a23 = input.a32;
+        output.a24 = input.a42;
+        output.a25 = input.a52;
+        output.a31 = input.a13;
+        output.a32 = input.a23;
+        output.a33 = input.a33;
+        output.a34 = input.a43;
+        output.a35 = input.a53;
+        output.a41 = input.a14;
+        output.a42 = input.a24;
+        output.a43 = input.a34;
+        output.a44 = input.a44;
+        output.a45 = input.a54;
+        output.a51 = input.a15;
+        output.a52 = input.a25;
+        output.a53 = input.a35;
+        output.a54 = input.a45;
+        output.a55 = input.a55;
+
+        return output;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a * b <br>
+     * <br>
+     * c<sub>ij</sub> = ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void mult( FixedMatrix5x5_64F a , FixedMatrix5x5_64F b , FixedMatrix5x5_64F c) {
+        c.a11 = a.a11*b.a11 + a.a12*b.a21 + a.a13*b.a31 + a.a14*b.a41 + a.a15*b.a51;
+        c.a12 = a.a11*b.a12 + a.a12*b.a22 + a.a13*b.a32 + a.a14*b.a42 + a.a15*b.a52;
+        c.a13 = a.a11*b.a13 + a.a12*b.a23 + a.a13*b.a33 + a.a14*b.a43 + a.a15*b.a53;
+        c.a14 = a.a11*b.a14 + a.a12*b.a24 + a.a13*b.a34 + a.a14*b.a44 + a.a15*b.a54;
+        c.a15 = a.a11*b.a15 + a.a12*b.a25 + a.a13*b.a35 + a.a14*b.a45 + a.a15*b.a55;
+        c.a21 = a.a21*b.a11 + a.a22*b.a21 + a.a23*b.a31 + a.a24*b.a41 + a.a25*b.a51;
+        c.a22 = a.a21*b.a12 + a.a22*b.a22 + a.a23*b.a32 + a.a24*b.a42 + a.a25*b.a52;
+        c.a23 = a.a21*b.a13 + a.a22*b.a23 + a.a23*b.a33 + a.a24*b.a43 + a.a25*b.a53;
+        c.a24 = a.a21*b.a14 + a.a22*b.a24 + a.a23*b.a34 + a.a24*b.a44 + a.a25*b.a54;
+        c.a25 = a.a21*b.a15 + a.a22*b.a25 + a.a23*b.a35 + a.a24*b.a45 + a.a25*b.a55;
+        c.a31 = a.a31*b.a11 + a.a32*b.a21 + a.a33*b.a31 + a.a34*b.a41 + a.a35*b.a51;
+        c.a32 = a.a31*b.a12 + a.a32*b.a22 + a.a33*b.a32 + a.a34*b.a42 + a.a35*b.a52;
+        c.a33 = a.a31*b.a13 + a.a32*b.a23 + a.a33*b.a33 + a.a34*b.a43 + a.a35*b.a53;
+        c.a34 = a.a31*b.a14 + a.a32*b.a24 + a.a33*b.a34 + a.a34*b.a44 + a.a35*b.a54;
+        c.a35 = a.a31*b.a15 + a.a32*b.a25 + a.a33*b.a35 + a.a34*b.a45 + a.a35*b.a55;
+        c.a41 = a.a41*b.a11 + a.a42*b.a21 + a.a43*b.a31 + a.a44*b.a41 + a.a45*b.a51;
+        c.a42 = a.a41*b.a12 + a.a42*b.a22 + a.a43*b.a32 + a.a44*b.a42 + a.a45*b.a52;
+        c.a43 = a.a41*b.a13 + a.a42*b.a23 + a.a43*b.a33 + a.a44*b.a43 + a.a45*b.a53;
+        c.a44 = a.a41*b.a14 + a.a42*b.a24 + a.a43*b.a34 + a.a44*b.a44 + a.a45*b.a54;
+        c.a45 = a.a41*b.a15 + a.a42*b.a25 + a.a43*b.a35 + a.a44*b.a45 + a.a45*b.a55;
+        c.a51 = a.a51*b.a11 + a.a52*b.a21 + a.a53*b.a31 + a.a54*b.a41 + a.a55*b.a51;
+        c.a52 = a.a51*b.a12 + a.a52*b.a22 + a.a53*b.a32 + a.a54*b.a42 + a.a55*b.a52;
+        c.a53 = a.a51*b.a13 + a.a52*b.a23 + a.a53*b.a33 + a.a54*b.a43 + a.a55*b.a53;
+        c.a54 = a.a51*b.a14 + a.a52*b.a24 + a.a53*b.a34 + a.a54*b.a44 + a.a55*b.a54;
+        c.a55 = a.a51*b.a15 + a.a52*b.a25 + a.a53*b.a35 + a.a54*b.a45 + a.a55*b.a55;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a<sup>T</sup> * b <br>
+     * <br>
+     * c<sub>ij</sub> = ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multTransA( FixedMatrix5x5_64F a , FixedMatrix5x5_64F b , FixedMatrix5x5_64F c) {
+        c.a11 = a.a11*b.a11 + a.a21*b.a21 + a.a31*b.a31 + a.a41*b.a41 + a.a51*b.a51;
+        c.a12 = a.a11*b.a12 + a.a21*b.a22 + a.a31*b.a32 + a.a41*b.a42 + a.a51*b.a52;
+        c.a13 = a.a11*b.a13 + a.a21*b.a23 + a.a31*b.a33 + a.a41*b.a43 + a.a51*b.a53;
+        c.a14 = a.a11*b.a14 + a.a21*b.a24 + a.a31*b.a34 + a.a41*b.a44 + a.a51*b.a54;
+        c.a15 = a.a11*b.a15 + a.a21*b.a25 + a.a31*b.a35 + a.a41*b.a45 + a.a51*b.a55;
+        c.a21 = a.a12*b.a11 + a.a22*b.a21 + a.a32*b.a31 + a.a42*b.a41 + a.a52*b.a51;
+        c.a22 = a.a12*b.a12 + a.a22*b.a22 + a.a32*b.a32 + a.a42*b.a42 + a.a52*b.a52;
+        c.a23 = a.a12*b.a13 + a.a22*b.a23 + a.a32*b.a33 + a.a42*b.a43 + a.a52*b.a53;
+        c.a24 = a.a12*b.a14 + a.a22*b.a24 + a.a32*b.a34 + a.a42*b.a44 + a.a52*b.a54;
+        c.a25 = a.a12*b.a15 + a.a22*b.a25 + a.a32*b.a35 + a.a42*b.a45 + a.a52*b.a55;
+        c.a31 = a.a13*b.a11 + a.a23*b.a21 + a.a33*b.a31 + a.a43*b.a41 + a.a53*b.a51;
+        c.a32 = a.a13*b.a12 + a.a23*b.a22 + a.a33*b.a32 + a.a43*b.a42 + a.a53*b.a52;
+        c.a33 = a.a13*b.a13 + a.a23*b.a23 + a.a33*b.a33 + a.a43*b.a43 + a.a53*b.a53;
+        c.a34 = a.a13*b.a14 + a.a23*b.a24 + a.a33*b.a34 + a.a43*b.a44 + a.a53*b.a54;
+        c.a35 = a.a13*b.a15 + a.a23*b.a25 + a.a33*b.a35 + a.a43*b.a45 + a.a53*b.a55;
+        c.a41 = a.a14*b.a11 + a.a24*b.a21 + a.a34*b.a31 + a.a44*b.a41 + a.a54*b.a51;
+        c.a42 = a.a14*b.a12 + a.a24*b.a22 + a.a34*b.a32 + a.a44*b.a42 + a.a54*b.a52;
+        c.a43 = a.a14*b.a13 + a.a24*b.a23 + a.a34*b.a33 + a.a44*b.a43 + a.a54*b.a53;
+        c.a44 = a.a14*b.a14 + a.a24*b.a24 + a.a34*b.a34 + a.a44*b.a44 + a.a54*b.a54;
+        c.a45 = a.a14*b.a15 + a.a24*b.a25 + a.a34*b.a35 + a.a44*b.a45 + a.a54*b.a55;
+        c.a51 = a.a15*b.a11 + a.a25*b.a21 + a.a35*b.a31 + a.a45*b.a41 + a.a55*b.a51;
+        c.a52 = a.a15*b.a12 + a.a25*b.a22 + a.a35*b.a32 + a.a45*b.a42 + a.a55*b.a52;
+        c.a53 = a.a15*b.a13 + a.a25*b.a23 + a.a35*b.a33 + a.a45*b.a43 + a.a55*b.a53;
+        c.a54 = a.a15*b.a14 + a.a25*b.a24 + a.a35*b.a34 + a.a45*b.a44 + a.a55*b.a54;
+        c.a55 = a.a15*b.a15 + a.a25*b.a25 + a.a35*b.a35 + a.a45*b.a45 + a.a55*b.a55;
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c = a<sup>T</sup> * b<sup>T</sup><br>
+     * c<sub>ij</sub> = ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>jk</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multTransAB( FixedMatrix5x5_64F a , FixedMatrix5x5_64F b , FixedMatrix5x5_64F c) {
+        c.a11 = a.a11*b.a11 + a.a21*b.a12 + a.a31*b.a13 + a.a41*b.a14 + a.a51*b.a15;
+        c.a12 = a.a11*b.a21 + a.a21*b.a22 + a.a31*b.a23 + a.a41*b.a24 + a.a51*b.a25;
+        c.a13 = a.a11*b.a31 + a.a21*b.a32 + a.a31*b.a33 + a.a41*b.a34 + a.a51*b.a35;
+        c.a14 = a.a11*b.a41 + a.a21*b.a42 + a.a31*b.a43 + a.a41*b.a44 + a.a51*b.a45;
+        c.a15 = a.a11*b.a51 + a.a21*b.a52 + a.a31*b.a53 + a.a41*b.a54 + a.a51*b.a55;
+        c.a21 = a.a12*b.a11 + a.a22*b.a12 + a.a32*b.a13 + a.a42*b.a14 + a.a52*b.a15;
+        c.a22 = a.a12*b.a21 + a.a22*b.a22 + a.a32*b.a23 + a.a42*b.a24 + a.a52*b.a25;
+        c.a23 = a.a12*b.a31 + a.a22*b.a32 + a.a32*b.a33 + a.a42*b.a34 + a.a52*b.a35;
+        c.a24 = a.a12*b.a41 + a.a22*b.a42 + a.a32*b.a43 + a.a42*b.a44 + a.a52*b.a45;
+        c.a25 = a.a12*b.a51 + a.a22*b.a52 + a.a32*b.a53 + a.a42*b.a54 + a.a52*b.a55;
+        c.a31 = a.a13*b.a11 + a.a23*b.a12 + a.a33*b.a13 + a.a43*b.a14 + a.a53*b.a15;
+        c.a32 = a.a13*b.a21 + a.a23*b.a22 + a.a33*b.a23 + a.a43*b.a24 + a.a53*b.a25;
+        c.a33 = a.a13*b.a31 + a.a23*b.a32 + a.a33*b.a33 + a.a43*b.a34 + a.a53*b.a35;
+        c.a34 = a.a13*b.a41 + a.a23*b.a42 + a.a33*b.a43 + a.a43*b.a44 + a.a53*b.a45;
+        c.a35 = a.a13*b.a51 + a.a23*b.a52 + a.a33*b.a53 + a.a43*b.a54 + a.a53*b.a55;
+        c.a41 = a.a14*b.a11 + a.a24*b.a12 + a.a34*b.a13 + a.a44*b.a14 + a.a54*b.a15;
+        c.a42 = a.a14*b.a21 + a.a24*b.a22 + a.a34*b.a23 + a.a44*b.a24 + a.a54*b.a25;
+        c.a43 = a.a14*b.a31 + a.a24*b.a32 + a.a34*b.a33 + a.a44*b.a34 + a.a54*b.a35;
+        c.a44 = a.a14*b.a41 + a.a24*b.a42 + a.a34*b.a43 + a.a44*b.a44 + a.a54*b.a45;
+        c.a45 = a.a14*b.a51 + a.a24*b.a52 + a.a34*b.a53 + a.a44*b.a54 + a.a54*b.a55;
+        c.a51 = a.a15*b.a11 + a.a25*b.a12 + a.a35*b.a13 + a.a45*b.a14 + a.a55*b.a15;
+        c.a52 = a.a15*b.a21 + a.a25*b.a22 + a.a35*b.a23 + a.a45*b.a24 + a.a55*b.a25;
+        c.a53 = a.a15*b.a31 + a.a25*b.a32 + a.a35*b.a33 + a.a45*b.a34 + a.a55*b.a35;
+        c.a54 = a.a15*b.a41 + a.a25*b.a42 + a.a35*b.a43 + a.a45*b.a44 + a.a55*b.a45;
+        c.a55 = a.a15*b.a51 + a.a25*b.a52 + a.a35*b.a53 + a.a45*b.a54 + a.a55*b.a55;
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c = a * b<sup>T</sup> <br>
+     * c<sub>ij</sub> = ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>jk</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multTransB( FixedMatrix5x5_64F a , FixedMatrix5x5_64F b , FixedMatrix5x5_64F c) {
+        c.a11 = a.a11*b.a11 + a.a12*b.a12 + a.a13*b.a13 + a.a14*b.a14 + a.a15*b.a15;
+        c.a12 = a.a11*b.a21 + a.a12*b.a22 + a.a13*b.a23 + a.a14*b.a24 + a.a15*b.a25;
+        c.a13 = a.a11*b.a31 + a.a12*b.a32 + a.a13*b.a33 + a.a14*b.a34 + a.a15*b.a35;
+        c.a14 = a.a11*b.a41 + a.a12*b.a42 + a.a13*b.a43 + a.a14*b.a44 + a.a15*b.a45;
+        c.a15 = a.a11*b.a51 + a.a12*b.a52 + a.a13*b.a53 + a.a14*b.a54 + a.a15*b.a55;
+        c.a21 = a.a21*b.a11 + a.a22*b.a12 + a.a23*b.a13 + a.a24*b.a14 + a.a25*b.a15;
+        c.a22 = a.a21*b.a21 + a.a22*b.a22 + a.a23*b.a23 + a.a24*b.a24 + a.a25*b.a25;
+        c.a23 = a.a21*b.a31 + a.a22*b.a32 + a.a23*b.a33 + a.a24*b.a34 + a.a25*b.a35;
+        c.a24 = a.a21*b.a41 + a.a22*b.a42 + a.a23*b.a43 + a.a24*b.a44 + a.a25*b.a45;
+        c.a25 = a.a21*b.a51 + a.a22*b.a52 + a.a23*b.a53 + a.a24*b.a54 + a.a25*b.a55;
+        c.a31 = a.a31*b.a11 + a.a32*b.a12 + a.a33*b.a13 + a.a34*b.a14 + a.a35*b.a15;
+        c.a32 = a.a31*b.a21 + a.a32*b.a22 + a.a33*b.a23 + a.a34*b.a24 + a.a35*b.a25;
+        c.a33 = a.a31*b.a31 + a.a32*b.a32 + a.a33*b.a33 + a.a34*b.a34 + a.a35*b.a35;
+        c.a34 = a.a31*b.a41 + a.a32*b.a42 + a.a33*b.a43 + a.a34*b.a44 + a.a35*b.a45;
+        c.a35 = a.a31*b.a51 + a.a32*b.a52 + a.a33*b.a53 + a.a34*b.a54 + a.a35*b.a55;
+        c.a41 = a.a41*b.a11 + a.a42*b.a12 + a.a43*b.a13 + a.a44*b.a14 + a.a45*b.a15;
+        c.a42 = a.a41*b.a21 + a.a42*b.a22 + a.a43*b.a23 + a.a44*b.a24 + a.a45*b.a25;
+        c.a43 = a.a41*b.a31 + a.a42*b.a32 + a.a43*b.a33 + a.a44*b.a34 + a.a45*b.a35;
+        c.a44 = a.a41*b.a41 + a.a42*b.a42 + a.a43*b.a43 + a.a44*b.a44 + a.a45*b.a45;
+        c.a45 = a.a41*b.a51 + a.a42*b.a52 + a.a43*b.a53 + a.a44*b.a54 + a.a45*b.a55;
+        c.a51 = a.a51*b.a11 + a.a52*b.a12 + a.a53*b.a13 + a.a54*b.a14 + a.a55*b.a15;
+        c.a52 = a.a51*b.a21 + a.a52*b.a22 + a.a53*b.a23 + a.a54*b.a24 + a.a55*b.a25;
+        c.a53 = a.a51*b.a31 + a.a52*b.a32 + a.a53*b.a33 + a.a54*b.a34 + a.a55*b.a35;
+        c.a54 = a.a51*b.a41 + a.a52*b.a42 + a.a53*b.a43 + a.a54*b.a44 + a.a55*b.a45;
+        c.a55 = a.a51*b.a51 + a.a52*b.a52 + a.a53*b.a53 + a.a54*b.a54 + a.a55*b.a55;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c += a * b <br>
+     * <br>
+     * c<sub>ij</sub> += ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAdd( FixedMatrix5x5_64F a , FixedMatrix5x5_64F b , FixedMatrix5x5_64F c) {
+        c.a11 += a.a11*b.a11 + a.a12*b.a21 + a.a13*b.a31 + a.a14*b.a41 + a.a15*b.a51;
+        c.a12 += a.a11*b.a12 + a.a12*b.a22 + a.a13*b.a32 + a.a14*b.a42 + a.a15*b.a52;
+        c.a13 += a.a11*b.a13 + a.a12*b.a23 + a.a13*b.a33 + a.a14*b.a43 + a.a15*b.a53;
+        c.a14 += a.a11*b.a14 + a.a12*b.a24 + a.a13*b.a34 + a.a14*b.a44 + a.a15*b.a54;
+        c.a15 += a.a11*b.a15 + a.a12*b.a25 + a.a13*b.a35 + a.a14*b.a45 + a.a15*b.a55;
+        c.a21 += a.a21*b.a11 + a.a22*b.a21 + a.a23*b.a31 + a.a24*b.a41 + a.a25*b.a51;
+        c.a22 += a.a21*b.a12 + a.a22*b.a22 + a.a23*b.a32 + a.a24*b.a42 + a.a25*b.a52;
+        c.a23 += a.a21*b.a13 + a.a22*b.a23 + a.a23*b.a33 + a.a24*b.a43 + a.a25*b.a53;
+        c.a24 += a.a21*b.a14 + a.a22*b.a24 + a.a23*b.a34 + a.a24*b.a44 + a.a25*b.a54;
+        c.a25 += a.a21*b.a15 + a.a22*b.a25 + a.a23*b.a35 + a.a24*b.a45 + a.a25*b.a55;
+        c.a31 += a.a31*b.a11 + a.a32*b.a21 + a.a33*b.a31 + a.a34*b.a41 + a.a35*b.a51;
+        c.a32 += a.a31*b.a12 + a.a32*b.a22 + a.a33*b.a32 + a.a34*b.a42 + a.a35*b.a52;
+        c.a33 += a.a31*b.a13 + a.a32*b.a23 + a.a33*b.a33 + a.a34*b.a43 + a.a35*b.a53;
+        c.a34 += a.a31*b.a14 + a.a32*b.a24 + a.a33*b.a34 + a.a34*b.a44 + a.a35*b.a54;
+        c.a35 += a.a31*b.a15 + a.a32*b.a25 + a.a33*b.a35 + a.a34*b.a45 + a.a35*b.a55;
+        c.a41 += a.a41*b.a11 + a.a42*b.a21 + a.a43*b.a31 + a.a44*b.a41 + a.a45*b.a51;
+        c.a42 += a.a41*b.a12 + a.a42*b.a22 + a.a43*b.a32 + a.a44*b.a42 + a.a45*b.a52;
+        c.a43 += a.a41*b.a13 + a.a42*b.a23 + a.a43*b.a33 + a.a44*b.a43 + a.a45*b.a53;
+        c.a44 += a.a41*b.a14 + a.a42*b.a24 + a.a43*b.a34 + a.a44*b.a44 + a.a45*b.a54;
+        c.a45 += a.a41*b.a15 + a.a42*b.a25 + a.a43*b.a35 + a.a44*b.a45 + a.a45*b.a55;
+        c.a51 += a.a51*b.a11 + a.a52*b.a21 + a.a53*b.a31 + a.a54*b.a41 + a.a55*b.a51;
+        c.a52 += a.a51*b.a12 + a.a52*b.a22 + a.a53*b.a32 + a.a54*b.a42 + a.a55*b.a52;
+        c.a53 += a.a51*b.a13 + a.a52*b.a23 + a.a53*b.a33 + a.a54*b.a43 + a.a55*b.a53;
+        c.a54 += a.a51*b.a14 + a.a52*b.a24 + a.a53*b.a34 + a.a54*b.a44 + a.a55*b.a54;
+        c.a55 += a.a51*b.a15 + a.a52*b.a25 + a.a53*b.a35 + a.a54*b.a45 + a.a55*b.a55;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c += a<sup>T</sup> * b <br>
+     * <br>
+     * c<sub>ij</sub> += ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAddTransA( FixedMatrix5x5_64F a , FixedMatrix5x5_64F b , FixedMatrix5x5_64F c) {
+        c.a11 += a.a11*b.a11 + a.a21*b.a21 + a.a31*b.a31 + a.a41*b.a41 + a.a51*b.a51;
+        c.a12 += a.a11*b.a12 + a.a21*b.a22 + a.a31*b.a32 + a.a41*b.a42 + a.a51*b.a52;
+        c.a13 += a.a11*b.a13 + a.a21*b.a23 + a.a31*b.a33 + a.a41*b.a43 + a.a51*b.a53;
+        c.a14 += a.a11*b.a14 + a.a21*b.a24 + a.a31*b.a34 + a.a41*b.a44 + a.a51*b.a54;
+        c.a15 += a.a11*b.a15 + a.a21*b.a25 + a.a31*b.a35 + a.a41*b.a45 + a.a51*b.a55;
+        c.a21 += a.a12*b.a11 + a.a22*b.a21 + a.a32*b.a31 + a.a42*b.a41 + a.a52*b.a51;
+        c.a22 += a.a12*b.a12 + a.a22*b.a22 + a.a32*b.a32 + a.a42*b.a42 + a.a52*b.a52;
+        c.a23 += a.a12*b.a13 + a.a22*b.a23 + a.a32*b.a33 + a.a42*b.a43 + a.a52*b.a53;
+        c.a24 += a.a12*b.a14 + a.a22*b.a24 + a.a32*b.a34 + a.a42*b.a44 + a.a52*b.a54;
+        c.a25 += a.a12*b.a15 + a.a22*b.a25 + a.a32*b.a35 + a.a42*b.a45 + a.a52*b.a55;
+        c.a31 += a.a13*b.a11 + a.a23*b.a21 + a.a33*b.a31 + a.a43*b.a41 + a.a53*b.a51;
+        c.a32 += a.a13*b.a12 + a.a23*b.a22 + a.a33*b.a32 + a.a43*b.a42 + a.a53*b.a52;
+        c.a33 += a.a13*b.a13 + a.a23*b.a23 + a.a33*b.a33 + a.a43*b.a43 + a.a53*b.a53;
+        c.a34 += a.a13*b.a14 + a.a23*b.a24 + a.a33*b.a34 + a.a43*b.a44 + a.a53*b.a54;
+        c.a35 += a.a13*b.a15 + a.a23*b.a25 + a.a33*b.a35 + a.a43*b.a45 + a.a53*b.a55;
+        c.a41 += a.a14*b.a11 + a.a24*b.a21 + a.a34*b.a31 + a.a44*b.a41 + a.a54*b.a51;
+        c.a42 += a.a14*b.a12 + a.a24*b.a22 + a.a34*b.a32 + a.a44*b.a42 + a.a54*b.a52;
+        c.a43 += a.a14*b.a13 + a.a24*b.a23 + a.a34*b.a33 + a.a44*b.a43 + a.a54*b.a53;
+        c.a44 += a.a14*b.a14 + a.a24*b.a24 + a.a34*b.a34 + a.a44*b.a44 + a.a54*b.a54;
+        c.a45 += a.a14*b.a15 + a.a24*b.a25 + a.a34*b.a35 + a.a44*b.a45 + a.a54*b.a55;
+        c.a51 += a.a15*b.a11 + a.a25*b.a21 + a.a35*b.a31 + a.a45*b.a41 + a.a55*b.a51;
+        c.a52 += a.a15*b.a12 + a.a25*b.a22 + a.a35*b.a32 + a.a45*b.a42 + a.a55*b.a52;
+        c.a53 += a.a15*b.a13 + a.a25*b.a23 + a.a35*b.a33 + a.a45*b.a43 + a.a55*b.a53;
+        c.a54 += a.a15*b.a14 + a.a25*b.a24 + a.a35*b.a34 + a.a45*b.a44 + a.a55*b.a54;
+        c.a55 += a.a15*b.a15 + a.a25*b.a25 + a.a35*b.a35 + a.a45*b.a45 + a.a55*b.a55;
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c += a<sup>T</sup> * b<sup>T</sup><br>
+     * c<sub>ij</sub> += ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>jk</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAddTransAB( FixedMatrix5x5_64F a , FixedMatrix5x5_64F b , FixedMatrix5x5_64F c) {
+        c.a11 += a.a11*b.a11 + a.a21*b.a12 + a.a31*b.a13 + a.a41*b.a14 + a.a51*b.a15;
+        c.a12 += a.a11*b.a21 + a.a21*b.a22 + a.a31*b.a23 + a.a41*b.a24 + a.a51*b.a25;
+        c.a13 += a.a11*b.a31 + a.a21*b.a32 + a.a31*b.a33 + a.a41*b.a34 + a.a51*b.a35;
+        c.a14 += a.a11*b.a41 + a.a21*b.a42 + a.a31*b.a43 + a.a41*b.a44 + a.a51*b.a45;
+        c.a15 += a.a11*b.a51 + a.a21*b.a52 + a.a31*b.a53 + a.a41*b.a54 + a.a51*b.a55;
+        c.a21 += a.a12*b.a11 + a.a22*b.a12 + a.a32*b.a13 + a.a42*b.a14 + a.a52*b.a15;
+        c.a22 += a.a12*b.a21 + a.a22*b.a22 + a.a32*b.a23 + a.a42*b.a24 + a.a52*b.a25;
+        c.a23 += a.a12*b.a31 + a.a22*b.a32 + a.a32*b.a33 + a.a42*b.a34 + a.a52*b.a35;
+        c.a24 += a.a12*b.a41 + a.a22*b.a42 + a.a32*b.a43 + a.a42*b.a44 + a.a52*b.a45;
+        c.a25 += a.a12*b.a51 + a.a22*b.a52 + a.a32*b.a53 + a.a42*b.a54 + a.a52*b.a55;
+        c.a31 += a.a13*b.a11 + a.a23*b.a12 + a.a33*b.a13 + a.a43*b.a14 + a.a53*b.a15;
+        c.a32 += a.a13*b.a21 + a.a23*b.a22 + a.a33*b.a23 + a.a43*b.a24 + a.a53*b.a25;
+        c.a33 += a.a13*b.a31 + a.a23*b.a32 + a.a33*b.a33 + a.a43*b.a34 + a.a53*b.a35;
+        c.a34 += a.a13*b.a41 + a.a23*b.a42 + a.a33*b.a43 + a.a43*b.a44 + a.a53*b.a45;
+        c.a35 += a.a13*b.a51 + a.a23*b.a52 + a.a33*b.a53 + a.a43*b.a54 + a.a53*b.a55;
+        c.a41 += a.a14*b.a11 + a.a24*b.a12 + a.a34*b.a13 + a.a44*b.a14 + a.a54*b.a15;
+        c.a42 += a.a14*b.a21 + a.a24*b.a22 + a.a34*b.a23 + a.a44*b.a24 + a.a54*b.a25;
+        c.a43 += a.a14*b.a31 + a.a24*b.a32 + a.a34*b.a33 + a.a44*b.a34 + a.a54*b.a35;
+        c.a44 += a.a14*b.a41 + a.a24*b.a42 + a.a34*b.a43 + a.a44*b.a44 + a.a54*b.a45;
+        c.a45 += a.a14*b.a51 + a.a24*b.a52 + a.a34*b.a53 + a.a44*b.a54 + a.a54*b.a55;
+        c.a51 += a.a15*b.a11 + a.a25*b.a12 + a.a35*b.a13 + a.a45*b.a14 + a.a55*b.a15;
+        c.a52 += a.a15*b.a21 + a.a25*b.a22 + a.a35*b.a23 + a.a45*b.a24 + a.a55*b.a25;
+        c.a53 += a.a15*b.a31 + a.a25*b.a32 + a.a35*b.a33 + a.a45*b.a34 + a.a55*b.a35;
+        c.a54 += a.a15*b.a41 + a.a25*b.a42 + a.a35*b.a43 + a.a45*b.a44 + a.a55*b.a45;
+        c.a55 += a.a15*b.a51 + a.a25*b.a52 + a.a35*b.a53 + a.a45*b.a54 + a.a55*b.a55;
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c += a * b<sup>T</sup> <br>
+     * c<sub>ij</sub> += ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>jk</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAddTransB( FixedMatrix5x5_64F a , FixedMatrix5x5_64F b , FixedMatrix5x5_64F c) {
+        c.a11 += a.a11*b.a11 + a.a12*b.a12 + a.a13*b.a13 + a.a14*b.a14 + a.a15*b.a15;
+        c.a12 += a.a11*b.a21 + a.a12*b.a22 + a.a13*b.a23 + a.a14*b.a24 + a.a15*b.a25;
+        c.a13 += a.a11*b.a31 + a.a12*b.a32 + a.a13*b.a33 + a.a14*b.a34 + a.a15*b.a35;
+        c.a14 += a.a11*b.a41 + a.a12*b.a42 + a.a13*b.a43 + a.a14*b.a44 + a.a15*b.a45;
+        c.a15 += a.a11*b.a51 + a.a12*b.a52 + a.a13*b.a53 + a.a14*b.a54 + a.a15*b.a55;
+        c.a21 += a.a21*b.a11 + a.a22*b.a12 + a.a23*b.a13 + a.a24*b.a14 + a.a25*b.a15;
+        c.a22 += a.a21*b.a21 + a.a22*b.a22 + a.a23*b.a23 + a.a24*b.a24 + a.a25*b.a25;
+        c.a23 += a.a21*b.a31 + a.a22*b.a32 + a.a23*b.a33 + a.a24*b.a34 + a.a25*b.a35;
+        c.a24 += a.a21*b.a41 + a.a22*b.a42 + a.a23*b.a43 + a.a24*b.a44 + a.a25*b.a45;
+        c.a25 += a.a21*b.a51 + a.a22*b.a52 + a.a23*b.a53 + a.a24*b.a54 + a.a25*b.a55;
+        c.a31 += a.a31*b.a11 + a.a32*b.a12 + a.a33*b.a13 + a.a34*b.a14 + a.a35*b.a15;
+        c.a32 += a.a31*b.a21 + a.a32*b.a22 + a.a33*b.a23 + a.a34*b.a24 + a.a35*b.a25;
+        c.a33 += a.a31*b.a31 + a.a32*b.a32 + a.a33*b.a33 + a.a34*b.a34 + a.a35*b.a35;
+        c.a34 += a.a31*b.a41 + a.a32*b.a42 + a.a33*b.a43 + a.a34*b.a44 + a.a35*b.a45;
+        c.a35 += a.a31*b.a51 + a.a32*b.a52 + a.a33*b.a53 + a.a34*b.a54 + a.a35*b.a55;
+        c.a41 += a.a41*b.a11 + a.a42*b.a12 + a.a43*b.a13 + a.a44*b.a14 + a.a45*b.a15;
+        c.a42 += a.a41*b.a21 + a.a42*b.a22 + a.a43*b.a23 + a.a44*b.a24 + a.a45*b.a25;
+        c.a43 += a.a41*b.a31 + a.a42*b.a32 + a.a43*b.a33 + a.a44*b.a34 + a.a45*b.a35;
+        c.a44 += a.a41*b.a41 + a.a42*b.a42 + a.a43*b.a43 + a.a44*b.a44 + a.a45*b.a45;
+        c.a45 += a.a41*b.a51 + a.a42*b.a52 + a.a43*b.a53 + a.a44*b.a54 + a.a45*b.a55;
+        c.a51 += a.a51*b.a11 + a.a52*b.a12 + a.a53*b.a13 + a.a54*b.a14 + a.a55*b.a15;
+        c.a52 += a.a51*b.a21 + a.a52*b.a22 + a.a53*b.a23 + a.a54*b.a24 + a.a55*b.a25;
+        c.a53 += a.a51*b.a31 + a.a52*b.a32 + a.a53*b.a33 + a.a54*b.a34 + a.a55*b.a35;
+        c.a54 += a.a51*b.a41 + a.a52*b.a42 + a.a53*b.a43 + a.a54*b.a44 + a.a55*b.a45;
+        c.a55 += a.a51*b.a51 + a.a52*b.a52 + a.a53*b.a53 + a.a54*b.a54 + a.a55*b.a55;
+    }
+
+    /**
+     * <p>Performs matrix to vector multiplication:<br>
+     * <br>
+     * c = a * b <br>
+     * <br>
+     * c<sub>i</sub> = ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>k</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right vector in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void mult( FixedMatrix5x5_64F a , FixedMatrix5_64F b , FixedMatrix5_64F c) {
+        c.a1 = a.a11*b.a1 + a.a12*b.a2 + a.a13*b.a3 + a.a14*b.a4 + a.a15*b.a5;
+        c.a2 = a.a21*b.a1 + a.a22*b.a2 + a.a23*b.a3 + a.a24*b.a4 + a.a25*b.a5;
+        c.a3 = a.a31*b.a1 + a.a32*b.a2 + a.a33*b.a3 + a.a34*b.a4 + a.a35*b.a5;
+        c.a4 = a.a41*b.a1 + a.a42*b.a2 + a.a43*b.a3 + a.a44*b.a4 + a.a45*b.a5;
+        c.a5 = a.a51*b.a1 + a.a52*b.a2 + a.a53*b.a3 + a.a54*b.a4 + a.a55*b.a5;
+    }
+
+    /**
+     * <p>Performs vector to matrix multiplication:<br>
+     * <br>
+     * c = a * b <br>
+     * <br>
+     * c<sub>j</sub> = ∑<sub>k=1:n</sub> { b<sub>k</sub> * a<sub>kj</sub> }
+     * </p>
+     *
+     * @param a The left vector in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void mult( FixedMatrix5_64F a , FixedMatrix5x5_64F b , FixedMatrix5_64F c) {
+        c.a1 = a.a1*b.a11 + a.a2*b.a21 + a.a3*b.a31 + a.a4*b.a41 + a.a5*b.a51;
+        c.a2 = a.a1*b.a12 + a.a2*b.a22 + a.a3*b.a32 + a.a4*b.a42 + a.a5*b.a52;
+        c.a3 = a.a1*b.a13 + a.a2*b.a23 + a.a3*b.a33 + a.a4*b.a43 + a.a5*b.a53;
+        c.a4 = a.a1*b.a14 + a.a2*b.a24 + a.a3*b.a34 + a.a4*b.a44 + a.a5*b.a54;
+        c.a5 = a.a1*b.a15 + a.a2*b.a25 + a.a3*b.a35 + a.a4*b.a45 + a.a5*b.a55;
+    }
+
+    /**
+     * <p>Performs the vector dot product:<br>
+     * <br>
+     * c = a * b <br>
+     * <br>
+     * c> = ∑<sub>k=1:n</sub> { b<sub>k</sub> * a<sub>k</sub> }
+     * </p>
+     *
+     * @param a The left vector in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @return The dot product
+     */
+    public static double dot( FixedMatrix5_64F a , FixedMatrix5_64F b ) {
+        return a.a1*b.a1 + a.a2*b.a2 + a.a3*b.a3 + a.a4*b.a4 + a.a5*b.a5;
+    }
+
+    /**
+     * Sets all the diagonal elements equal to one and everything else equal to zero.
+     * If this is a square matrix then it will be an identity matrix.
+     *
+     * @param a A matrix.
+     */
+    public static void setIdentity( FixedMatrix5x5_64F a ) {
+        a.a11 = 1; a.a21 = 0; a.a31 = 0; a.a41 = 0; a.a51 = 0;
+        a.a12 = 0; a.a22 = 1; a.a32 = 0; a.a42 = 0; a.a52 = 0;
+        a.a13 = 0; a.a23 = 0; a.a33 = 1; a.a43 = 0; a.a53 = 0;
+        a.a14 = 0; a.a24 = 0; a.a34 = 0; a.a44 = 1; a.a54 = 0;
+        a.a15 = 0; a.a25 = 0; a.a35 = 0; a.a45 = 0; a.a55 = 1;
+    }
+
+    /**
+     * Inverts matrix 'a' using minor matrices and stores the results in 'inv'.  Scaling is applied to improve
+     * stability against overflow and underflow.
+     *
+     * WARNING: Potentially less stable than using LU decomposition.
+     *
+     * @param a Input matrix. Not modified.
+     * @param inv Inverted output matrix.  Modified.
+     * @return true if it was successful or false if it failed.  Not always reliable.
+     */
+    public static boolean invert( FixedMatrix5x5_64F a , FixedMatrix5x5_64F inv ) {
+
+        double scale = 1.0/elementMaxAbs(a);
+
+        double a11 = a.a11*scale;
+        double a12 = a.a12*scale;
+        double a13 = a.a13*scale;
+        double a14 = a.a14*scale;
+        double a15 = a.a15*scale;
+        double a21 = a.a21*scale;
+        double a22 = a.a22*scale;
+        double a23 = a.a23*scale;
+        double a24 = a.a24*scale;
+        double a25 = a.a25*scale;
+        double a31 = a.a31*scale;
+        double a32 = a.a32*scale;
+        double a33 = a.a33*scale;
+        double a34 = a.a34*scale;
+        double a35 = a.a35*scale;
+        double a41 = a.a41*scale;
+        double a42 = a.a42*scale;
+        double a43 = a.a43*scale;
+        double a44 = a.a44*scale;
+        double a45 = a.a45*scale;
+        double a51 = a.a51*scale;
+        double a52 = a.a52*scale;
+        double a53 = a.a53*scale;
+        double a54 = a.a54*scale;
+        double a55 = a.a55*scale;
+
+        double m11 =  + a22*( + a33*(a44*a55 - a45*a54) - a34*(a43*a55 - a45*a53) + a35*(a43*a54 - a44*a53)) - a23*( + a32*(a44*a55 - a45*a54) - a34*(a42*a55 - a45*a52) + a35*(a42*a54 - a44*a52)) + a24*( + a32*(a43*a55 - a45*a53) - a33*(a42*a55 - a45*a52) + a35*(a42*a53 - a43*a52)) - a25*( + a32*(a43*a54 - a44*a53) - a33*(a42*a54 - a44*a52) + a34*(a42*a53 - a43*a52));
+        double m12 = -(  + a21*( + a33*(a44*a55 - a45*a54) - a34*(a43*a55 - a45*a53) + a35*(a43*a54 - a44*a53)) - a23*( + a31*(a44*a55 - a45*a54) - a34*(a41*a55 - a45*a51) + a35*(a41*a54 - a44*a51)) + a24*( + a31*(a43*a55 - a45*a53) - a33*(a41*a55 - a45*a51) + a35*(a41*a53 - a43*a51)) - a25*( + a31*(a43*a54 - a44*a53) - a33*(a41*a54 - a44*a51) + a34*(a41*a53 - a43*a51)));
+        double m13 =  + a21*( + a32*(a44*a55 - a45*a54) - a34*(a42*a55 - a45*a52) + a35*(a42*a54 - a44*a52)) - a22*( + a31*(a44*a55 - a45*a54) - a34*(a41*a55 - a45*a51) + a35*(a41*a54 - a44*a51)) + a24*( + a31*(a42*a55 - a45*a52) - a32*(a41*a55 - a45*a51) + a35*(a41*a52 - a42*a51)) - a25*( + a31*(a42*a54 - a44*a52) - a32*(a41*a54 - a44*a51) + a34*(a41*a52 - a42*a51));
+        double m14 = -(  + a21*( + a32*(a43*a55 - a45*a53) - a33*(a42*a55 - a45*a52) + a35*(a42*a53 - a43*a52)) - a22*( + a31*(a43*a55 - a45*a53) - a33*(a41*a55 - a45*a51) + a35*(a41*a53 - a43*a51)) + a23*( + a31*(a42*a55 - a45*a52) - a32*(a41*a55 - a45*a51) + a35*(a41*a52 - a42*a51)) - a25*( + a31*(a42*a53 - a43*a52) - a32*(a41*a53 - a43*a51) + a33*(a41*a52 - a42*a51)));
+        double m15 =  + a21*( + a32*(a43*a54 - a44*a53) - a33*(a42*a54 - a44*a52) + a34*(a42*a53 - a43*a52)) - a22*( + a31*(a43*a54 - a44*a53) - a33*(a41*a54 - a44*a51) + a34*(a41*a53 - a43*a51)) + a23*( + a31*(a42*a54 - a44*a52) - a32*(a41*a54 - a44*a51) + a34*(a41*a52 - a42*a51)) - a24*( + a31*(a42*a53 - a43*a52) - a32*(a41*a53 - a43*a51) + a33*(a41*a52 - a42*a51));
+        double m21 = -(  + a12*( + a33*(a44*a55 - a45*a54) - a34*(a43*a55 - a45*a53) + a35*(a43*a54 - a44*a53)) - a13*( + a32*(a44*a55 - a45*a54) - a34*(a42*a55 - a45*a52) + a35*(a42*a54 - a44*a52)) + a14*( + a32*(a43*a55 - a45*a53) - a33*(a42*a55 - a45*a52) + a35*(a42*a53 - a43*a52)) - a15*( + a32*(a43*a54 - a44*a53) - a33*(a42*a54 - a44*a52) + a34*(a42*a53 - a43*a52)));
+        double m22 =  + a11*( + a33*(a44*a55 - a45*a54) - a34*(a43*a55 - a45*a53) + a35*(a43*a54 - a44*a53)) - a13*( + a31*(a44*a55 - a45*a54) - a34*(a41*a55 - a45*a51) + a35*(a41*a54 - a44*a51)) + a14*( + a31*(a43*a55 - a45*a53) - a33*(a41*a55 - a45*a51) + a35*(a41*a53 - a43*a51)) - a15*( + a31*(a43*a54 - a44*a53) - a33*(a41*a54 - a44*a51) + a34*(a41*a53 - a43*a51));
+        double m23 = -(  + a11*( + a32*(a44*a55 - a45*a54) - a34*(a42*a55 - a45*a52) + a35*(a42*a54 - a44*a52)) - a12*( + a31*(a44*a55 - a45*a54) - a34*(a41*a55 - a45*a51) + a35*(a41*a54 - a44*a51)) + a14*( + a31*(a42*a55 - a45*a52) - a32*(a41*a55 - a45*a51) + a35*(a41*a52 - a42*a51)) - a15*( + a31*(a42*a54 - a44*a52) - a32*(a41*a54 - a44*a51) + a34*(a41*a52 - a42*a51)));
+        double m24 =  + a11*( + a32*(a43*a55 - a45*a53) - a33*(a42*a55 - a45*a52) + a35*(a42*a53 - a43*a52)) - a12*( + a31*(a43*a55 - a45*a53) - a33*(a41*a55 - a45*a51) + a35*(a41*a53 - a43*a51)) + a13*( + a31*(a42*a55 - a45*a52) - a32*(a41*a55 - a45*a51) + a35*(a41*a52 - a42*a51)) - a15*( + a31*(a42*a53 - a43*a52) - a32*(a41*a53 - a43*a51) + a33*(a41*a52 - a42*a51));
+        double m25 = -(  + a11*( + a32*(a43*a54 - a44*a53) - a33*(a42*a54 - a44*a52) + a34*(a42*a53 - a43*a52)) - a12*( + a31*(a43*a54 - a44*a53) - a33*(a41*a54 - a44*a51) + a34*(a41*a53 - a43*a51)) + a13*( + a31*(a42*a54 - a44*a52) - a32*(a41*a54 - a44*a51) + a34*(a41*a52 - a42*a51)) - a14*( + a31*(a42*a53 - a43*a52) - a32*(a41*a53 - a43*a51) + a33*(a41*a52 - a42*a51)));
+        double m31 =  + a12*( + a23*(a44*a55 - a45*a54) - a24*(a43*a55 - a45*a53) + a25*(a43*a54 - a44*a53)) - a13*( + a22*(a44*a55 - a45*a54) - a24*(a42*a55 - a45*a52) + a25*(a42*a54 - a44*a52)) + a14*( + a22*(a43*a55 - a45*a53) - a23*(a42*a55 - a45*a52) + a25*(a42*a53 - a43*a52)) - a15*( + a22*(a43*a54 - a44*a53) - a23*(a42*a54 - a44*a52) + a24*(a42*a53 - a43*a52));
+        double m32 = -(  + a11*( + a23*(a44*a55 - a45*a54) - a24*(a43*a55 - a45*a53) + a25*(a43*a54 - a44*a53)) - a13*( + a21*(a44*a55 - a45*a54) - a24*(a41*a55 - a45*a51) + a25*(a41*a54 - a44*a51)) + a14*( + a21*(a43*a55 - a45*a53) - a23*(a41*a55 - a45*a51) + a25*(a41*a53 - a43*a51)) - a15*( + a21*(a43*a54 - a44*a53) - a23*(a41*a54 - a44*a51) + a24*(a41*a53 - a43*a51)));
+        double m33 =  + a11*( + a22*(a44*a55 - a45*a54) - a24*(a42*a55 - a45*a52) + a25*(a42*a54 - a44*a52)) - a12*( + a21*(a44*a55 - a45*a54) - a24*(a41*a55 - a45*a51) + a25*(a41*a54 - a44*a51)) + a14*( + a21*(a42*a55 - a45*a52) - a22*(a41*a55 - a45*a51) + a25*(a41*a52 - a42*a51)) - a15*( + a21*(a42*a54 - a44*a52) - a22*(a41*a54 - a44*a51) + a24*(a41*a52 - a42*a51));
+        double m34 = -(  + a11*( + a22*(a43*a55 - a45*a53) - a23*(a42*a55 - a45*a52) + a25*(a42*a53 - a43*a52)) - a12*( + a21*(a43*a55 - a45*a53) - a23*(a41*a55 - a45*a51) + a25*(a41*a53 - a43*a51)) + a13*( + a21*(a42*a55 - a45*a52) - a22*(a41*a55 - a45*a51) + a25*(a41*a52 - a42*a51)) - a15*( + a21*(a42*a53 - a43*a52) - a22*(a41*a53 - a43*a51) + a23*(a41*a52 - a42*a51)));
+        double m35 =  + a11*( + a22*(a43*a54 - a44*a53) - a23*(a42*a54 - a44*a52) + a24*(a42*a53 - a43*a52)) - a12*( + a21*(a43*a54 - a44*a53) - a23*(a41*a54 - a44*a51) + a24*(a41*a53 - a43*a51)) + a13*( + a21*(a42*a54 - a44*a52) - a22*(a41*a54 - a44*a51) + a24*(a41*a52 - a42*a51)) - a14*( + a21*(a42*a53 - a43*a52) - a22*(a41*a53 - a43*a51) + a23*(a41*a52 - a42*a51));
+        double m41 = -(  + a12*( + a23*(a34*a55 - a35*a54) - a24*(a33*a55 - a35*a53) + a25*(a33*a54 - a34*a53)) - a13*( + a22*(a34*a55 - a35*a54) - a24*(a32*a55 - a35*a52) + a25*(a32*a54 - a34*a52)) + a14*( + a22*(a33*a55 - a35*a53) - a23*(a32*a55 - a35*a52) + a25*(a32*a53 - a33*a52)) - a15*( + a22*(a33*a54 - a34*a53) - a23*(a32*a54 - a34*a52) + a24*(a32*a53 - a33*a52)));
+        double m42 =  + a11*( + a23*(a34*a55 - a35*a54) - a24*(a33*a55 - a35*a53) + a25*(a33*a54 - a34*a53)) - a13*( + a21*(a34*a55 - a35*a54) - a24*(a31*a55 - a35*a51) + a25*(a31*a54 - a34*a51)) + a14*( + a21*(a33*a55 - a35*a53) - a23*(a31*a55 - a35*a51) + a25*(a31*a53 - a33*a51)) - a15*( + a21*(a33*a54 - a34*a53) - a23*(a31*a54 - a34*a51) + a24*(a31*a53 - a33*a51));
+        double m43 = -(  + a11*( + a22*(a34*a55 - a35*a54) - a24*(a32*a55 - a35*a52) + a25*(a32*a54 - a34*a52)) - a12*( + a21*(a34*a55 - a35*a54) - a24*(a31*a55 - a35*a51) + a25*(a31*a54 - a34*a51)) + a14*( + a21*(a32*a55 - a35*a52) - a22*(a31*a55 - a35*a51) + a25*(a31*a52 - a32*a51)) - a15*( + a21*(a32*a54 - a34*a52) - a22*(a31*a54 - a34*a51) + a24*(a31*a52 - a32*a51)));
+        double m44 =  + a11*( + a22*(a33*a55 - a35*a53) - a23*(a32*a55 - a35*a52) + a25*(a32*a53 - a33*a52)) - a12*( + a21*(a33*a55 - a35*a53) - a23*(a31*a55 - a35*a51) + a25*(a31*a53 - a33*a51)) + a13*( + a21*(a32*a55 - a35*a52) - a22*(a31*a55 - a35*a51) + a25*(a31*a52 - a32*a51)) - a15*( + a21*(a32*a53 - a33*a52) - a22*(a31*a53 - a33*a51) + a23*(a31*a52 - a32*a51));
+        double m45 = -(  + a11*( + a22*(a33*a54 - a34*a53) - a23*(a32*a54 - a34*a52) + a24*(a32*a53 - a33*a52)) - a12*( + a21*(a33*a54 - a34*a53) - a23*(a31*a54 - a34*a51) + a24*(a31*a53 - a33*a51)) + a13*( + a21*(a32*a54 - a34*a52) - a22*(a31*a54 - a34*a51) + a24*(a31*a52 - a32*a51)) - a14*( + a21*(a32*a53 - a33*a52) - a22*(a31*a53 - a33*a51) + a23*(a31*a52 - a32*a51)));
+        double m51 =  + a12*( + a23*(a34*a45 - a35*a44) - a24*(a33*a45 - a35*a43) + a25*(a33*a44 - a34*a43)) - a13*( + a22*(a34*a45 - a35*a44) - a24*(a32*a45 - a35*a42) + a25*(a32*a44 - a34*a42)) + a14*( + a22*(a33*a45 - a35*a43) - a23*(a32*a45 - a35*a42) + a25*(a32*a43 - a33*a42)) - a15*( + a22*(a33*a44 - a34*a43) - a23*(a32*a44 - a34*a42) + a24*(a32*a43 - a33*a42));
+        double m52 = -(  + a11*( + a23*(a34*a45 - a35*a44) - a24*(a33*a45 - a35*a43) + a25*(a33*a44 - a34*a43)) - a13*( + a21*(a34*a45 - a35*a44) - a24*(a31*a45 - a35*a41) + a25*(a31*a44 - a34*a41)) + a14*( + a21*(a33*a45 - a35*a43) - a23*(a31*a45 - a35*a41) + a25*(a31*a43 - a33*a41)) - a15*( + a21*(a33*a44 - a34*a43) - a23*(a31*a44 - a34*a41) + a24*(a31*a43 - a33*a41)));
+        double m53 =  + a11*( + a22*(a34*a45 - a35*a44) - a24*(a32*a45 - a35*a42) + a25*(a32*a44 - a34*a42)) - a12*( + a21*(a34*a45 - a35*a44) - a24*(a31*a45 - a35*a41) + a25*(a31*a44 - a34*a41)) + a14*( + a21*(a32*a45 - a35*a42) - a22*(a31*a45 - a35*a41) + a25*(a31*a42 - a32*a41)) - a15*( + a21*(a32*a44 - a34*a42) - a22*(a31*a44 - a34*a41) + a24*(a31*a42 - a32*a41));
+        double m54 = -(  + a11*( + a22*(a33*a45 - a35*a43) - a23*(a32*a45 - a35*a42) + a25*(a32*a43 - a33*a42)) - a12*( + a21*(a33*a45 - a35*a43) - a23*(a31*a45 - a35*a41) + a25*(a31*a43 - a33*a41)) + a13*( + a21*(a32*a45 - a35*a42) - a22*(a31*a45 - a35*a41) + a25*(a31*a42 - a32*a41)) - a15*( + a21*(a32*a43 - a33*a42) - a22*(a31*a43 - a33*a41) + a23*(a31*a42 - a32*a41)));
+        double m55 =  + a11*( + a22*(a33*a44 - a34*a43) - a23*(a32*a44 - a34*a42) + a24*(a32*a43 - a33*a42)) - a12*( + a21*(a33*a44 - a34*a43) - a23*(a31*a44 - a34*a41) + a24*(a31*a43 - a33*a41)) + a13*( + a21*(a32*a44 - a34*a42) - a22*(a31*a44 - a34*a41) + a24*(a31*a42 - a32*a41)) - a14*( + a21*(a32*a43 - a33*a42) - a22*(a31*a43 - a33*a41) + a23*(a31*a42 - a32*a41));
+
+        double det = (a11*m11 + a12*m12 + a13*m13 + a14*m14 + a15*m15)/scale;
+
+        inv.a11 = m11/det;
+        inv.a12 = m21/det;
+        inv.a13 = m31/det;
+        inv.a14 = m41/det;
+        inv.a15 = m51/det;
+        inv.a21 = m12/det;
+        inv.a22 = m22/det;
+        inv.a23 = m32/det;
+        inv.a24 = m42/det;
+        inv.a25 = m52/det;
+        inv.a31 = m13/det;
+        inv.a32 = m23/det;
+        inv.a33 = m33/det;
+        inv.a34 = m43/det;
+        inv.a35 = m53/det;
+        inv.a41 = m14/det;
+        inv.a42 = m24/det;
+        inv.a43 = m34/det;
+        inv.a44 = m44/det;
+        inv.a45 = m54/det;
+        inv.a51 = m15/det;
+        inv.a52 = m25/det;
+        inv.a53 = m35/det;
+        inv.a54 = m45/det;
+        inv.a55 = m55/det;
+
+        return !Double.isNaN(det) && !Double.isInfinite(det);
+    }
+
+    /**
+     * Computes the determinant using minor matrices.
+     * <p></p>
+     * WARNING: Potentially less stable than using LU decomposition.
+     *
+     * @param mat Input matrix.  Not modified.
+     * @return The determinant.
+     */
+    public static double det( FixedMatrix5x5_64F mat ) {
+
+        double  a11 = mat.a22;
+        double  a12 = mat.a23;
+        double  a13 = mat.a24;
+        double  a14 = mat.a25;
+        double  a21 = mat.a32;
+        double  a22 = mat.a33;
+        double  a23 = mat.a34;
+        double  a24 = mat.a35;
+        double  a31 = mat.a42;
+        double  a32 = mat.a43;
+        double  a33 = mat.a44;
+        double  a34 = mat.a45;
+        double  a41 = mat.a52;
+        double  a42 = mat.a53;
+        double  a43 = mat.a54;
+        double  a44 = mat.a55;
+
+        double ret = 0;
+        ret += mat.a11 * ( + a11*( + a22*(a33*a44 - a34*a43) - a23*(a32*a44 - a34*a42) + a24*(a32*a43 - a33*a42)) - a12*( + a21*(a33*a44 - a34*a43) - a23*(a31*a44 - a34*a41) + a24*(a31*a43 - a33*a41)) + a13*( + a21*(a32*a44 - a34*a42) - a22*(a31*a44 - a34*a41) + a24*(a31*a42 - a32*a41)) - a14*( + a21*(a32*a43 - a33*a42) - a22*(a31*a43 - a33*a41) + a23*(a31*a42 - a32*a41)));
+        a11 = mat.a21;
+        a21 = mat.a31;
+        a31 = mat.a41;
+        a41 = mat.a51;
+        ret -= mat.a12 * ( + a11*( + a22*(a33*a44 - a34*a43) - a23*(a32*a44 - a34*a42) + a24*(a32*a43 - a33*a42)) - a12*( + a21*(a33*a44 - a34*a43) - a23*(a31*a44 - a34*a41) + a24*(a31*a43 - a33*a41)) + a13*( + a21*(a32*a44 - a34*a42) - a22*(a31*a44 - a34*a41) + a24*(a31*a42 - a32*a41)) - a14*( + a21*(a32*a43 - a33*a42) - a22*(a31*a43 - a33*a41) + a23*(a31*a42 - a32*a41)));
+        a12 = mat.a22;
+        a22 = mat.a32;
+        a32 = mat.a42;
+        a42 = mat.a52;
+        ret += mat.a13 * ( + a11*( + a22*(a33*a44 - a34*a43) - a23*(a32*a44 - a34*a42) + a24*(a32*a43 - a33*a42)) - a12*( + a21*(a33*a44 - a34*a43) - a23*(a31*a44 - a34*a41) + a24*(a31*a43 - a33*a41)) + a13*( + a21*(a32*a44 - a34*a42) - a22*(a31*a44 - a34*a41) + a24*(a31*a42 - a32*a41)) - a14*( + a21*(a32*a43 - a33*a42) - a22*(a31*a43 - a33*a41) + a23*(a31*a42 - a32*a41)));
+        a13 = mat.a23;
+        a23 = mat.a33;
+        a33 = mat.a43;
+        a43 = mat.a53;
+        ret -= mat.a14 * ( + a11*( + a22*(a33*a44 - a34*a43) - a23*(a32*a44 - a34*a42) + a24*(a32*a43 - a33*a42)) - a12*( + a21*(a33*a44 - a34*a43) - a23*(a31*a44 - a34*a41) + a24*(a31*a43 - a33*a41)) + a13*( + a21*(a32*a44 - a34*a42) - a22*(a31*a44 - a34*a41) + a24*(a31*a42 - a32*a41)) - a14*( + a21*(a32*a43 - a33*a42) - a22*(a31*a43 - a33*a41) + a23*(a31*a42 - a32*a41)));
+        a14 = mat.a24;
+        a24 = mat.a34;
+        a34 = mat.a44;
+        a44 = mat.a54;
+        ret += mat.a15 * ( + a11*( + a22*(a33*a44 - a34*a43) - a23*(a32*a44 - a34*a42) + a24*(a32*a43 - a33*a42)) - a12*( + a21*(a33*a44 - a34*a43) - a23*(a31*a44 - a34*a41) + a24*(a31*a43 - a33*a41)) + a13*( + a21*(a32*a44 - a34*a42) - a22*(a31*a44 - a34*a41) + a24*(a31*a42 - a32*a41)) - a14*( + a21*(a32*a43 - a33*a42) - a22*(a31*a43 - a33*a41) + a23*(a31*a42 - a32*a41)));
+
+        return ret;
+    }
+
+    /**
+     * <p>
+     * This computes the trace of the matrix:<br>
+     * <br>
+     * trace = ∑<sub>i=1:n</sub> { a<sub>ii</sub> }
+     * </p>
+     * <p>
+     * The trace is only defined for square matrices.
+     * </p>
+     *
+     * @param a A square matrix.  Not modified.
+     */
+    public static double trace( FixedMatrix5x5_64F a ) {
+        return a.a11 + a.a21 + a.a31 + a.a41 + a.a51;
+    }
+
+    /**
+     * <p>
+     * Extracts all diagonal elements from 'input' and places them inside the 'out' vector. Elements
+     * are in sequential order.
+     * </p>
+     *
+     *
+     * @param input Matrix.  Not modified.
+     * @param out Vector containing diagonal elements.  Modified.
+     */
+    public static void diag( FixedMatrix5x5_64F input , FixedMatrix5_64F out ) {
+        out.a1 = input.a11;
+        out.a2 = input.a22;
+        out.a3 = input.a33;
+        out.a4 = input.a44;
+        out.a5 = input.a55;
+    }
+
+    /**
+     * <p>
+     * Returns the value of the element in the matrix that has the largest value.<br>
+     * <br>
+     * Max{ a<sub>ij</sub> } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max element value of the matrix.
+     */
+    public static double elementMax( FixedMatrix5x5_64F a ) {
+        double max = a.a11;
+        max = Math.max(max,a.a12);
+        max = Math.max(max,a.a13);
+        max = Math.max(max,a.a14);
+        max = Math.max(max,a.a15);
+        max = Math.max(max,a.a21);
+        max = Math.max(max,a.a22);
+        max = Math.max(max,a.a23);
+        max = Math.max(max,a.a24);
+        max = Math.max(max,a.a25);
+        max = Math.max(max,a.a31);
+        max = Math.max(max,a.a32);
+        max = Math.max(max,a.a33);
+        max = Math.max(max,a.a34);
+        max = Math.max(max,a.a35);
+        max = Math.max(max,a.a41);
+        max = Math.max(max,a.a42);
+        max = Math.max(max,a.a43);
+        max = Math.max(max,a.a44);
+        max = Math.max(max,a.a45);
+        max = Math.max(max,a.a51);
+        max = Math.max(max,a.a52);
+        max = Math.max(max,a.a53);
+        max = Math.max(max,a.a54);
+        max = Math.max(max,a.a55);
+
+        return max;
+    }
+
+    /**
+     * <p>
+     * Returns the value of the element in the vector that has the largest value.<br>
+     * <br>
+     * Max{ a<sub>i</sub> } for all i<br>
+     * </p>
+     *
+     * @param a A vector. Not modified.
+     * @return The max element value of the matrix.
+     */
+    public static double elementMax( FixedMatrix5_64F a ) {
+        double max = a.a1;
+        max = Math.max(max,a.a2);
+        max = Math.max(max,a.a3);
+        max = Math.max(max,a.a4);
+        max = Math.max(max,a.a5);
+
+        return max;
+    }
+
+    /**
+     * <p>
+     * Returns the absolute value of the element in the matrix that has the largest absolute value.<br>
+     * <br>
+     * Max{ |a<sub>ij</sub>| } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max abs element value of the matrix.
+     */
+    public static double elementMaxAbs( FixedMatrix5x5_64F a ) {
+        double max = a.a11;
+        max = Math.max(max,Math.abs(a.a12));
+        max = Math.max(max,Math.abs(a.a13));
+        max = Math.max(max,Math.abs(a.a14));
+        max = Math.max(max,Math.abs(a.a15));
+        max = Math.max(max,Math.abs(a.a21));
+        max = Math.max(max,Math.abs(a.a22));
+        max = Math.max(max,Math.abs(a.a23));
+        max = Math.max(max,Math.abs(a.a24));
+        max = Math.max(max,Math.abs(a.a25));
+        max = Math.max(max,Math.abs(a.a31));
+        max = Math.max(max,Math.abs(a.a32));
+        max = Math.max(max,Math.abs(a.a33));
+        max = Math.max(max,Math.abs(a.a34));
+        max = Math.max(max,Math.abs(a.a35));
+        max = Math.max(max,Math.abs(a.a41));
+        max = Math.max(max,Math.abs(a.a42));
+        max = Math.max(max,Math.abs(a.a43));
+        max = Math.max(max,Math.abs(a.a44));
+        max = Math.max(max,Math.abs(a.a45));
+        max = Math.max(max,Math.abs(a.a51));
+        max = Math.max(max,Math.abs(a.a52));
+        max = Math.max(max,Math.abs(a.a53));
+        max = Math.max(max,Math.abs(a.a54));
+        max = Math.max(max,Math.abs(a.a55));
+
+        return max;
+    }
+
+    /**
+     * <p>
+     * Returns the absolute value of the element in the vector that has the largest absolute value.<br>
+     * <br>
+     * Max{ |a<sub>i</sub>| } for all i<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max abs element value of the vector.
+     */
+    public static double elementMaxAbs( FixedMatrix5_64F a ) {
+        double max = a.a1;
+        max = Math.max(max,Math.abs(a.a2));
+        max = Math.max(max,Math.abs(a.a3));
+        max = Math.max(max,Math.abs(a.a4));
+        max = Math.max(max,Math.abs(a.a5));
+
+        return max;
+    }
+
+    /**
+     * <p>
+     * Returns the value of the element in the matrix that has the minimum value.<br>
+     * <br>
+     * Min{ a<sub>ij</sub> } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The value of element in the matrix with the minimum value.
+     */
+    public static double elementMin( FixedMatrix5x5_64F a ) {
+        double min = a.a11;
+        min = Math.min(min, a.a12);
+        min = Math.min(min, a.a13);
+        min = Math.min(min, a.a14);
+        min = Math.min(min, a.a15);
+        min = Math.min(min, a.a21);
+        min = Math.min(min, a.a22);
+        min = Math.min(min, a.a23);
+        min = Math.min(min, a.a24);
+        min = Math.min(min, a.a25);
+        min = Math.min(min, a.a31);
+        min = Math.min(min, a.a32);
+        min = Math.min(min, a.a33);
+        min = Math.min(min, a.a34);
+        min = Math.min(min, a.a35);
+        min = Math.min(min, a.a41);
+        min = Math.min(min, a.a42);
+        min = Math.min(min, a.a43);
+        min = Math.min(min, a.a44);
+        min = Math.min(min, a.a45);
+        min = Math.min(min, a.a51);
+        min = Math.min(min, a.a52);
+        min = Math.min(min, a.a53);
+        min = Math.min(min, a.a54);
+        min = Math.min(min, a.a55);
+
+        return min;
+    }
+
+    /**
+     * <p>
+     * Returns the value of the element in the vector that has the minimum value.<br>
+     * <br>
+     * Min{ a<sub>i</sub> } for all<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The value of element in the vector with the minimum value.
+     */
+    public static double elementMin( FixedMatrix5_64F a ) {
+        double min = a.a1;
+        min = Math.min(min, a.a2);
+        min = Math.min(min, a.a3);
+        min = Math.min(min, a.a4);
+        min = Math.min(min, a.a5);
+
+        return min;
+    }
+
+    /**
+     * <p>
+     * Returns the absolute value of the element in the matrix that has the smallest absolute value.<br>
+     * <br>
+     * Min{ |a<sub>ij</sub>| } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max element value of the matrix.
+     */
+    public static double elementMinAbs( FixedMatrix5x5_64F a ) {
+        double min = a.a11;
+        min = Math.min(min,Math.abs(a.a12));
+        min = Math.min(min,Math.abs(a.a13));
+        min = Math.min(min,Math.abs(a.a14));
+        min = Math.min(min,Math.abs(a.a15));
+        min = Math.min(min,Math.abs(a.a21));
+        min = Math.min(min,Math.abs(a.a22));
+        min = Math.min(min,Math.abs(a.a23));
+        min = Math.min(min,Math.abs(a.a24));
+        min = Math.min(min,Math.abs(a.a25));
+        min = Math.min(min,Math.abs(a.a31));
+        min = Math.min(min,Math.abs(a.a32));
+        min = Math.min(min,Math.abs(a.a33));
+        min = Math.min(min,Math.abs(a.a34));
+        min = Math.min(min,Math.abs(a.a35));
+        min = Math.min(min,Math.abs(a.a41));
+        min = Math.min(min,Math.abs(a.a42));
+        min = Math.min(min,Math.abs(a.a43));
+        min = Math.min(min,Math.abs(a.a44));
+        min = Math.min(min,Math.abs(a.a45));
+        min = Math.min(min,Math.abs(a.a51));
+        min = Math.min(min,Math.abs(a.a52));
+        min = Math.min(min,Math.abs(a.a53));
+        min = Math.min(min,Math.abs(a.a54));
+        min = Math.min(min,Math.abs(a.a55));
+
+        return min;
+    }
+
+    /**
+     * <p>
+     * Returns the absolute value of the element in the vector that has the smallest absolute value.<br>
+     * <br>
+     * Min{ |a<sub>i</sub>| } for all i<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max element value of the vector.
+     */
+    public static double elementMinAbs( FixedMatrix5_64F a ) {
+        double min = a.a1;
+        min = Math.min(min,Math.abs(a.a2));
+        min = Math.min(min,Math.abs(a.a3));
+        min = Math.min(min,Math.abs(a.a4));
+        min = Math.min(min,Math.abs(a.a5));
+
+        return min;
+    }
+
+    /**
+     * <p>Performs an element by element multiplication operation:<br>
+     * <br>
+     * a<sub>ij</sub> = a<sub>ij</sub> * b<sub>ij</sub> <br>
+     * </p>
+     * @param a The left matrix in the multiplication operation. Modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     */
+    public static void elementMult( FixedMatrix5x5_64F a , FixedMatrix5x5_64F b) {
+        a.a11 *= b.a11; a.a12 *= b.a12; a.a13 *= b.a13; a.a14 *= b.a14; a.a15 *= b.a15;
+        a.a21 *= b.a21; a.a22 *= b.a22; a.a23 *= b.a23; a.a24 *= b.a24; a.a25 *= b.a25;
+        a.a31 *= b.a31; a.a32 *= b.a32; a.a33 *= b.a33; a.a34 *= b.a34; a.a35 *= b.a35;
+        a.a41 *= b.a41; a.a42 *= b.a42; a.a43 *= b.a43; a.a44 *= b.a44; a.a45 *= b.a45;
+        a.a51 *= b.a51; a.a52 *= b.a52; a.a53 *= b.a53; a.a54 *= b.a54; a.a55 *= b.a55;
+    }
+
+    /**
+     * <p>Performs an element by element multiplication operation:<br>
+     * <br>
+     * a<sub>i</sub> = a<sub>i</sub> * b<sub>i</sub> <br>
+     * </p>
+     * @param a The left vector in the multiplication operation. Modified.
+     * @param b The right vector in the multiplication operation. Not modified.
+     */
+    public static void elementMult( FixedMatrix5_64F a , FixedMatrix5_64F b) {
+        a.a1 *= b.a1;
+        a.a2 *= b.a2;
+        a.a3 *= b.a3;
+        a.a4 *= b.a4;
+        a.a5 *= b.a5;
+    }
+
+    /**
+     * <p>Performs an element by element multiplication operation:<br>
+     * <br>
+     * c<sub>ij</sub> = a<sub>ij</sub> * b<sub>ij</sub> <br>
+     * </p>
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void elementMult( FixedMatrix5x5_64F a , FixedMatrix5x5_64F b , FixedMatrix5x5_64F c ) {
+        c.a11 = a.a11*b.a11; c.a12 = a.a12*b.a12; c.a13 = a.a13*b.a13; c.a14 = a.a14*b.a14; c.a15 = a.a15*b.a15;
+        c.a21 = a.a21*b.a21; c.a22 = a.a22*b.a22; c.a23 = a.a23*b.a23; c.a24 = a.a24*b.a24; c.a25 = a.a25*b.a25;
+        c.a31 = a.a31*b.a31; c.a32 = a.a32*b.a32; c.a33 = a.a33*b.a33; c.a34 = a.a34*b.a34; c.a35 = a.a35*b.a35;
+        c.a41 = a.a41*b.a41; c.a42 = a.a42*b.a42; c.a43 = a.a43*b.a43; c.a44 = a.a44*b.a44; c.a45 = a.a45*b.a45;
+        c.a51 = a.a51*b.a51; c.a52 = a.a52*b.a52; c.a53 = a.a53*b.a53; c.a54 = a.a54*b.a54; c.a55 = a.a55*b.a55;
+    }
+
+    /**
+     * <p>Performs an element by element multiplication operation:<br>
+     * <br>
+     * c<sub>i</sub> = a<sub>i</sub> * b<sub>j</sub> <br>
+     * </p>
+     * @param a The left vector in the multiplication operation. Not modified.
+     * @param b The right vector in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void elementMult( FixedMatrix5_64F a , FixedMatrix5_64F b , FixedMatrix5_64F c ) {
+        c.a1 = a.a1*b.a1;
+        c.a2 = a.a2*b.a2;
+        c.a3 = a.a3*b.a3;
+        c.a4 = a.a4*b.a4;
+        c.a5 = a.a5*b.a5;
+    }
+
+    /**
+     * <p>Performs an element by element division operation:<br>
+     * <br>
+     * a<sub>ij</sub> = a<sub>ij</sub> / b<sub>ij</sub> <br>
+     * </p>
+     * @param a The left matrix in the division operation. Modified.
+     * @param b The right matrix in the division operation. Not modified.
+     */
+    public static void elementDiv( FixedMatrix5x5_64F a , FixedMatrix5x5_64F b) {
+        a.a11 /= b.a11; a.a12 /= b.a12; a.a13 /= b.a13; a.a14 /= b.a14; a.a15 /= b.a15;
+        a.a21 /= b.a21; a.a22 /= b.a22; a.a23 /= b.a23; a.a24 /= b.a24; a.a25 /= b.a25;
+        a.a31 /= b.a31; a.a32 /= b.a32; a.a33 /= b.a33; a.a34 /= b.a34; a.a35 /= b.a35;
+        a.a41 /= b.a41; a.a42 /= b.a42; a.a43 /= b.a43; a.a44 /= b.a44; a.a45 /= b.a45;
+        a.a51 /= b.a51; a.a52 /= b.a52; a.a53 /= b.a53; a.a54 /= b.a54; a.a55 /= b.a55;
+    }
+
+    /**
+     * <p>Performs an element by element division operation:<br>
+     * <br>
+     * a<sub>i</sub> = a<sub>i</sub> / b<sub>i</sub> <br>
+     * </p>
+     * @param a The left vector in the division operation. Modified.
+     * @param b The right vector in the division operation. Not modified.
+     */
+    public static void elementDiv( FixedMatrix5_64F a , FixedMatrix5_64F b) {
+        a.a1 /= b.a1;
+        a.a2 /= b.a2;
+        a.a3 /= b.a3;
+        a.a4 /= b.a4;
+        a.a5 /= b.a5;
+    }
+
+    /**
+     * <p>Performs an element by element division operation:<br>
+     * <br>
+     * c<sub>ij</sub> = a<sub>ij</sub> / b<sub>ij</sub> <br>
+     * </p>
+     * @param a The left matrix in the division operation. Not modified.
+     * @param b The right matrix in the division operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void elementDiv( FixedMatrix5x5_64F a , FixedMatrix5x5_64F b , FixedMatrix5x5_64F c ) {
+        c.a11 = a.a11/b.a11; c.a12 = a.a12/b.a12; c.a13 = a.a13/b.a13; c.a14 = a.a14/b.a14; c.a15 = a.a15/b.a15;
+        c.a21 = a.a21/b.a21; c.a22 = a.a22/b.a22; c.a23 = a.a23/b.a23; c.a24 = a.a24/b.a24; c.a25 = a.a25/b.a25;
+        c.a31 = a.a31/b.a31; c.a32 = a.a32/b.a32; c.a33 = a.a33/b.a33; c.a34 = a.a34/b.a34; c.a35 = a.a35/b.a35;
+        c.a41 = a.a41/b.a41; c.a42 = a.a42/b.a42; c.a43 = a.a43/b.a43; c.a44 = a.a44/b.a44; c.a45 = a.a45/b.a45;
+        c.a51 = a.a51/b.a51; c.a52 = a.a52/b.a52; c.a53 = a.a53/b.a53; c.a54 = a.a54/b.a54; c.a55 = a.a55/b.a55;
+    }
+
+    /**
+     * <p>Performs an element by element division operation:<br>
+     * <br>
+     * c<sub>i</sub> = a<sub>i</sub> / b<sub>i</sub> <br>
+     * </p>
+     * @param a The left vector in the division operation. Not modified.
+     * @param b The right vector in the division operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void elementDiv( FixedMatrix5_64F a , FixedMatrix5_64F b , FixedMatrix5_64F c ) {
+        c.a1 = a.a1/b.a1;
+        c.a2 = a.a2/b.a2;
+        c.a3 = a.a3/b.a3;
+        c.a4 = a.a4/b.a4;
+        c.a5 = a.a5/b.a5;
+    }
+
+    /**
+     * <p>
+     * Performs an in-place element by element scalar multiplication.<br>
+     * <br>
+     * a<sub>ij</sub> = α*a<sub>ij</sub>
+     * </p>
+     *
+     * @param a The matrix that is to be scaled.  Modified.
+     * @param alpha the amount each element is multiplied by.
+     */
+    public static void scale( double alpha , FixedMatrix5x5_64F a ) {
+        a.a11 *= alpha; a.a12 *= alpha; a.a13 *= alpha; a.a14 *= alpha; a.a15 *= alpha;
+        a.a21 *= alpha; a.a22 *= alpha; a.a23 *= alpha; a.a24 *= alpha; a.a25 *= alpha;
+        a.a31 *= alpha; a.a32 *= alpha; a.a33 *= alpha; a.a34 *= alpha; a.a35 *= alpha;
+        a.a41 *= alpha; a.a42 *= alpha; a.a43 *= alpha; a.a44 *= alpha; a.a45 *= alpha;
+        a.a51 *= alpha; a.a52 *= alpha; a.a53 *= alpha; a.a54 *= alpha; a.a55 *= alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an in-place element by element scalar multiplication.<br>
+     * <br>
+     * a<sub>ij</sub> = α*a<sub>ij</sub>
+     * </p>
+     *
+     * @param a The vector that is to be scaled.  Modified.
+     * @param alpha the amount each element is multiplied by.
+     */
+    public static void scale( double alpha , FixedMatrix5_64F a ) {
+        a.a1 *= alpha;
+        a.a2 *= alpha;
+        a.a3 *= alpha;
+        a.a4 *= alpha;
+        a.a5 *= alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an element by element scalar multiplication.<br>
+     * <br>
+     * b<sub>ij</sub> = α*a<sub>ij</sub>
+     * </p>
+     *
+     * @param alpha the amount each element is multiplied by.
+     * @param a The matrix that is to be scaled.  Not modified.
+     * @param b Where the scaled matrix is stored. Modified.
+     */
+    public static void scale( double alpha , FixedMatrix5x5_64F a , FixedMatrix5x5_64F b ) {
+        b.a11 = a.a11*alpha; b.a12 = a.a12*alpha; b.a13 = a.a13*alpha; b.a14 = a.a14*alpha; b.a15 = a.a15*alpha;
+        b.a21 = a.a21*alpha; b.a22 = a.a22*alpha; b.a23 = a.a23*alpha; b.a24 = a.a24*alpha; b.a25 = a.a25*alpha;
+        b.a31 = a.a31*alpha; b.a32 = a.a32*alpha; b.a33 = a.a33*alpha; b.a34 = a.a34*alpha; b.a35 = a.a35*alpha;
+        b.a41 = a.a41*alpha; b.a42 = a.a42*alpha; b.a43 = a.a43*alpha; b.a44 = a.a44*alpha; b.a45 = a.a45*alpha;
+        b.a51 = a.a51*alpha; b.a52 = a.a52*alpha; b.a53 = a.a53*alpha; b.a54 = a.a54*alpha; b.a55 = a.a55*alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an element by element scalar multiplication.<br>
+     * <br>
+     * b<sub>i</sub> = α*a<sub>i</sub>
+     * </p>
+     *
+     * @param alpha the amount each element is multiplied by.
+     * @param a The vector that is to be scaled.  Not modified.
+     * @param b Where the scaled matrix is stored. Modified.
+     */
+    public static void scale( double alpha , FixedMatrix5_64F a , FixedMatrix5_64F b ) {
+        b.a1 = a.a1*alpha;
+        b.a2 = a.a2*alpha;
+        b.a3 = a.a3*alpha;
+        b.a4 = a.a4*alpha;
+        b.a5 = a.a5*alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an in-place element by element scalar division. Scalar denominator.<br>
+     * <br>
+     * a<sub>ij</sub> = a<sub>ij</sub>/α
+     * </p>
+     *
+     * @param a The matrix whose elements are to be divided.  Modified.
+     * @param alpha the amount each element is divided by.
+     */
+    public static void divide( FixedMatrix5x5_64F a , double alpha ) {
+        a.a11 /= alpha; a.a12 /= alpha; a.a13 /= alpha; a.a14 /= alpha; a.a15 /= alpha;
+        a.a21 /= alpha; a.a22 /= alpha; a.a23 /= alpha; a.a24 /= alpha; a.a25 /= alpha;
+        a.a31 /= alpha; a.a32 /= alpha; a.a33 /= alpha; a.a34 /= alpha; a.a35 /= alpha;
+        a.a41 /= alpha; a.a42 /= alpha; a.a43 /= alpha; a.a44 /= alpha; a.a45 /= alpha;
+        a.a51 /= alpha; a.a52 /= alpha; a.a53 /= alpha; a.a54 /= alpha; a.a55 /= alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an in-place element by element scalar division. Scalar denominator.<br>
+     * <br>
+     * a<sub>i</sub> = a<sub>i</sub>/α
+     * </p>
+     *
+     * @param a The vector whose elements are to be divided.  Modified.
+     * @param alpha the amount each element is divided by.
+     */
+    public static void divide( FixedMatrix5_64F a , double alpha ) {
+        a.a1 /= alpha;
+        a.a2 /= alpha;
+        a.a3 /= alpha;
+        a.a4 /= alpha;
+        a.a5 /= alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an element by element scalar division.  Scalar denominator.<br>
+     * <br>
+     * b<sub>ij</sub> = a<sub>ij</sub> /α
+     * </p>
+     *
+     * @param alpha the amount each element is divided by.
+     * @param a The matrix whose elements are to be divided.  Not modified.
+     * @param b Where the results are stored. Modified.
+     */
+    public static void divide( FixedMatrix5x5_64F a , double alpha , FixedMatrix5x5_64F b ) {
+        b.a11 = a.a11/alpha; b.a12 = a.a12/alpha; b.a13 = a.a13/alpha; b.a14 = a.a14/alpha; b.a15 = a.a15/alpha;
+        b.a21 = a.a21/alpha; b.a22 = a.a22/alpha; b.a23 = a.a23/alpha; b.a24 = a.a24/alpha; b.a25 = a.a25/alpha;
+        b.a31 = a.a31/alpha; b.a32 = a.a32/alpha; b.a33 = a.a33/alpha; b.a34 = a.a34/alpha; b.a35 = a.a35/alpha;
+        b.a41 = a.a41/alpha; b.a42 = a.a42/alpha; b.a43 = a.a43/alpha; b.a44 = a.a44/alpha; b.a45 = a.a45/alpha;
+        b.a51 = a.a51/alpha; b.a52 = a.a52/alpha; b.a53 = a.a53/alpha; b.a54 = a.a54/alpha; b.a55 = a.a55/alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an element by element scalar division.  Scalar denominator.<br>
+     * <br>
+     * b<sub>i</sub> = a<sub>i</sub> /α
+     * </p>
+     *
+     * @param alpha the amount each element is divided by.
+     * @param a The vector whose elements are to be divided.  Not modified.
+     * @param b Where the results are stored. Modified.
+     */
+    public static void divide( FixedMatrix5_64F a , double alpha , FixedMatrix5_64F b ) {
+        b.a1 = a.a1/alpha;
+        b.a2 = a.a2/alpha;
+        b.a3 = a.a3/alpha;
+        b.a4 = a.a4/alpha;
+        b.a5 = a.a5/alpha;
+    }
+
+    /**
+     * <p>
+     * Changes the sign of every element in the matrix.<br>
+     * <br>
+     * a<sub>ij</sub> = -a<sub>ij</sub>
+     * </p>
+     *
+     * @param a A matrix. Modified.
+     */
+    public static void changeSign( FixedMatrix5x5_64F a )
+    {
+        a.a11 = -a.a11; a.a12 = -a.a12; a.a13 = -a.a13; a.a14 = -a.a14; a.a15 = -a.a15;
+        a.a21 = -a.a21; a.a22 = -a.a22; a.a23 = -a.a23; a.a24 = -a.a24; a.a25 = -a.a25;
+        a.a31 = -a.a31; a.a32 = -a.a32; a.a33 = -a.a33; a.a34 = -a.a34; a.a35 = -a.a35;
+        a.a41 = -a.a41; a.a42 = -a.a42; a.a43 = -a.a43; a.a44 = -a.a44; a.a45 = -a.a45;
+        a.a51 = -a.a51; a.a52 = -a.a52; a.a53 = -a.a53; a.a54 = -a.a54; a.a55 = -a.a55;
+    }
+
+    /**
+     * <p>
+     * Changes the sign of every element in the vector.<br>
+     * <br>
+     * a<sub>i</sub> = -a<sub>i</sub>
+     * </p>
+     *
+     * @param a A vector. Modified.
+     */
+    public static void changeSign( FixedMatrix5_64F a )
+    {
+        a.a1 = -a.a1;
+        a.a2 = -a.a2;
+        a.a3 = -a.a3;
+        a.a4 = -a.a4;
+        a.a5 = -a.a5;
+    }
+
+    /**
+     * <p>
+     * Sets every element in the matrix to the specified value.<br>
+     * <br>
+     * a<sub>ij</sub> = value
+     * <p>
+     *
+     * @param a A matrix whose elements are about to be set. Modified.
+     * @param v The value each element will have.
+     */
+    public static void fill( FixedMatrix5x5_64F a , double v  ) {
+        a.a11 = v; a.a12 = v; a.a13 = v; a.a14 = v; a.a15 = v;
+        a.a21 = v; a.a22 = v; a.a23 = v; a.a24 = v; a.a25 = v;
+        a.a31 = v; a.a32 = v; a.a33 = v; a.a34 = v; a.a35 = v;
+        a.a41 = v; a.a42 = v; a.a43 = v; a.a44 = v; a.a45 = v;
+        a.a51 = v; a.a52 = v; a.a53 = v; a.a54 = v; a.a55 = v;
+    }
+
+    /**
+     * <p>
+     * Sets every element in the vector to the specified value.<br>
+     * <br>
+     * a<sub>i</sub> = value
+     * <p>
+     *
+     * @param a A vector whose elements are about to be set. Modified.
+     * @param v The value each element will have.
+     */
+    public static void fill( FixedMatrix5_64F a , double v  ) {
+        a.a1 = v;
+        a.a2 = v;
+        a.a3 = v;
+        a.a4 = v;
+        a.a5 = v;
+    }
+
+    /**
+     * Extracts the row from the matrix a.
+     * @param a Input matrix
+     * @param row Which row is to be extracted
+     * @param out output. Storage for the extracted row. If null then a new vector will be returned.
+     * @return The extracted row.
+     */
+    public static FixedMatrix5_64F extractRow( FixedMatrix5x5_64F a , int row , FixedMatrix5_64F out ) {
+        if( out == null) out = new FixedMatrix5_64F();
+        switch( row ) {
+            case 0:
+                out.a1 = a.a11;
+                out.a2 = a.a12;
+                out.a3 = a.a13;
+                out.a4 = a.a14;
+                out.a5 = a.a15;
+            break;
+            case 1:
+                out.a1 = a.a21;
+                out.a2 = a.a22;
+                out.a3 = a.a23;
+                out.a4 = a.a24;
+                out.a5 = a.a25;
+            break;
+            case 2:
+                out.a1 = a.a31;
+                out.a2 = a.a32;
+                out.a3 = a.a33;
+                out.a4 = a.a34;
+                out.a5 = a.a35;
+            break;
+            case 3:
+                out.a1 = a.a41;
+                out.a2 = a.a42;
+                out.a3 = a.a43;
+                out.a4 = a.a44;
+                out.a5 = a.a45;
+            break;
+            case 4:
+                out.a1 = a.a51;
+                out.a2 = a.a52;
+                out.a3 = a.a53;
+                out.a4 = a.a54;
+                out.a5 = a.a55;
+            break;
+            default:
+                throw new IllegalArgumentException("Out of bounds row.  row = "+row);
+        }
+        return out;
+    }
+
+    /**
+     * Extracts the column from the matrix a.
+     * @param a Input matrix
+     * @param column Which column is to be extracted
+     * @param out output. Storage for the extracted column. If null then a new vector will be returned.
+     * @return The extracted column.
+     */
+    public static FixedMatrix5_64F extractColumn( FixedMatrix5x5_64F a , int column , FixedMatrix5_64F out ) {
+        if( out == null) out = new FixedMatrix5_64F();
+        switch( column ) {
+            case 0:
+                out.a1 = a.a11;
+                out.a2 = a.a21;
+                out.a3 = a.a31;
+                out.a4 = a.a41;
+                out.a5 = a.a51;
+            break;
+            case 1:
+                out.a1 = a.a12;
+                out.a2 = a.a22;
+                out.a3 = a.a32;
+                out.a4 = a.a42;
+                out.a5 = a.a52;
+            break;
+            case 2:
+                out.a1 = a.a13;
+                out.a2 = a.a23;
+                out.a3 = a.a33;
+                out.a4 = a.a43;
+                out.a5 = a.a53;
+            break;
+            case 3:
+                out.a1 = a.a14;
+                out.a2 = a.a24;
+                out.a3 = a.a34;
+                out.a4 = a.a44;
+                out.a5 = a.a54;
+            break;
+            case 4:
+                out.a1 = a.a15;
+                out.a2 = a.a25;
+                out.a3 = a.a35;
+                out.a4 = a.a45;
+                out.a5 = a.a55;
+            break;
+            default:
+                throw new IllegalArgumentException("Out of bounds column.  column = "+column);
+        }
+        return out;
+    }
+
+}
+
diff --git a/main/dense64/src/org/ejml/alg/fixed/FixedOps6.java b/main/dense64/src/org/ejml/alg/fixed/FixedOps6.java
new file mode 100644
index 0000000..fed6826
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/fixed/FixedOps6.java
@@ -0,0 +1,1708 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+package org.ejml.alg.fixed;
+
+import org.ejml.data.FixedMatrix6_64F;
+import org.ejml.data.FixedMatrix6x6_64F;
+
+/**
+ * <p>Common matrix operations for fixed sized matrices which are 6 x 6 or 6 element vectors.</p>
+ * <p>DO NOT MODIFY.  Automatically generated code created by GenerateFixedOps</p>
+ *
+ * @author Peter Abeles
+ */
+public class FixedOps6 {
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a + b <br>
+     * c<sub>ij</sub> = a<sub>ij</sub> + b<sub>ij</sub> <br>
+     * </p>
+     *
+     * <p>
+     * Matrix C can be the same instance as Matrix A and/or B.
+     * </p>
+     *
+     * @param a A Matrix. Not modified.
+     * @param b A Matrix. Not modified.
+     * @param c A Matrix where the results are stored. Modified.
+     */
+    public static void add( FixedMatrix6x6_64F a , FixedMatrix6x6_64F b , FixedMatrix6x6_64F c ) {
+        c.a11 = a.a11 + b.a11;
+        c.a12 = a.a12 + b.a12;
+        c.a13 = a.a13 + b.a13;
+        c.a14 = a.a14 + b.a14;
+        c.a15 = a.a15 + b.a15;
+        c.a16 = a.a16 + b.a16;
+        c.a21 = a.a21 + b.a21;
+        c.a22 = a.a22 + b.a22;
+        c.a23 = a.a23 + b.a23;
+        c.a24 = a.a24 + b.a24;
+        c.a25 = a.a25 + b.a25;
+        c.a26 = a.a26 + b.a26;
+        c.a31 = a.a31 + b.a31;
+        c.a32 = a.a32 + b.a32;
+        c.a33 = a.a33 + b.a33;
+        c.a34 = a.a34 + b.a34;
+        c.a35 = a.a35 + b.a35;
+        c.a36 = a.a36 + b.a36;
+        c.a41 = a.a41 + b.a41;
+        c.a42 = a.a42 + b.a42;
+        c.a43 = a.a43 + b.a43;
+        c.a44 = a.a44 + b.a44;
+        c.a45 = a.a45 + b.a45;
+        c.a46 = a.a46 + b.a46;
+        c.a51 = a.a51 + b.a51;
+        c.a52 = a.a52 + b.a52;
+        c.a53 = a.a53 + b.a53;
+        c.a54 = a.a54 + b.a54;
+        c.a55 = a.a55 + b.a55;
+        c.a56 = a.a56 + b.a56;
+        c.a61 = a.a61 + b.a61;
+        c.a62 = a.a62 + b.a62;
+        c.a63 = a.a63 + b.a63;
+        c.a64 = a.a64 + b.a64;
+        c.a65 = a.a65 + b.a65;
+        c.a66 = a.a66 + b.a66;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a + b <br>
+     * c<sub>i</sub> = a<sub>i</sub> + b<sub>i</sub> <br>
+     * </p>
+     *
+     * <p>
+     * Vector C can be the same instance as Vector A and/or B.
+     * </p>
+     *
+     * @param a A Vector. Not modified.
+     * @param b A Vector. Not modified.
+     * @param c A Vector where the results are stored. Modified.
+     */
+    public static void add( FixedMatrix6_64F a , FixedMatrix6_64F b , FixedMatrix6_64F c ) {
+        c.a1 = a.a1 + b.a1;
+        c.a2 = a.a2 + b.a2;
+        c.a3 = a.a3 + b.a3;
+        c.a4 = a.a4 + b.a4;
+        c.a5 = a.a5 + b.a5;
+        c.a6 = a.a6 + b.a6;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * a = a + b <br>
+     * a<sub>ij</sub> = a<sub>ij</sub> + b<sub>ij</sub> <br>
+     * </p>
+     *
+     * @param a A Matrix. Modified.
+     * @param b A Matrix. Not modified.
+     */
+    public static void addEquals( FixedMatrix6x6_64F a , FixedMatrix6x6_64F b ) {
+        a.a11 += b.a11;
+        a.a12 += b.a12;
+        a.a13 += b.a13;
+        a.a14 += b.a14;
+        a.a15 += b.a15;
+        a.a16 += b.a16;
+        a.a21 += b.a21;
+        a.a22 += b.a22;
+        a.a23 += b.a23;
+        a.a24 += b.a24;
+        a.a25 += b.a25;
+        a.a26 += b.a26;
+        a.a31 += b.a31;
+        a.a32 += b.a32;
+        a.a33 += b.a33;
+        a.a34 += b.a34;
+        a.a35 += b.a35;
+        a.a36 += b.a36;
+        a.a41 += b.a41;
+        a.a42 += b.a42;
+        a.a43 += b.a43;
+        a.a44 += b.a44;
+        a.a45 += b.a45;
+        a.a46 += b.a46;
+        a.a51 += b.a51;
+        a.a52 += b.a52;
+        a.a53 += b.a53;
+        a.a54 += b.a54;
+        a.a55 += b.a55;
+        a.a56 += b.a56;
+        a.a61 += b.a61;
+        a.a62 += b.a62;
+        a.a63 += b.a63;
+        a.a64 += b.a64;
+        a.a65 += b.a65;
+        a.a66 += b.a66;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * a = a + b <br>
+     * a<sub>i</sub> = a<sub>i</sub> + b<sub>i</sub> <br>
+     * </p>
+     *
+     * @param a A Vector. Modified.
+     * @param b A Vector. Not modified.
+     */
+    public static void addEquals( FixedMatrix6_64F a , FixedMatrix6_64F b ) {
+        a.a1 += b.a1;
+        a.a2 += b.a2;
+        a.a3 += b.a3;
+        a.a4 += b.a4;
+        a.a5 += b.a5;
+        a.a6 += b.a6;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a - b <br>
+     * c<sub>ij</sub> = a<sub>ij</sub> - b<sub>ij</sub> <br>
+     * </p>
+     *
+     * <p>
+     * Matrix C can be the same instance as Matrix A and/or B.
+     * </p>
+     *
+     * @param a A Matrix. Not modified.
+     * @param b A Matrix. Not modified.
+     * @param c A Matrix where the results are stored. Modified.
+     */
+    public static void subtract( FixedMatrix6x6_64F a , FixedMatrix6x6_64F b , FixedMatrix6x6_64F c ) {
+        c.a11 = a.a11 - b.a11;
+        c.a12 = a.a12 - b.a12;
+        c.a13 = a.a13 - b.a13;
+        c.a14 = a.a14 - b.a14;
+        c.a15 = a.a15 - b.a15;
+        c.a16 = a.a16 - b.a16;
+        c.a21 = a.a21 - b.a21;
+        c.a22 = a.a22 - b.a22;
+        c.a23 = a.a23 - b.a23;
+        c.a24 = a.a24 - b.a24;
+        c.a25 = a.a25 - b.a25;
+        c.a26 = a.a26 - b.a26;
+        c.a31 = a.a31 - b.a31;
+        c.a32 = a.a32 - b.a32;
+        c.a33 = a.a33 - b.a33;
+        c.a34 = a.a34 - b.a34;
+        c.a35 = a.a35 - b.a35;
+        c.a36 = a.a36 - b.a36;
+        c.a41 = a.a41 - b.a41;
+        c.a42 = a.a42 - b.a42;
+        c.a43 = a.a43 - b.a43;
+        c.a44 = a.a44 - b.a44;
+        c.a45 = a.a45 - b.a45;
+        c.a46 = a.a46 - b.a46;
+        c.a51 = a.a51 - b.a51;
+        c.a52 = a.a52 - b.a52;
+        c.a53 = a.a53 - b.a53;
+        c.a54 = a.a54 - b.a54;
+        c.a55 = a.a55 - b.a55;
+        c.a56 = a.a56 - b.a56;
+        c.a61 = a.a61 - b.a61;
+        c.a62 = a.a62 - b.a62;
+        c.a63 = a.a63 - b.a63;
+        c.a64 = a.a64 - b.a64;
+        c.a65 = a.a65 - b.a65;
+        c.a66 = a.a66 - b.a66;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a - b <br>
+     * c<sub>i</sub> = a<sub>i</sub> - b<sub>i</sub> <br>
+     * </p>
+     *
+     * <p>
+     * Vector C can be the same instance as Vector A and/or B.
+     * </p>
+     *
+     * @param a A Vector. Not modified.
+     * @param b A Vector. Not modified.
+     * @param c A Vector where the results are stored. Modified.
+     */
+    public static void subtract( FixedMatrix6_64F a , FixedMatrix6_64F b , FixedMatrix6_64F c ) {
+        c.a1 = a.a1 - b.a1;
+        c.a2 = a.a2 - b.a2;
+        c.a3 = a.a3 - b.a3;
+        c.a4 = a.a4 - b.a4;
+        c.a5 = a.a5 - b.a5;
+        c.a6 = a.a6 - b.a6;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * a = a - b <br>
+     * a<sub>ij</sub> = a<sub>ij</sub> - b<sub>ij</sub> <br>
+     * </p>
+     *
+     * @param a A Matrix. Modified.
+     * @param b A Matrix. Not modified.
+     */
+    public static void subtractEquals( FixedMatrix6x6_64F a , FixedMatrix6x6_64F b ) {
+        a.a11 -= b.a11;
+        a.a12 -= b.a12;
+        a.a13 -= b.a13;
+        a.a14 -= b.a14;
+        a.a15 -= b.a15;
+        a.a16 -= b.a16;
+        a.a21 -= b.a21;
+        a.a22 -= b.a22;
+        a.a23 -= b.a23;
+        a.a24 -= b.a24;
+        a.a25 -= b.a25;
+        a.a26 -= b.a26;
+        a.a31 -= b.a31;
+        a.a32 -= b.a32;
+        a.a33 -= b.a33;
+        a.a34 -= b.a34;
+        a.a35 -= b.a35;
+        a.a36 -= b.a36;
+        a.a41 -= b.a41;
+        a.a42 -= b.a42;
+        a.a43 -= b.a43;
+        a.a44 -= b.a44;
+        a.a45 -= b.a45;
+        a.a46 -= b.a46;
+        a.a51 -= b.a51;
+        a.a52 -= b.a52;
+        a.a53 -= b.a53;
+        a.a54 -= b.a54;
+        a.a55 -= b.a55;
+        a.a56 -= b.a56;
+        a.a61 -= b.a61;
+        a.a62 -= b.a62;
+        a.a63 -= b.a63;
+        a.a64 -= b.a64;
+        a.a65 -= b.a65;
+        a.a66 -= b.a66;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * a = a - b <br>
+     * a<sub>i</sub> = a<sub>i</sub> - b<sub>i</sub> <br>
+     * </p>
+     *
+     * @param a A Vector. Modified.
+     * @param b A Vector. Not modified.
+     */
+    public static void subtractEquals( FixedMatrix6_64F a , FixedMatrix6_64F b ) {
+        a.a1 -= b.a1;
+        a.a2 -= b.a2;
+        a.a3 -= b.a3;
+        a.a4 -= b.a4;
+        a.a5 -= b.a5;
+        a.a6 -= b.a6;
+    }
+
+    /**
+     * Performs an in-place transpose.  This algorithm is only efficient for square
+     * matrices.
+     *
+     * @param m The matrix that is to be transposed. Modified.
+     */
+    public static void transpose( FixedMatrix6x6_64F m ) {
+        double tmp;
+        tmp = m.a12; m.a12 = m.a21; m.a21 = tmp;
+        tmp = m.a13; m.a13 = m.a31; m.a31 = tmp;
+        tmp = m.a14; m.a14 = m.a41; m.a41 = tmp;
+        tmp = m.a15; m.a15 = m.a51; m.a51 = tmp;
+        tmp = m.a16; m.a16 = m.a61; m.a61 = tmp;
+        tmp = m.a23; m.a23 = m.a32; m.a32 = tmp;
+        tmp = m.a24; m.a24 = m.a42; m.a42 = tmp;
+        tmp = m.a25; m.a25 = m.a52; m.a52 = tmp;
+        tmp = m.a26; m.a26 = m.a62; m.a62 = tmp;
+        tmp = m.a34; m.a34 = m.a43; m.a43 = tmp;
+        tmp = m.a35; m.a35 = m.a53; m.a53 = tmp;
+        tmp = m.a36; m.a36 = m.a63; m.a63 = tmp;
+        tmp = m.a45; m.a45 = m.a54; m.a54 = tmp;
+        tmp = m.a46; m.a46 = m.a64; m.a64 = tmp;
+        tmp = m.a56; m.a56 = m.a65; m.a65 = tmp;
+    }
+
+    /**
+     * <p>
+     * Transposes matrix 'a' and stores the results in 'b':<br>
+     * <br>
+     * b<sub>ij</sub> = a<sub>ji</sub><br>
+     * where 'b' is the transpose of 'a'.
+     * </p>
+     *
+     * @param input The original matrix.  Not modified.
+     * @param output Where the transpose is stored. If null a new matrix is created. Modified.
+     * @return The transposed matrix.
+     */
+    public static FixedMatrix6x6_64F transpose( FixedMatrix6x6_64F input , FixedMatrix6x6_64F output ) {
+        if( input == null )
+            input = new FixedMatrix6x6_64F();
+
+        output.a11 = input.a11;
+        output.a12 = input.a21;
+        output.a13 = input.a31;
+        output.a14 = input.a41;
+        output.a15 = input.a51;
+        output.a16 = input.a61;
+        output.a21 = input.a12;
+        output.a22 = input.a22;
+        output.a23 = input.a32;
+        output.a24 = input.a42;
+        output.a25 = input.a52;
+        output.a26 = input.a62;
+        output.a31 = input.a13;
+        output.a32 = input.a23;
+        output.a33 = input.a33;
+        output.a34 = input.a43;
+        output.a35 = input.a53;
+        output.a36 = input.a63;
+        output.a41 = input.a14;
+        output.a42 = input.a24;
+        output.a43 = input.a34;
+        output.a44 = input.a44;
+        output.a45 = input.a54;
+        output.a46 = input.a64;
+        output.a51 = input.a15;
+        output.a52 = input.a25;
+        output.a53 = input.a35;
+        output.a54 = input.a45;
+        output.a55 = input.a55;
+        output.a56 = input.a65;
+        output.a61 = input.a16;
+        output.a62 = input.a26;
+        output.a63 = input.a36;
+        output.a64 = input.a46;
+        output.a65 = input.a56;
+        output.a66 = input.a66;
+
+        return output;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a * b <br>
+     * <br>
+     * c<sub>ij</sub> = ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void mult( FixedMatrix6x6_64F a , FixedMatrix6x6_64F b , FixedMatrix6x6_64F c) {
+        c.a11 = a.a11*b.a11 + a.a12*b.a21 + a.a13*b.a31 + a.a14*b.a41 + a.a15*b.a51 + a.a16*b.a61;
+        c.a12 = a.a11*b.a12 + a.a12*b.a22 + a.a13*b.a32 + a.a14*b.a42 + a.a15*b.a52 + a.a16*b.a62;
+        c.a13 = a.a11*b.a13 + a.a12*b.a23 + a.a13*b.a33 + a.a14*b.a43 + a.a15*b.a53 + a.a16*b.a63;
+        c.a14 = a.a11*b.a14 + a.a12*b.a24 + a.a13*b.a34 + a.a14*b.a44 + a.a15*b.a54 + a.a16*b.a64;
+        c.a15 = a.a11*b.a15 + a.a12*b.a25 + a.a13*b.a35 + a.a14*b.a45 + a.a15*b.a55 + a.a16*b.a65;
+        c.a16 = a.a11*b.a16 + a.a12*b.a26 + a.a13*b.a36 + a.a14*b.a46 + a.a15*b.a56 + a.a16*b.a66;
+        c.a21 = a.a21*b.a11 + a.a22*b.a21 + a.a23*b.a31 + a.a24*b.a41 + a.a25*b.a51 + a.a26*b.a61;
+        c.a22 = a.a21*b.a12 + a.a22*b.a22 + a.a23*b.a32 + a.a24*b.a42 + a.a25*b.a52 + a.a26*b.a62;
+        c.a23 = a.a21*b.a13 + a.a22*b.a23 + a.a23*b.a33 + a.a24*b.a43 + a.a25*b.a53 + a.a26*b.a63;
+        c.a24 = a.a21*b.a14 + a.a22*b.a24 + a.a23*b.a34 + a.a24*b.a44 + a.a25*b.a54 + a.a26*b.a64;
+        c.a25 = a.a21*b.a15 + a.a22*b.a25 + a.a23*b.a35 + a.a24*b.a45 + a.a25*b.a55 + a.a26*b.a65;
+        c.a26 = a.a21*b.a16 + a.a22*b.a26 + a.a23*b.a36 + a.a24*b.a46 + a.a25*b.a56 + a.a26*b.a66;
+        c.a31 = a.a31*b.a11 + a.a32*b.a21 + a.a33*b.a31 + a.a34*b.a41 + a.a35*b.a51 + a.a36*b.a61;
+        c.a32 = a.a31*b.a12 + a.a32*b.a22 + a.a33*b.a32 + a.a34*b.a42 + a.a35*b.a52 + a.a36*b.a62;
+        c.a33 = a.a31*b.a13 + a.a32*b.a23 + a.a33*b.a33 + a.a34*b.a43 + a.a35*b.a53 + a.a36*b.a63;
+        c.a34 = a.a31*b.a14 + a.a32*b.a24 + a.a33*b.a34 + a.a34*b.a44 + a.a35*b.a54 + a.a36*b.a64;
+        c.a35 = a.a31*b.a15 + a.a32*b.a25 + a.a33*b.a35 + a.a34*b.a45 + a.a35*b.a55 + a.a36*b.a65;
+        c.a36 = a.a31*b.a16 + a.a32*b.a26 + a.a33*b.a36 + a.a34*b.a46 + a.a35*b.a56 + a.a36*b.a66;
+        c.a41 = a.a41*b.a11 + a.a42*b.a21 + a.a43*b.a31 + a.a44*b.a41 + a.a45*b.a51 + a.a46*b.a61;
+        c.a42 = a.a41*b.a12 + a.a42*b.a22 + a.a43*b.a32 + a.a44*b.a42 + a.a45*b.a52 + a.a46*b.a62;
+        c.a43 = a.a41*b.a13 + a.a42*b.a23 + a.a43*b.a33 + a.a44*b.a43 + a.a45*b.a53 + a.a46*b.a63;
+        c.a44 = a.a41*b.a14 + a.a42*b.a24 + a.a43*b.a34 + a.a44*b.a44 + a.a45*b.a54 + a.a46*b.a64;
+        c.a45 = a.a41*b.a15 + a.a42*b.a25 + a.a43*b.a35 + a.a44*b.a45 + a.a45*b.a55 + a.a46*b.a65;
+        c.a46 = a.a41*b.a16 + a.a42*b.a26 + a.a43*b.a36 + a.a44*b.a46 + a.a45*b.a56 + a.a46*b.a66;
+        c.a51 = a.a51*b.a11 + a.a52*b.a21 + a.a53*b.a31 + a.a54*b.a41 + a.a55*b.a51 + a.a56*b.a61;
+        c.a52 = a.a51*b.a12 + a.a52*b.a22 + a.a53*b.a32 + a.a54*b.a42 + a.a55*b.a52 + a.a56*b.a62;
+        c.a53 = a.a51*b.a13 + a.a52*b.a23 + a.a53*b.a33 + a.a54*b.a43 + a.a55*b.a53 + a.a56*b.a63;
+        c.a54 = a.a51*b.a14 + a.a52*b.a24 + a.a53*b.a34 + a.a54*b.a44 + a.a55*b.a54 + a.a56*b.a64;
+        c.a55 = a.a51*b.a15 + a.a52*b.a25 + a.a53*b.a35 + a.a54*b.a45 + a.a55*b.a55 + a.a56*b.a65;
+        c.a56 = a.a51*b.a16 + a.a52*b.a26 + a.a53*b.a36 + a.a54*b.a46 + a.a55*b.a56 + a.a56*b.a66;
+        c.a61 = a.a61*b.a11 + a.a62*b.a21 + a.a63*b.a31 + a.a64*b.a41 + a.a65*b.a51 + a.a66*b.a61;
+        c.a62 = a.a61*b.a12 + a.a62*b.a22 + a.a63*b.a32 + a.a64*b.a42 + a.a65*b.a52 + a.a66*b.a62;
+        c.a63 = a.a61*b.a13 + a.a62*b.a23 + a.a63*b.a33 + a.a64*b.a43 + a.a65*b.a53 + a.a66*b.a63;
+        c.a64 = a.a61*b.a14 + a.a62*b.a24 + a.a63*b.a34 + a.a64*b.a44 + a.a65*b.a54 + a.a66*b.a64;
+        c.a65 = a.a61*b.a15 + a.a62*b.a25 + a.a63*b.a35 + a.a64*b.a45 + a.a65*b.a55 + a.a66*b.a65;
+        c.a66 = a.a61*b.a16 + a.a62*b.a26 + a.a63*b.a36 + a.a64*b.a46 + a.a65*b.a56 + a.a66*b.a66;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a<sup>T</sup> * b <br>
+     * <br>
+     * c<sub>ij</sub> = ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multTransA( FixedMatrix6x6_64F a , FixedMatrix6x6_64F b , FixedMatrix6x6_64F c) {
+        c.a11 = a.a11*b.a11 + a.a21*b.a21 + a.a31*b.a31 + a.a41*b.a41 + a.a51*b.a51 + a.a61*b.a61;
+        c.a12 = a.a11*b.a12 + a.a21*b.a22 + a.a31*b.a32 + a.a41*b.a42 + a.a51*b.a52 + a.a61*b.a62;
+        c.a13 = a.a11*b.a13 + a.a21*b.a23 + a.a31*b.a33 + a.a41*b.a43 + a.a51*b.a53 + a.a61*b.a63;
+        c.a14 = a.a11*b.a14 + a.a21*b.a24 + a.a31*b.a34 + a.a41*b.a44 + a.a51*b.a54 + a.a61*b.a64;
+        c.a15 = a.a11*b.a15 + a.a21*b.a25 + a.a31*b.a35 + a.a41*b.a45 + a.a51*b.a55 + a.a61*b.a65;
+        c.a16 = a.a11*b.a16 + a.a21*b.a26 + a.a31*b.a36 + a.a41*b.a46 + a.a51*b.a56 + a.a61*b.a66;
+        c.a21 = a.a12*b.a11 + a.a22*b.a21 + a.a32*b.a31 + a.a42*b.a41 + a.a52*b.a51 + a.a62*b.a61;
+        c.a22 = a.a12*b.a12 + a.a22*b.a22 + a.a32*b.a32 + a.a42*b.a42 + a.a52*b.a52 + a.a62*b.a62;
+        c.a23 = a.a12*b.a13 + a.a22*b.a23 + a.a32*b.a33 + a.a42*b.a43 + a.a52*b.a53 + a.a62*b.a63;
+        c.a24 = a.a12*b.a14 + a.a22*b.a24 + a.a32*b.a34 + a.a42*b.a44 + a.a52*b.a54 + a.a62*b.a64;
+        c.a25 = a.a12*b.a15 + a.a22*b.a25 + a.a32*b.a35 + a.a42*b.a45 + a.a52*b.a55 + a.a62*b.a65;
+        c.a26 = a.a12*b.a16 + a.a22*b.a26 + a.a32*b.a36 + a.a42*b.a46 + a.a52*b.a56 + a.a62*b.a66;
+        c.a31 = a.a13*b.a11 + a.a23*b.a21 + a.a33*b.a31 + a.a43*b.a41 + a.a53*b.a51 + a.a63*b.a61;
+        c.a32 = a.a13*b.a12 + a.a23*b.a22 + a.a33*b.a32 + a.a43*b.a42 + a.a53*b.a52 + a.a63*b.a62;
+        c.a33 = a.a13*b.a13 + a.a23*b.a23 + a.a33*b.a33 + a.a43*b.a43 + a.a53*b.a53 + a.a63*b.a63;
+        c.a34 = a.a13*b.a14 + a.a23*b.a24 + a.a33*b.a34 + a.a43*b.a44 + a.a53*b.a54 + a.a63*b.a64;
+        c.a35 = a.a13*b.a15 + a.a23*b.a25 + a.a33*b.a35 + a.a43*b.a45 + a.a53*b.a55 + a.a63*b.a65;
+        c.a36 = a.a13*b.a16 + a.a23*b.a26 + a.a33*b.a36 + a.a43*b.a46 + a.a53*b.a56 + a.a63*b.a66;
+        c.a41 = a.a14*b.a11 + a.a24*b.a21 + a.a34*b.a31 + a.a44*b.a41 + a.a54*b.a51 + a.a64*b.a61;
+        c.a42 = a.a14*b.a12 + a.a24*b.a22 + a.a34*b.a32 + a.a44*b.a42 + a.a54*b.a52 + a.a64*b.a62;
+        c.a43 = a.a14*b.a13 + a.a24*b.a23 + a.a34*b.a33 + a.a44*b.a43 + a.a54*b.a53 + a.a64*b.a63;
+        c.a44 = a.a14*b.a14 + a.a24*b.a24 + a.a34*b.a34 + a.a44*b.a44 + a.a54*b.a54 + a.a64*b.a64;
+        c.a45 = a.a14*b.a15 + a.a24*b.a25 + a.a34*b.a35 + a.a44*b.a45 + a.a54*b.a55 + a.a64*b.a65;
+        c.a46 = a.a14*b.a16 + a.a24*b.a26 + a.a34*b.a36 + a.a44*b.a46 + a.a54*b.a56 + a.a64*b.a66;
+        c.a51 = a.a15*b.a11 + a.a25*b.a21 + a.a35*b.a31 + a.a45*b.a41 + a.a55*b.a51 + a.a65*b.a61;
+        c.a52 = a.a15*b.a12 + a.a25*b.a22 + a.a35*b.a32 + a.a45*b.a42 + a.a55*b.a52 + a.a65*b.a62;
+        c.a53 = a.a15*b.a13 + a.a25*b.a23 + a.a35*b.a33 + a.a45*b.a43 + a.a55*b.a53 + a.a65*b.a63;
+        c.a54 = a.a15*b.a14 + a.a25*b.a24 + a.a35*b.a34 + a.a45*b.a44 + a.a55*b.a54 + a.a65*b.a64;
+        c.a55 = a.a15*b.a15 + a.a25*b.a25 + a.a35*b.a35 + a.a45*b.a45 + a.a55*b.a55 + a.a65*b.a65;
+        c.a56 = a.a15*b.a16 + a.a25*b.a26 + a.a35*b.a36 + a.a45*b.a46 + a.a55*b.a56 + a.a65*b.a66;
+        c.a61 = a.a16*b.a11 + a.a26*b.a21 + a.a36*b.a31 + a.a46*b.a41 + a.a56*b.a51 + a.a66*b.a61;
+        c.a62 = a.a16*b.a12 + a.a26*b.a22 + a.a36*b.a32 + a.a46*b.a42 + a.a56*b.a52 + a.a66*b.a62;
+        c.a63 = a.a16*b.a13 + a.a26*b.a23 + a.a36*b.a33 + a.a46*b.a43 + a.a56*b.a53 + a.a66*b.a63;
+        c.a64 = a.a16*b.a14 + a.a26*b.a24 + a.a36*b.a34 + a.a46*b.a44 + a.a56*b.a54 + a.a66*b.a64;
+        c.a65 = a.a16*b.a15 + a.a26*b.a25 + a.a36*b.a35 + a.a46*b.a45 + a.a56*b.a55 + a.a66*b.a65;
+        c.a66 = a.a16*b.a16 + a.a26*b.a26 + a.a36*b.a36 + a.a46*b.a46 + a.a56*b.a56 + a.a66*b.a66;
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c = a<sup>T</sup> * b<sup>T</sup><br>
+     * c<sub>ij</sub> = ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>jk</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multTransAB( FixedMatrix6x6_64F a , FixedMatrix6x6_64F b , FixedMatrix6x6_64F c) {
+        c.a11 = a.a11*b.a11 + a.a21*b.a12 + a.a31*b.a13 + a.a41*b.a14 + a.a51*b.a15 + a.a61*b.a16;
+        c.a12 = a.a11*b.a21 + a.a21*b.a22 + a.a31*b.a23 + a.a41*b.a24 + a.a51*b.a25 + a.a61*b.a26;
+        c.a13 = a.a11*b.a31 + a.a21*b.a32 + a.a31*b.a33 + a.a41*b.a34 + a.a51*b.a35 + a.a61*b.a36;
+        c.a14 = a.a11*b.a41 + a.a21*b.a42 + a.a31*b.a43 + a.a41*b.a44 + a.a51*b.a45 + a.a61*b.a46;
+        c.a15 = a.a11*b.a51 + a.a21*b.a52 + a.a31*b.a53 + a.a41*b.a54 + a.a51*b.a55 + a.a61*b.a56;
+        c.a16 = a.a11*b.a61 + a.a21*b.a62 + a.a31*b.a63 + a.a41*b.a64 + a.a51*b.a65 + a.a61*b.a66;
+        c.a21 = a.a12*b.a11 + a.a22*b.a12 + a.a32*b.a13 + a.a42*b.a14 + a.a52*b.a15 + a.a62*b.a16;
+        c.a22 = a.a12*b.a21 + a.a22*b.a22 + a.a32*b.a23 + a.a42*b.a24 + a.a52*b.a25 + a.a62*b.a26;
+        c.a23 = a.a12*b.a31 + a.a22*b.a32 + a.a32*b.a33 + a.a42*b.a34 + a.a52*b.a35 + a.a62*b.a36;
+        c.a24 = a.a12*b.a41 + a.a22*b.a42 + a.a32*b.a43 + a.a42*b.a44 + a.a52*b.a45 + a.a62*b.a46;
+        c.a25 = a.a12*b.a51 + a.a22*b.a52 + a.a32*b.a53 + a.a42*b.a54 + a.a52*b.a55 + a.a62*b.a56;
+        c.a26 = a.a12*b.a61 + a.a22*b.a62 + a.a32*b.a63 + a.a42*b.a64 + a.a52*b.a65 + a.a62*b.a66;
+        c.a31 = a.a13*b.a11 + a.a23*b.a12 + a.a33*b.a13 + a.a43*b.a14 + a.a53*b.a15 + a.a63*b.a16;
+        c.a32 = a.a13*b.a21 + a.a23*b.a22 + a.a33*b.a23 + a.a43*b.a24 + a.a53*b.a25 + a.a63*b.a26;
+        c.a33 = a.a13*b.a31 + a.a23*b.a32 + a.a33*b.a33 + a.a43*b.a34 + a.a53*b.a35 + a.a63*b.a36;
+        c.a34 = a.a13*b.a41 + a.a23*b.a42 + a.a33*b.a43 + a.a43*b.a44 + a.a53*b.a45 + a.a63*b.a46;
+        c.a35 = a.a13*b.a51 + a.a23*b.a52 + a.a33*b.a53 + a.a43*b.a54 + a.a53*b.a55 + a.a63*b.a56;
+        c.a36 = a.a13*b.a61 + a.a23*b.a62 + a.a33*b.a63 + a.a43*b.a64 + a.a53*b.a65 + a.a63*b.a66;
+        c.a41 = a.a14*b.a11 + a.a24*b.a12 + a.a34*b.a13 + a.a44*b.a14 + a.a54*b.a15 + a.a64*b.a16;
+        c.a42 = a.a14*b.a21 + a.a24*b.a22 + a.a34*b.a23 + a.a44*b.a24 + a.a54*b.a25 + a.a64*b.a26;
+        c.a43 = a.a14*b.a31 + a.a24*b.a32 + a.a34*b.a33 + a.a44*b.a34 + a.a54*b.a35 + a.a64*b.a36;
+        c.a44 = a.a14*b.a41 + a.a24*b.a42 + a.a34*b.a43 + a.a44*b.a44 + a.a54*b.a45 + a.a64*b.a46;
+        c.a45 = a.a14*b.a51 + a.a24*b.a52 + a.a34*b.a53 + a.a44*b.a54 + a.a54*b.a55 + a.a64*b.a56;
+        c.a46 = a.a14*b.a61 + a.a24*b.a62 + a.a34*b.a63 + a.a44*b.a64 + a.a54*b.a65 + a.a64*b.a66;
+        c.a51 = a.a15*b.a11 + a.a25*b.a12 + a.a35*b.a13 + a.a45*b.a14 + a.a55*b.a15 + a.a65*b.a16;
+        c.a52 = a.a15*b.a21 + a.a25*b.a22 + a.a35*b.a23 + a.a45*b.a24 + a.a55*b.a25 + a.a65*b.a26;
+        c.a53 = a.a15*b.a31 + a.a25*b.a32 + a.a35*b.a33 + a.a45*b.a34 + a.a55*b.a35 + a.a65*b.a36;
+        c.a54 = a.a15*b.a41 + a.a25*b.a42 + a.a35*b.a43 + a.a45*b.a44 + a.a55*b.a45 + a.a65*b.a46;
+        c.a55 = a.a15*b.a51 + a.a25*b.a52 + a.a35*b.a53 + a.a45*b.a54 + a.a55*b.a55 + a.a65*b.a56;
+        c.a56 = a.a15*b.a61 + a.a25*b.a62 + a.a35*b.a63 + a.a45*b.a64 + a.a55*b.a65 + a.a65*b.a66;
+        c.a61 = a.a16*b.a11 + a.a26*b.a12 + a.a36*b.a13 + a.a46*b.a14 + a.a56*b.a15 + a.a66*b.a16;
+        c.a62 = a.a16*b.a21 + a.a26*b.a22 + a.a36*b.a23 + a.a46*b.a24 + a.a56*b.a25 + a.a66*b.a26;
+        c.a63 = a.a16*b.a31 + a.a26*b.a32 + a.a36*b.a33 + a.a46*b.a34 + a.a56*b.a35 + a.a66*b.a36;
+        c.a64 = a.a16*b.a41 + a.a26*b.a42 + a.a36*b.a43 + a.a46*b.a44 + a.a56*b.a45 + a.a66*b.a46;
+        c.a65 = a.a16*b.a51 + a.a26*b.a52 + a.a36*b.a53 + a.a46*b.a54 + a.a56*b.a55 + a.a66*b.a56;
+        c.a66 = a.a16*b.a61 + a.a26*b.a62 + a.a36*b.a63 + a.a46*b.a64 + a.a56*b.a65 + a.a66*b.a66;
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c = a * b<sup>T</sup> <br>
+     * c<sub>ij</sub> = ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>jk</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multTransB( FixedMatrix6x6_64F a , FixedMatrix6x6_64F b , FixedMatrix6x6_64F c) {
+        c.a11 = a.a11*b.a11 + a.a12*b.a12 + a.a13*b.a13 + a.a14*b.a14 + a.a15*b.a15 + a.a16*b.a16;
+        c.a12 = a.a11*b.a21 + a.a12*b.a22 + a.a13*b.a23 + a.a14*b.a24 + a.a15*b.a25 + a.a16*b.a26;
+        c.a13 = a.a11*b.a31 + a.a12*b.a32 + a.a13*b.a33 + a.a14*b.a34 + a.a15*b.a35 + a.a16*b.a36;
+        c.a14 = a.a11*b.a41 + a.a12*b.a42 + a.a13*b.a43 + a.a14*b.a44 + a.a15*b.a45 + a.a16*b.a46;
+        c.a15 = a.a11*b.a51 + a.a12*b.a52 + a.a13*b.a53 + a.a14*b.a54 + a.a15*b.a55 + a.a16*b.a56;
+        c.a16 = a.a11*b.a61 + a.a12*b.a62 + a.a13*b.a63 + a.a14*b.a64 + a.a15*b.a65 + a.a16*b.a66;
+        c.a21 = a.a21*b.a11 + a.a22*b.a12 + a.a23*b.a13 + a.a24*b.a14 + a.a25*b.a15 + a.a26*b.a16;
+        c.a22 = a.a21*b.a21 + a.a22*b.a22 + a.a23*b.a23 + a.a24*b.a24 + a.a25*b.a25 + a.a26*b.a26;
+        c.a23 = a.a21*b.a31 + a.a22*b.a32 + a.a23*b.a33 + a.a24*b.a34 + a.a25*b.a35 + a.a26*b.a36;
+        c.a24 = a.a21*b.a41 + a.a22*b.a42 + a.a23*b.a43 + a.a24*b.a44 + a.a25*b.a45 + a.a26*b.a46;
+        c.a25 = a.a21*b.a51 + a.a22*b.a52 + a.a23*b.a53 + a.a24*b.a54 + a.a25*b.a55 + a.a26*b.a56;
+        c.a26 = a.a21*b.a61 + a.a22*b.a62 + a.a23*b.a63 + a.a24*b.a64 + a.a25*b.a65 + a.a26*b.a66;
+        c.a31 = a.a31*b.a11 + a.a32*b.a12 + a.a33*b.a13 + a.a34*b.a14 + a.a35*b.a15 + a.a36*b.a16;
+        c.a32 = a.a31*b.a21 + a.a32*b.a22 + a.a33*b.a23 + a.a34*b.a24 + a.a35*b.a25 + a.a36*b.a26;
+        c.a33 = a.a31*b.a31 + a.a32*b.a32 + a.a33*b.a33 + a.a34*b.a34 + a.a35*b.a35 + a.a36*b.a36;
+        c.a34 = a.a31*b.a41 + a.a32*b.a42 + a.a33*b.a43 + a.a34*b.a44 + a.a35*b.a45 + a.a36*b.a46;
+        c.a35 = a.a31*b.a51 + a.a32*b.a52 + a.a33*b.a53 + a.a34*b.a54 + a.a35*b.a55 + a.a36*b.a56;
+        c.a36 = a.a31*b.a61 + a.a32*b.a62 + a.a33*b.a63 + a.a34*b.a64 + a.a35*b.a65 + a.a36*b.a66;
+        c.a41 = a.a41*b.a11 + a.a42*b.a12 + a.a43*b.a13 + a.a44*b.a14 + a.a45*b.a15 + a.a46*b.a16;
+        c.a42 = a.a41*b.a21 + a.a42*b.a22 + a.a43*b.a23 + a.a44*b.a24 + a.a45*b.a25 + a.a46*b.a26;
+        c.a43 = a.a41*b.a31 + a.a42*b.a32 + a.a43*b.a33 + a.a44*b.a34 + a.a45*b.a35 + a.a46*b.a36;
+        c.a44 = a.a41*b.a41 + a.a42*b.a42 + a.a43*b.a43 + a.a44*b.a44 + a.a45*b.a45 + a.a46*b.a46;
+        c.a45 = a.a41*b.a51 + a.a42*b.a52 + a.a43*b.a53 + a.a44*b.a54 + a.a45*b.a55 + a.a46*b.a56;
+        c.a46 = a.a41*b.a61 + a.a42*b.a62 + a.a43*b.a63 + a.a44*b.a64 + a.a45*b.a65 + a.a46*b.a66;
+        c.a51 = a.a51*b.a11 + a.a52*b.a12 + a.a53*b.a13 + a.a54*b.a14 + a.a55*b.a15 + a.a56*b.a16;
+        c.a52 = a.a51*b.a21 + a.a52*b.a22 + a.a53*b.a23 + a.a54*b.a24 + a.a55*b.a25 + a.a56*b.a26;
+        c.a53 = a.a51*b.a31 + a.a52*b.a32 + a.a53*b.a33 + a.a54*b.a34 + a.a55*b.a35 + a.a56*b.a36;
+        c.a54 = a.a51*b.a41 + a.a52*b.a42 + a.a53*b.a43 + a.a54*b.a44 + a.a55*b.a45 + a.a56*b.a46;
+        c.a55 = a.a51*b.a51 + a.a52*b.a52 + a.a53*b.a53 + a.a54*b.a54 + a.a55*b.a55 + a.a56*b.a56;
+        c.a56 = a.a51*b.a61 + a.a52*b.a62 + a.a53*b.a63 + a.a54*b.a64 + a.a55*b.a65 + a.a56*b.a66;
+        c.a61 = a.a61*b.a11 + a.a62*b.a12 + a.a63*b.a13 + a.a64*b.a14 + a.a65*b.a15 + a.a66*b.a16;
+        c.a62 = a.a61*b.a21 + a.a62*b.a22 + a.a63*b.a23 + a.a64*b.a24 + a.a65*b.a25 + a.a66*b.a26;
+        c.a63 = a.a61*b.a31 + a.a62*b.a32 + a.a63*b.a33 + a.a64*b.a34 + a.a65*b.a35 + a.a66*b.a36;
+        c.a64 = a.a61*b.a41 + a.a62*b.a42 + a.a63*b.a43 + a.a64*b.a44 + a.a65*b.a45 + a.a66*b.a46;
+        c.a65 = a.a61*b.a51 + a.a62*b.a52 + a.a63*b.a53 + a.a64*b.a54 + a.a65*b.a55 + a.a66*b.a56;
+        c.a66 = a.a61*b.a61 + a.a62*b.a62 + a.a63*b.a63 + a.a64*b.a64 + a.a65*b.a65 + a.a66*b.a66;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c += a * b <br>
+     * <br>
+     * c<sub>ij</sub> += ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAdd( FixedMatrix6x6_64F a , FixedMatrix6x6_64F b , FixedMatrix6x6_64F c) {
+        c.a11 += a.a11*b.a11 + a.a12*b.a21 + a.a13*b.a31 + a.a14*b.a41 + a.a15*b.a51 + a.a16*b.a61;
+        c.a12 += a.a11*b.a12 + a.a12*b.a22 + a.a13*b.a32 + a.a14*b.a42 + a.a15*b.a52 + a.a16*b.a62;
+        c.a13 += a.a11*b.a13 + a.a12*b.a23 + a.a13*b.a33 + a.a14*b.a43 + a.a15*b.a53 + a.a16*b.a63;
+        c.a14 += a.a11*b.a14 + a.a12*b.a24 + a.a13*b.a34 + a.a14*b.a44 + a.a15*b.a54 + a.a16*b.a64;
+        c.a15 += a.a11*b.a15 + a.a12*b.a25 + a.a13*b.a35 + a.a14*b.a45 + a.a15*b.a55 + a.a16*b.a65;
+        c.a16 += a.a11*b.a16 + a.a12*b.a26 + a.a13*b.a36 + a.a14*b.a46 + a.a15*b.a56 + a.a16*b.a66;
+        c.a21 += a.a21*b.a11 + a.a22*b.a21 + a.a23*b.a31 + a.a24*b.a41 + a.a25*b.a51 + a.a26*b.a61;
+        c.a22 += a.a21*b.a12 + a.a22*b.a22 + a.a23*b.a32 + a.a24*b.a42 + a.a25*b.a52 + a.a26*b.a62;
+        c.a23 += a.a21*b.a13 + a.a22*b.a23 + a.a23*b.a33 + a.a24*b.a43 + a.a25*b.a53 + a.a26*b.a63;
+        c.a24 += a.a21*b.a14 + a.a22*b.a24 + a.a23*b.a34 + a.a24*b.a44 + a.a25*b.a54 + a.a26*b.a64;
+        c.a25 += a.a21*b.a15 + a.a22*b.a25 + a.a23*b.a35 + a.a24*b.a45 + a.a25*b.a55 + a.a26*b.a65;
+        c.a26 += a.a21*b.a16 + a.a22*b.a26 + a.a23*b.a36 + a.a24*b.a46 + a.a25*b.a56 + a.a26*b.a66;
+        c.a31 += a.a31*b.a11 + a.a32*b.a21 + a.a33*b.a31 + a.a34*b.a41 + a.a35*b.a51 + a.a36*b.a61;
+        c.a32 += a.a31*b.a12 + a.a32*b.a22 + a.a33*b.a32 + a.a34*b.a42 + a.a35*b.a52 + a.a36*b.a62;
+        c.a33 += a.a31*b.a13 + a.a32*b.a23 + a.a33*b.a33 + a.a34*b.a43 + a.a35*b.a53 + a.a36*b.a63;
+        c.a34 += a.a31*b.a14 + a.a32*b.a24 + a.a33*b.a34 + a.a34*b.a44 + a.a35*b.a54 + a.a36*b.a64;
+        c.a35 += a.a31*b.a15 + a.a32*b.a25 + a.a33*b.a35 + a.a34*b.a45 + a.a35*b.a55 + a.a36*b.a65;
+        c.a36 += a.a31*b.a16 + a.a32*b.a26 + a.a33*b.a36 + a.a34*b.a46 + a.a35*b.a56 + a.a36*b.a66;
+        c.a41 += a.a41*b.a11 + a.a42*b.a21 + a.a43*b.a31 + a.a44*b.a41 + a.a45*b.a51 + a.a46*b.a61;
+        c.a42 += a.a41*b.a12 + a.a42*b.a22 + a.a43*b.a32 + a.a44*b.a42 + a.a45*b.a52 + a.a46*b.a62;
+        c.a43 += a.a41*b.a13 + a.a42*b.a23 + a.a43*b.a33 + a.a44*b.a43 + a.a45*b.a53 + a.a46*b.a63;
+        c.a44 += a.a41*b.a14 + a.a42*b.a24 + a.a43*b.a34 + a.a44*b.a44 + a.a45*b.a54 + a.a46*b.a64;
+        c.a45 += a.a41*b.a15 + a.a42*b.a25 + a.a43*b.a35 + a.a44*b.a45 + a.a45*b.a55 + a.a46*b.a65;
+        c.a46 += a.a41*b.a16 + a.a42*b.a26 + a.a43*b.a36 + a.a44*b.a46 + a.a45*b.a56 + a.a46*b.a66;
+        c.a51 += a.a51*b.a11 + a.a52*b.a21 + a.a53*b.a31 + a.a54*b.a41 + a.a55*b.a51 + a.a56*b.a61;
+        c.a52 += a.a51*b.a12 + a.a52*b.a22 + a.a53*b.a32 + a.a54*b.a42 + a.a55*b.a52 + a.a56*b.a62;
+        c.a53 += a.a51*b.a13 + a.a52*b.a23 + a.a53*b.a33 + a.a54*b.a43 + a.a55*b.a53 + a.a56*b.a63;
+        c.a54 += a.a51*b.a14 + a.a52*b.a24 + a.a53*b.a34 + a.a54*b.a44 + a.a55*b.a54 + a.a56*b.a64;
+        c.a55 += a.a51*b.a15 + a.a52*b.a25 + a.a53*b.a35 + a.a54*b.a45 + a.a55*b.a55 + a.a56*b.a65;
+        c.a56 += a.a51*b.a16 + a.a52*b.a26 + a.a53*b.a36 + a.a54*b.a46 + a.a55*b.a56 + a.a56*b.a66;
+        c.a61 += a.a61*b.a11 + a.a62*b.a21 + a.a63*b.a31 + a.a64*b.a41 + a.a65*b.a51 + a.a66*b.a61;
+        c.a62 += a.a61*b.a12 + a.a62*b.a22 + a.a63*b.a32 + a.a64*b.a42 + a.a65*b.a52 + a.a66*b.a62;
+        c.a63 += a.a61*b.a13 + a.a62*b.a23 + a.a63*b.a33 + a.a64*b.a43 + a.a65*b.a53 + a.a66*b.a63;
+        c.a64 += a.a61*b.a14 + a.a62*b.a24 + a.a63*b.a34 + a.a64*b.a44 + a.a65*b.a54 + a.a66*b.a64;
+        c.a65 += a.a61*b.a15 + a.a62*b.a25 + a.a63*b.a35 + a.a64*b.a45 + a.a65*b.a55 + a.a66*b.a65;
+        c.a66 += a.a61*b.a16 + a.a62*b.a26 + a.a63*b.a36 + a.a64*b.a46 + a.a65*b.a56 + a.a66*b.a66;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c += a<sup>T</sup> * b <br>
+     * <br>
+     * c<sub>ij</sub> += ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAddTransA( FixedMatrix6x6_64F a , FixedMatrix6x6_64F b , FixedMatrix6x6_64F c) {
+        c.a11 += a.a11*b.a11 + a.a21*b.a21 + a.a31*b.a31 + a.a41*b.a41 + a.a51*b.a51 + a.a61*b.a61;
+        c.a12 += a.a11*b.a12 + a.a21*b.a22 + a.a31*b.a32 + a.a41*b.a42 + a.a51*b.a52 + a.a61*b.a62;
+        c.a13 += a.a11*b.a13 + a.a21*b.a23 + a.a31*b.a33 + a.a41*b.a43 + a.a51*b.a53 + a.a61*b.a63;
+        c.a14 += a.a11*b.a14 + a.a21*b.a24 + a.a31*b.a34 + a.a41*b.a44 + a.a51*b.a54 + a.a61*b.a64;
+        c.a15 += a.a11*b.a15 + a.a21*b.a25 + a.a31*b.a35 + a.a41*b.a45 + a.a51*b.a55 + a.a61*b.a65;
+        c.a16 += a.a11*b.a16 + a.a21*b.a26 + a.a31*b.a36 + a.a41*b.a46 + a.a51*b.a56 + a.a61*b.a66;
+        c.a21 += a.a12*b.a11 + a.a22*b.a21 + a.a32*b.a31 + a.a42*b.a41 + a.a52*b.a51 + a.a62*b.a61;
+        c.a22 += a.a12*b.a12 + a.a22*b.a22 + a.a32*b.a32 + a.a42*b.a42 + a.a52*b.a52 + a.a62*b.a62;
+        c.a23 += a.a12*b.a13 + a.a22*b.a23 + a.a32*b.a33 + a.a42*b.a43 + a.a52*b.a53 + a.a62*b.a63;
+        c.a24 += a.a12*b.a14 + a.a22*b.a24 + a.a32*b.a34 + a.a42*b.a44 + a.a52*b.a54 + a.a62*b.a64;
+        c.a25 += a.a12*b.a15 + a.a22*b.a25 + a.a32*b.a35 + a.a42*b.a45 + a.a52*b.a55 + a.a62*b.a65;
+        c.a26 += a.a12*b.a16 + a.a22*b.a26 + a.a32*b.a36 + a.a42*b.a46 + a.a52*b.a56 + a.a62*b.a66;
+        c.a31 += a.a13*b.a11 + a.a23*b.a21 + a.a33*b.a31 + a.a43*b.a41 + a.a53*b.a51 + a.a63*b.a61;
+        c.a32 += a.a13*b.a12 + a.a23*b.a22 + a.a33*b.a32 + a.a43*b.a42 + a.a53*b.a52 + a.a63*b.a62;
+        c.a33 += a.a13*b.a13 + a.a23*b.a23 + a.a33*b.a33 + a.a43*b.a43 + a.a53*b.a53 + a.a63*b.a63;
+        c.a34 += a.a13*b.a14 + a.a23*b.a24 + a.a33*b.a34 + a.a43*b.a44 + a.a53*b.a54 + a.a63*b.a64;
+        c.a35 += a.a13*b.a15 + a.a23*b.a25 + a.a33*b.a35 + a.a43*b.a45 + a.a53*b.a55 + a.a63*b.a65;
+        c.a36 += a.a13*b.a16 + a.a23*b.a26 + a.a33*b.a36 + a.a43*b.a46 + a.a53*b.a56 + a.a63*b.a66;
+        c.a41 += a.a14*b.a11 + a.a24*b.a21 + a.a34*b.a31 + a.a44*b.a41 + a.a54*b.a51 + a.a64*b.a61;
+        c.a42 += a.a14*b.a12 + a.a24*b.a22 + a.a34*b.a32 + a.a44*b.a42 + a.a54*b.a52 + a.a64*b.a62;
+        c.a43 += a.a14*b.a13 + a.a24*b.a23 + a.a34*b.a33 + a.a44*b.a43 + a.a54*b.a53 + a.a64*b.a63;
+        c.a44 += a.a14*b.a14 + a.a24*b.a24 + a.a34*b.a34 + a.a44*b.a44 + a.a54*b.a54 + a.a64*b.a64;
+        c.a45 += a.a14*b.a15 + a.a24*b.a25 + a.a34*b.a35 + a.a44*b.a45 + a.a54*b.a55 + a.a64*b.a65;
+        c.a46 += a.a14*b.a16 + a.a24*b.a26 + a.a34*b.a36 + a.a44*b.a46 + a.a54*b.a56 + a.a64*b.a66;
+        c.a51 += a.a15*b.a11 + a.a25*b.a21 + a.a35*b.a31 + a.a45*b.a41 + a.a55*b.a51 + a.a65*b.a61;
+        c.a52 += a.a15*b.a12 + a.a25*b.a22 + a.a35*b.a32 + a.a45*b.a42 + a.a55*b.a52 + a.a65*b.a62;
+        c.a53 += a.a15*b.a13 + a.a25*b.a23 + a.a35*b.a33 + a.a45*b.a43 + a.a55*b.a53 + a.a65*b.a63;
+        c.a54 += a.a15*b.a14 + a.a25*b.a24 + a.a35*b.a34 + a.a45*b.a44 + a.a55*b.a54 + a.a65*b.a64;
+        c.a55 += a.a15*b.a15 + a.a25*b.a25 + a.a35*b.a35 + a.a45*b.a45 + a.a55*b.a55 + a.a65*b.a65;
+        c.a56 += a.a15*b.a16 + a.a25*b.a26 + a.a35*b.a36 + a.a45*b.a46 + a.a55*b.a56 + a.a65*b.a66;
+        c.a61 += a.a16*b.a11 + a.a26*b.a21 + a.a36*b.a31 + a.a46*b.a41 + a.a56*b.a51 + a.a66*b.a61;
+        c.a62 += a.a16*b.a12 + a.a26*b.a22 + a.a36*b.a32 + a.a46*b.a42 + a.a56*b.a52 + a.a66*b.a62;
+        c.a63 += a.a16*b.a13 + a.a26*b.a23 + a.a36*b.a33 + a.a46*b.a43 + a.a56*b.a53 + a.a66*b.a63;
+        c.a64 += a.a16*b.a14 + a.a26*b.a24 + a.a36*b.a34 + a.a46*b.a44 + a.a56*b.a54 + a.a66*b.a64;
+        c.a65 += a.a16*b.a15 + a.a26*b.a25 + a.a36*b.a35 + a.a46*b.a45 + a.a56*b.a55 + a.a66*b.a65;
+        c.a66 += a.a16*b.a16 + a.a26*b.a26 + a.a36*b.a36 + a.a46*b.a46 + a.a56*b.a56 + a.a66*b.a66;
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c += a<sup>T</sup> * b<sup>T</sup><br>
+     * c<sub>ij</sub> += ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>jk</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAddTransAB( FixedMatrix6x6_64F a , FixedMatrix6x6_64F b , FixedMatrix6x6_64F c) {
+        c.a11 += a.a11*b.a11 + a.a21*b.a12 + a.a31*b.a13 + a.a41*b.a14 + a.a51*b.a15 + a.a61*b.a16;
+        c.a12 += a.a11*b.a21 + a.a21*b.a22 + a.a31*b.a23 + a.a41*b.a24 + a.a51*b.a25 + a.a61*b.a26;
+        c.a13 += a.a11*b.a31 + a.a21*b.a32 + a.a31*b.a33 + a.a41*b.a34 + a.a51*b.a35 + a.a61*b.a36;
+        c.a14 += a.a11*b.a41 + a.a21*b.a42 + a.a31*b.a43 + a.a41*b.a44 + a.a51*b.a45 + a.a61*b.a46;
+        c.a15 += a.a11*b.a51 + a.a21*b.a52 + a.a31*b.a53 + a.a41*b.a54 + a.a51*b.a55 + a.a61*b.a56;
+        c.a16 += a.a11*b.a61 + a.a21*b.a62 + a.a31*b.a63 + a.a41*b.a64 + a.a51*b.a65 + a.a61*b.a66;
+        c.a21 += a.a12*b.a11 + a.a22*b.a12 + a.a32*b.a13 + a.a42*b.a14 + a.a52*b.a15 + a.a62*b.a16;
+        c.a22 += a.a12*b.a21 + a.a22*b.a22 + a.a32*b.a23 + a.a42*b.a24 + a.a52*b.a25 + a.a62*b.a26;
+        c.a23 += a.a12*b.a31 + a.a22*b.a32 + a.a32*b.a33 + a.a42*b.a34 + a.a52*b.a35 + a.a62*b.a36;
+        c.a24 += a.a12*b.a41 + a.a22*b.a42 + a.a32*b.a43 + a.a42*b.a44 + a.a52*b.a45 + a.a62*b.a46;
+        c.a25 += a.a12*b.a51 + a.a22*b.a52 + a.a32*b.a53 + a.a42*b.a54 + a.a52*b.a55 + a.a62*b.a56;
+        c.a26 += a.a12*b.a61 + a.a22*b.a62 + a.a32*b.a63 + a.a42*b.a64 + a.a52*b.a65 + a.a62*b.a66;
+        c.a31 += a.a13*b.a11 + a.a23*b.a12 + a.a33*b.a13 + a.a43*b.a14 + a.a53*b.a15 + a.a63*b.a16;
+        c.a32 += a.a13*b.a21 + a.a23*b.a22 + a.a33*b.a23 + a.a43*b.a24 + a.a53*b.a25 + a.a63*b.a26;
+        c.a33 += a.a13*b.a31 + a.a23*b.a32 + a.a33*b.a33 + a.a43*b.a34 + a.a53*b.a35 + a.a63*b.a36;
+        c.a34 += a.a13*b.a41 + a.a23*b.a42 + a.a33*b.a43 + a.a43*b.a44 + a.a53*b.a45 + a.a63*b.a46;
+        c.a35 += a.a13*b.a51 + a.a23*b.a52 + a.a33*b.a53 + a.a43*b.a54 + a.a53*b.a55 + a.a63*b.a56;
+        c.a36 += a.a13*b.a61 + a.a23*b.a62 + a.a33*b.a63 + a.a43*b.a64 + a.a53*b.a65 + a.a63*b.a66;
+        c.a41 += a.a14*b.a11 + a.a24*b.a12 + a.a34*b.a13 + a.a44*b.a14 + a.a54*b.a15 + a.a64*b.a16;
+        c.a42 += a.a14*b.a21 + a.a24*b.a22 + a.a34*b.a23 + a.a44*b.a24 + a.a54*b.a25 + a.a64*b.a26;
+        c.a43 += a.a14*b.a31 + a.a24*b.a32 + a.a34*b.a33 + a.a44*b.a34 + a.a54*b.a35 + a.a64*b.a36;
+        c.a44 += a.a14*b.a41 + a.a24*b.a42 + a.a34*b.a43 + a.a44*b.a44 + a.a54*b.a45 + a.a64*b.a46;
+        c.a45 += a.a14*b.a51 + a.a24*b.a52 + a.a34*b.a53 + a.a44*b.a54 + a.a54*b.a55 + a.a64*b.a56;
+        c.a46 += a.a14*b.a61 + a.a24*b.a62 + a.a34*b.a63 + a.a44*b.a64 + a.a54*b.a65 + a.a64*b.a66;
+        c.a51 += a.a15*b.a11 + a.a25*b.a12 + a.a35*b.a13 + a.a45*b.a14 + a.a55*b.a15 + a.a65*b.a16;
+        c.a52 += a.a15*b.a21 + a.a25*b.a22 + a.a35*b.a23 + a.a45*b.a24 + a.a55*b.a25 + a.a65*b.a26;
+        c.a53 += a.a15*b.a31 + a.a25*b.a32 + a.a35*b.a33 + a.a45*b.a34 + a.a55*b.a35 + a.a65*b.a36;
+        c.a54 += a.a15*b.a41 + a.a25*b.a42 + a.a35*b.a43 + a.a45*b.a44 + a.a55*b.a45 + a.a65*b.a46;
+        c.a55 += a.a15*b.a51 + a.a25*b.a52 + a.a35*b.a53 + a.a45*b.a54 + a.a55*b.a55 + a.a65*b.a56;
+        c.a56 += a.a15*b.a61 + a.a25*b.a62 + a.a35*b.a63 + a.a45*b.a64 + a.a55*b.a65 + a.a65*b.a66;
+        c.a61 += a.a16*b.a11 + a.a26*b.a12 + a.a36*b.a13 + a.a46*b.a14 + a.a56*b.a15 + a.a66*b.a16;
+        c.a62 += a.a16*b.a21 + a.a26*b.a22 + a.a36*b.a23 + a.a46*b.a24 + a.a56*b.a25 + a.a66*b.a26;
+        c.a63 += a.a16*b.a31 + a.a26*b.a32 + a.a36*b.a33 + a.a46*b.a34 + a.a56*b.a35 + a.a66*b.a36;
+        c.a64 += a.a16*b.a41 + a.a26*b.a42 + a.a36*b.a43 + a.a46*b.a44 + a.a56*b.a45 + a.a66*b.a46;
+        c.a65 += a.a16*b.a51 + a.a26*b.a52 + a.a36*b.a53 + a.a46*b.a54 + a.a56*b.a55 + a.a66*b.a56;
+        c.a66 += a.a16*b.a61 + a.a26*b.a62 + a.a36*b.a63 + a.a46*b.a64 + a.a56*b.a65 + a.a66*b.a66;
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c += a * b<sup>T</sup> <br>
+     * c<sub>ij</sub> += ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>jk</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAddTransB( FixedMatrix6x6_64F a , FixedMatrix6x6_64F b , FixedMatrix6x6_64F c) {
+        c.a11 += a.a11*b.a11 + a.a12*b.a12 + a.a13*b.a13 + a.a14*b.a14 + a.a15*b.a15 + a.a16*b.a16;
+        c.a12 += a.a11*b.a21 + a.a12*b.a22 + a.a13*b.a23 + a.a14*b.a24 + a.a15*b.a25 + a.a16*b.a26;
+        c.a13 += a.a11*b.a31 + a.a12*b.a32 + a.a13*b.a33 + a.a14*b.a34 + a.a15*b.a35 + a.a16*b.a36;
+        c.a14 += a.a11*b.a41 + a.a12*b.a42 + a.a13*b.a43 + a.a14*b.a44 + a.a15*b.a45 + a.a16*b.a46;
+        c.a15 += a.a11*b.a51 + a.a12*b.a52 + a.a13*b.a53 + a.a14*b.a54 + a.a15*b.a55 + a.a16*b.a56;
+        c.a16 += a.a11*b.a61 + a.a12*b.a62 + a.a13*b.a63 + a.a14*b.a64 + a.a15*b.a65 + a.a16*b.a66;
+        c.a21 += a.a21*b.a11 + a.a22*b.a12 + a.a23*b.a13 + a.a24*b.a14 + a.a25*b.a15 + a.a26*b.a16;
+        c.a22 += a.a21*b.a21 + a.a22*b.a22 + a.a23*b.a23 + a.a24*b.a24 + a.a25*b.a25 + a.a26*b.a26;
+        c.a23 += a.a21*b.a31 + a.a22*b.a32 + a.a23*b.a33 + a.a24*b.a34 + a.a25*b.a35 + a.a26*b.a36;
+        c.a24 += a.a21*b.a41 + a.a22*b.a42 + a.a23*b.a43 + a.a24*b.a44 + a.a25*b.a45 + a.a26*b.a46;
+        c.a25 += a.a21*b.a51 + a.a22*b.a52 + a.a23*b.a53 + a.a24*b.a54 + a.a25*b.a55 + a.a26*b.a56;
+        c.a26 += a.a21*b.a61 + a.a22*b.a62 + a.a23*b.a63 + a.a24*b.a64 + a.a25*b.a65 + a.a26*b.a66;
+        c.a31 += a.a31*b.a11 + a.a32*b.a12 + a.a33*b.a13 + a.a34*b.a14 + a.a35*b.a15 + a.a36*b.a16;
+        c.a32 += a.a31*b.a21 + a.a32*b.a22 + a.a33*b.a23 + a.a34*b.a24 + a.a35*b.a25 + a.a36*b.a26;
+        c.a33 += a.a31*b.a31 + a.a32*b.a32 + a.a33*b.a33 + a.a34*b.a34 + a.a35*b.a35 + a.a36*b.a36;
+        c.a34 += a.a31*b.a41 + a.a32*b.a42 + a.a33*b.a43 + a.a34*b.a44 + a.a35*b.a45 + a.a36*b.a46;
+        c.a35 += a.a31*b.a51 + a.a32*b.a52 + a.a33*b.a53 + a.a34*b.a54 + a.a35*b.a55 + a.a36*b.a56;
+        c.a36 += a.a31*b.a61 + a.a32*b.a62 + a.a33*b.a63 + a.a34*b.a64 + a.a35*b.a65 + a.a36*b.a66;
+        c.a41 += a.a41*b.a11 + a.a42*b.a12 + a.a43*b.a13 + a.a44*b.a14 + a.a45*b.a15 + a.a46*b.a16;
+        c.a42 += a.a41*b.a21 + a.a42*b.a22 + a.a43*b.a23 + a.a44*b.a24 + a.a45*b.a25 + a.a46*b.a26;
+        c.a43 += a.a41*b.a31 + a.a42*b.a32 + a.a43*b.a33 + a.a44*b.a34 + a.a45*b.a35 + a.a46*b.a36;
+        c.a44 += a.a41*b.a41 + a.a42*b.a42 + a.a43*b.a43 + a.a44*b.a44 + a.a45*b.a45 + a.a46*b.a46;
+        c.a45 += a.a41*b.a51 + a.a42*b.a52 + a.a43*b.a53 + a.a44*b.a54 + a.a45*b.a55 + a.a46*b.a56;
+        c.a46 += a.a41*b.a61 + a.a42*b.a62 + a.a43*b.a63 + a.a44*b.a64 + a.a45*b.a65 + a.a46*b.a66;
+        c.a51 += a.a51*b.a11 + a.a52*b.a12 + a.a53*b.a13 + a.a54*b.a14 + a.a55*b.a15 + a.a56*b.a16;
+        c.a52 += a.a51*b.a21 + a.a52*b.a22 + a.a53*b.a23 + a.a54*b.a24 + a.a55*b.a25 + a.a56*b.a26;
+        c.a53 += a.a51*b.a31 + a.a52*b.a32 + a.a53*b.a33 + a.a54*b.a34 + a.a55*b.a35 + a.a56*b.a36;
+        c.a54 += a.a51*b.a41 + a.a52*b.a42 + a.a53*b.a43 + a.a54*b.a44 + a.a55*b.a45 + a.a56*b.a46;
+        c.a55 += a.a51*b.a51 + a.a52*b.a52 + a.a53*b.a53 + a.a54*b.a54 + a.a55*b.a55 + a.a56*b.a56;
+        c.a56 += a.a51*b.a61 + a.a52*b.a62 + a.a53*b.a63 + a.a54*b.a64 + a.a55*b.a65 + a.a56*b.a66;
+        c.a61 += a.a61*b.a11 + a.a62*b.a12 + a.a63*b.a13 + a.a64*b.a14 + a.a65*b.a15 + a.a66*b.a16;
+        c.a62 += a.a61*b.a21 + a.a62*b.a22 + a.a63*b.a23 + a.a64*b.a24 + a.a65*b.a25 + a.a66*b.a26;
+        c.a63 += a.a61*b.a31 + a.a62*b.a32 + a.a63*b.a33 + a.a64*b.a34 + a.a65*b.a35 + a.a66*b.a36;
+        c.a64 += a.a61*b.a41 + a.a62*b.a42 + a.a63*b.a43 + a.a64*b.a44 + a.a65*b.a45 + a.a66*b.a46;
+        c.a65 += a.a61*b.a51 + a.a62*b.a52 + a.a63*b.a53 + a.a64*b.a54 + a.a65*b.a55 + a.a66*b.a56;
+        c.a66 += a.a61*b.a61 + a.a62*b.a62 + a.a63*b.a63 + a.a64*b.a64 + a.a65*b.a65 + a.a66*b.a66;
+    }
+
+    /**
+     * <p>Performs matrix to vector multiplication:<br>
+     * <br>
+     * c = a * b <br>
+     * <br>
+     * c<sub>i</sub> = ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>k</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right vector in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void mult( FixedMatrix6x6_64F a , FixedMatrix6_64F b , FixedMatrix6_64F c) {
+        c.a1 = a.a11*b.a1 + a.a12*b.a2 + a.a13*b.a3 + a.a14*b.a4 + a.a15*b.a5 + a.a16*b.a6;
+        c.a2 = a.a21*b.a1 + a.a22*b.a2 + a.a23*b.a3 + a.a24*b.a4 + a.a25*b.a5 + a.a26*b.a6;
+        c.a3 = a.a31*b.a1 + a.a32*b.a2 + a.a33*b.a3 + a.a34*b.a4 + a.a35*b.a5 + a.a36*b.a6;
+        c.a4 = a.a41*b.a1 + a.a42*b.a2 + a.a43*b.a3 + a.a44*b.a4 + a.a45*b.a5 + a.a46*b.a6;
+        c.a5 = a.a51*b.a1 + a.a52*b.a2 + a.a53*b.a3 + a.a54*b.a4 + a.a55*b.a5 + a.a56*b.a6;
+        c.a6 = a.a61*b.a1 + a.a62*b.a2 + a.a63*b.a3 + a.a64*b.a4 + a.a65*b.a5 + a.a66*b.a6;
+    }
+
+    /**
+     * <p>Performs vector to matrix multiplication:<br>
+     * <br>
+     * c = a * b <br>
+     * <br>
+     * c<sub>j</sub> = ∑<sub>k=1:n</sub> { b<sub>k</sub> * a<sub>kj</sub> }
+     * </p>
+     *
+     * @param a The left vector in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void mult( FixedMatrix6_64F a , FixedMatrix6x6_64F b , FixedMatrix6_64F c) {
+        c.a1 = a.a1*b.a11 + a.a2*b.a21 + a.a3*b.a31 + a.a4*b.a41 + a.a5*b.a51 + a.a6*b.a61;
+        c.a2 = a.a1*b.a12 + a.a2*b.a22 + a.a3*b.a32 + a.a4*b.a42 + a.a5*b.a52 + a.a6*b.a62;
+        c.a3 = a.a1*b.a13 + a.a2*b.a23 + a.a3*b.a33 + a.a4*b.a43 + a.a5*b.a53 + a.a6*b.a63;
+        c.a4 = a.a1*b.a14 + a.a2*b.a24 + a.a3*b.a34 + a.a4*b.a44 + a.a5*b.a54 + a.a6*b.a64;
+        c.a5 = a.a1*b.a15 + a.a2*b.a25 + a.a3*b.a35 + a.a4*b.a45 + a.a5*b.a55 + a.a6*b.a65;
+        c.a6 = a.a1*b.a16 + a.a2*b.a26 + a.a3*b.a36 + a.a4*b.a46 + a.a5*b.a56 + a.a6*b.a66;
+    }
+
+    /**
+     * <p>Performs the vector dot product:<br>
+     * <br>
+     * c = a * b <br>
+     * <br>
+     * c> = ∑<sub>k=1:n</sub> { b<sub>k</sub> * a<sub>k</sub> }
+     * </p>
+     *
+     * @param a The left vector in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @return The dot product
+     */
+    public static double dot( FixedMatrix6_64F a , FixedMatrix6_64F b ) {
+        return a.a1*b.a1 + a.a2*b.a2 + a.a3*b.a3 + a.a4*b.a4 + a.a5*b.a5 + a.a6*b.a6;
+    }
+
+    /**
+     * Sets all the diagonal elements equal to one and everything else equal to zero.
+     * If this is a square matrix then it will be an identity matrix.
+     *
+     * @param a A matrix.
+     */
+    public static void setIdentity( FixedMatrix6x6_64F a ) {
+        a.a11 = 1; a.a21 = 0; a.a31 = 0; a.a41 = 0; a.a51 = 0; a.a61 = 0;
+        a.a12 = 0; a.a22 = 1; a.a32 = 0; a.a42 = 0; a.a52 = 0; a.a62 = 0;
+        a.a13 = 0; a.a23 = 0; a.a33 = 1; a.a43 = 0; a.a53 = 0; a.a63 = 0;
+        a.a14 = 0; a.a24 = 0; a.a34 = 0; a.a44 = 1; a.a54 = 0; a.a64 = 0;
+        a.a15 = 0; a.a25 = 0; a.a35 = 0; a.a45 = 0; a.a55 = 1; a.a65 = 0;
+        a.a16 = 0; a.a26 = 0; a.a36 = 0; a.a46 = 0; a.a56 = 0; a.a66 = 1;
+    }
+
+    /**
+     * <p>
+     * This computes the trace of the matrix:<br>
+     * <br>
+     * trace = ∑<sub>i=1:n</sub> { a<sub>ii</sub> }
+     * </p>
+     * <p>
+     * The trace is only defined for square matrices.
+     * </p>
+     *
+     * @param a A square matrix.  Not modified.
+     */
+    public static double trace( FixedMatrix6x6_64F a ) {
+        return a.a11 + a.a21 + a.a31 + a.a41 + a.a51 + a.a61;
+    }
+
+    /**
+     * <p>
+     * Extracts all diagonal elements from 'input' and places them inside the 'out' vector. Elements
+     * are in sequential order.
+     * </p>
+     *
+     *
+     * @param input Matrix.  Not modified.
+     * @param out Vector containing diagonal elements.  Modified.
+     */
+    public static void diag( FixedMatrix6x6_64F input , FixedMatrix6_64F out ) {
+        out.a1 = input.a11;
+        out.a2 = input.a22;
+        out.a3 = input.a33;
+        out.a4 = input.a44;
+        out.a5 = input.a55;
+        out.a6 = input.a66;
+    }
+
+    /**
+     * <p>
+     * Returns the value of the element in the matrix that has the largest value.<br>
+     * <br>
+     * Max{ a<sub>ij</sub> } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max element value of the matrix.
+     */
+    public static double elementMax( FixedMatrix6x6_64F a ) {
+        double max = a.a11;
+        max = Math.max(max,a.a12);
+        max = Math.max(max,a.a13);
+        max = Math.max(max,a.a14);
+        max = Math.max(max,a.a15);
+        max = Math.max(max,a.a16);
+        max = Math.max(max,a.a21);
+        max = Math.max(max,a.a22);
+        max = Math.max(max,a.a23);
+        max = Math.max(max,a.a24);
+        max = Math.max(max,a.a25);
+        max = Math.max(max,a.a26);
+        max = Math.max(max,a.a31);
+        max = Math.max(max,a.a32);
+        max = Math.max(max,a.a33);
+        max = Math.max(max,a.a34);
+        max = Math.max(max,a.a35);
+        max = Math.max(max,a.a36);
+        max = Math.max(max,a.a41);
+        max = Math.max(max,a.a42);
+        max = Math.max(max,a.a43);
+        max = Math.max(max,a.a44);
+        max = Math.max(max,a.a45);
+        max = Math.max(max,a.a46);
+        max = Math.max(max,a.a51);
+        max = Math.max(max,a.a52);
+        max = Math.max(max,a.a53);
+        max = Math.max(max,a.a54);
+        max = Math.max(max,a.a55);
+        max = Math.max(max,a.a56);
+        max = Math.max(max,a.a61);
+        max = Math.max(max,a.a62);
+        max = Math.max(max,a.a63);
+        max = Math.max(max,a.a64);
+        max = Math.max(max,a.a65);
+        max = Math.max(max,a.a66);
+
+        return max;
+    }
+
+    /**
+     * <p>
+     * Returns the value of the element in the vector that has the largest value.<br>
+     * <br>
+     * Max{ a<sub>i</sub> } for all i<br>
+     * </p>
+     *
+     * @param a A vector. Not modified.
+     * @return The max element value of the matrix.
+     */
+    public static double elementMax( FixedMatrix6_64F a ) {
+        double max = a.a1;
+        max = Math.max(max,a.a2);
+        max = Math.max(max,a.a3);
+        max = Math.max(max,a.a4);
+        max = Math.max(max,a.a5);
+        max = Math.max(max,a.a6);
+
+        return max;
+    }
+
+    /**
+     * <p>
+     * Returns the absolute value of the element in the matrix that has the largest absolute value.<br>
+     * <br>
+     * Max{ |a<sub>ij</sub>| } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max abs element value of the matrix.
+     */
+    public static double elementMaxAbs( FixedMatrix6x6_64F a ) {
+        double max = a.a11;
+        max = Math.max(max,Math.abs(a.a12));
+        max = Math.max(max,Math.abs(a.a13));
+        max = Math.max(max,Math.abs(a.a14));
+        max = Math.max(max,Math.abs(a.a15));
+        max = Math.max(max,Math.abs(a.a16));
+        max = Math.max(max,Math.abs(a.a21));
+        max = Math.max(max,Math.abs(a.a22));
+        max = Math.max(max,Math.abs(a.a23));
+        max = Math.max(max,Math.abs(a.a24));
+        max = Math.max(max,Math.abs(a.a25));
+        max = Math.max(max,Math.abs(a.a26));
+        max = Math.max(max,Math.abs(a.a31));
+        max = Math.max(max,Math.abs(a.a32));
+        max = Math.max(max,Math.abs(a.a33));
+        max = Math.max(max,Math.abs(a.a34));
+        max = Math.max(max,Math.abs(a.a35));
+        max = Math.max(max,Math.abs(a.a36));
+        max = Math.max(max,Math.abs(a.a41));
+        max = Math.max(max,Math.abs(a.a42));
+        max = Math.max(max,Math.abs(a.a43));
+        max = Math.max(max,Math.abs(a.a44));
+        max = Math.max(max,Math.abs(a.a45));
+        max = Math.max(max,Math.abs(a.a46));
+        max = Math.max(max,Math.abs(a.a51));
+        max = Math.max(max,Math.abs(a.a52));
+        max = Math.max(max,Math.abs(a.a53));
+        max = Math.max(max,Math.abs(a.a54));
+        max = Math.max(max,Math.abs(a.a55));
+        max = Math.max(max,Math.abs(a.a56));
+        max = Math.max(max,Math.abs(a.a61));
+        max = Math.max(max,Math.abs(a.a62));
+        max = Math.max(max,Math.abs(a.a63));
+        max = Math.max(max,Math.abs(a.a64));
+        max = Math.max(max,Math.abs(a.a65));
+        max = Math.max(max,Math.abs(a.a66));
+
+        return max;
+    }
+
+    /**
+     * <p>
+     * Returns the absolute value of the element in the vector that has the largest absolute value.<br>
+     * <br>
+     * Max{ |a<sub>i</sub>| } for all i<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max abs element value of the vector.
+     */
+    public static double elementMaxAbs( FixedMatrix6_64F a ) {
+        double max = a.a1;
+        max = Math.max(max,Math.abs(a.a2));
+        max = Math.max(max,Math.abs(a.a3));
+        max = Math.max(max,Math.abs(a.a4));
+        max = Math.max(max,Math.abs(a.a5));
+        max = Math.max(max,Math.abs(a.a6));
+
+        return max;
+    }
+
+    /**
+     * <p>
+     * Returns the value of the element in the matrix that has the minimum value.<br>
+     * <br>
+     * Min{ a<sub>ij</sub> } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The value of element in the matrix with the minimum value.
+     */
+    public static double elementMin( FixedMatrix6x6_64F a ) {
+        double min = a.a11;
+        min = Math.min(min, a.a12);
+        min = Math.min(min, a.a13);
+        min = Math.min(min, a.a14);
+        min = Math.min(min, a.a15);
+        min = Math.min(min, a.a16);
+        min = Math.min(min, a.a21);
+        min = Math.min(min, a.a22);
+        min = Math.min(min, a.a23);
+        min = Math.min(min, a.a24);
+        min = Math.min(min, a.a25);
+        min = Math.min(min, a.a26);
+        min = Math.min(min, a.a31);
+        min = Math.min(min, a.a32);
+        min = Math.min(min, a.a33);
+        min = Math.min(min, a.a34);
+        min = Math.min(min, a.a35);
+        min = Math.min(min, a.a36);
+        min = Math.min(min, a.a41);
+        min = Math.min(min, a.a42);
+        min = Math.min(min, a.a43);
+        min = Math.min(min, a.a44);
+        min = Math.min(min, a.a45);
+        min = Math.min(min, a.a46);
+        min = Math.min(min, a.a51);
+        min = Math.min(min, a.a52);
+        min = Math.min(min, a.a53);
+        min = Math.min(min, a.a54);
+        min = Math.min(min, a.a55);
+        min = Math.min(min, a.a56);
+        min = Math.min(min, a.a61);
+        min = Math.min(min, a.a62);
+        min = Math.min(min, a.a63);
+        min = Math.min(min, a.a64);
+        min = Math.min(min, a.a65);
+        min = Math.min(min, a.a66);
+
+        return min;
+    }
+
+    /**
+     * <p>
+     * Returns the value of the element in the vector that has the minimum value.<br>
+     * <br>
+     * Min{ a<sub>i</sub> } for all<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The value of element in the vector with the minimum value.
+     */
+    public static double elementMin( FixedMatrix6_64F a ) {
+        double min = a.a1;
+        min = Math.min(min, a.a2);
+        min = Math.min(min, a.a3);
+        min = Math.min(min, a.a4);
+        min = Math.min(min, a.a5);
+        min = Math.min(min, a.a6);
+
+        return min;
+    }
+
+    /**
+     * <p>
+     * Returns the absolute value of the element in the matrix that has the smallest absolute value.<br>
+     * <br>
+     * Min{ |a<sub>ij</sub>| } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max element value of the matrix.
+     */
+    public static double elementMinAbs( FixedMatrix6x6_64F a ) {
+        double min = a.a11;
+        min = Math.min(min,Math.abs(a.a12));
+        min = Math.min(min,Math.abs(a.a13));
+        min = Math.min(min,Math.abs(a.a14));
+        min = Math.min(min,Math.abs(a.a15));
+        min = Math.min(min,Math.abs(a.a16));
+        min = Math.min(min,Math.abs(a.a21));
+        min = Math.min(min,Math.abs(a.a22));
+        min = Math.min(min,Math.abs(a.a23));
+        min = Math.min(min,Math.abs(a.a24));
+        min = Math.min(min,Math.abs(a.a25));
+        min = Math.min(min,Math.abs(a.a26));
+        min = Math.min(min,Math.abs(a.a31));
+        min = Math.min(min,Math.abs(a.a32));
+        min = Math.min(min,Math.abs(a.a33));
+        min = Math.min(min,Math.abs(a.a34));
+        min = Math.min(min,Math.abs(a.a35));
+        min = Math.min(min,Math.abs(a.a36));
+        min = Math.min(min,Math.abs(a.a41));
+        min = Math.min(min,Math.abs(a.a42));
+        min = Math.min(min,Math.abs(a.a43));
+        min = Math.min(min,Math.abs(a.a44));
+        min = Math.min(min,Math.abs(a.a45));
+        min = Math.min(min,Math.abs(a.a46));
+        min = Math.min(min,Math.abs(a.a51));
+        min = Math.min(min,Math.abs(a.a52));
+        min = Math.min(min,Math.abs(a.a53));
+        min = Math.min(min,Math.abs(a.a54));
+        min = Math.min(min,Math.abs(a.a55));
+        min = Math.min(min,Math.abs(a.a56));
+        min = Math.min(min,Math.abs(a.a61));
+        min = Math.min(min,Math.abs(a.a62));
+        min = Math.min(min,Math.abs(a.a63));
+        min = Math.min(min,Math.abs(a.a64));
+        min = Math.min(min,Math.abs(a.a65));
+        min = Math.min(min,Math.abs(a.a66));
+
+        return min;
+    }
+
+    /**
+     * <p>
+     * Returns the absolute value of the element in the vector that has the smallest absolute value.<br>
+     * <br>
+     * Min{ |a<sub>i</sub>| } for all i<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max element value of the vector.
+     */
+    public static double elementMinAbs( FixedMatrix6_64F a ) {
+        double min = a.a1;
+        min = Math.min(min,Math.abs(a.a2));
+        min = Math.min(min,Math.abs(a.a3));
+        min = Math.min(min,Math.abs(a.a4));
+        min = Math.min(min,Math.abs(a.a5));
+        min = Math.min(min,Math.abs(a.a6));
+
+        return min;
+    }
+
+    /**
+     * <p>Performs an element by element multiplication operation:<br>
+     * <br>
+     * a<sub>ij</sub> = a<sub>ij</sub> * b<sub>ij</sub> <br>
+     * </p>
+     * @param a The left matrix in the multiplication operation. Modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     */
+    public static void elementMult( FixedMatrix6x6_64F a , FixedMatrix6x6_64F b) {
+        a.a11 *= b.a11; a.a12 *= b.a12; a.a13 *= b.a13; a.a14 *= b.a14; a.a15 *= b.a15; a.a16 *= b.a16;
+        a.a21 *= b.a21; a.a22 *= b.a22; a.a23 *= b.a23; a.a24 *= b.a24; a.a25 *= b.a25; a.a26 *= b.a26;
+        a.a31 *= b.a31; a.a32 *= b.a32; a.a33 *= b.a33; a.a34 *= b.a34; a.a35 *= b.a35; a.a36 *= b.a36;
+        a.a41 *= b.a41; a.a42 *= b.a42; a.a43 *= b.a43; a.a44 *= b.a44; a.a45 *= b.a45; a.a46 *= b.a46;
+        a.a51 *= b.a51; a.a52 *= b.a52; a.a53 *= b.a53; a.a54 *= b.a54; a.a55 *= b.a55; a.a56 *= b.a56;
+        a.a61 *= b.a61; a.a62 *= b.a62; a.a63 *= b.a63; a.a64 *= b.a64; a.a65 *= b.a65; a.a66 *= b.a66;
+    }
+
+    /**
+     * <p>Performs an element by element multiplication operation:<br>
+     * <br>
+     * a<sub>i</sub> = a<sub>i</sub> * b<sub>i</sub> <br>
+     * </p>
+     * @param a The left vector in the multiplication operation. Modified.
+     * @param b The right vector in the multiplication operation. Not modified.
+     */
+    public static void elementMult( FixedMatrix6_64F a , FixedMatrix6_64F b) {
+        a.a1 *= b.a1;
+        a.a2 *= b.a2;
+        a.a3 *= b.a3;
+        a.a4 *= b.a4;
+        a.a5 *= b.a5;
+        a.a6 *= b.a6;
+    }
+
+    /**
+     * <p>Performs an element by element multiplication operation:<br>
+     * <br>
+     * c<sub>ij</sub> = a<sub>ij</sub> * b<sub>ij</sub> <br>
+     * </p>
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void elementMult( FixedMatrix6x6_64F a , FixedMatrix6x6_64F b , FixedMatrix6x6_64F c ) {
+        c.a11 = a.a11*b.a11; c.a12 = a.a12*b.a12; c.a13 = a.a13*b.a13; c.a14 = a.a14*b.a14; c.a15 = a.a15*b.a15; c.a16 = a.a16*b.a16;
+        c.a21 = a.a21*b.a21; c.a22 = a.a22*b.a22; c.a23 = a.a23*b.a23; c.a24 = a.a24*b.a24; c.a25 = a.a25*b.a25; c.a26 = a.a26*b.a26;
+        c.a31 = a.a31*b.a31; c.a32 = a.a32*b.a32; c.a33 = a.a33*b.a33; c.a34 = a.a34*b.a34; c.a35 = a.a35*b.a35; c.a36 = a.a36*b.a36;
+        c.a41 = a.a41*b.a41; c.a42 = a.a42*b.a42; c.a43 = a.a43*b.a43; c.a44 = a.a44*b.a44; c.a45 = a.a45*b.a45; c.a46 = a.a46*b.a46;
+        c.a51 = a.a51*b.a51; c.a52 = a.a52*b.a52; c.a53 = a.a53*b.a53; c.a54 = a.a54*b.a54; c.a55 = a.a55*b.a55; c.a56 = a.a56*b.a56;
+        c.a61 = a.a61*b.a61; c.a62 = a.a62*b.a62; c.a63 = a.a63*b.a63; c.a64 = a.a64*b.a64; c.a65 = a.a65*b.a65; c.a66 = a.a66*b.a66;
+    }
+
+    /**
+     * <p>Performs an element by element multiplication operation:<br>
+     * <br>
+     * c<sub>i</sub> = a<sub>i</sub> * b<sub>j</sub> <br>
+     * </p>
+     * @param a The left vector in the multiplication operation. Not modified.
+     * @param b The right vector in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void elementMult( FixedMatrix6_64F a , FixedMatrix6_64F b , FixedMatrix6_64F c ) {
+        c.a1 = a.a1*b.a1;
+        c.a2 = a.a2*b.a2;
+        c.a3 = a.a3*b.a3;
+        c.a4 = a.a4*b.a4;
+        c.a5 = a.a5*b.a5;
+        c.a6 = a.a6*b.a6;
+    }
+
+    /**
+     * <p>Performs an element by element division operation:<br>
+     * <br>
+     * a<sub>ij</sub> = a<sub>ij</sub> / b<sub>ij</sub> <br>
+     * </p>
+     * @param a The left matrix in the division operation. Modified.
+     * @param b The right matrix in the division operation. Not modified.
+     */
+    public static void elementDiv( FixedMatrix6x6_64F a , FixedMatrix6x6_64F b) {
+        a.a11 /= b.a11; a.a12 /= b.a12; a.a13 /= b.a13; a.a14 /= b.a14; a.a15 /= b.a15; a.a16 /= b.a16;
+        a.a21 /= b.a21; a.a22 /= b.a22; a.a23 /= b.a23; a.a24 /= b.a24; a.a25 /= b.a25; a.a26 /= b.a26;
+        a.a31 /= b.a31; a.a32 /= b.a32; a.a33 /= b.a33; a.a34 /= b.a34; a.a35 /= b.a35; a.a36 /= b.a36;
+        a.a41 /= b.a41; a.a42 /= b.a42; a.a43 /= b.a43; a.a44 /= b.a44; a.a45 /= b.a45; a.a46 /= b.a46;
+        a.a51 /= b.a51; a.a52 /= b.a52; a.a53 /= b.a53; a.a54 /= b.a54; a.a55 /= b.a55; a.a56 /= b.a56;
+        a.a61 /= b.a61; a.a62 /= b.a62; a.a63 /= b.a63; a.a64 /= b.a64; a.a65 /= b.a65; a.a66 /= b.a66;
+    }
+
+    /**
+     * <p>Performs an element by element division operation:<br>
+     * <br>
+     * a<sub>i</sub> = a<sub>i</sub> / b<sub>i</sub> <br>
+     * </p>
+     * @param a The left vector in the division operation. Modified.
+     * @param b The right vector in the division operation. Not modified.
+     */
+    public static void elementDiv( FixedMatrix6_64F a , FixedMatrix6_64F b) {
+        a.a1 /= b.a1;
+        a.a2 /= b.a2;
+        a.a3 /= b.a3;
+        a.a4 /= b.a4;
+        a.a5 /= b.a5;
+        a.a6 /= b.a6;
+    }
+
+    /**
+     * <p>Performs an element by element division operation:<br>
+     * <br>
+     * c<sub>ij</sub> = a<sub>ij</sub> / b<sub>ij</sub> <br>
+     * </p>
+     * @param a The left matrix in the division operation. Not modified.
+     * @param b The right matrix in the division operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void elementDiv( FixedMatrix6x6_64F a , FixedMatrix6x6_64F b , FixedMatrix6x6_64F c ) {
+        c.a11 = a.a11/b.a11; c.a12 = a.a12/b.a12; c.a13 = a.a13/b.a13; c.a14 = a.a14/b.a14; c.a15 = a.a15/b.a15; c.a16 = a.a16/b.a16;
+        c.a21 = a.a21/b.a21; c.a22 = a.a22/b.a22; c.a23 = a.a23/b.a23; c.a24 = a.a24/b.a24; c.a25 = a.a25/b.a25; c.a26 = a.a26/b.a26;
+        c.a31 = a.a31/b.a31; c.a32 = a.a32/b.a32; c.a33 = a.a33/b.a33; c.a34 = a.a34/b.a34; c.a35 = a.a35/b.a35; c.a36 = a.a36/b.a36;
+        c.a41 = a.a41/b.a41; c.a42 = a.a42/b.a42; c.a43 = a.a43/b.a43; c.a44 = a.a44/b.a44; c.a45 = a.a45/b.a45; c.a46 = a.a46/b.a46;
+        c.a51 = a.a51/b.a51; c.a52 = a.a52/b.a52; c.a53 = a.a53/b.a53; c.a54 = a.a54/b.a54; c.a55 = a.a55/b.a55; c.a56 = a.a56/b.a56;
+        c.a61 = a.a61/b.a61; c.a62 = a.a62/b.a62; c.a63 = a.a63/b.a63; c.a64 = a.a64/b.a64; c.a65 = a.a65/b.a65; c.a66 = a.a66/b.a66;
+    }
+
+    /**
+     * <p>Performs an element by element division operation:<br>
+     * <br>
+     * c<sub>i</sub> = a<sub>i</sub> / b<sub>i</sub> <br>
+     * </p>
+     * @param a The left vector in the division operation. Not modified.
+     * @param b The right vector in the division operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void elementDiv( FixedMatrix6_64F a , FixedMatrix6_64F b , FixedMatrix6_64F c ) {
+        c.a1 = a.a1/b.a1;
+        c.a2 = a.a2/b.a2;
+        c.a3 = a.a3/b.a3;
+        c.a4 = a.a4/b.a4;
+        c.a5 = a.a5/b.a5;
+        c.a6 = a.a6/b.a6;
+    }
+
+    /**
+     * <p>
+     * Performs an in-place element by element scalar multiplication.<br>
+     * <br>
+     * a<sub>ij</sub> = α*a<sub>ij</sub>
+     * </p>
+     *
+     * @param a The matrix that is to be scaled.  Modified.
+     * @param alpha the amount each element is multiplied by.
+     */
+    public static void scale( double alpha , FixedMatrix6x6_64F a ) {
+        a.a11 *= alpha; a.a12 *= alpha; a.a13 *= alpha; a.a14 *= alpha; a.a15 *= alpha; a.a16 *= alpha;
+        a.a21 *= alpha; a.a22 *= alpha; a.a23 *= alpha; a.a24 *= alpha; a.a25 *= alpha; a.a26 *= alpha;
+        a.a31 *= alpha; a.a32 *= alpha; a.a33 *= alpha; a.a34 *= alpha; a.a35 *= alpha; a.a36 *= alpha;
+        a.a41 *= alpha; a.a42 *= alpha; a.a43 *= alpha; a.a44 *= alpha; a.a45 *= alpha; a.a46 *= alpha;
+        a.a51 *= alpha; a.a52 *= alpha; a.a53 *= alpha; a.a54 *= alpha; a.a55 *= alpha; a.a56 *= alpha;
+        a.a61 *= alpha; a.a62 *= alpha; a.a63 *= alpha; a.a64 *= alpha; a.a65 *= alpha; a.a66 *= alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an in-place element by element scalar multiplication.<br>
+     * <br>
+     * a<sub>ij</sub> = α*a<sub>ij</sub>
+     * </p>
+     *
+     * @param a The vector that is to be scaled.  Modified.
+     * @param alpha the amount each element is multiplied by.
+     */
+    public static void scale( double alpha , FixedMatrix6_64F a ) {
+        a.a1 *= alpha;
+        a.a2 *= alpha;
+        a.a3 *= alpha;
+        a.a4 *= alpha;
+        a.a5 *= alpha;
+        a.a6 *= alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an element by element scalar multiplication.<br>
+     * <br>
+     * b<sub>ij</sub> = α*a<sub>ij</sub>
+     * </p>
+     *
+     * @param alpha the amount each element is multiplied by.
+     * @param a The matrix that is to be scaled.  Not modified.
+     * @param b Where the scaled matrix is stored. Modified.
+     */
+    public static void scale( double alpha , FixedMatrix6x6_64F a , FixedMatrix6x6_64F b ) {
+        b.a11 = a.a11*alpha; b.a12 = a.a12*alpha; b.a13 = a.a13*alpha; b.a14 = a.a14*alpha; b.a15 = a.a15*alpha; b.a16 = a.a16*alpha;
+        b.a21 = a.a21*alpha; b.a22 = a.a22*alpha; b.a23 = a.a23*alpha; b.a24 = a.a24*alpha; b.a25 = a.a25*alpha; b.a26 = a.a26*alpha;
+        b.a31 = a.a31*alpha; b.a32 = a.a32*alpha; b.a33 = a.a33*alpha; b.a34 = a.a34*alpha; b.a35 = a.a35*alpha; b.a36 = a.a36*alpha;
+        b.a41 = a.a41*alpha; b.a42 = a.a42*alpha; b.a43 = a.a43*alpha; b.a44 = a.a44*alpha; b.a45 = a.a45*alpha; b.a46 = a.a46*alpha;
+        b.a51 = a.a51*alpha; b.a52 = a.a52*alpha; b.a53 = a.a53*alpha; b.a54 = a.a54*alpha; b.a55 = a.a55*alpha; b.a56 = a.a56*alpha;
+        b.a61 = a.a61*alpha; b.a62 = a.a62*alpha; b.a63 = a.a63*alpha; b.a64 = a.a64*alpha; b.a65 = a.a65*alpha; b.a66 = a.a66*alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an element by element scalar multiplication.<br>
+     * <br>
+     * b<sub>i</sub> = α*a<sub>i</sub>
+     * </p>
+     *
+     * @param alpha the amount each element is multiplied by.
+     * @param a The vector that is to be scaled.  Not modified.
+     * @param b Where the scaled matrix is stored. Modified.
+     */
+    public static void scale( double alpha , FixedMatrix6_64F a , FixedMatrix6_64F b ) {
+        b.a1 = a.a1*alpha;
+        b.a2 = a.a2*alpha;
+        b.a3 = a.a3*alpha;
+        b.a4 = a.a4*alpha;
+        b.a5 = a.a5*alpha;
+        b.a6 = a.a6*alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an in-place element by element scalar division. Scalar denominator.<br>
+     * <br>
+     * a<sub>ij</sub> = a<sub>ij</sub>/α
+     * </p>
+     *
+     * @param a The matrix whose elements are to be divided.  Modified.
+     * @param alpha the amount each element is divided by.
+     */
+    public static void divide( FixedMatrix6x6_64F a , double alpha ) {
+        a.a11 /= alpha; a.a12 /= alpha; a.a13 /= alpha; a.a14 /= alpha; a.a15 /= alpha; a.a16 /= alpha;
+        a.a21 /= alpha; a.a22 /= alpha; a.a23 /= alpha; a.a24 /= alpha; a.a25 /= alpha; a.a26 /= alpha;
+        a.a31 /= alpha; a.a32 /= alpha; a.a33 /= alpha; a.a34 /= alpha; a.a35 /= alpha; a.a36 /= alpha;
+        a.a41 /= alpha; a.a42 /= alpha; a.a43 /= alpha; a.a44 /= alpha; a.a45 /= alpha; a.a46 /= alpha;
+        a.a51 /= alpha; a.a52 /= alpha; a.a53 /= alpha; a.a54 /= alpha; a.a55 /= alpha; a.a56 /= alpha;
+        a.a61 /= alpha; a.a62 /= alpha; a.a63 /= alpha; a.a64 /= alpha; a.a65 /= alpha; a.a66 /= alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an in-place element by element scalar division. Scalar denominator.<br>
+     * <br>
+     * a<sub>i</sub> = a<sub>i</sub>/α
+     * </p>
+     *
+     * @param a The vector whose elements are to be divided.  Modified.
+     * @param alpha the amount each element is divided by.
+     */
+    public static void divide( FixedMatrix6_64F a , double alpha ) {
+        a.a1 /= alpha;
+        a.a2 /= alpha;
+        a.a3 /= alpha;
+        a.a4 /= alpha;
+        a.a5 /= alpha;
+        a.a6 /= alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an element by element scalar division.  Scalar denominator.<br>
+     * <br>
+     * b<sub>ij</sub> = a<sub>ij</sub> /α
+     * </p>
+     *
+     * @param alpha the amount each element is divided by.
+     * @param a The matrix whose elements are to be divided.  Not modified.
+     * @param b Where the results are stored. Modified.
+     */
+    public static void divide( FixedMatrix6x6_64F a , double alpha , FixedMatrix6x6_64F b ) {
+        b.a11 = a.a11/alpha; b.a12 = a.a12/alpha; b.a13 = a.a13/alpha; b.a14 = a.a14/alpha; b.a15 = a.a15/alpha; b.a16 = a.a16/alpha;
+        b.a21 = a.a21/alpha; b.a22 = a.a22/alpha; b.a23 = a.a23/alpha; b.a24 = a.a24/alpha; b.a25 = a.a25/alpha; b.a26 = a.a26/alpha;
+        b.a31 = a.a31/alpha; b.a32 = a.a32/alpha; b.a33 = a.a33/alpha; b.a34 = a.a34/alpha; b.a35 = a.a35/alpha; b.a36 = a.a36/alpha;
+        b.a41 = a.a41/alpha; b.a42 = a.a42/alpha; b.a43 = a.a43/alpha; b.a44 = a.a44/alpha; b.a45 = a.a45/alpha; b.a46 = a.a46/alpha;
+        b.a51 = a.a51/alpha; b.a52 = a.a52/alpha; b.a53 = a.a53/alpha; b.a54 = a.a54/alpha; b.a55 = a.a55/alpha; b.a56 = a.a56/alpha;
+        b.a61 = a.a61/alpha; b.a62 = a.a62/alpha; b.a63 = a.a63/alpha; b.a64 = a.a64/alpha; b.a65 = a.a65/alpha; b.a66 = a.a66/alpha;
+    }
+
+    /**
+     * <p>
+     * Performs an element by element scalar division.  Scalar denominator.<br>
+     * <br>
+     * b<sub>i</sub> = a<sub>i</sub> /α
+     * </p>
+     *
+     * @param alpha the amount each element is divided by.
+     * @param a The vector whose elements are to be divided.  Not modified.
+     * @param b Where the results are stored. Modified.
+     */
+    public static void divide( FixedMatrix6_64F a , double alpha , FixedMatrix6_64F b ) {
+        b.a1 = a.a1/alpha;
+        b.a2 = a.a2/alpha;
+        b.a3 = a.a3/alpha;
+        b.a4 = a.a4/alpha;
+        b.a5 = a.a5/alpha;
+        b.a6 = a.a6/alpha;
+    }
+
+    /**
+     * <p>
+     * Changes the sign of every element in the matrix.<br>
+     * <br>
+     * a<sub>ij</sub> = -a<sub>ij</sub>
+     * </p>
+     *
+     * @param a A matrix. Modified.
+     */
+    public static void changeSign( FixedMatrix6x6_64F a )
+    {
+        a.a11 = -a.a11; a.a12 = -a.a12; a.a13 = -a.a13; a.a14 = -a.a14; a.a15 = -a.a15; a.a16 = -a.a16;
+        a.a21 = -a.a21; a.a22 = -a.a22; a.a23 = -a.a23; a.a24 = -a.a24; a.a25 = -a.a25; a.a26 = -a.a26;
+        a.a31 = -a.a31; a.a32 = -a.a32; a.a33 = -a.a33; a.a34 = -a.a34; a.a35 = -a.a35; a.a36 = -a.a36;
+        a.a41 = -a.a41; a.a42 = -a.a42; a.a43 = -a.a43; a.a44 = -a.a44; a.a45 = -a.a45; a.a46 = -a.a46;
+        a.a51 = -a.a51; a.a52 = -a.a52; a.a53 = -a.a53; a.a54 = -a.a54; a.a55 = -a.a55; a.a56 = -a.a56;
+        a.a61 = -a.a61; a.a62 = -a.a62; a.a63 = -a.a63; a.a64 = -a.a64; a.a65 = -a.a65; a.a66 = -a.a66;
+    }
+
+    /**
+     * <p>
+     * Changes the sign of every element in the vector.<br>
+     * <br>
+     * a<sub>i</sub> = -a<sub>i</sub>
+     * </p>
+     *
+     * @param a A vector. Modified.
+     */
+    public static void changeSign( FixedMatrix6_64F a )
+    {
+        a.a1 = -a.a1;
+        a.a2 = -a.a2;
+        a.a3 = -a.a3;
+        a.a4 = -a.a4;
+        a.a5 = -a.a5;
+        a.a6 = -a.a6;
+    }
+
+    /**
+     * <p>
+     * Sets every element in the matrix to the specified value.<br>
+     * <br>
+     * a<sub>ij</sub> = value
+     * <p>
+     *
+     * @param a A matrix whose elements are about to be set. Modified.
+     * @param v The value each element will have.
+     */
+    public static void fill( FixedMatrix6x6_64F a , double v  ) {
+        a.a11 = v; a.a12 = v; a.a13 = v; a.a14 = v; a.a15 = v; a.a16 = v;
+        a.a21 = v; a.a22 = v; a.a23 = v; a.a24 = v; a.a25 = v; a.a26 = v;
+        a.a31 = v; a.a32 = v; a.a33 = v; a.a34 = v; a.a35 = v; a.a36 = v;
+        a.a41 = v; a.a42 = v; a.a43 = v; a.a44 = v; a.a45 = v; a.a46 = v;
+        a.a51 = v; a.a52 = v; a.a53 = v; a.a54 = v; a.a55 = v; a.a56 = v;
+        a.a61 = v; a.a62 = v; a.a63 = v; a.a64 = v; a.a65 = v; a.a66 = v;
+    }
+
+    /**
+     * <p>
+     * Sets every element in the vector to the specified value.<br>
+     * <br>
+     * a<sub>i</sub> = value
+     * <p>
+     *
+     * @param a A vector whose elements are about to be set. Modified.
+     * @param v The value each element will have.
+     */
+    public static void fill( FixedMatrix6_64F a , double v  ) {
+        a.a1 = v;
+        a.a2 = v;
+        a.a3 = v;
+        a.a4 = v;
+        a.a5 = v;
+        a.a6 = v;
+    }
+
+    /**
+     * Extracts the row from the matrix a.
+     * @param a Input matrix
+     * @param row Which row is to be extracted
+     * @param out output. Storage for the extracted row. If null then a new vector will be returned.
+     * @return The extracted row.
+     */
+    public static FixedMatrix6_64F extractRow( FixedMatrix6x6_64F a , int row , FixedMatrix6_64F out ) {
+        if( out == null) out = new FixedMatrix6_64F();
+        switch( row ) {
+            case 0:
+                out.a1 = a.a11;
+                out.a2 = a.a12;
+                out.a3 = a.a13;
+                out.a4 = a.a14;
+                out.a5 = a.a15;
+                out.a6 = a.a16;
+            break;
+            case 1:
+                out.a1 = a.a21;
+                out.a2 = a.a22;
+                out.a3 = a.a23;
+                out.a4 = a.a24;
+                out.a5 = a.a25;
+                out.a6 = a.a26;
+            break;
+            case 2:
+                out.a1 = a.a31;
+                out.a2 = a.a32;
+                out.a3 = a.a33;
+                out.a4 = a.a34;
+                out.a5 = a.a35;
+                out.a6 = a.a36;
+            break;
+            case 3:
+                out.a1 = a.a41;
+                out.a2 = a.a42;
+                out.a3 = a.a43;
+                out.a4 = a.a44;
+                out.a5 = a.a45;
+                out.a6 = a.a46;
+            break;
+            case 4:
+                out.a1 = a.a51;
+                out.a2 = a.a52;
+                out.a3 = a.a53;
+                out.a4 = a.a54;
+                out.a5 = a.a55;
+                out.a6 = a.a56;
+            break;
+            case 5:
+                out.a1 = a.a61;
+                out.a2 = a.a62;
+                out.a3 = a.a63;
+                out.a4 = a.a64;
+                out.a5 = a.a65;
+                out.a6 = a.a66;
+            break;
+            default:
+                throw new IllegalArgumentException("Out of bounds row.  row = "+row);
+        }
+        return out;
+    }
+
+    /**
+     * Extracts the column from the matrix a.
+     * @param a Input matrix
+     * @param column Which column is to be extracted
+     * @param out output. Storage for the extracted column. If null then a new vector will be returned.
+     * @return The extracted column.
+     */
+    public static FixedMatrix6_64F extractColumn( FixedMatrix6x6_64F a , int column , FixedMatrix6_64F out ) {
+        if( out == null) out = new FixedMatrix6_64F();
+        switch( column ) {
+            case 0:
+                out.a1 = a.a11;
+                out.a2 = a.a21;
+                out.a3 = a.a31;
+                out.a4 = a.a41;
+                out.a5 = a.a51;
+                out.a6 = a.a61;
+            break;
+            case 1:
+                out.a1 = a.a12;
+                out.a2 = a.a22;
+                out.a3 = a.a32;
+                out.a4 = a.a42;
+                out.a5 = a.a52;
+                out.a6 = a.a62;
+            break;
+            case 2:
+                out.a1 = a.a13;
+                out.a2 = a.a23;
+                out.a3 = a.a33;
+                out.a4 = a.a43;
+                out.a5 = a.a53;
+                out.a6 = a.a63;
+            break;
+            case 3:
+                out.a1 = a.a14;
+                out.a2 = a.a24;
+                out.a3 = a.a34;
+                out.a4 = a.a44;
+                out.a5 = a.a54;
+                out.a6 = a.a64;
+            break;
+            case 4:
+                out.a1 = a.a15;
+                out.a2 = a.a25;
+                out.a3 = a.a35;
+                out.a4 = a.a45;
+                out.a5 = a.a55;
+                out.a6 = a.a65;
+            break;
+            case 5:
+                out.a1 = a.a16;
+                out.a2 = a.a26;
+                out.a3 = a.a36;
+                out.a4 = a.a46;
+                out.a5 = a.a56;
+                out.a6 = a.a66;
+            break;
+            default:
+                throw new IllegalArgumentException("Out of bounds column.  column = "+column);
+        }
+        return out;
+    }
+
+}
+
diff --git a/main/dense64/src/org/ejml/alg/generic/CodeGeneratorMisc.java b/main/dense64/src/org/ejml/alg/generic/CodeGeneratorMisc.java
new file mode 100644
index 0000000..cf0e57c
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/generic/CodeGeneratorMisc.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.generic;
+
+
+/**
+ * Various things related to auto generating code.
+ *
+ * @author Peter Abeles
+ */
+public class CodeGeneratorMisc {
+
+    public final static String COPYRIGHT = "/*\n" +
+            " * Copyright (c) 2009-2013, Peter Abeles. All Rights Reserved.\n" +
+            " *\n" +
+            " * This file is part of Efficient Java Matrix Library (EJML).\n" +
+            " *\n" +
+            " * Licensed under the Apache License, Version 2.0 (the \"License\");\n" +
+            " * you may not use this file except in compliance with the License.\n" +
+            " * You may obtain a copy of the License at\n" +
+            " *\n" +
+            " *   http://www.apache.org/licenses/LICENSE-2.0\n" +
+            " *\n" +
+            " * Unless required by applicable law or agreed to in writing, software\n" +
+            " * distributed under the License is distributed on an \"AS IS\" BASIS,\n" +
+            " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" +
+            " * See the License for the specific language governing permissions and\n" +
+            " * limitations under the License.\n" +
+            " */\n";
+}
diff --git a/main/dense64/src/org/ejml/alg/generic/GenericMatrixOps.java b/main/dense64/src/org/ejml/alg/generic/GenericMatrixOps.java
new file mode 100644
index 0000000..625d4ab
--- /dev/null
+++ b/main/dense64/src/org/ejml/alg/generic/GenericMatrixOps.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.generic;
+
+import org.ejml.data.RealMatrix64F;
+
+import java.util.Random;
+
+/**
+ * @author Peter Abeles
+ */
+public class GenericMatrixOps {
+
+//    public static DenseD2Matrix64F convertToD2( DenseMatrix64F orig ) {
+//        DenseD2Matrix64F ret = new DenseD2Matrix64F(orig.getNumRows(),orig.getNumCols());
+//
+//        copy(orig,ret);
+//
+//        return ret;
+//    }
+
+    public static boolean isEquivalent( RealMatrix64F a , RealMatrix64F b , double tol )
+    {
+        if( a.getNumRows() != b.getNumRows() || a.getNumCols() != b.getNumCols() )
+            return false;
+
+        for( int i = 0; i < a.getNumRows(); i++ ) {
+            for( int j = 0; j < a.getNumCols(); j++ ) {
+                double diff = Math.abs(a.get(i,j) - b.get(i,j));
+
+                if( diff > tol )
+                    return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns true if the provided matrix is has a value of 1 along the diagonal
+     * elements and zero along all the other elements.
+     *
+     * @param a Matrix being inspected.
+     * @param tol How close to zero or one each element needs to be.
+     * @return If it is within tolerance to an identity matrix.
+     */
+    public static boolean isIdentity( RealMatrix64F a , double tol )
+    {
+        for( int i = 0; i < a.getNumRows(); i++ ) {
+            for( int j = 0; j < a.getNumCols(); j++ ) {
+                if( i == j ) {
+                    if( Math.abs(a.get(i,j)-1.0) > tol )
+                        return false;
+                } else {
+                    if( Math.abs(a.get(i,j)) > tol )
+                        return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    public static boolean isEquivalentTriangle( boolean upper , RealMatrix64F a , RealMatrix64F b , double tol )
+    {
+        if( a.getNumRows() != b.getNumRows() || a.getNumCols() != b.getNumCols() )
+            return false;
+
+        if( upper ) {
+            for( int i = 0; i < a.getNumRows(); i++ ) {
+                for( int j = i; j < a.getNumCols(); j++ ) {
+                    double diff = Math.abs(a.get(i,j) - b.get(i,j));
+
+                    if( diff > tol )
+                        return false;
+                }
+            }
+        } else {
+            for( int j = 0; j < a.getNumCols(); j++ ) {
+                for( int i = j; i < a.getNumRows(); i++ ) {
+                    double diff = Math.abs(a.get(i,j) - b.get(i,j));
+
+                    if( diff > tol )
+                        return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    public static void copy( RealMatrix64F from , RealMatrix64F to )
+    {
+        int numCols = from.getNumCols();
+        int numRows = from.getNumRows();
+
+        for( int i = 0; i < numRows; i++ ) {
+            for( int j = 0; j < numCols; j++ ) {
+                to.set(i,j,from.get(i,j));
+            }
+        }
+    }
+
+    public static void setRandom( RealMatrix64F a , double min , double max , Random rand )
+    {
+        for( int i = 0; i < a.getNumRows(); i++ ) {
+            for( int j = 0; j < a.getNumCols(); j++ ) {
+                double val = rand.nextDouble()*(max-min)+min;
+                a.set(i,j,val);
+            }
+        }
+    }
+}
diff --git a/main/dense64/src/org/ejml/factory/DecompositionFactory.java b/main/dense64/src/org/ejml/factory/DecompositionFactory.java
new file mode 100644
index 0000000..2e0395a
--- /dev/null
+++ b/main/dense64/src/org/ejml/factory/DecompositionFactory.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.factory;
+
+import org.ejml.EjmlParameters;
+import org.ejml.alg.dense.decomposition.chol.CholeskyDecompositionBlock_D64;
+import org.ejml.alg.dense.decomposition.chol.CholeskyDecompositionInner_D64;
+import org.ejml.alg.dense.decomposition.chol.CholeskyDecompositionLDL_D64;
+import org.ejml.alg.dense.decomposition.chol.CholeskyDecomposition_B64_to_D64;
+import org.ejml.alg.dense.decomposition.eig.SwitchingEigenDecomposition;
+import org.ejml.alg.dense.decomposition.eig.SymmetricQRAlgorithmDecomposition_D64;
+import org.ejml.alg.dense.decomposition.eig.WatchedDoubleStepQRDecomposition_D64;
+import org.ejml.alg.dense.decomposition.hessenberg.TridiagonalDecompositionHouseholder_D64;
+import org.ejml.alg.dense.decomposition.hessenberg.TridiagonalDecomposition_B64_to_D64;
+import org.ejml.alg.dense.decomposition.lu.LUDecompositionAlt_D64;
+import org.ejml.alg.dense.decomposition.qr.QRColPivDecompositionHouseholderColumn_D64;
+import org.ejml.alg.dense.decomposition.qr.QRDecompositionHouseholderColumn_D64;
+import org.ejml.alg.dense.decomposition.svd.SvdImplicitQrDecompose_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.data.RealMatrix64F;
+import org.ejml.interfaces.decomposition.*;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.EigenOps;
+import org.ejml.ops.NormOps;
+import org.ejml.ops.SpecializedOps;
+
+
+/**
+ * <p>
+ * Contains operations related to creating and evaluating the quality of common matrix decompositions.  Except
+ * in specialized situations, matrix decompositions should be instantiated from this factory instead of being
+ * directly constructed.  Low level implementations are more prone to changes and new algorithms will be
+ * automatically placed here.
+ * </p>
+ *
+ * <p>
+ * Several functions are also provided to evaluate the quality of a decomposition.  This is provided
+ * as a way to sanity check a decomposition.  Often times a significant error in a decomposition will
+ * result in a poor (larger) quality value. Typically a quality value of around 1e-15 means it is within
+ * machine precision.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class DecompositionFactory {
+
+    /**
+     * <p>
+     * Returns a {@link CholeskyDecomposition} that has been optimized for the specified matrix size.
+     * </p>
+     *
+     * @param matrixSize Number of rows and columns that the returned decomposition is optimized for.
+     * @param lower should a lower or upper triangular matrix be used. If not sure set to true.
+     * @return A new CholeskyDecomposition.
+     */
+    public static CholeskyDecomposition<DenseMatrix64F> chol( int matrixSize , boolean lower )
+    {
+        if( matrixSize < EjmlParameters.SWITCH_BLOCK64_CHOLESKY ) {
+            return new CholeskyDecompositionInner_D64(lower);
+        } else if( EjmlParameters.MEMORY == EjmlParameters.MemoryUsage.FASTER ){
+            return new CholeskyDecomposition_B64_to_D64(lower);
+        } else {
+            return new CholeskyDecompositionBlock_D64(EjmlParameters.BLOCK_WIDTH_CHOL);
+        }
+    }
+
+    /**
+     * <p>
+     * Returns a {@link org.ejml.alg.dense.decomposition.chol.CholeskyDecompositionLDL_D64} that has been optimized for the specified matrix size.
+     * </p>
+     *
+     * @param matrixSize Number of rows and columns that the returned decomposition is optimized for.
+     * @return CholeskyLDLDecomposition
+     */
+    public static CholeskyLDLDecomposition<DenseMatrix64F> cholLDL( int matrixSize ) {
+        return new CholeskyDecompositionLDL_D64();
+    }
+
+    /**
+     * <p>
+     * Returns a {@link org.ejml.interfaces.decomposition.LUDecomposition} that has been optimized for the specified matrix size.
+     * </p>
+     *
+     * @parm matrixWidth The matrix size that the decomposition should be optimized for.
+     * @return LUDecomposition
+     */
+    public static LUDecomposition<DenseMatrix64F> lu( int numRows , int numCol ) {
+        return new LUDecompositionAlt_D64();
+    }
+
+    /**
+     * <p>
+     * Returns a {@link SingularValueDecomposition} that has been optimized for the specified matrix size.
+     * For improved performance only the portion of the decomposition that the user requests will be computed.
+     * </p>
+     *
+     * @param numRows Number of rows the returned decomposition is optimized for.
+     * @param numCols Number of columns that the returned decomposition is optimized for.
+     * @param needU Should it compute the U matrix. If not sure set to true.
+     * @param needV Should it compute the V matrix. If not sure set to true.
+     * @param compact Should it compute the SVD in compact form.  If not sure set to false.
+     * @return
+     */
+    public static SingularValueDecomposition<DenseMatrix64F> svd( int numRows , int numCols , 
+                                                                  boolean needU , boolean needV , boolean compact ) {
+        // Don't allow the tall decomposition by default since it *might* be less stable
+        return new SvdImplicitQrDecompose_D64(compact,needU,needV,false);
+    }
+
+    /**
+     * <p>
+     * Returns a {@link org.ejml.interfaces.decomposition.QRDecomposition} that has been optimized for the specified matrix size.
+     * </p>
+     *
+     * @param numRows Number of rows the returned decomposition is optimized for.
+     * @param numCols Number of columns that the returned decomposition is optimized for.
+     * @return QRDecomposition
+     */
+    public static QRDecomposition<DenseMatrix64F> qr( int numRows , int numCols ) {
+        return new QRDecompositionHouseholderColumn_D64();
+    }
+
+    /**
+     * <p>
+     * Returns a {@link org.ejml.interfaces.decomposition.QRPDecomposition} that has been optimized for the specified matrix size.
+     * </p>
+     *
+     * @param numRows Number of rows the returned decomposition is optimized for.
+     * @param numCols Number of columns that the returned decomposition is optimized for.
+     * @return QRPDecomposition
+     */
+    public static QRPDecomposition<DenseMatrix64F> qrp( int numRows , int numCols ) {
+        return new QRColPivDecompositionHouseholderColumn_D64();
+    }
+
+    /**
+     * <p>
+     * Returns an {@link EigenDecomposition} that has been optimized for the specified matrix size.
+     * If the input matrix is symmetric within tolerance then the symmetric algorithm will be used, otherwise
+     * a general purpose eigenvalue decomposition is used.
+     * </p>
+     *
+     * @param matrixSize Number of rows and columns that the returned decomposition is optimized for.
+     * @param needVectors Should eigenvectors be computed or not.  If not sure set to true.
+     * @return A new EigenDecomposition
+     */
+    public static EigenDecomposition<DenseMatrix64F> eig( int matrixSize , boolean needVectors ) {
+        return new SwitchingEigenDecomposition(matrixSize,needVectors,1e-8);
+    }
+
+    /**
+     * <p>
+     * Returns an {@link EigenDecomposition} which is specialized for symmetric matrices or the general problem.
+     * </p>
+     *
+     * @param matrixSize Number of rows and columns that the returned decomposition is optimized for.
+     * @param computeVectors Should it compute the eigenvectors or just eigenvalues.
+     * @param isSymmetric If true then the returned algorithm is specialized only for symmetric matrices, if false
+     *                    then a general purpose algorithm is returned.
+     * @return EVD for any matrix.
+     */
+    public static EigenDecomposition<DenseMatrix64F> eig( int matrixSize , boolean computeVectors ,
+                                                          boolean isSymmetric ) {
+        if( isSymmetric ) {
+            TridiagonalSimilarDecomposition<DenseMatrix64F> decomp = DecompositionFactory.tridiagonal(matrixSize);
+            return new SymmetricQRAlgorithmDecomposition_D64(decomp,computeVectors);
+        } else
+            return new WatchedDoubleStepQRDecomposition_D64(computeVectors);
+    }
+
+    /**
+     * <p>
+     * Computes a metric which measures the the quality of a singular value decomposition.  If a
+     * value is returned that is close to or smaller than 1e-15 then it is within machine precision.
+     * </p>
+     *
+     * <p>
+     * SVD quality is defined as:<br>
+     * <br>
+     * Quality = || A - U W V<sup>T</sup>|| / || A || <br>
+     * where A is the original matrix , U W V is the decomposition, and ||A|| is the norm-f of A.
+     * </p>
+     *
+     * @param orig The original matrix which was decomposed. Not modified.
+     * @param svd The decomposition after processing 'orig'. Not modified.
+     * @return The quality of the decomposition.
+     */
+    public static double quality( DenseMatrix64F orig , SingularValueDecomposition<DenseMatrix64F> svd )
+    {
+        return quality(orig,svd.getU(null,false),svd.getW(null),svd.getV(null,true));
+    }
+
+    public static double quality( DenseMatrix64F orig , DenseMatrix64F U , DenseMatrix64F W , DenseMatrix64F Vt )
+    {
+        // foundA = U*W*Vt
+        DenseMatrix64F UW = new DenseMatrix64F(U.numRows,W.numCols);
+        CommonOps.mult(U,W,UW);
+        DenseMatrix64F foundA = new DenseMatrix64F(UW.numRows,Vt.numCols);
+        CommonOps.mult(UW,Vt,foundA);
+
+        double normA = NormOps.normF(foundA);
+
+        return SpecializedOps.diffNormF(orig,foundA)/normA;
+    }
+
+    /**
+     * <p>
+     * Computes a metric which measures the the quality of an eigen value decomposition.  If a
+     * value is returned that is close to or smaller than 1e-15 then it is within machine precision.
+     * </p>
+     * <p>
+     * EVD quality is defined as:<br>
+     * <br>
+     * Quality = ||A*V - V*D|| / ||A*V||.
+     *  </p>
+     *
+     * @param orig The original matrix. Not modified.
+     * @param eig EVD of the original matrix. Not modified.
+     * @return The quality of the decomposition.
+     */
+    public static double quality( DenseMatrix64F orig , EigenDecomposition<DenseMatrix64F> eig )
+    {
+        DenseMatrix64F A = orig;
+        DenseMatrix64F V = EigenOps.createMatrixV(eig);
+        DenseMatrix64F D = EigenOps.createMatrixD(eig);
+
+        // L = A*V
+        DenseMatrix64F L = new DenseMatrix64F(A.numRows,V.numCols);
+        CommonOps.mult(A,V,L);
+        // R = V*D
+        DenseMatrix64F R = new DenseMatrix64F(V.numRows,D.numCols);
+        CommonOps.mult(V,D,R);
+
+        DenseMatrix64F diff = new DenseMatrix64F(L.numRows,L.numCols);
+        CommonOps.subtract(L,R,diff);
+
+        double top = NormOps.normF(diff);
+        double bottom = NormOps.normF(L);
+
+        double error = top/bottom;
+
+        return error;
+    }
+
+    /**
+     * Checks to see if the passed in tridiagonal decomposition is of the appropriate type
+     * for the matrix of the provided size.  Returns the same instance or a new instance.
+     *
+     * @param matrixSize Number of rows and columns that the returned decomposition is optimized for.
+     */
+    public static TridiagonalSimilarDecomposition<DenseMatrix64F> tridiagonal(  int matrixSize ) {
+        if( matrixSize >= 1800 ) {
+            return new TridiagonalDecomposition_B64_to_D64();
+        } else {
+            return new TridiagonalDecompositionHouseholder_D64();
+        }
+    }
+
+    /**
+     * A simple convinience function that decomposes the matrix but automatically checks the input ti make
+     * sure is not being modified.
+     *
+     * @param decomp Decomposition which is being wrapped
+     * @param M THe matrix being decomposed.
+     * @param <T> Matrix type.
+     * @return If the decomposition was successful or not.
+     */
+    public static <T extends RealMatrix64F> boolean decomposeSafe( DecompositionInterface<T> decomp, T M ) {
+        if( decomp.inputModified() ) {
+            return decomp.decompose(M.<T>copy());
+        } else {
+            return decomp.decompose(M);
+        }
+    }
+}
diff --git a/main/dense64/src/org/ejml/factory/LinearSolverFactory.java b/main/dense64/src/org/ejml/factory/LinearSolverFactory.java
new file mode 100644
index 0000000..a480f08
--- /dev/null
+++ b/main/dense64/src/org/ejml/factory/LinearSolverFactory.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.factory;
+
+import org.ejml.EjmlParameters;
+import org.ejml.alg.dense.decomposition.chol.CholeskyDecompositionCommon_D64;
+import org.ejml.alg.dense.decomposition.chol.CholeskyDecompositionInner_D64;
+import org.ejml.alg.dense.decomposition.lu.LUDecompositionAlt_D64;
+import org.ejml.alg.dense.decomposition.qr.QRColPivDecompositionHouseholderColumn_D64;
+import org.ejml.alg.dense.linsol.AdjustableLinearSolver;
+import org.ejml.alg.dense.linsol.chol.LinearSolverChol_B64;
+import org.ejml.alg.dense.linsol.chol.LinearSolverChol_D64;
+import org.ejml.alg.dense.linsol.lu.LinearSolverLu_D64;
+import org.ejml.alg.dense.linsol.qr.*;
+import org.ejml.alg.dense.linsol.svd.SolvePseudoInverseSvd;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+
+
+/**
+ * A factory for generating solvers for systems of the form A*x=b, where A and B are known and x is unknown. 
+ *
+ * @author Peter Abeles
+ */
+public class LinearSolverFactory {
+
+    /**
+     * Creates a linear solver using LU decomposition
+     */
+    public static LinearSolver<DenseMatrix64F> lu( int numRows ) {
+        return linear(numRows);
+    }
+
+    /**
+     * Creates a linear solver using Cholesky decomposition
+     */
+    public static LinearSolver<DenseMatrix64F> chol( int numRows ) {
+        return symmPosDef(numRows);
+    }
+
+    /**
+     * Creates a linear solver using QR decomposition
+     */
+    public static LinearSolver<DenseMatrix64F> qr( int numRows , int numCols ) {
+        return leastSquares(numRows,numCols);
+    }
+
+    /**
+     * Creates a linear solver using QRP decomposition
+     */
+    public static LinearSolver<DenseMatrix64F> qrp( boolean computeNorm2, boolean computeQ ) {
+        return leastSquaresQrPivot(computeNorm2,computeQ);
+    }
+
+    /**
+     * Creates a general purpose solver.  Use this if you are not sure what you need.
+     *
+     * @param numRows The number of rows that the decomposition is optimized for.
+     * @param numCols The number of columns that the decomposition is optimized for.
+     */
+    public static LinearSolver<DenseMatrix64F> general( int numRows , int numCols ) {
+        if( numRows == numCols )
+            return linear(numRows);
+        else
+            return leastSquares(numRows,numCols);
+    }
+
+    /**
+     * Creates a solver for linear systems.  The A matrix will have dimensions (m,m).
+     *
+     * @return A new linear solver.
+     */
+    public static LinearSolver<DenseMatrix64F> linear( int matrixSize ) {
+        return new LinearSolverLu_D64(new LUDecompositionAlt_D64());
+    }
+
+    /**
+     * Creates a good general purpose solver for over determined systems and returns the optimal least-squares
+     * solution.  The A matrix will have dimensions (m,n) where m ≥ n.
+     *
+     * @param numRows The number of rows that the decomposition is optimized for.
+     * @param numCols The number of columns that the decomposition is optimized for.
+     * @return A new least-squares solver for over determined systems.
+     */
+    public static LinearSolver<DenseMatrix64F> leastSquares( int numRows , int numCols ) {
+        if(numCols < EjmlParameters.SWITCH_BLOCK64_QR )  {
+            return new LinearSolverQrHouseCol_D64();
+        } else {
+            if( EjmlParameters.MEMORY == EjmlParameters.MemoryUsage.FASTER )
+                return new LinearSolverQrBlock64_D64();
+            else
+                return new LinearSolverQrHouseCol_D64();
+        }
+    }
+
+    /**
+     * Creates a solver for symmetric positive definite matrices.
+     *
+     * @return A new solver for symmetric positive definite matrices.
+     */
+    public static LinearSolver<DenseMatrix64F> symmPosDef( int matrixWidth ) {
+        if(matrixWidth < EjmlParameters.SWITCH_BLOCK64_CHOLESKY )  {
+            CholeskyDecompositionCommon_D64 decomp = new CholeskyDecompositionInner_D64(true);
+            return new LinearSolverChol_D64(decomp);
+        } else {
+            if( EjmlParameters.MEMORY == EjmlParameters.MemoryUsage.FASTER )
+                return new LinearSolverChol_B64();
+            else {
+                CholeskyDecompositionCommon_D64 decomp = new CholeskyDecompositionInner_D64(true);
+                return new LinearSolverChol_D64(decomp);
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Linear solver which uses QR pivot decomposition.  These solvers can handle singular systems
+     * and should never fail.  For singular systems, the solution might not be as accurate as a
+     * pseudo inverse that uses SVD.
+     * </p>
+     * 
+     * <p>
+     * For singular systems there are multiple correct solutions.  The optimal 2-norm solution is the
+     * solution vector with the minimal 2-norm and is unique.  If the optimal solution is not computed
+     * then the basic solution is returned.  See {@link org.ejml.alg.dense.linsol.qr.BaseLinearSolverQrp_D64}
+     * for details.  There is only a runtime difference for small matrices, 2-norm solution is slower.
+     * </p>
+     *
+     * <p>
+     * Two different solvers are available.  Compute Q will compute the Q matrix once then use it multiple times.
+     * If the solution for a single vector is being found then this should be set to false.  If the pseudo inverse
+     * is being found or the solution matrix has more than one columns AND solve is being called numerous multiples
+     * times then this should be set to true.
+     * </p>
+     *
+     * @param computeNorm2 true to compute the minimum 2-norm solution for singular systems. Try true.
+     * @param computeQ Should it precompute Q or use house holder.  Try false;
+     * @return Pseudo inverse type solver using QR with column pivots.
+     */
+    public static LinearSolver<DenseMatrix64F> leastSquaresQrPivot( boolean computeNorm2 , boolean computeQ ) {
+        QRColPivDecompositionHouseholderColumn_D64 decomposition =
+                new QRColPivDecompositionHouseholderColumn_D64();
+
+        if( computeQ )
+            return new SolvePseudoInverseQrp_D64(decomposition,computeNorm2);
+        else
+            return new LinearSolverQrpHouseCol_D64(decomposition,computeNorm2);
+    }
+
+    /**
+     * <p>
+     * Returns a solver which uses the pseudo inverse.  Useful when a matrix
+     * needs to be inverted which is singular.  Two variants of pseudo inverse are provided.  SVD
+     * will tend to be the most robust but the slowest and QR decomposition with column pivots will
+     * be faster, but less robust.
+     * </p>
+     * 
+     * <p>
+     * See {@link #leastSquaresQrPivot} for additional options specific to QR decomposition based
+     * pseudo inverse.  These options allow for better runtime performance in different situations.
+     * </p>
+     *
+     * @param useSVD If true SVD will be used, otherwise QR with column pivot will be used.
+     * @return Solver for singular matrices.
+     */
+    public static LinearSolver<DenseMatrix64F> pseudoInverse( boolean useSVD ) {
+        if( useSVD )
+            return new SolvePseudoInverseSvd();
+        else
+            return leastSquaresQrPivot(true,false);
+    }
+
+    /**
+     * Create a solver which can efficiently add and remove elements instead of recomputing
+     * everything from scratch.
+     */
+    public static AdjustableLinearSolver adjustable() {
+        return new AdjLinearSolverQr_D64();
+    }
+}
diff --git a/main/dense64/src/org/ejml/factory/SingularMatrixException.java b/main/dense64/src/org/ejml/factory/SingularMatrixException.java
new file mode 100644
index 0000000..77c1eef
--- /dev/null
+++ b/main/dense64/src/org/ejml/factory/SingularMatrixException.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.factory;
+
+
+/**
+ * This exception is thrown if an operation can not be finished because the matrix is singular.
+ * It is a RuntimeException to allow the code to be written cleaner and also because singular
+ * matrices are not always detected.  Forcing an exception to be caught provides a false sense
+ * of security.
+ *
+ * @author Peter Abeles
+ */
+public class SingularMatrixException extends RuntimeException {
+
+    public SingularMatrixException() {
+    }
+
+    public SingularMatrixException(String message) {
+        super(message);
+    }
+}
diff --git a/main/dense64/src/org/ejml/ops/CommonOps.java b/main/dense64/src/org/ejml/ops/CommonOps.java
new file mode 100644
index 0000000..3b3ee60
--- /dev/null
+++ b/main/dense64/src/org/ejml/ops/CommonOps.java
@@ -0,0 +1,2101 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.EjmlParameters;
+import org.ejml.UtilEjml;
+import org.ejml.alg.dense.decomposition.lu.LUDecompositionAlt_D64;
+import org.ejml.alg.dense.linsol.LinearSolverSafe;
+import org.ejml.alg.dense.linsol.lu.LinearSolverLu_D64;
+import org.ejml.alg.dense.misc.*;
+import org.ejml.alg.dense.mult.MatrixMatrixMult;
+import org.ejml.alg.dense.mult.MatrixMultProduct;
+import org.ejml.alg.dense.mult.MatrixVectorMult;
+import org.ejml.alg.dense.mult.VectorVectorMult;
+import org.ejml.data.D1Matrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.data.RealMatrix64F;
+import org.ejml.data.RowD1Matrix64F;
+import org.ejml.factory.LinearSolverFactory;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.ejml.interfaces.linsol.ReducedRowEchelonForm;
+
+import java.util.Arrays;
+
+/**
+ * <p>
+ * Common matrix operations are contained here.  Which specific underlying algorithm is used
+ * is not specified just the out come of the operation.  Nor should calls to these functions
+ * reply on the underlying implementation.  Which algorithm is used can depend on the matrix
+ * being passed in.
+ * </p>
+ * <p>
+ * For more exotic and specialized generic operations see {@link org.ejml.ops.SpecializedOps}.
+ * </p>
+ * @see org.ejml.alg.dense.mult.MatrixMatrixMult
+ * @see org.ejml.alg.dense.mult.MatrixVectorMult
+ * @see org.ejml.ops.SpecializedOps
+ * @see org.ejml.ops.MatrixFeatures
+ *
+ * @author Peter Abeles
+ */
+ at SuppressWarnings({"ForLoopReplaceableByForEach"})
+public class CommonOps {
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a * b <br>
+     * <br>
+     * c<sub>ij</sub> = ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void mult( RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( b.numCols == 1 ) {
+            MatrixVectorMult.mult(a,b,c);
+        } else if( b.numCols >= EjmlParameters.MULT_COLUMN_SWITCH ) {
+            MatrixMatrixMult.mult_reorder(a,b,c);
+        } else {
+            MatrixMatrixMult.mult_small(a,b,c);
+        }
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = α * a * b <br>
+     * <br>
+     * c<sub>ij</sub> = α ∑<sub>k=1:n</sub> { * a<sub>ik</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param alpha Scaling factor.
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void mult( double alpha , RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        // TODO add a matrix vectory multiply here
+        if( b.numCols >= EjmlParameters.MULT_COLUMN_SWITCH ) {
+            MatrixMatrixMult.mult_reorder(alpha,a,b,c);
+        } else {
+            MatrixMatrixMult.mult_small(alpha,a,b,c);
+        }
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a<sup>T</sup> * b <br>
+     * <br>
+     * c<sub>ij</sub> = ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multTransA( RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( b.numCols == 1 ) {
+            // todo check a.numCols == 1 and do inner product?
+            // there are significantly faster algorithms when dealing with vectors
+            if( a.numCols >= EjmlParameters.MULT_COLUMN_SWITCH ) {
+                MatrixVectorMult.multTransA_reorder(a,b,c);
+            } else {
+                MatrixVectorMult.multTransA_small(a,b,c);
+            }
+        } else if( a.numCols >= EjmlParameters.MULT_COLUMN_SWITCH ||
+                b.numCols >= EjmlParameters.MULT_COLUMN_SWITCH  ) {
+            MatrixMatrixMult.multTransA_reorder(a,b,c);
+        } else {
+            MatrixMatrixMult.multTransA_small(a,b,c);
+        }
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = α * a<sup>T</sup> * b <br>
+     * <br>
+     * c<sub>ij</sub> = α ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param alpha Scaling factor.
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multTransA( double alpha , RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        // TODO add a matrix vectory multiply here
+        if( a.numCols >= EjmlParameters.MULT_COLUMN_SWITCH ||
+                b.numCols >= EjmlParameters.MULT_COLUMN_SWITCH ) {
+            MatrixMatrixMult.multTransA_reorder(alpha,a,b,c);
+        } else {
+            MatrixMatrixMult.multTransA_small(alpha,a,b,c);
+        }
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c = a * b<sup>T</sup> <br>
+     * c<sub>ij</sub> = ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>jk</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multTransB( RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( b.numRows == 1 ) {
+            MatrixVectorMult.mult(a,b,c);
+        } else {
+            MatrixMatrixMult.multTransB(a,b,c);
+        }
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c =  α * a * b<sup>T</sup> <br>
+     * c<sub>ij</sub> = α ∑<sub>k=1:n</sub> {  a<sub>ik</sub> * b<sub>jk</sub>}
+     * </p>
+     *
+     * @param alpha Scaling factor.
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multTransB( double alpha , RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        // TODO add a matrix vectory multiply here
+        MatrixMatrixMult.multTransB(alpha,a,b,c);
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c = a<sup>T</sup> * b<sup>T</sup><br>
+     * c<sub>ij</sub> = ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>jk</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multTransAB( RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( b.numRows == 1) {
+            // there are significantly faster algorithms when dealing with vectors
+            if( a.numCols >= EjmlParameters.MULT_COLUMN_SWITCH ) {
+                MatrixVectorMult.multTransA_reorder(a,b,c);
+            } else {
+                MatrixVectorMult.multTransA_small(a,b,c);
+            }
+        } else if( a.numCols >= EjmlParameters.MULT_TRANAB_COLUMN_SWITCH ) {
+            MatrixMatrixMult.multTransAB_aux(a,b,c,null);
+        } else {
+            MatrixMatrixMult.multTransAB(a,b,c);
+        }
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c = α * a<sup>T</sup> * b<sup>T</sup><br>
+     * c<sub>ij</sub> = α ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>jk</sub>}
+     * </p>
+     *
+     * @param alpha Scaling factor.
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multTransAB( double alpha , RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        // TODO add a matrix vectory multiply here
+        if( a.numCols >= EjmlParameters.MULT_TRANAB_COLUMN_SWITCH ) {
+            MatrixMatrixMult.multTransAB_aux(alpha,a,b,c,null);
+        } else {
+            MatrixMatrixMult.multTransAB(alpha,a,b,c);
+        }
+    }
+
+    /**
+     * <p></p>
+     * Computes the dot product or inner product between two vectors.  If the two vectors are columns vectors
+     * then it is defined as:<br>
+     * {@code dot(a,b) = a<sup>T</sup> * b}<br>
+     * If the vectors are column or row or both is ignored by this function.
+     * </p>
+     * @param a Vector
+     * @param b Vector
+     * @return Dot product of the two vectors
+     */
+    public static double dot( D1Matrix64F a , D1Matrix64F b ) {
+        if( !MatrixFeatures.isVector(a) || !MatrixFeatures.isVector(b))
+            throw new RuntimeException("Both inputs must be vectors");
+
+        return VectorVectorMult.innerProd(a,b);
+    }
+
+    /**
+     * <p>Computes the matrix multiplication inner product:<br>
+     * <br>
+     * c = a<sup>T</sup> * a <br>
+     * <br>
+     * c<sub>ij</sub> = ∑<sub>k=1:n</sub> { a<sub>ki</sub> * a<sub>kj</sub>}
+     * </p>
+     * 
+     * <p>
+     * Is faster than using a generic matrix multiplication by taking advantage of symmetry.  For
+     * vectors there is an even faster option, see {@link org.ejml.alg.dense.mult.VectorVectorMult#innerProd(org.ejml.data.D1Matrix64F, org.ejml.data.D1Matrix64F)}
+     * </p>
+     *
+     * @param a The matrix being multiplied. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multInner( RowD1Matrix64F a , RowD1Matrix64F c )
+    {
+        if( a.numCols != c.numCols || a.numCols != c.numRows )
+            throw new IllegalArgumentException("Rows and columns of 'c' must be the same as the columns in 'a'");
+        
+        if( a.numCols >= EjmlParameters.MULT_INNER_SWITCH ) {
+            MatrixMultProduct.inner_small(a, c);
+        } else {
+            MatrixMultProduct.inner_reorder(a, c);
+        }
+    }
+
+    /**
+     * <p>Computes the matrix multiplication outer product:<br>
+     * <br>
+     * c = a * a<sup>T</sup> <br>
+     * <br>
+     * c<sub>ij</sub> = ∑<sub>k=1:m</sub> { a<sub>ik</sub> * a<sub>jk</sub>}
+     * </p>
+     *
+     * <p>
+     * Is faster than using a generic matrix multiplication by taking advantage of symmetry.
+     * </p>
+     *
+     * @param a The matrix being multiplied. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multOuter( RowD1Matrix64F a , RowD1Matrix64F c )
+    {
+        if( a.numRows != c.numCols || a.numRows != c.numRows )
+            throw new IllegalArgumentException("Rows and columns of 'c' must be the same as the rows in 'a'");
+
+        MatrixMultProduct.outer(a, c);
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c = c + a * b<br>
+     * c<sub>ij</sub> = c<sub>ij</sub> + ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAdd( RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( b.numCols == 1 ) {
+            MatrixVectorMult.multAdd(a,b,c);
+        } else {
+            if( b.numCols >= EjmlParameters.MULT_COLUMN_SWITCH ) {
+                MatrixMatrixMult.multAdd_reorder(a,b,c);
+            } else {
+                MatrixMatrixMult.multAdd_small(a,b,c);
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c = c + α * a * b<br>
+     * c<sub>ij</sub> = c<sub>ij</sub> +  α * ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param alpha scaling factor.
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAdd( double alpha , RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        // TODO add a matrix vectory multiply here
+        if( b.numCols >= EjmlParameters.MULT_COLUMN_SWITCH ) {
+            MatrixMatrixMult.multAdd_reorder(alpha,a,b,c);
+        } else {
+            MatrixMatrixMult.multAdd_small(alpha,a,b,c);
+        }
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c = c + a<sup>T</sup> * b<br>
+     * c<sub>ij</sub> = c<sub>ij</sub> + ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAddTransA( RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( b.numCols == 1 ) {
+            if( a.numCols >= EjmlParameters.MULT_COLUMN_SWITCH ) {
+                MatrixVectorMult.multAddTransA_reorder(a,b,c);
+            } else {
+                MatrixVectorMult.multAddTransA_small(a,b,c);
+            }
+        } else {
+            if( a.numCols >= EjmlParameters.MULT_COLUMN_SWITCH ||
+                    b.numCols >= EjmlParameters.MULT_COLUMN_SWITCH  ) {
+                MatrixMatrixMult.multAddTransA_reorder(a,b,c);
+            } else {
+                MatrixMatrixMult.multAddTransA_small(a,b,c);
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c = c + α * a<sup>T</sup> * b<br>
+     * c<sub>ij</sub> =c<sub>ij</sub> +  α * ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param alpha scaling factor
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAddTransA( double alpha , RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        // TODO add a matrix vectory multiply here
+        if( a.numCols >= EjmlParameters.MULT_COLUMN_SWITCH ||
+                b.numCols >= EjmlParameters.MULT_COLUMN_SWITCH ) {
+            MatrixMatrixMult.multAddTransA_reorder(alpha,a,b,c);
+        } else {
+            MatrixMatrixMult.multAddTransA_small(alpha,a,b,c);
+        }
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c = c + a * b<sup>T</sup> <br>
+     * c<sub>ij</sub> = c<sub>ij</sub> + ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>jk</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAddTransB( RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        MatrixMatrixMult.multAddTransB(a,b,c);
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c = c + α * a * b<sup>T</sup><br>
+     * c<sub>ij</sub> = c<sub>ij</sub> + α * ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>jk</sub>}
+     * </p>
+     *
+     * @param alpha Scaling factor.
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAddTransB( double alpha , RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        // TODO add a matrix vectory multiply here
+        MatrixMatrixMult.multAddTransB(alpha,a,b,c);
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c = c + a<sup>T</sup> * b<sup>T</sup><br>
+     * c<sub>ij</sub> = c<sub>ij</sub> + ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>jk</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not Modified.
+     * @param b The right matrix in the multiplication operation. Not Modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAddTransAB( RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        if( b.numRows == 1 ) {
+            // there are significantly faster algorithms when dealing with vectors
+            if( a.numCols >= EjmlParameters.MULT_COLUMN_SWITCH ) {
+                MatrixVectorMult.multAddTransA_reorder(a,b,c);
+            } else {
+                MatrixVectorMult.multAddTransA_small(a,b,c);
+            }
+        } else if( a.numCols >= EjmlParameters.MULT_TRANAB_COLUMN_SWITCH ) {
+            MatrixMatrixMult.multAddTransAB_aux(a,b,c,null);
+        } else {
+            MatrixMatrixMult.multAddTransAB(a,b,c);
+        }
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c = c + α * a<sup>T</sup> * b<sup>T</sup><br>
+     * c<sub>ij</sub> = c<sub>ij</sub> + α * ∑<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>jk</sub>}
+     * </p>
+     *
+     * @param alpha Scaling factor.
+     * @param a The left matrix in the multiplication operation. Not Modified.
+     * @param b The right matrix in the multiplication operation. Not Modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAddTransAB( double alpha , RowD1Matrix64F a , RowD1Matrix64F b , RowD1Matrix64F c )
+    {
+        // TODO add a matrix vectory multiply here
+        if( a.numCols >= EjmlParameters.MULT_TRANAB_COLUMN_SWITCH ) {
+            MatrixMatrixMult.multAddTransAB_aux(alpha,a,b,c,null);
+        } else {
+            MatrixMatrixMult.multAddTransAB(alpha,a,b,c);
+        }
+    }
+
+    /**
+     * <p>
+     * Solves for x in the following equation:<br>
+     * <br>
+     * A*x = b
+     * </p>
+     *
+     * <p>
+     * If the system could not be solved then false is returned.  If it returns true
+     * that just means the algorithm finished operating, but the results could still be bad
+     * because 'A' is singular or nearly singular.
+     * </p>
+     *
+     * <p>
+     * If repeat calls to solve are being made then one should consider using {@link LinearSolverFactory}
+     * instead.
+     * </p>
+     *
+     * <p>
+     * It is ok for 'b' and 'x' to be the same matrix.
+     * </p>
+     *
+     * @param a A matrix that is m by n. Not modified.
+     * @param b A matrix that is n by k. Not modified.
+     * @param x A matrix that is m by k. Modified.
+     *
+     * @return true if it could invert the matrix false if it could not.
+     */
+    public static boolean solve( DenseMatrix64F a , DenseMatrix64F b , DenseMatrix64F x )
+    {
+        LinearSolver<DenseMatrix64F> solver = LinearSolverFactory.general(a.numRows,a.numCols);
+
+        // make sure the inputs 'a' and 'b' are not modified
+        solver = new LinearSolverSafe<DenseMatrix64F>(solver);
+
+        if( !solver.setA(a) )
+            return false;
+
+        solver.solve(b,x);
+        return true;
+    }
+
+    /**
+     * <p>Performs an "in-place" transpose.</p>
+     *
+     * <p>
+     * For square matrices the transpose is truly in-place and does not require
+     * additional memory.  For non-square matrices, internally a temporary matrix is declared and
+     * {@link #transpose(org.ejml.data.DenseMatrix64F, org.ejml.data.DenseMatrix64F)} is invoked.
+     * </p>
+     *
+     * @param mat The matrix that is to be transposed. Modified.
+     */
+    public static void transpose( DenseMatrix64F mat ) {
+        if( mat.numCols == mat.numRows ){
+            TransposeAlgs.square(mat);
+        } else {
+            DenseMatrix64F b = new DenseMatrix64F(mat.numCols,mat.numRows);
+            transpose(mat,b);
+            mat.set(b);
+        }
+    }
+
+    /**
+     * <p>
+     * Transposes matrix 'a' and stores the results in 'b':<br>
+     * <br>
+     * b<sub>ij</sub> = a<sub>ji</sub><br>
+     * where 'b' is the transpose of 'a'.
+     * </p>
+     *
+     * @param A The original matrix.  Not modified.
+     * @param A_tran Where the transpose is stored. If null a new matrix is created. Modified.
+     * @return The transposed matrix.
+     */
+    public static DenseMatrix64F transpose( DenseMatrix64F A, DenseMatrix64F A_tran)
+    {
+        if( A_tran == null ) {
+            A_tran = new DenseMatrix64F(A.numCols,A.numRows);
+        } else {
+            if( A.numRows != A_tran.numCols || A.numCols != A_tran.numRows ) {
+                throw new IllegalArgumentException("Incompatible matrix dimensions");
+            }
+        }
+
+        if( A.numRows > EjmlParameters.TRANSPOSE_SWITCH &&
+                A.numCols > EjmlParameters.TRANSPOSE_SWITCH )
+            TransposeAlgs.block(A,A_tran,EjmlParameters.BLOCK_WIDTH);
+        else
+            TransposeAlgs.standard(A,A_tran);
+
+        return A_tran;
+    }
+
+
+    /**
+     * <p>
+     * This computes the trace of the matrix:<br>
+     * <br>
+     * trace = ∑<sub>i=1:n</sub> { a<sub>ii</sub> }<br>
+     * where n = min(numRows,numCols)
+     * </p>
+     *
+     * @param a A square matrix.  Not modified.
+     */
+    public static double trace( RowD1Matrix64F a ) {
+        int N = Math.min(a.numRows,a.numCols);
+        double sum = 0;
+        int index = 0;
+        for( int i = 0; i < N; i++ ) {
+            sum += a.get(index);
+            index += 1 + a.numCols;
+        }
+
+        return sum;
+    }
+
+    /**
+     * Returns the determinant of the matrix.  If the inverse of the matrix is also
+     * needed, then using {@link org.ejml.alg.dense.decomposition.lu.LUDecompositionAlt_D64} directly (or any
+     * similar algorithm) can be more efficient.
+     *
+     * @param mat The matrix whose determinant is to be computed.  Not modified.
+     * @return The determinant.
+     */
+    public static double det( DenseMatrix64F mat )
+    {
+
+        int numCol = mat.getNumCols();
+        int numRow = mat.getNumRows();
+
+        if( numCol != numRow ) {
+            throw new IllegalArgumentException("Must be a square matrix.");
+        } else if( numCol <= UnrolledDeterminantFromMinor.MAX ) {
+            // slight performance boost overall by doing it this way
+            // when it was the case statement the VM did some strange optimization
+            // and made case 2 about 1/2 the speed
+            if( numCol >= 2 ) {
+                return UnrolledDeterminantFromMinor.det(mat);
+            } else {
+                return mat.get(0);
+            }
+        } else {
+            LUDecompositionAlt_D64 alg = new LUDecompositionAlt_D64();
+
+            if( alg.inputModified() ) {
+                mat = mat.copy();
+            }
+
+            if( !alg.decompose(mat) )
+                return 0.0;
+            return alg.computeDeterminant().real;
+        }
+    }
+
+    /**
+     * <p>
+     * Performs a matrix inversion operation on the specified matrix and stores the results
+     * in the same matrix.<br>
+     * <br>
+     * a = a<sup>-1<sup>
+     * </p>
+     *
+     * <p>
+     * If the algorithm could not invert the matrix then false is returned.  If it returns true
+     * that just means the algorithm finished.  The results could still be bad
+     * because the matrix is singular or nearly singular.
+     * </p>
+     *
+     * @param mat The matrix that is to be inverted.  Results are stored here.  Modified.
+     * @return true if it could invert the matrix false if it could not.
+     */
+    public static boolean invert( DenseMatrix64F mat) {
+        if( mat.numCols <= UnrolledInverseFromMinor.MAX ) {
+            if( mat.numCols != mat.numRows ) {
+                throw new IllegalArgumentException("Must be a square matrix.");
+            }
+
+            if( mat.numCols >= 2 ) {
+                UnrolledInverseFromMinor.inv(mat,mat);
+            } else {
+                mat.set(0, 1.0/mat.get(0));
+            }
+        } else {
+            LUDecompositionAlt_D64 alg = new LUDecompositionAlt_D64();
+            LinearSolverLu_D64 solver = new LinearSolverLu_D64(alg);
+            if( solver.setA(mat) ) {
+                solver.invert(mat);
+            } else {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * <p>
+     * Performs a matrix inversion operation that does not modify the original
+     * and stores the results in another matrix.  The two matrices must have the
+     * same dimension.<br>
+     * <br>
+     * b = a<sup>-1<sup>
+     * </p>
+     *
+     * <p>
+     * If the algorithm could not invert the matrix then false is returned.  If it returns true
+     * that just means the algorithm finished.  The results could still be bad
+     * because the matrix is singular or nearly singular.
+     * </p>
+     *
+     * <p>
+     * For medium to large matrices there might be a slight performance boost to using
+     * {@link LinearSolverFactory} instead.
+     * </p>
+     *
+     * @param mat The matrix that is to be inverted. Not modified.
+     * @param result Where the inverse matrix is stored.  Modified.
+     * @return true if it could invert the matrix false if it could not.
+     */
+    public static boolean invert( DenseMatrix64F mat, DenseMatrix64F result ) {
+        if( mat.numCols <= UnrolledInverseFromMinor.MAX ) {
+            if( mat.numCols != mat.numRows ) {
+                throw new IllegalArgumentException("Must be a square matrix.");
+            }
+            if( result.numCols >= 2 ) {
+                UnrolledInverseFromMinor.inv(mat,result);
+            } else {
+                result.set(0,  1.0/mat.get(0));
+            }
+        } else {
+            LUDecompositionAlt_D64 alg = new LUDecompositionAlt_D64();
+            LinearSolverLu_D64 solver = new LinearSolverLu_D64(alg);
+
+            if( solver.modifiesA() )
+                mat = mat.copy();
+
+            if( !solver.setA(mat))
+                return false;
+            solver.invert(result);
+        }
+        return true;
+    }
+
+    /**
+     * <p>
+     * Computes the Moore-Penrose pseudo-inverse:<br>
+     * <br>
+     * pinv(A) = (A<sup>T</sup>A)<sup>-1</sup> A<sup>T</sup><br>
+     * or<br>
+     * pinv(A) = A<sup>T</sup>(AA<sup>T</sup>)<sup>-1</sup><br>
+     * </p>
+     * <p>
+     * Internally it uses {@link org.ejml.alg.dense.linsol.svd.SolvePseudoInverseSvd} to compute the inverse.  For performance reasons, this should only
+     * be used when a matrix is singular or nearly singular.
+     * </p>
+     * @param A  A m by n Matrix.  Not modified.
+     * @param invA Where the computed pseudo inverse is stored. n by m.  Modified.
+     * @return
+     */
+    public static void pinv( DenseMatrix64F A , DenseMatrix64F invA )
+    {
+        LinearSolver<DenseMatrix64F> solver = LinearSolverFactory.pseudoInverse(true);
+        if( solver.modifiesA())
+            A = A.copy();
+
+        if( !solver.setA(A) )
+            throw new IllegalArgumentException("Invert failed, maybe a bug?");
+
+        solver.invert(invA);
+    }
+
+    /**
+     * Converts the columns in a matrix into a set of vectors.
+     *
+     * @param A Matrix.  Not modified.
+     * @param v
+     * @return An array of vectors.
+     */
+    public static DenseMatrix64F[] columnsToVector(DenseMatrix64F A, DenseMatrix64F[] v)
+    {
+        DenseMatrix64F []ret;
+        if( v == null || v.length < A.numCols ) {
+            ret = new DenseMatrix64F[ A.numCols ];
+        } else {
+            ret = v;
+        }
+
+        for( int i = 0; i < ret.length; i++ ) {
+            if( ret[i] == null ) {
+                ret[i] = new DenseMatrix64F(A.numRows,1);
+            } else {
+                ret[i].reshape(A.numRows,1, false);
+            }
+
+            DenseMatrix64F u = ret[i];
+
+            for( int j = 0; j < A.numRows; j++ ) {
+                u.set(j,0, A.get(j,i));
+            }
+        }
+
+        return ret;
+    }
+
+    /**
+     * Converts the rows in a matrix into a set of vectors.
+     *
+     * @param A Matrix.  Not modified.
+     * @param v
+     * @return An array of vectors.
+     */
+    public static DenseMatrix64F[] rowsToVector(DenseMatrix64F A, DenseMatrix64F[] v)
+    {
+        DenseMatrix64F []ret;
+        if( v == null || v.length < A.numRows ) {
+            ret = new DenseMatrix64F[ A.numRows ];
+        } else {
+            ret = v;
+        }
+
+
+        for( int i = 0; i < ret.length; i++ ) {
+            if( ret[i] == null ) {
+                ret[i] = new DenseMatrix64F(A.numCols,1);
+            } else {
+                ret[i].reshape(A.numCols,1, false);
+            }
+
+            DenseMatrix64F u = ret[i];
+
+            for( int j = 0; j < A.numCols; j++ ) {
+                u.set(j,0, A.get(i,j));
+            }
+        }
+
+        return ret;
+    }
+
+    /**
+     * Sets all the diagonal elements equal to one and everything else equal to zero.
+     * If this is a square matrix then it will be an identity matrix.
+     *
+     * @see #identity(int)
+     *
+     * @param mat A square matrix.
+     */
+    public static void setIdentity( RowD1Matrix64F mat )
+    {
+        int width = mat.numRows < mat.numCols ? mat.numRows : mat.numCols;
+
+        Arrays.fill(mat.data,0,mat.getNumElements(),0);
+
+        int index = 0;
+        for( int i = 0; i < width; i++ , index += mat.numCols + 1) {
+            mat.data[index] = 1;
+        }
+    }
+
+    /**
+     * <p>
+     * Creates an identity matrix of the specified size.<br>
+     * <br>
+     * a<sub>ij</sub> = 0   if i ≠ j<br>
+     * a<sub>ij</sub> = 1   if i = j<br>
+     * </p>
+     *
+     * @param width The width and height of the identity matrix.
+     * @return A new instance of an identity matrix.
+     */
+    public static DenseMatrix64F identity( int width )
+    {
+        DenseMatrix64F ret = new DenseMatrix64F(width,width);
+
+        for( int i = 0; i < width; i++ ) {
+            ret.set(i,i,1.0);
+        }
+
+        return ret;
+    }
+
+    /**
+     * Creates a rectangular matrix which is zero except along the diagonals.
+     *
+     * @param numRows Number of rows in the matrix.
+     * @param numCols NUmber of columns in the matrix.
+     * @return A matrix with diagonal elements equal to one.
+     */
+    public static DenseMatrix64F identity( int numRows , int numCols )
+    {
+        DenseMatrix64F ret = new DenseMatrix64F(numRows,numCols);
+
+        int small = numRows < numCols ? numRows : numCols;
+
+        for( int i = 0; i < small; i++ ) {
+            ret.set(i,i,1.0);
+        }
+
+        return ret;
+    }
+
+    /**
+     * <p>
+     * Creates a new square matrix whose diagonal elements are specified by diagEl and all
+     * the other elements are zero.<br>
+     * <br>
+     * a<sub>ij</sub> = 0         if i ≤ j<br>
+     * a<sub>ij</sub> = diag[i]   if i = j<br>
+     * </p>
+     *
+     * @see #diagR
+     *
+     * @param diagEl Contains the values of the diagonal elements of the resulting matrix.
+     * @return A new matrix.
+     */
+    public static DenseMatrix64F diag( double ...diagEl )
+    {
+        return diag(null,diagEl.length,diagEl);
+    }
+
+    /**
+     * @see #diag(double...)
+     */
+    public static DenseMatrix64F diag( DenseMatrix64F ret , int width , double ...diagEl )
+    {
+        if( ret == null ) {
+            ret = new DenseMatrix64F(width,width);
+        } else {
+            if( ret.numRows != width || ret.numCols != width )
+                throw new IllegalArgumentException("Unexpected matrix size");
+
+            CommonOps.fill(ret, 0);
+        }
+
+        for( int i = 0; i < width; i++ ) {
+            ret.unsafe_set(i, i, diagEl[i]);
+        }
+
+        return ret;
+    }
+
+    /**
+     * <p>
+     * Creates a new rectangular matrix whose diagonal elements are specified by diagEl and all
+     * the other elements are zero.<br>
+     * <br>
+     * a<sub>ij</sub> = 0         if i ≤ j<br>
+     * a<sub>ij</sub> = diag[i]   if i = j<br>
+     * </p>
+     *
+     * @see #diag
+     *
+     * @param numRows Number of rows in the matrix.
+     * @param numCols Number of columns in the matrix.
+     * @param diagEl Contains the values of the diagonal elements of the resulting matrix.
+     * @return A new matrix.
+     */
+    public static DenseMatrix64F diagR( int numRows , int numCols , double ...diagEl )
+    {
+        DenseMatrix64F ret = new DenseMatrix64F(numRows,numCols);
+
+        int o = Math.min(numRows,numCols);
+
+        for( int i = 0; i < o; i++ ) {
+            ret.set(i,i,diagEl[i]);
+        }
+
+        return ret;
+    }
+
+    /**
+     * <p>
+     * The Kronecker product of two matrices is defined as:<br>
+     * C<sub>ij</sub> = a<sub>ij</sub>B<br>
+     * where C<sub>ij</sub> is a sub matrix inside of C ∈ ℜ <sup>m*k × n*l</sup>,
+     * A ∈ ℜ <sup>m × n</sup>, and B ∈ ℜ <sup>k × l</sup>.
+     * </p>
+     *
+     * @param A The left matrix in the operation. Not modified.
+     * @param B The right matrix in the operation. Not modified.
+     * @param C Where the results of the operation are stored. Modified.
+     * @return The results of the operation.
+     */
+    public static void kron( DenseMatrix64F A , DenseMatrix64F B , DenseMatrix64F C )
+    {
+        int numColsC = A.numCols*B.numCols;
+        int numRowsC = A.numRows*B.numRows;
+
+        if( C.numCols != numColsC || C.numRows != numRowsC) {
+            throw new IllegalArgumentException("C does not have the expected dimensions");
+        }
+
+        // TODO see comment below
+        // this will work well for small matrices
+        // but an alternative version should be made for large matrices
+        for( int i = 0; i < A.numRows; i++ ) {
+            for( int j = 0; j < A.numCols; j++ ) {
+                double a = A.get(i,j);
+
+                for( int rowB = 0; rowB < B.numRows; rowB++ ) {
+                    for( int colB = 0; colB < B.numCols; colB++ ) {
+                        double val = a*B.get(rowB,colB);
+                        C.set(i*B.numRows+rowB,j*B.numCols+colB,val);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Extracts a submatrix from 'src' and inserts it in a submatrix in 'dst'.
+     * </p>
+     * <p>
+     * s<sub>i-y0 , j-x0</sub> = o<sub>ij</sub> for all y0 ≤ i < y1 and x0 ≤ j < x1 <br>
+     * <br>
+     * where 's<sub>ij</sub>' is an element in the submatrix and 'o<sub>ij</sub>' is an element in the
+     * original matrix.
+     * </p>
+     *
+     * @param src The original matrix which is to be copied.  Not modified.
+     * @param srcX0 Start column.
+     * @param srcX1 Stop column+1.
+     * @param srcY0 Start row.
+     * @param srcY1 Stop row+1.
+     * @param dst Where the submatrix are stored.  Modified.
+     * @param dstY0 Start row in dst.
+     * @param dstX0 start column in dst.
+     */
+    public static void extract( RealMatrix64F src,
+                                int srcY0, int srcY1,
+                                int srcX0, int srcX1,
+                                RealMatrix64F dst ,
+                                int dstY0, int dstX0 )
+    {
+        if( srcY1 < srcY0 || srcY0 < 0 || srcY1 > src.getNumRows() )
+            throw new IllegalArgumentException("srcY1 < srcY0 || srcY0 < 0 || srcY1 > src.numRows");
+        if( srcX1 < srcX0 || srcX0 < 0 || srcX1 > src.getNumCols() )
+            throw new IllegalArgumentException("srcX1 < srcX0 || srcX0 < 0 || srcX1 > src.numCols");
+
+        int w = srcX1-srcX0;
+        int h = srcY1-srcY0;
+
+        if( dstY0+h > dst.getNumRows() )
+            throw new IllegalArgumentException("dst is too small in rows");
+        if( dstX0+w > dst.getNumCols() )
+            throw new IllegalArgumentException("dst is too small in columns");
+
+        // interestingly, the performance is only different for small matrices but identical for larger ones
+        if( src instanceof DenseMatrix64F && dst instanceof DenseMatrix64F ) {
+            ImplCommonOps_DenseMatrix64F.extract((DenseMatrix64F)src,srcY0,srcX0,(DenseMatrix64F)dst,dstY0,dstX0, h, w);
+        } else {
+            ImplCommonOps_Matrix64F.extract(src,srcY0,srcX0,dst,dstY0,dstX0, h, w);
+        }
+    }
+
+    /**
+     * <p>
+     * Creates a new matrix which is the specified submatrix of 'src'
+     * </p>
+     * <p>
+     * s<sub>i-y0 , j-x0</sub> = o<sub>ij</sub> for all y0 ≤ i < y1 and x0 ≤ j < x1 <br>
+     * <br>
+     * where 's<sub>ij</sub>' is an element in the submatrix and 'o<sub>ij</sub>' is an element in the
+     * original matrix.
+     * </p>
+     *
+     * @param src The original matrix which is to be copied.  Not modified.
+     * @param srcX0 Start column.
+     * @param srcX1 Stop column+1.
+     * @param srcY0 Start row.
+     * @param srcY1 Stop row+1.
+     * @return Extracted submatrix.
+     */
+    public static DenseMatrix64F extract( DenseMatrix64F src,
+                                          int srcY0, int srcY1,
+                                          int srcX0, int srcX1 )
+    {
+        if( srcY1 <= srcY0 || srcY0 < 0 || srcY1 > src.numRows )
+            throw new IllegalArgumentException("srcY1 <= srcY0 || srcY0 < 0 || srcY1 > src.numRows");
+        if( srcX1 <= srcX0 || srcX0 < 0 || srcX1 > src.numCols )
+            throw new IllegalArgumentException("srcX1 <= srcX0 || srcX0 < 0 || srcX1 > src.numCols");
+
+        int w = srcX1-srcX0;
+        int h = srcY1-srcY0;
+
+        DenseMatrix64F dst = new DenseMatrix64F(h,w);
+
+        ImplCommonOps_DenseMatrix64F.extract(src,srcY0,srcX0,dst,0,0, h, w);
+
+        return dst;
+    }
+
+    /**
+     * <p>
+     * Extracts the diagonal elements 'src' write it to the 'dst' vector.  'dst'
+     * can either be a row or column vector.
+     * <p>
+     *
+     * @param src Matrix whose diagonal elements are being extracted. Not modified.
+     * @param dst A vector the results will be written into. Modified.
+     */
+    public static void extractDiag( DenseMatrix64F src, DenseMatrix64F dst )
+    {
+        int N = Math.min(src.numRows, src.numCols);
+
+        if( !MatrixFeatures.isVector(dst) ) {
+            throw new IllegalArgumentException("Expected a vector for dst.");
+        } else if( dst.getNumElements() != N ) {
+            throw new IllegalArgumentException("Expected "+N+" elements in dst.");
+        }
+
+        for( int i = 0; i < N; i++ ) {
+            dst.set( i , src.unsafe_get(i,i) );
+        }
+    }
+
+    /**
+     * Extracts the row from a matrix.
+     * @param a Input matrix
+     * @param row Which row is to be extracted
+     * @param out output. Storage for the extracted row. If null then a new vector will be returned.
+     * @return The extracted row.
+     */
+    public static DenseMatrix64F extractRow( DenseMatrix64F a , int row , DenseMatrix64F out ) {
+        if( out == null)
+            out = new DenseMatrix64F(1,a.numCols);
+        else if( !MatrixFeatures.isVector(out) || out.getNumElements() != a.numCols )
+            throw new IllegalArgumentException("Output must be a vector of length "+a.numCols);
+
+        System.arraycopy(a.data,a.getIndex(row,0),out.data,0,a.numCols);
+
+        return out;
+    }
+
+    /**
+     * Extracts the column from a matrix.
+     * @param a Input matrix
+     * @param column Which column is to be extracted
+     * @param out output. Storage for the extracted column. If null then a new vector will be returned.
+     * @return The extracted column.
+     */
+    public static DenseMatrix64F extractColumn( DenseMatrix64F a , int column , DenseMatrix64F out ) {
+        if( out == null)
+            out = new DenseMatrix64F(a.numRows,1);
+        else if( !MatrixFeatures.isVector(out) || out.getNumElements() != a.numRows )
+            throw new IllegalArgumentException("Output must be a vector of length "+a.numRows);
+
+        int index = column;
+        for (int i = 0; i < a.numRows; i++, index += a.numCols ) {
+            out.data[i] = a.data[index];
+        }
+        return out;
+    }
+
+    /**
+     * Inserts matrix 'src' into matrix 'dest' with the (0,0) of src at (row,col) in dest.
+     * This is equivalent to calling extract(src,0,src.numRows,0,src.numCols,dest,destY0,destX0).
+     *
+     * @param src matrix that is being copied into dest. Not modified.
+     * @param dest Where src is being copied into. Modified.
+     * @param destY0 Start row for the copy into dest.
+     * @param destX0 Start column for the copy into dest.
+     */
+    public static void insert( RealMatrix64F src, RealMatrix64F dest, int destY0, int destX0) {
+        extract(src,0,src.getNumRows(),0,src.getNumCols(),dest,destY0,destX0);
+    }
+
+    /**
+     * <p>
+     * Returns the value of the element in the matrix that has the largest value.<br>
+     * <br>
+     * Max{ a<sub>ij</sub> } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max element value of the matrix.
+     */
+    public static double elementMax( D1Matrix64F a ) {
+        final int size = a.getNumElements();
+
+        double max = a.get(0);
+        for( int i = 1; i < size; i++ ) {
+            double val = a.get(i);
+            if( val >= max ) {
+                max = val;
+            }
+        }
+
+        return max;
+    }
+
+    /**
+     * <p>
+     * Returns the absolute value of the element in the matrix that has the largest absolute value.<br>
+     * <br>
+     * Max{ |a<sub>ij</sub>| } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max abs element value of the matrix.
+     */
+    public static double elementMaxAbs( D1Matrix64F a ) {
+        final int size = a.getNumElements();
+
+        double max = 0;
+        for( int i = 0; i < size; i++ ) {
+            double val = Math.abs(a.get( i ));
+            if( val > max ) {
+                max = val;
+            }
+        }
+
+        return max;
+    }
+
+    /**
+     * <p>
+     * Returns the value of the element in the matrix that has the minimum value.<br>
+     * <br>
+     * Min{ a<sub>ij</sub> } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The value of element in the matrix with the minimum value.
+     */
+    public static double elementMin( D1Matrix64F a ) {
+        final int size = a.getNumElements();
+
+        double min = a.get(0);
+        for( int i = 1; i < size; i++ ) {
+            double val = a.get(i);
+            if( val < min ) {
+                min = val;
+            }
+        }
+
+        return min;
+    }
+
+    /**
+     * <p>
+     * Returns the absolute value of the element in the matrix that has the smallest absolute value.<br>
+     * <br>
+     * Min{ |a<sub>ij</sub>| } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max element value of the matrix.
+     */
+    public static double elementMinAbs( D1Matrix64F a ) {
+        final int size = a.getNumElements();
+
+        double min = Double.MAX_VALUE;
+        for( int i = 0; i < size; i++ ) {
+            double val = Math.abs(a.get(i));
+            if( val < min ) {
+                min = val;
+            }
+        }
+
+        return min;
+    }
+
+    /**
+     * <p>Performs the an element by element multiplication operation:<br>
+     * <br>
+     * a<sub>ij</sub> = a<sub>ij</sub> * b<sub>ij</sub> <br>
+     * </p>
+     * @param a The left matrix in the multiplication operation. Modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     */
+    public static void elementMult( D1Matrix64F a , D1Matrix64F b )
+    {
+        if( a.numCols != b.numCols || a.numRows != b.numRows ) {
+            throw new IllegalArgumentException("The 'a' and 'b' matrices do not have compatible dimensions");
+        }
+
+        int length = a.getNumElements();
+
+        for( int i = 0; i < length; i++ ) {
+            a.times(i , b.get(i));
+        }
+    }
+
+    /**
+     * <p>Performs the an element by element multiplication operation:<br>
+     * <br>
+     * c<sub>ij</sub> = a<sub>ij</sub> * b<sub>ij</sub> <br>
+     * </p>
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void elementMult( D1Matrix64F a , D1Matrix64F b , D1Matrix64F c )
+    {
+        if( a.numCols != b.numCols || a.numRows != b.numRows
+                || a.numRows != c.numRows || a.numCols != c.numCols ) {
+            throw new IllegalArgumentException("The 'a' and 'b' matrices do not have compatible dimensions");
+        }
+
+        int length = a.getNumElements();
+
+        for( int i = 0; i < length; i++ ) {
+            c.set( i , a.get(i) * b.get(i) );
+        }
+    }
+
+    /**
+     * <p>Performs the an element by element division operation:<br>
+     * <br>
+     * a<sub>ij</sub> = a<sub>ij</sub> / b<sub>ij</sub> <br>
+     * </p>
+     * @param a The left matrix in the division operation. Modified.
+     * @param b The right matrix in the division operation. Not modified.
+     */
+    public static void elementDiv( D1Matrix64F a , D1Matrix64F b )
+    {
+        if( a.numCols != b.numCols || a.numRows != b.numRows ) {
+            throw new IllegalArgumentException("The 'a' and 'b' matrices do not have compatible dimensions");
+        }
+
+        int length = a.getNumElements();
+
+        for( int i = 0; i < length; i++ ) {
+            a.div(i , b.get(i));
+        }
+    }
+
+    /**
+     * <p>Performs the an element by element division operation:<br>
+     * <br>
+     * c<sub>ij</sub> = a<sub>ij</sub> / b<sub>ij</sub> <br>
+     * </p>
+     * @param a The left matrix in the division operation. Not modified.
+     * @param b The right matrix in the division operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void elementDiv( D1Matrix64F a , D1Matrix64F b , D1Matrix64F c )
+    {
+        if( a.numCols != b.numCols || a.numRows != b.numRows
+                || a.numRows != c.numRows || a.numCols != c.numCols ) {
+            throw new IllegalArgumentException("The 'a' and 'b' matrices do not have compatible dimensions");
+        }
+
+        int length = a.getNumElements();
+
+        for( int i = 0; i < length; i++ ) {
+            c.set( i , a.get(i) / b.get(i) );
+        }
+    }
+
+    /**
+     * <p>
+     * Computes the sum of all the elements in the matrix:<br>
+     * <br>
+     * sum(i=1:m , j=1:n ; a<sub>ij</sub>)
+     * <p>
+     *
+     * @param mat An m by n matrix. Not modified.
+     * @return The sum of the elements.
+     */
+    public static double elementSum( D1Matrix64F mat ) {
+        double total = 0;
+
+        int size = mat.getNumElements();
+
+        for( int i = 0; i < size; i++ ) {
+            total += mat.get(i);
+        }
+
+        return total;
+    }
+
+    /**
+     * <p>
+     * Computes the sum of the absolute value all the elements in the matrix:<br>
+     * <br>
+     * sum(i=1:m , j=1:n ; |a<sub>ij</sub>|)
+     * <p>
+     *
+     * @param mat An m by n matrix. Not modified.
+     * @return The sum of the absolute value of each element.
+     */
+    public static double elementSumAbs( D1Matrix64F mat ) {
+        double total = 0;
+
+        int size = mat.getNumElements();
+
+        for( int i = 0; i < size; i++ ) {
+            total += Math.abs(mat.get(i));
+        }
+
+        return total;
+    }
+
+    /**
+     * <p>
+     * Element-wise power operation  <br>
+     * c<sub>ij</sub> = a<sub>ij</sub> ^ b<sub>ij</sub>
+     * <p>
+     *
+     * @param A left side
+     * @param B right side
+     * @param C output (modified)
+     */
+    public static void elementPower( D1Matrix64F A , D1Matrix64F B , D1Matrix64F C ) {
+
+        if( A.numRows != B.numRows || A.numRows != C.numRows ||
+                A.numCols != B.numCols || A.numCols != C.numCols ) {
+            throw new IllegalArgumentException("All matrices must be the same shape");
+        }
+
+        int size = A.getNumElements();
+        for( int i = 0; i < size; i++ ) {
+            C.data[i] = Math.pow(A.data[i],B.data[i]);
+        }
+    }
+
+    /**
+     * <p>
+     * Element-wise power operation  <br>
+     * c<sub>ij</sub> = a ^ b<sub>ij</sub>
+     * <p>
+     *
+     * @param a left scalar
+     * @param B right side
+     * @param C output (modified)
+     */
+    public static void elementPower( double a , D1Matrix64F B , D1Matrix64F C ) {
+
+        if( B.numRows != C.numRows || B.numCols != C.numCols ) {
+            throw new IllegalArgumentException("All matrices must be the same shape");
+        }
+
+        int size = B.getNumElements();
+        for( int i = 0; i < size; i++ ) {
+            C.data[i] = Math.pow(a,B.data[i]);
+        }
+    }
+
+    /**
+     * <p>
+     * Element-wise power operation  <br>
+     * c<sub>ij</sub> = a<sub>ij</sub> ^ b
+     * <p>
+     *
+     * @param A left side
+     * @param b right scalar
+     * @param C output (modified)
+     */
+    public static void elementPower( D1Matrix64F A , double b, D1Matrix64F C ) {
+
+        if( A.numRows != C.numRows || A.numCols != C.numCols ) {
+            throw new IllegalArgumentException("All matrices must be the same shape");
+        }
+
+        int size = A.getNumElements();
+        for( int i = 0; i < size; i++ ) {
+            C.data[i] = Math.pow(A.data[i],b);
+        }
+    }
+
+    /**
+     * <p>
+     * Element-wise log operation  <br>
+     * c<sub>ij</sub> = Math.log(a<sub>ij</sub>)
+     * <p>
+     *
+     * @param A input
+     * @param C output (modified)
+     */
+    public static void elementLog( D1Matrix64F A , D1Matrix64F C ) {
+
+        if( A.numCols != C.numCols || A.numRows != C.numRows ) {
+            throw new IllegalArgumentException("All matrices must be the same shape");
+        }
+
+        int size = A.getNumElements();
+        for( int i = 0; i < size; i++ ) {
+            C.data[i] = Math.log(A.data[i]);
+        }
+    }
+
+    /**
+     * <p>
+     * Element-wise exp operation  <br>
+     * c<sub>ij</sub> = Math.log(a<sub>ij</sub>)
+     * <p>
+     *
+     * @param A input
+     * @param C output (modified)
+     */
+    public static void elementExp( D1Matrix64F A , D1Matrix64F C ) {
+
+        if( A.numCols != C.numCols || A.numRows != C.numRows ) {
+            throw new IllegalArgumentException("All matrices must be the same shape");
+        }
+
+        int size = A.getNumElements();
+        for( int i = 0; i < size; i++ ) {
+            C.data[i] = Math.exp(A.data[i]);
+        }
+    }
+
+    /**
+     * <p>
+     * Computes the sum of each row in the input matrix and returns the results in a vector:<br>
+     * <br>
+     * b<sub>j</sub> = sum(i=1:n ; |a<sub>ji</sub>|)
+     * </p>
+     *
+     * @param input INput matrix whose rows are summed.
+     * @param output Optional storage for output.  Must be a vector. If null a row vector is returned. Modified.
+     * @return Vector containing the sum of each row in the input.
+     */
+    public static DenseMatrix64F sumRows( DenseMatrix64F input , DenseMatrix64F output ) {
+        if( output == null ) {
+            output = new DenseMatrix64F(input.numRows,1);
+        } else if( output.getNumElements() != input.numRows )
+            throw new IllegalArgumentException("Output does not have enough elements to store the results");
+
+        for( int row = 0; row < input.numRows; row++ ) {
+            double total = 0;
+
+            int end = (row+1)*input.numCols;
+            for( int index = row*input.numCols; index < end; index++ ) {
+                total += input.data[index];
+            }
+
+            output.set(row,total);
+        }
+        return output;
+    }
+
+    /**
+     * <p>
+     * Computes the sum of each column in the input matrix and returns the results in a vector:<br>
+     * <br>
+     * b<sub>j</sub> = sum(i=1:m ; |a<sub>ij</sub>|)
+     * </p>
+     *
+     * @param input INput matrix whose rows are summed.
+     * @param output Optional storage for output.  Must be a vector. If null a column vector is returned. Modified.
+     * @return Vector containing the sum of each row in the input.
+     */
+    public static DenseMatrix64F sumCols( DenseMatrix64F input , DenseMatrix64F output ) {
+        if( output == null ) {
+            output = new DenseMatrix64F(1,input.numCols);
+        } else if( output.getNumElements() != input.numCols )
+            throw new IllegalArgumentException("Output does not have enough elements to store the results");
+
+        for( int cols = 0; cols < input.numCols; cols++ ) {
+            double total = 0;
+
+            int index = cols;
+            int end = index + input.numCols*input.numRows;
+            for( ; index < end; index += input.numCols ) {
+                total += input.data[index];
+            }
+
+            output.set(cols,total);
+        }
+        return output;
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * a = a + b <br>
+     * a<sub>ij</sub> = a<sub>ij</sub> + b<sub>ij</sub> <br>
+     * </p>
+     *
+     * @param a A Matrix. Modified.
+     * @param b A Matrix. Not modified.
+     */
+    public static void addEquals( D1Matrix64F a , D1Matrix64F b )
+    {
+        if( a.numCols != b.numCols || a.numRows != b.numRows ) {
+            throw new IllegalArgumentException("The 'a' and 'b' matrices do not have compatible dimensions");
+        }
+
+        final int length = a.getNumElements();
+
+        for( int i = 0; i < length; i++ ) {
+            a.plus(i, b.get(i));
+        }
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * a = a +  β * b  <br>
+     * a<sub>ij</sub> = a<sub>ij</sub> + β * b<sub>ij</sub>
+     * </p>
+     *
+     * @param beta The number that matrix 'b' is multiplied by.
+     * @param a A Matrix. Modified.
+     * @param b A Matrix. Not modified.
+     */
+    public static void addEquals( D1Matrix64F a , double beta, D1Matrix64F b )
+    {
+        if( a.numCols != b.numCols || a.numRows != b.numRows ) {
+            throw new IllegalArgumentException("The 'a' and 'b' matrices do not have compatible dimensions");
+        }
+
+        final int length = a.getNumElements();
+
+        for( int i = 0; i < length; i++ ) {
+            a.plus(i, beta * b.get(i));
+        }
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a + b <br>
+     * c<sub>ij</sub> = a<sub>ij</sub> + b<sub>ij</sub> <br>
+     * </p>
+     *
+     * <p>
+     * Matrix C can be the same instance as Matrix A and/or B.
+     * </p>
+     *
+     * @param a A Matrix. Not modified.
+     * @param b A Matrix. Not modified.
+     * @param c A Matrix where the results are stored. Modified.
+     */
+    public static void add( final D1Matrix64F a , final D1Matrix64F b , final D1Matrix64F c )
+    {
+        if( a.numCols != b.numCols || a.numRows != b.numRows
+                || a.numCols != c.numCols || a.numRows != c.numRows ) {
+            throw new IllegalArgumentException("The matrices are not all the same dimension.");
+        }
+
+        final int length = a.getNumElements();
+
+        for( int i = 0; i < length; i++ ) {
+            c.set( i , a.get(i)+b.get(i) );
+        }
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a + β * b <br>
+     * c<sub>ij</sub> = a<sub>ij</sub> + β * b<sub>ij</sub> <br>
+     * </p>
+     *
+     * <p>
+     * Matrix C can be the same instance as Matrix A and/or B.
+     * </p>
+     *
+     * @param a A Matrix. Not modified.
+     * @param beta Scaling factor for matrix b.
+     * @param b A Matrix. Not modified.
+     * @param c A Matrix where the results are stored. Modified.
+     */
+    public static void add( D1Matrix64F a , double beta , D1Matrix64F b , D1Matrix64F c )
+    {
+        if( a.numCols != b.numCols || a.numRows != b.numRows
+                || a.numCols != c.numCols || a.numRows != c.numRows ) {
+            throw new IllegalArgumentException("The matrices are not all the same dimension.");
+        }
+
+        final int length = a.getNumElements();
+
+        for( int i = 0; i < length; i++ ) {
+            c.set( i , a.get(i)+beta*b.get(i) );
+        }
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = α * a + β * b <br>
+     * c<sub>ij</sub> = α * a<sub>ij</sub> + β * b<sub>ij</sub> <br>
+     * </p>
+     *
+     * <p>
+     * Matrix C can be the same instance as Matrix A and/or B.
+     * </p>
+     *
+     * @param alpha A scaling factor for matrix a.
+     * @param a A Matrix. Not modified.
+     * @param beta A scaling factor for matrix b.
+     * @param b A Matrix. Not modified.
+     * @param c A Matrix where the results are stored. Modified.
+     */
+    public static void add( double alpha , D1Matrix64F a , double beta , D1Matrix64F b , D1Matrix64F c )
+    {
+        if( a.numCols != b.numCols || a.numRows != b.numRows
+                || a.numCols != c.numCols || a.numRows != c.numRows ) {
+            throw new IllegalArgumentException("The matrices are not all the same dimension.");
+        }
+
+        final int length = a.getNumElements();
+
+        for( int i = 0; i < length; i++ ) {
+            c.set(i , alpha*a.get(i) + beta*b.get(i));
+        }
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = α * a + b <br>
+     * c<sub>ij</sub> = α * a<sub>ij</sub> + b<sub>ij</sub> <br>
+     * </p>
+     *
+     * <p>
+     * Matrix C can be the same instance as Matrix A and/or B.
+     * </p>
+     *
+     * @param alpha A scaling factor for matrix a.
+     * @param a A Matrix. Not modified.
+     * @param b A Matrix. Not modified.
+     * @param c A Matrix where the results are stored. Modified.
+     */
+    public static void add( double alpha , D1Matrix64F a , D1Matrix64F b , D1Matrix64F c )
+    {
+        if( a.numCols != b.numCols || a.numRows != b.numRows
+                || a.numCols != c.numCols || a.numRows != c.numRows ) {
+            throw new IllegalArgumentException("The matrices are not all the same dimension.");
+        }
+
+        final int length = a.getNumElements();
+
+        for( int i = 0; i < length; i++ ) {
+            c.set( i , alpha*a.get(i) + b.get(i));
+        }
+    }
+
+    /**
+     * <p>Performs an in-place scalar addition:<br>
+     * <br>
+     * a = a + val<br>
+     * a<sub>ij</sub> = a<sub>ij</sub> + val<br>
+     * </p>
+     *
+     * @param a A matrix.  Modified.
+     * @param val The value that's added to each element.
+     */
+    public static void add( D1Matrix64F a , double val ) {
+        final int length = a.getNumElements();
+
+        for( int i = 0; i < length; i++ ) {
+            a.plus( i , val);
+        }
+    }
+
+    /**
+     * <p>Performs scalar addition:<br>
+     * <br>
+     * c = a + val<br>
+     * c<sub>ij</sub> = a<sub>ij</sub> + val<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @param c A matrix. Modified.
+     * @param val The value that's added to each element.
+     */
+    public static void add( D1Matrix64F a , double val , D1Matrix64F c ) {
+        if( a.numRows != c.numRows || a.numCols != c.numCols ) {
+            throw new IllegalArgumentException("Dimensions of a and c do not match.");
+        }
+
+        final int length = a.getNumElements();
+
+        for( int i = 0; i < length; i++ ) {
+            c.data[i] = a.data[i] + val;
+        }
+    }
+
+    /**
+     * <p>Performs matrix scalar subtraction:<br>
+     * <br>
+     * c = a - val<br>
+     * c<sub>ij</sub> = a<sub>ij</sub> - val<br>
+     * </p>
+     *
+     * @param a (input) A matrix. Not modified.
+     * @param val (input) The value that's subtracted to each element.
+     * @param c (Output) A matrix. Modified.
+     */
+    public static void subtract( D1Matrix64F a , double val , D1Matrix64F c ) {
+        if( a.numRows != c.numRows || a.numCols != c.numCols ) {
+            throw new IllegalArgumentException("Dimensions of a and c do not match.");
+        }
+
+        final int length = a.getNumElements();
+
+        for( int i = 0; i < length; i++ ) {
+            c.data[i] = a.data[i] - val;
+        }
+    }
+
+    /**
+     * <p>Performs matrix scalar subtraction:<br>
+     * <br>
+     * c = val - a<br>
+     * c<sub>ij</sub> = val - a<sub>ij</sub><br>
+     * </p>
+     *
+     * @param val (input) The value that's subtracted to each element.
+     * @param a (input) A matrix. Not modified.
+     * @param c (Output) A matrix. Modified.
+     */
+    public static void subtract( double val , D1Matrix64F a , D1Matrix64F c ) {
+        if( a.numRows != c.numRows || a.numCols != c.numCols ) {
+            throw new IllegalArgumentException("Dimensions of a and c do not match.");
+        }
+
+        final int length = a.getNumElements();
+
+        for( int i = 0; i < length; i++ ) {
+            c.data[i] = val - a.data[i];
+        }
+    }
+
+    /**
+     * <p>Performs the following subtraction operation:<br>
+     * <br>
+     * a = a - b  <br>
+     * a<sub>ij</sub> = a<sub>ij</sub> - b<sub>ij</sub>
+     * </p>
+     *
+     * @param a A Matrix. Modified.
+     * @param b A Matrix. Not modified.
+     */
+    public static void subtractEquals(D1Matrix64F a, D1Matrix64F b)
+    {
+        if( a.numCols != b.numCols || a.numRows != b.numRows ) {
+            throw new IllegalArgumentException("The 'a' and 'b' matrices do not have compatible dimensions");
+        }
+
+        final int length = a.getNumElements();
+
+        for( int i = 0; i < length; i++ ) {
+            a.data[i] -= b.data[i];
+        }
+    }
+
+    /**
+     * <p>Performs the following subtraction operation:<br>
+     * <br>
+     * c = a - b  <br>
+     * c<sub>ij</sub> = a<sub>ij</sub> - b<sub>ij</sub>
+     * </p>
+     * <p>
+     * Matrix C can be the same instance as Matrix A and/or B.
+     * </p>
+     *
+     * @param a A Matrix. Not modified.
+     * @param b A Matrix. Not modified.
+     * @param c A Matrix. Modified.
+     */
+    public static void subtract(D1Matrix64F a, D1Matrix64F b, D1Matrix64F c)
+    {
+        if( a.numCols != b.numCols || a.numRows != b.numRows ) {
+            throw new IllegalArgumentException("The 'a' and 'b' matrices do not have compatible dimensions");
+        }
+
+        final int length = a.getNumElements();
+
+        for( int i = 0; i < length; i++ ) {
+            c.data[i] = a.data[i] - b.data[i];
+        }
+    }
+
+    /**
+     * <p>
+     * Performs an in-place element by element scalar multiplication.<br>
+     * <br>
+     * a<sub>ij</sub> = α*a<sub>ij</sub>
+     * </p>
+     *
+     * @param a The matrix that is to be scaled.  Modified.
+     * @param alpha the amount each element is multiplied by.
+     */
+    public static void scale( double alpha , D1Matrix64F a )
+    {
+        // on very small matrices (2 by 2) the call to getNumElements() can slow it down
+        // slightly compared to other libraries since it involves an extra multiplication.
+        final int size = a.getNumElements();
+
+        for( int i = 0; i < size; i++ ) {
+            a.data[i] *= alpha;
+        }
+    }
+
+    /**
+     * <p>
+     * Performs an element by element scalar multiplication.<br>
+     * <br>
+     * b<sub>ij</sub> = α*a<sub>ij</sub>
+     * </p>
+     *
+     * @param alpha the amount each element is multiplied by.
+     * @param a The matrix that is to be scaled.  Not modified.
+     * @param b Where the scaled matrix is stored. Modified.
+     */
+    public static void scale( double alpha , D1Matrix64F a , D1Matrix64F b)
+    {
+        if( a.numRows != b.numRows || a.numCols != b.numCols )
+            throw new IllegalArgumentException("Matrices must have the same shape");
+
+        final int size = a.getNumElements();
+
+        for( int i = 0; i < size; i++ ) {
+            b.data[i] = a.data[i]*alpha;
+        }
+    }
+
+    /**
+     * <p>
+     * Performs an in-place element by element scalar division with the scalar on top.<br>
+     * <br>
+     * a<sub>ij</sub> = &alpha/a<sub>ij</sub>;
+     * </p>
+     *
+     * @param a The matrix whose elements are divide the scalar.  Modified.
+     * @param alpha top value in division
+     */
+    public static void divide( double alpha , D1Matrix64F a )
+    {
+        final int size = a.getNumElements();
+
+        for( int i = 0; i < size; i++ ) {
+            a.data[i] = alpha/a.data[i];
+        }
+    }
+
+    /**
+     * <p>
+     * Performs an in-place element by element scalar division with the scalar on bottom.<br>
+     * <br>
+     * a<sub>ij</sub> = a<sub>ij</sub>/α
+     * </p>
+     *
+     * @param a The matrix whose elements are to be divided.  Modified.
+     * @param alpha the amount each element is divided by.
+     */
+    public static void divide( D1Matrix64F a , double alpha)
+    {
+        final int size = a.getNumElements();
+
+        for( int i = 0; i < size; i++ ) {
+            a.data[i] /= alpha;
+        }
+    }
+
+    /**
+     * <p>
+     * Performs an element by element scalar division with the scalar on top.<br>
+     * <br>
+     * b<sub>ij</sub> = &alpha/a<sub>ij</sub>;
+     * </p>
+     *
+     * @param alpha The numerator.
+     * @param a The matrix whose elements are the divisor.  Not modified.
+     * @param b Where the results are stored. Modified.
+     */
+    public static void divide( double alpha , D1Matrix64F a , D1Matrix64F b)
+    {
+        if( a.numRows != b.numRows || a.numCols != b.numCols )
+            throw new IllegalArgumentException("Matrices must have the same shape");
+
+        final int size = a.getNumElements();
+
+        for( int i = 0; i < size; i++ ) {
+            b.data[i] = alpha/a.data[i];
+        }
+    }
+
+    /**
+     * <p>
+     * Performs an element by element scalar division with the scalar on botton.<br>
+     * <br>
+     * b<sub>ij</sub> = a<sub>ij</sub> /α
+     * </p>
+     *
+     * @param a The matrix whose elements are to be divided.  Not modified.
+     * @param alpha the amount each element is divided by.
+     * @param b Where the results are stored. Modified.
+     */
+    public static void divide( D1Matrix64F a , double alpha  , D1Matrix64F b)
+    {
+        if( a.numRows != b.numRows || a.numCols != b.numCols )
+            throw new IllegalArgumentException("Matrices must have the same shape");
+
+        final int size = a.getNumElements();
+
+        for( int i = 0; i < size; i++ ) {
+            b.data[i] = a.data[i]/alpha;
+        }
+    }
+
+    /**
+     * <p>
+     * Changes the sign of every element in the matrix.<br>
+     * <br>
+     * a<sub>ij</sub> = -a<sub>ij</sub>
+     * </p>
+     *
+     * @param a A matrix. Modified.
+     */
+    public static void changeSign( D1Matrix64F a )
+    {
+        final int size = a.getNumElements();
+
+        for( int i = 0; i < size; i++ ) {
+            a.data[i] = -a.data[i];
+        }
+    }
+
+    /**
+     * <p>
+     * Changes the sign of every element in the matrix.<br>
+     * <br>
+     * output<sub>ij</sub> = -input<sub>ij</sub>
+     * </p>
+     *
+     * @param input A matrix. Modified.
+     */
+    public static void changeSign( D1Matrix64F input , D1Matrix64F output)
+    {
+        if( input.numRows != output.numRows || input.numCols != output.numCols )
+            throw new IllegalArgumentException("Matrices must have the same shape");
+
+        final int size = input.getNumElements();
+
+        for( int i = 0; i < size; i++ ) {
+            output.data[i] = -input.data[i];
+        }
+    }
+
+    /**
+     * <p>
+     * Sets every element in the matrix to the specified value.<br>
+     * <br>
+     * a<sub>ij</sub> = value
+     * <p>
+     *
+     * @param a A matrix whose elements are about to be set. Modified.
+     * @param value The value each element will have.
+     */
+    public static void fill(D1Matrix64F a, double value)
+    {
+        Arrays.fill(a.data,0,a.getNumElements(),value);
+    }
+
+    /**
+     * <p>
+     * Puts the augmented system matrix into reduced row echelon form (RREF) using Gauss-Jordan
+     * elimination with row (partial) pivots.  A matrix is said to be in RREF is the following conditions are true:
+     * </p>
+     *
+     * <ol>
+     *     <li>If a row has non-zero entries, then the first non-zero entry is 1.  This is known as the leading one.</li>
+     *     <li>If a column contains a leading one then all other entries in that column are zero.</li>
+     *     <li>If a row contains a leading 1, then each row above contains a leading 1 further to the left.</li>
+     * </ol>
+     *
+     * <p>
+     * [1] Page 19 in, Otter Bretscherm "Linear Algebra with Applications" Prentice-Hall Inc, 1997
+     * </p>
+     *
+     * @see RrefGaussJordanRowPivot
+     *
+     * @param A Input matrix.  Unmodified.
+     * @param numUnknowns Number of unknowns/columns that are reduced. Set to -1 to default to
+     *                       Math.min(A.numRows,A.numCols), which works for most systems.
+     * @param reduced Storage for reduced echelon matrix. If null then a new matrix is returned. Modified.
+     * @return Reduced echelon form of A
+     */
+    public static DenseMatrix64F rref( DenseMatrix64F A , int numUnknowns, DenseMatrix64F reduced ) {
+        if( reduced == null ) {
+            reduced = new DenseMatrix64F(A.numRows,A.numCols);
+        } else if( reduced.numCols != A.numCols || reduced.numRows != A.numRows )
+            throw new IllegalArgumentException("'re' must have the same shape as the original input matrix");
+
+        if( numUnknowns <= 0 )
+            numUnknowns = Math.min(A.numCols,A.numRows);
+
+        ReducedRowEchelonForm<DenseMatrix64F> alg = new RrefGaussJordanRowPivot();
+        alg.setTolerance(elementMaxAbs(A)* UtilEjml.EPS*Math.max(A.numRows,A.numCols));
+
+        reduced.set(A);
+        alg.reduce(reduced, numUnknowns);
+
+        return reduced;
+    }
+}
diff --git a/main/dense64/src/org/ejml/ops/CovarianceOps.java b/main/dense64/src/org/ejml/ops/CovarianceOps.java
new file mode 100644
index 0000000..b88ff62
--- /dev/null
+++ b/main/dense64/src/org/ejml/ops/CovarianceOps.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.alg.dense.linsol.LinearSolverSafe;
+import org.ejml.alg.dense.misc.UnrolledInverseFromMinor;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.LinearSolverFactory;
+import org.ejml.interfaces.linsol.LinearSolver;
+
+import java.util.Random;
+
+
+/**
+ * Contains operations specific to covariance matrices.
+ *
+ * @author Peter Abeles
+ */
+public class CovarianceOps {
+
+    public static double TOL = 1e-9;
+
+    /**
+     * This is a fairly light weight check to see of a covariance matrix is valid.
+     * It checks to see if the diagonal elements are all positive, which they should be
+     * if it is valid.  Not all invalid covariance matrices will be caught by this method.
+	 *
+	 * @return true if valid and false if invalid
+     */
+    public static boolean isValidFast( DenseMatrix64F cov ) {
+        return MatrixFeatures.isDiagonalPositive(cov);
+    }
+
+    /**
+     * Performs a variety of tests to see if the provided matrix is a valid
+     * covariance matrix.
+     *
+     * @return  0 = is valid 1 = failed positive diagonal, 2 = failed on symmetry, 2 = failed on positive definite
+     */
+    public static int isValid( DenseMatrix64F cov ) {
+        if( !MatrixFeatures.isDiagonalPositive(cov) )
+            return 1;
+
+        if( !MatrixFeatures.isSymmetric(cov,TOL) )
+            return 2;
+
+        if( !MatrixFeatures.isPositiveSemidefinite(cov) )
+            return 3;
+
+        return 0;
+    }
+
+    /**
+     * Performs a matrix inversion operations that takes advantage of the special
+     * properties of a covariance matrix.
+     *
+     * @param cov On input it is a covariance matrix, on output it is the inverse.  Modified.
+     * @return true if it could invert the matrix false if it could not.
+     */
+    public static boolean invert( DenseMatrix64F cov ) {
+        return invert(cov,cov);
+    }
+
+    /**
+     * Performs a matrix inversion operations that takes advantage of the special
+     * properties of a covariance matrix.
+     *
+     * @param cov A covariance matrix. Not modified.
+     * @param cov_inv The inverse of cov.  Modified.
+     * @return true if it could invert the matrix false if it could not.
+     */
+    public static boolean invert( final DenseMatrix64F cov , final DenseMatrix64F cov_inv ) {
+        if( cov.numCols <= 4 ) {
+            if( cov.numCols != cov.numRows ) {
+                throw new IllegalArgumentException("Must be a square matrix.");
+            }
+
+            if( cov.numCols >= 2 )
+                UnrolledInverseFromMinor.inv(cov,cov_inv);
+            else
+                cov_inv.data[0] = 1.0/cov_inv.data[0];
+
+        } else {
+            LinearSolver<DenseMatrix64F> solver = LinearSolverFactory.symmPosDef(cov.numRows);
+            // wrap it to make sure the covariance is not modified.
+            solver = new LinearSolverSafe<DenseMatrix64F>(solver);
+            if( !solver.setA(cov) )
+                return false;
+            solver.invert(cov_inv);
+        }
+        return true;
+    }
+
+    /**
+     * Sets vector to a random value based upon a zero-mean multivariate Gaussian distribution with
+     * covariance 'cov'.  If repeat calls are made to this class, consider using {@link CovarianceRandomDraw} instead.
+     *
+     * @param cov The distirbutions covariance.  Not modified.
+     * @param vector The random vector. Modified.
+     * @param rand Random number generator.
+     */
+    public static void randomVector( DenseMatrix64F cov ,
+                                     DenseMatrix64F vector ,
+                                     Random rand  )
+    {
+        CovarianceRandomDraw rng = new CovarianceRandomDraw(rand,cov);
+        rng.next(vector);
+    }
+}
diff --git a/main/dense64/src/org/ejml/ops/CovarianceRandomDraw.java b/main/dense64/src/org/ejml/ops/CovarianceRandomDraw.java
new file mode 100644
index 0000000..cf54582
--- /dev/null
+++ b/main/dense64/src/org/ejml/ops/CovarianceRandomDraw.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.alg.dense.decomposition.chol.CholeskyDecompositionInner_D64;
+import org.ejml.data.DenseMatrix64F;
+
+import java.util.Random;
+
+import static org.ejml.ops.CommonOps.multAdd;
+
+/**
+ * Generates random vectors based on a zero mean multivariate Gaussian distribution.  The covariance
+ * matrix is provided in the constructor.
+ */
+public class CovarianceRandomDraw {
+    private DenseMatrix64F A;
+    private Random rand;
+    private DenseMatrix64F r;
+
+    /**
+     * Creates a random distribution with the specified mean and covariance.  The references
+     * to the variables are not saved, their value are copied.
+     *
+     * @param rand Used to create the random numbers for the draw. Reference is saved.
+     * @param cov The covariance of the distribution.  Not modified.
+     */
+    public CovarianceRandomDraw( Random rand , DenseMatrix64F cov )
+    {
+        r = new DenseMatrix64F(cov.numRows,1);
+        CholeskyDecompositionInner_D64 cholesky = new CholeskyDecompositionInner_D64( true);
+
+        if( cholesky.inputModified() )
+            cov = cov.copy();
+        if( !cholesky.decompose(cov) )
+            throw new RuntimeException("Decomposition failed!");
+
+        A = cholesky.getT();
+        this.rand = rand;
+    }
+
+    /**
+     * Makes a draw on the distribution.  The results are added to parameter 'x'
+     */
+    public void next( DenseMatrix64F x )
+    {
+        for( int i = 0; i < r.numRows; i++ ) {
+            r.set(i,0,rand.nextGaussian());
+        }
+
+        multAdd(A,r,x);
+    }
+
+    /**
+     * Computes the likelihood of the random draw
+     *
+     * @return The likelihood.
+     */
+    public double computeLikelihoodP() {
+        double ret = 1.0;
+
+        for( int i = 0; i < r.numRows; i++ ) {
+            double a = r.get(i,0);
+
+            ret *= Math.exp(-a*a/2.0);
+        }
+
+        return ret;
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/src/org/ejml/ops/EigenOps.java b/main/dense64/src/org/ejml/ops/EigenOps.java
new file mode 100644
index 0000000..bed371a
--- /dev/null
+++ b/main/dense64/src/org/ejml/ops/EigenOps.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.UtilEjml;
+import org.ejml.alg.dense.decomposition.eig.EigenPowerMethod;
+import org.ejml.alg.dense.mult.VectorVectorMult;
+import org.ejml.data.Complex64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.data.Eigenpair64F;
+import org.ejml.factory.LinearSolverFactory;
+import org.ejml.interfaces.decomposition.EigenDecomposition;
+import org.ejml.interfaces.linsol.LinearSolver;
+
+
+/**
+ * Additional functions related to eigenvalues and eigenvectors of a matrix.
+ *
+ * @author Peter Abeles
+ */
+public class EigenOps {
+    /**
+     * <p>
+     * Given matrix A and an eigen vector of A, compute the corresponding eigen value.  This is
+     * the Rayleigh quotient.<br>
+     * <br>
+     * x<sup>T</sup>Ax / x<sup>T</sup>x
+     * </p>
+     *
+     *
+     * @param A Matrix. Not modified.
+     * @param eigenVector An eigen vector of A. Not modified.
+     * @return The corresponding eigen value.
+     */
+    public static double computeEigenValue( DenseMatrix64F A , DenseMatrix64F eigenVector )
+    {
+        double bottom = VectorVectorMult.innerProd(eigenVector,eigenVector);
+        double top = VectorVectorMult.innerProdA(eigenVector,A,eigenVector);
+
+        return top/bottom;
+    }
+
+    /**
+     * <p>
+     * Given an eigenvalue it computes an eigenvector using inverse iteration:
+     * <br>
+     * for i=1:MAX {<br>
+     *   (A - μI)z<sup>(i)</sup> = q<sup>(i-1)</sup><br>
+     *   q<sup>(i)</sup> = z<sup>(i)</sup> / ||z<sup>(i)</sup>||<br>
+     * λ<sup>(i)</sup> =  q<sup>(i)</sup><sup>T</sup> A  q<sup>(i)</sup><br>
+     * }<br>
+     * </p>
+     * <p>
+     * NOTE: If there is another eigenvalue that is very similar to the provided one then there
+     * is a chance of it converging towards that one instead.  The larger a matrix is the more
+     * likely this is to happen.
+     * </p>
+     * @param A Matrix whose eigenvector is being computed.  Not modified.
+     * @param eigenvalue The eigenvalue in the eigen pair.
+     * @return The eigenvector or null if none could be found.
+     */
+    public static Eigenpair64F computeEigenVector( DenseMatrix64F A , double eigenvalue )
+    {
+        if( A.numRows != A.numCols )
+            throw new IllegalArgumentException("Must be a square matrix.");
+
+        DenseMatrix64F M = new DenseMatrix64F(A.numRows,A.numCols);
+
+        DenseMatrix64F x = new DenseMatrix64F(A.numRows,1);
+        DenseMatrix64F b = new DenseMatrix64F(A.numRows,1);
+
+        CommonOps.fill(b, 1);
+        
+        // perturb the eigenvalue slightly so that its not an exact solution the first time
+//        eigenvalue -= eigenvalue*UtilEjml.EPS*10;
+
+        double origEigenvalue = eigenvalue;
+
+        SpecializedOps.addIdentity(A,M,-eigenvalue);
+
+        double threshold = NormOps.normPInf(A)*UtilEjml.EPS;
+
+        double prevError = Double.MAX_VALUE;
+        boolean hasWorked = false;
+
+        LinearSolver<DenseMatrix64F> solver = LinearSolverFactory.linear(M.numRows);
+
+        double perp = 0.0001;
+
+        for( int i = 0; i < 200; i++ ) {
+            boolean failed = false;
+            // if the matrix is singular then the eigenvalue is within machine precision
+            // of the true value, meaning that x must also be.
+            if( !solver.setA(M) ) {
+                failed = true;
+            } else {
+                solver.solve(b,x);
+            }
+
+            // see if solve silently failed
+            if( MatrixFeatures.hasUncountable(x)) {
+                failed = true;
+            }
+
+            if( failed ) {
+                if( !hasWorked ) {
+                     // if it failed on the first trial try perturbing it some more
+                    double val = i % 2 == 0 ? 1.0-perp : 1.0 + perp;
+                    // maybe this should be turn into a parameter allowing the user
+                    // to configure the wise of each step
+
+                    eigenvalue = origEigenvalue * Math.pow(val,i/2+1);
+                    SpecializedOps.addIdentity(A,M,-eigenvalue);
+                } else {
+                    // otherwise assume that it was so accurate that the matrix was singular
+                    // and return that result
+                    return new Eigenpair64F(eigenvalue,b);
+                }
+            } else {
+                hasWorked = true;
+                
+                b.set(x);
+                NormOps.normalizeF(b);
+
+                // compute the residual
+                CommonOps.mult(M,b,x);
+                double error = NormOps.normPInf(x);
+
+                if( error-prevError > UtilEjml.EPS*10) {
+                    // if the error increased it is probably converging towards a different
+                    // eigenvalue
+//                    CommonOps.set(b,1);
+                    prevError = Double.MAX_VALUE;
+                    hasWorked = false;
+                    double val = i % 2 == 0 ? 1.0-perp : 1.0 + perp;
+                    eigenvalue = origEigenvalue * Math.pow(val,1);
+                } else {
+                    // see if it has converged
+                    if(error <= threshold || Math.abs(prevError-error) <= UtilEjml.EPS)
+                        return new Eigenpair64F(eigenvalue,b);
+
+                    // update everything
+                    prevError = error;
+                    eigenvalue = VectorVectorMult.innerProdA(b,A,b);
+                }
+
+                SpecializedOps.addIdentity(A,M,-eigenvalue);
+            }
+        }
+
+        return null;
+    }
+
+
+    /**
+     * <p>
+     * Computes the dominant eigen vector for a matrix.  The dominant eigen vector is an
+     * eigen vector associated with the largest eigen value.
+     * </p>
+     *
+     * <p>
+     * WARNING: This function uses the power method.  There are known cases where it will not converge.
+     * It also seems to converge to non-dominant eigen vectors some times.  Use at your own risk.
+     * </p>
+     *
+     * @param A A matrix.  Not modified.
+     */
+    // TODO maybe do the regular power method, estimate the eigenvalue, then shift invert?
+    public static Eigenpair64F dominantEigenpair( DenseMatrix64F A ) {
+
+        EigenPowerMethod power = new EigenPowerMethod(A.numRows);
+
+        // eh maybe 0.1 is a good value.  who knows.
+        if( !power.computeShiftInvert(A,0.1) )
+            return null;
+
+        return null;//power.getEigenVector();
+    }
+
+    /**
+     * <p>
+     * Generates a bound for the largest eigen value of the provided matrix using Perron-Frobenius
+     * theorem.   This function only applies to non-negative real matrices.
+     * </p>
+     *
+     * <p>
+     * For "stochastic" matrices (Markov process) this should return one for the upper and lower bound.
+     * </p>
+     *
+     * @param A Square matrix with positive elements.  Not modified.
+     * @param bound Where the results are stored.  If null then a matrix will be declared. Modified.
+     * @return Lower and upper bound in the first and second elements respectively.
+     */
+    public static double [] boundLargestEigenValue( DenseMatrix64F A , double []bound ) {
+        if( A.numRows != A.numCols )
+            throw new IllegalArgumentException("A must be a square matrix.");
+
+        double min = Double.MAX_VALUE;
+        double max = 0;
+
+        int n = A.numRows;
+
+        for( int i = 0; i < n; i++ ) {
+            double total = 0;
+            for( int j = 0; j < n; j++ ) {
+                double v = A.get(i,j);
+                if( v < 0 ) throw new IllegalArgumentException("Matrix must be positive");
+
+                total += v;
+            }
+
+            if( total < min ) {
+                min = total;
+            }
+
+            if( total > max ) {
+                max = total;
+            }
+        }
+
+        if( bound == null )
+            bound = new double[2];
+
+        bound[0] = min;
+        bound[1] = max;
+
+        return bound;
+    }
+
+    /**
+     * <p>
+     * A diagonal matrix where real diagonal element contains a real eigenvalue.  If an eigenvalue
+     * is imaginary then zero is stored in its place.
+     * </p>
+     *
+     * @param eig An eigenvalue decomposition which has already decomposed a matrix.
+     * @return A diagonal matrix containing the eigenvalues.
+     */
+    public static DenseMatrix64F createMatrixD( EigenDecomposition eig )
+    {
+        int N = eig.getNumberOfEigenvalues();
+
+        DenseMatrix64F D = new DenseMatrix64F( N , N );
+
+        for( int i = 0; i < N; i++ ) {
+            Complex64F c = eig.getEigenvalue(i);
+
+            if( c.isReal() ) {
+                D.set(i,i,c.real);
+            }
+        }
+
+        return D;
+    }
+
+    /**
+     * <p>
+     * Puts all the real eigenvectors into the columns of a matrix.  If an eigenvalue is imaginary
+     * then the corresponding eigenvector will have zeros in its column.
+     * </p>
+     * 
+     * @param eig An eigenvalue decomposition which has already decomposed a matrix.
+     * @return An m by m matrix containing eigenvectors in its columns.
+     */
+    public static DenseMatrix64F createMatrixV( EigenDecomposition<DenseMatrix64F> eig )
+    {
+        int N = eig.getNumberOfEigenvalues();
+
+        DenseMatrix64F V = new DenseMatrix64F( N , N );
+
+        for( int i = 0; i < N; i++ ) {
+            Complex64F c = eig.getEigenvalue(i);
+
+            if( c.isReal() ) {
+                DenseMatrix64F v = eig.getEigenVector(i);
+
+                if( v != null ) {
+                    for( int j = 0; j < N; j++ ) {
+                        V.set(j,i,v.get(j,0));
+                    }
+                }
+            }
+        }
+
+        return V;
+    }
+}
diff --git a/main/dense64/src/org/ejml/ops/EjmlUnitTests.java b/main/dense64/src/org/ejml/ops/EjmlUnitTests.java
new file mode 100644
index 0000000..36660b8
--- /dev/null
+++ b/main/dense64/src/org/ejml/ops/EjmlUnitTests.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.data.*;
+
+
+/**
+ * Contains various functions related to unit testing matrix operations.
+ *
+ * @author Peter Abeles
+ */
+public class EjmlUnitTests {
+
+    /**
+     * Checks to see if every element in A is countable.  A doesn't have any element with
+     * a value of NaN or infinite.
+     *
+     * @param A Matrix
+     */
+    public static void assertCountable( RealMatrix64F A ) {
+        for( int i = 0; i < A.getNumRows(); i++ ){
+            for( int j = 0; j < A.getNumCols(); j++ ) {
+                assertTrue(  !Double.isNaN(A.get(i,j)) , "NaN found at "+i+" "+j );
+                assertTrue(  !Double.isInfinite(A.get(i,j)) , "Infinite found at "+i+" "+j );
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Checks to see if A and B have the same shape.
+     * </p>
+     *
+     * @param A Matrix
+     * @param B Matrix
+     */
+    public static void assertShape( Matrix A , Matrix B ) {
+        assertTrue(  A.getNumRows() == B.getNumRows() , "Number of rows do not match");
+        assertTrue(  A.getNumCols() == B.getNumCols() , "Number of columns do not match");
+    }
+
+    /**
+     * <p>
+     * Checks to see if the matrix has the specified number of rows and columns.
+     * </p>
+     *
+     * @param A Matrix
+     * @param numRows expected number of rows in the matrix
+     * @param numCols expected number of columns in the matrix
+     */
+    public static void assertShape( RealMatrix64F A , int numRows , int numCols ) {
+        assertTrue(  A.getNumRows() == numRows , "Unexpected number of rows.");
+        assertTrue(  A.getNumCols() == numCols , "Unexpected number of columns.");
+    }
+
+    /**
+     * <p>
+     * Checks to see if each element in the matrix is within tolerance of each other:
+     * </p>
+     *
+     * <p>
+     * The two matrices are identical with in tolerance if:<br>
+     * |a<sub>ij</sub> - b<sub>ij</sub>| ≤ tol
+     * </p>
+     *
+     * <p>
+     * In addition if an element is NaN or infinite in one matrix it must be the same in the other.
+     * </p>
+     *
+     * @param A Matrix A
+     * @param B Matrix B
+     * @param tol Tolerance
+     */
+    public static void assertEqualsUncountable( RealMatrix64F A , RealMatrix64F B , double tol ) {
+        assertShape(A, B);
+
+        for (int i = 0; i < A.getNumRows(); i++) {
+            for (int j = 0; j < A.getNumCols(); j++) {
+                double valA = A.get(i, j);
+                double valB = B.get(i, j);
+
+                if (Double.isNaN(valA)) {
+                    assertTrue(Double.isNaN(valB), "At (" + i + "," + j + ") A = " + valA + " B = " + valB);
+                } else if (Double.isInfinite(valA)) {
+                    assertTrue(Double.isInfinite(valB), "At (" + i + "," + j + ") A = " + valA + " B = " + valB);
+                } else {
+                    double diff = Math.abs(valA - valB);
+                    assertTrue(diff <= tol, "At (" + i + "," + j + ") A = " + valA + " B = " + valB);
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Checks to see if each element in the matrices are within tolerance of each other and countable:
+     * </p>
+     *
+     * <p>
+     * The two matrices are identical with in tolerance if:<br>
+     * |a<sub>ij</sub> - b<sub>ij</sub>| ≤ tol
+     * </p>
+     *
+     * <p>
+     * The test will fail if any element in either matrix is NaN or infinite.
+     * </p>
+     *
+     * @param A Matrix A
+     * @param B Matrix B
+     * @param tol Tolerance
+     */
+    public static void assertEquals( RealMatrix64F A , RealMatrix64F B , double tol ) {
+        assertShape(A,B);
+
+        for( int i = 0; i < A.getNumRows(); i++ ){
+            for( int j = 0; j < A.getNumCols(); j++ ) {
+                double valA = A.get(i,j);
+                double valB = B.get(i,j);
+
+                assertTrue(!Double.isNaN(valA) && !Double.isNaN(valB) ,"At ("+i+","+j+") A = "+valA+" B = "+valB);
+                assertTrue(!Double.isInfinite(valA) && !Double.isInfinite(valB) ,"At ("+i+","+j+") A = "+valA+" B = "+valB);
+                assertTrue(Math.abs( valA-valB) <= tol,"At ("+i+","+j+") A = "+valA+" B = "+valB);
+            }
+        }
+    }
+
+    public static void assertEquals( RealMatrix32F A , RealMatrix32F B , float tol ) {
+        assertShape(A,B);
+
+        for( int i = 0; i < A.getNumRows(); i++ ){
+            for( int j = 0; j < A.getNumCols(); j++ ) {
+                float valA = A.get(i,j);
+                float valB = B.get(i,j);
+
+                assertTrue(!Float.isNaN(valA) && !Float.isNaN(valB) ,"At ("+i+","+j+") A = "+valA+" B = "+valB);
+                assertTrue(!Float.isInfinite(valA) && !Float.isInfinite(valB) ,"At ("+i+","+j+") A = "+valA+" B = "+valB);
+                assertTrue(Math.abs( valA-valB) <= tol,"At ("+i+","+j+") A = "+valA+" B = "+valB);
+            }
+        }
+    }
+
+    public static void assertEquals( Complex64F a , Complex64F b , double tol ) {
+        assertTrue(!Double.isNaN(a.real) && !Double.isNaN(b.real) ,"real a = "+a.real+" b = "+b.real);
+        assertTrue(!Double.isInfinite(a.real) && !Double.isInfinite(b.real) ,"real a = "+a.real+" b = "+b.real);
+        assertTrue(Math.abs( a.real-b.real) <= tol,"real a = "+a.real+" b = "+b.real);
+
+        assertTrue(!Double.isNaN(a.imaginary) && !Double.isNaN(b.imaginary) ,"imaginary a = "+a.imaginary+" b = "+b.imaginary);
+        assertTrue(!Double.isInfinite(a.imaginary) && !Double.isInfinite(b.imaginary) ,"imaginary a = "+a.imaginary+" b = "+b.imaginary);
+        assertTrue(Math.abs( a.imaginary-b.imaginary) <= tol,"imaginary a = "+a.imaginary+" b = "+b.imaginary);
+    }
+
+    public static void assertEquals( ComplexMatrix64F A , ComplexMatrix64F B , double tol ) {
+        assertShape(A,B);
+
+        Complex64F a = new Complex64F();
+        Complex64F b = new Complex64F();
+
+        for( int i = 0; i < A.getNumRows(); i++ ){
+            for( int j = 0; j < A.getNumCols(); j++ ) {
+                A.get(i, j, a);
+                B.get(i, j, b);
+
+                assertTrue(!Double.isNaN(a.real) && !Double.isNaN(b.real) ,"Real At ("+i+","+j+") A = "+a.real+" B = "+b.real);
+                assertTrue(!Double.isInfinite(a.real) && !Double.isInfinite(b.real) ,"Real At ("+i+","+j+") A = "+a.real+" B = "+b.real);
+                assertTrue(Math.abs( a.real-b.real) <= tol,"Real At ("+i+","+j+") A = "+a.real+" B = "+b.real);
+
+                assertTrue(!Double.isNaN(a.imaginary) && !Double.isNaN(b.imaginary) ,"Img At ("+i+","+j+") A = "+a.imaginary+" B = "+b.imaginary);
+                assertTrue(!Double.isInfinite(a.imaginary) && !Double.isInfinite(b.imaginary) ,"Img At ("+i+","+j+") A = "+a.imaginary+" B = "+b.imaginary);
+                assertTrue(Math.abs( a.imaginary-b.imaginary) <= tol,"Img At ("+i+","+j+") A = "+a.imaginary+" B = "+b.imaginary);
+
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Checks to see if the transpose of B is equal to A and countable:
+     * </p>
+     *
+     * <p>
+     * |a<sub>ij</sub> - b<sub>ji</sub>| ≤ tol
+     * </p>
+     *
+     * <p>
+     * The test will fail if any element in either matrix is NaN or infinite.
+     * </p>
+     *
+     * @param A Matrix A
+     * @param B Matrix B
+     * @param tol Tolerance
+     */
+    public static void assertEqualsTrans( RealMatrix64F A , RealMatrix64F B , double tol ) {
+        assertShape(A,B.getNumCols(),B.getNumRows());
+
+        for( int i = 0; i < A.getNumRows(); i++ ){
+            for( int j = 0; j < A.getNumCols(); j++ ) {
+                double valA = A.get(i,j);
+                double valB = B.get(j,i);
+
+                assertTrue(!Double.isNaN(valA) && !Double.isNaN(valB) ,"A("+i+","+j+") = "+valA+") B("+j+","+i+") = "+valB);
+                assertTrue(!Double.isInfinite(valA) && !Double.isInfinite(valB) ,"A("+i+","+j+") = "+valA+") B("+j+","+i+") = "+valB);
+                assertTrue(Math.abs( valA-valB) <= tol,"A("+i+","+j+") = "+valA+") B("+j+","+i+") = "+valB);
+            }
+        }
+    }
+
+    @SuppressWarnings({"ConstantConditions"})
+    private static void assertTrue( boolean result , String message ) {
+        // if turned on use asserts
+        assert result : message;
+        // otherwise throw an exception
+        if( !result ) throw new TestException(message);
+    }
+
+    public static class TestException extends RuntimeException {
+        public TestException(String message) {
+            super(message);
+        }
+    }
+}
diff --git a/main/dense64/src/org/ejml/ops/MatrixComponent.java b/main/dense64/src/org/ejml/ops/MatrixComponent.java
new file mode 100644
index 0000000..6385992
--- /dev/null
+++ b/main/dense64/src/org/ejml/ops/MatrixComponent.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.data.D1Matrix64F;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+
+
+/**
+ * Renders a matrix as an image.  Positive elements are shades of red, negative shades of blue, 0 is black.
+ *
+ * @author Peter Abeles
+ */
+public class MatrixComponent extends JPanel {
+    BufferedImage image;
+
+    public MatrixComponent( int width , int height ) {
+        image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
+        setPreferredSize(new Dimension(width,height));
+        setMinimumSize(new Dimension(width,height));
+    }
+
+    public synchronized void setMatrix( D1Matrix64F A ) {
+        double maxValue = CommonOps.elementMaxAbs(A);
+        renderMatrix(A,image,maxValue);
+        repaint();
+    }
+
+    public static void renderMatrix( D1Matrix64F M , BufferedImage image , double maxValue )
+    {
+        int w = image.getWidth();
+        int h = image.getHeight();
+
+        double widthStep = (double)M.numCols / image.getWidth();
+        double heightStep = (double)M.numRows / image.getHeight();
+
+        for( int i = 0; i < h; i++ ) {
+            for( int j = 0; j < w; j++ ) {
+                double value = M.get( (int)(i*heightStep) , (int)(j*widthStep) );
+
+                if( value == 0 ){
+                    image.setRGB(j,i,255 << 24);
+                } else if( value > 0 ) {
+                    int p = 255-(int)(255.0*(value/maxValue));
+                    int rgb = 255 << 24 | 255 << 16 | p << 8 | p;
+
+                    image.setRGB(j,i,rgb);
+                } else {
+                    int p = 255+(int)(255.0*(value/maxValue));
+                    int rgb = 255 << 24 | p << 16 | p << 8 | 255;
+
+                    image.setRGB(j,i,rgb);
+                }
+            }
+        }
+
+
+    }
+
+    @Override
+    public synchronized void paint( Graphics g ) {
+        g.drawImage(image,0,0,this);
+    }
+
+}
diff --git a/main/dense64/src/org/ejml/ops/MatrixFeatures.java b/main/dense64/src/org/ejml/ops/MatrixFeatures.java
new file mode 100644
index 0000000..bba4acb
--- /dev/null
+++ b/main/dense64/src/org/ejml/ops/MatrixFeatures.java
@@ -0,0 +1,684 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.UtilEjml;
+import org.ejml.alg.dense.decomposition.chol.CholeskyDecompositionInner_D64;
+import org.ejml.alg.dense.mult.VectorVectorMult;
+import org.ejml.data.*;
+import org.ejml.factory.DecompositionFactory;
+import org.ejml.interfaces.decomposition.EigenDecomposition;
+import org.ejml.interfaces.decomposition.LUDecomposition;
+import org.ejml.interfaces.decomposition.SingularValueDecomposition;
+
+
+/**
+ * <p>
+ * Used to compute features that describe the structure of a matrix.
+ * <p>
+ *
+ * <p>
+ * Unless explicitly stated otherwise it is assumed that the elements of input matrices
+ * contain only real numbers.  If an element is NaN or infinite then the behavior is undefined.
+ * See IEEE 754 for more information on this issue.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class MatrixFeatures {
+
+    /**
+     * Checks to see if any element in the matrix is NaN.
+     *
+     * @param m A matrix. Not modified.
+     * @return True if any element in the matrix is NaN.
+     */
+    public static boolean hasNaN( D1Matrix64F m )
+    {
+        int length = m.getNumElements();
+
+        for( int i = 0; i < length; i++ ) {
+            if( Double.isNaN(m.get(i)))
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Checks to see if any element in the matrix is NaN of Infinite.
+     *
+     * @param m A matrix. Not modified.
+     * @return True if any element in the matrix is NaN of Infinite.
+     */
+    public static boolean hasUncountable( D1Matrix64F m )
+    {
+        int length = m.getNumElements();
+
+        for( int i = 0; i < length; i++ ) {
+            double a = m.get(i);
+            if( Double.isNaN(a) || Double.isInfinite(a))
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Checks to see all the elements in the matrix are zeros
+     *
+     * @param m A matrix. Not modified.
+     * @return True if all elements are zeros or false if not
+     */
+    public static boolean isZeros( D1Matrix64F m , double tol )
+    {
+        int length = m.getNumElements();
+
+        for( int i = 0; i < length; i++ ) {
+            if( Math.abs(m.get(i)) > tol )
+                return false;
+        }
+        return true;
+    }
+
+    /**
+     * Checks to see if the matrix is a vector or not.
+     *
+     * @param mat A matrix. Not modified.
+     *
+     * @return True if it is a vector and false if it is not.
+     */
+    public static boolean isVector( Matrix mat ) {
+        return (mat.getNumCols() == 1 || mat.getNumRows() == 1);
+    }
+
+    /**
+     * <p>
+     * Checks to see if the matrix is positive definite.
+     * </p>
+     * <p>
+     * x<sup>T</sup> A x > 0<br>
+     * for all x where x is a non-zero vector and A is a symmetric matrix.
+     * </p>
+     *
+     * @param A square symmetric matrix. Not modified.
+     *
+     * @return True if it is positive definite and false if it is not.
+     */
+    public static boolean isPositiveDefinite( DenseMatrix64F A ) {
+        if( !isSquare(A))
+           return false;
+
+        CholeskyDecompositionInner_D64 chol = new CholeskyDecompositionInner_D64(true);
+        if( chol.inputModified() )
+            A = A.copy();
+
+        return chol.decompose(A);
+    }
+
+    /**
+     * <p>
+     * Checks to see if the matrix is positive semidefinite:
+     * </p>
+     * <p>
+     * x<sup>T</sup> A x >= 0<br>
+     * for all x where x is a non-zero vector and A is a symmetric matrix.
+     * </p>
+     *
+     * @param A square symmetric matrix. Not modified.
+     *
+     * @return True if it is positive semidefinite and false if it is not.
+     */
+    public static boolean isPositiveSemidefinite( DenseMatrix64F A ) {
+        if( !isSquare(A))
+           return false;
+
+        EigenDecomposition<DenseMatrix64F> eig = DecompositionFactory.eig(A.numCols,false);
+        if( eig.inputModified() )
+            A = A.copy();
+        eig.decompose(A);
+
+        for( int i = 0; i < A.numRows; i++ ) {
+            Complex64F v = eig.getEigenvalue(i);
+
+            if( v.getReal() < 0 )
+                return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Checks to see if it is a square matrix.  A square matrix has
+     * the same number of rows and columns.
+     *
+     * @param mat A matrix. Not modified.
+     * @return True if it is a square matrix and false if it is not.
+     */
+    public static boolean isSquare( D1Matrix64F mat ) {
+        return mat.numCols == mat.numRows;
+    }
+
+    /**
+     * <p>
+     * Returns true if the matrix is symmetric within the tolerance.  Only square matrices can be
+     * symmetric.
+     * </p>
+     * <p>
+     * A matrix is symmetric if:<br>
+     * |a<sub>ij</sub> - a<sub>ji</sub>| ≤ tol
+     * </p>
+     *
+     * @param m A matrix. Not modified.
+     * @param tol Tolerance for how similar two elements need to be.
+     * @return true if it is symmetric and false if it is not.
+     */
+    public static boolean isSymmetric( DenseMatrix64F m , double tol ) {
+        if( m.numCols != m.numRows )
+            return false;
+
+        double max = CommonOps.elementMaxAbs(m);
+
+        for( int i = 0; i < m.numRows; i++ ) {
+            for( int j = 0; j < i; j++ ) {
+                double a = m.get(i,j)/max;
+                double b = m.get(j,i)/max;
+
+                double diff = Math.abs(a-b);
+
+                if( !(diff <= tol) ) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * <p>
+     * Returns true if the matrix is perfectly symmetric.  Only square matrices can be symmetric.
+     * </p>
+     * <p>
+     * A matrix is symmetric if:<br>
+     * a<sub>ij</sub> == a<sub>ji</sub>
+     * </p>
+     *
+     * @param m A matrix. Not modified.
+     * @return true if it is symmetric and false if it is not.
+     */
+    public static boolean isSymmetric( DenseMatrix64F m ) {
+        return isSymmetric(m,0.0);
+    }
+
+    /**
+     * <p>
+     * Checks to see if a matrix is skew symmetric with in tolerance:<br>
+     * <br>
+     * -A = A<sup>T</sup><br>
+     * or<br>
+     * |a<sub>ij</sub> + a<sub>ji</sub>| ≤ tol
+     * </p>
+     *
+     * @param A The matrix being tested.
+     * @param tol Tolerance for being skew symmetric.
+     * @return True if it is skew symmetric and false if it is not.
+     */
+    public static boolean isSkewSymmetric( DenseMatrix64F A , double tol ){
+        if( A.numCols != A.numRows )
+            return false;
+
+        for( int i = 0; i < A.numRows; i++ ) {
+            for( int j = 0; j < i; j++ ) {
+                double a = A.get(i,j);
+                double b = A.get(j,i);
+
+                double diff = Math.abs(a+b);
+
+                if( !(diff <= tol) ) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Checks to see if the two matrices are inverses of each other.
+     *
+     * @param a A matrix. Not modified.
+     * @param b A matrix. Not modified.
+     */
+    public static boolean isInverse( DenseMatrix64F a , DenseMatrix64F b , double tol ) {
+        if( a.numRows != b.numRows || a.numCols != b.numCols ) {
+            return false;
+        }
+
+        int numRows = a.numRows;
+        int numCols = a.numCols;
+
+        for( int i = 0; i < numRows; i++ ) {
+            for( int j = 0; j < numCols; j++ ) {
+                double total = 0;
+                for( int k = 0; k < numCols; k++ ) {
+                    total += a.get(i,k)*b.get(k,j);
+                }
+
+                if( i == j ) {
+                    if( !(Math.abs(total-1) <= tol) )
+                        return false;
+                } else if( !(Math.abs(total) <= tol) )
+                    return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * <p>
+     * Checks to see if each element in the two matrices are within tolerance of
+     * each other: tol ≥ |a<sub>ij</sub> - b<sub>ij</sub>|.
+     * <p>
+     *
+     * <p>
+     * NOTE: If any of the elements are not countable then false is returned.<br>
+     * NOTE: If a tolerance of zero is passed in this is equivalent to calling
+     * {@link #isEquals(org.ejml.data.D1Matrix64F, org.ejml.data.D1Matrix64F)}
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @param b A matrix. Not modified.
+     * @param tol How close to being identical each element needs to be.
+     * @return true if equals and false otherwise.
+     */
+    public static boolean isEquals( D1Matrix64F a , D1Matrix64F b , double tol )
+    {
+        if( a.numRows != b.numRows || a.numCols != b.numCols ) {
+            return false;
+        }
+
+        if( tol == 0.0 )
+            return isEquals(a,b);
+
+        final int length = a.getNumElements();
+
+        for( int i = 0; i < length; i++ ) {
+            if( !(tol >= Math.abs(a.get(i) - b.get(i))) ) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * <p>
+     * Checks to see if each element in the upper or lower triangular portion of the two matrices are within tolerance of
+     * each other: tol ≥ |a<sub>ij</sub> - b<sub>ij</sub>|.
+     * <p>
+     *
+     * <p>
+     * NOTE: If any of the elements are not countable then false is returned.<br>
+     * NOTE: If a tolerance of zero is passed in this is equivalent to calling
+     * {@link #isEquals(org.ejml.data.D1Matrix64F, org.ejml.data.D1Matrix64F)}
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @param b A matrix. Not modified.
+     * @param upper true of upper triangular and false for lower.
+     * @param tol How close to being identical each element needs to be.
+     * @return true if equals and false otherwise.
+     */
+    public static boolean isEqualsTriangle(RealMatrix64F a, RealMatrix64F b, boolean upper, double tol)
+    {
+        if( a.getNumRows() != b.getNumRows() || a.getNumCols() != b.getNumCols() ) {
+            return false;
+        }
+
+        if( upper ) {
+            for( int i = 0; i < a.getNumRows(); i++ ) {
+                for( int j = i; j < a.getNumCols(); j++ ) {
+                    if( Math.abs(a.get(i,j)-b.get(i,j)) > tol )
+                        return false;
+                }
+            }
+        } else {
+            for( int i = 0; i < a.getNumRows(); i++ ) {
+                int end = Math.min(i,a.getNumCols()-1);
+
+                for( int j = 0; j <= end; j++ ) {
+                    if( Math.abs(a.get(i,j)-b.get(i,j)) > tol )
+                        return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * <p>
+     * Checks to see if each element in the two matrices are equal:
+     * a<sub>ij</sub> == b<sub>ij</sub>
+     * <p>
+     *
+     * <p>
+     * NOTE: If any of the elements are NaN then false is returned.  If two corresponding
+     * elements are both positive or negative infinity then they are equal.
+     * </p>
+     * 
+     * @param a A matrix. Not modified.
+     * @param b A matrix. Not modified.
+     * @return true if identical and false otherwise.
+     */
+    public static boolean isEquals( D1Matrix64F a, D1Matrix64F b ) {
+        if( a.numRows != b.numRows || a.numCols != b.numCols ) {
+            return false;
+        }
+
+        final int length = a.getNumElements();
+        for( int i = 0; i < length; i++ ) {
+            if( !(a.get(i) == b.get(i)) ) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * <p>
+     * Checks to see if each corresponding element in the two matrices are
+     * within tolerance of each other or have the some symbolic meaning.  This
+     * can handle NaN and Infinite numbers.
+     * <p>
+     *
+     * <p>
+     * If both elements are countable then the following equality test is used:<br>
+     * |a<sub>ij</sub> - b<sub>ij</sub>| ≤ tol.<br>
+     * Otherwise both numbers must both be Double.NaN, Double.POSITIVE_INFINITY, or
+     * Double.NEGATIVE_INFINITY to be identical.
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @param b A matrix. Not modified.
+     * @param tol Tolerance for equality.
+     * @return true if identical and false otherwise.
+     */
+     public static boolean isIdentical( D1Matrix64F a, D1Matrix64F b , double tol ) {
+        if( a.numRows != b.numRows || a.numCols != b.numCols ) {
+            return false;
+        }
+        if( tol < 0 )
+            throw new IllegalArgumentException("Tolerance must be greater than or equal to zero.");
+
+        final int length = a.getNumElements();
+        for( int i = 0; i < length; i++ ) {
+            double valA = a.get(i);
+            double valB = b.get(i);
+
+            // if either is negative or positive infinity the result will be positive infinity
+            // if either is NaN the result will be NaN
+            double diff = Math.abs(valA-valB);
+
+            // diff = NaN == false
+            // diff = infinity == false
+            if( tol >= diff )
+                continue;
+
+            if( Double.isNaN(valA) ) {
+                return Double.isNaN(valB);
+            } else if( Double.isInfinite(valA) ) {
+                return valA == valB;
+            } else {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * <p>
+     * Checks to see if a matrix is orthogonal or isometric.
+     * </p>
+     *
+     * @param Q The matrix being tested. Not modified.
+     * @param tol Tolerance.
+     * @return True if it passes the test.
+     */
+    public static boolean isOrthogonal( DenseMatrix64F Q , double tol )
+    {
+       if( Q.numRows < Q.numCols ) {
+            throw new IllegalArgumentException("The number of rows must be more than or equal to the number of columns");
+        }
+
+        DenseMatrix64F u[] = CommonOps.columnsToVector(Q, null);
+
+        for( int i = 0; i < u.length; i++ ) {
+            DenseMatrix64F a = u[i];
+
+            for( int j = i+1; j < u.length; j++ ) {
+                double val = VectorVectorMult.innerProd(a,u[j]);
+
+                if( !(Math.abs(val) <= tol))
+                    return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Checks to see if the rows of the provided matrix are linearly independent.
+     *
+     * @param A Matrix whose rows are being tested for linear independence.
+     * @return true if linearly independent and false otherwise.
+     */
+    public static boolean isRowsLinearIndependent( DenseMatrix64F A )
+    {
+        // LU decomposition
+        LUDecomposition<DenseMatrix64F> lu = DecompositionFactory.lu(A.numRows,A.numCols);
+        if( lu.inputModified() )
+            A = A.copy();
+
+        if( !lu.decompose(A))
+            throw new RuntimeException("Decompositon failed?");
+
+        // if they are linearly independent it should not be singular
+        return !lu.isSingular();
+    }
+
+    /**
+     * Checks to see if the provided matrix is within tolerance to an identity matrix.
+     *
+     * @param mat Matrix being examined.  Not modified.
+     * @param tol Tolerance.
+     * @return True if it is within tolerance to an identify matrix.
+     */
+    public static boolean isIdentity( DenseMatrix64F mat , double tol )
+    {
+        // see if the result is an identity matrix
+        int index = 0;
+        for( int i = 0; i < mat.numRows; i++ ) {
+            for( int j = 0; j < mat.numCols; j++ ) {
+                if( i == j ) {
+                    if( !(Math.abs(mat.get(index++)-1) <= tol) )
+                        return false;
+                } else {
+                    if( !(Math.abs(mat.get(index++)) <= tol) )
+                        return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Checks to see if every value in the matrix is the specified value.
+     *
+     * @param mat The matrix being tested.  Not modified.
+     * @param val Checks to see if every element in the matrix has this value.
+     * @param tol True if all the elements are within this tolerance.
+     * @return true if the test passes.
+     */
+    public static boolean isConstantVal( DenseMatrix64F mat , double val , double tol )
+    {
+        // see if the result is an identity matrix
+        int index = 0;
+        for( int i = 0; i < mat.numRows; i++ ) {
+            for( int j = 0; j < mat.numCols; j++ ) {
+                if( !(Math.abs(mat.get(index++)-val) <= tol) )
+                    return false;
+
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Checks to see if all the diagonal elements in the matrix are positive.
+     *
+     * @param a A matrix. Not modified.
+     * @return true if all the  diagonal elements are positive, false otherwise.
+     */
+    public static boolean isDiagonalPositive( DenseMatrix64F a ) {
+        for( int i = 0; i < a.numRows; i++ ) {
+            if( !(a.get(i,i) >= 0) )
+                return false;
+        }
+        return true;
+    }
+
+    // TODO write this
+    public static boolean isFullRank( DenseMatrix64F a ) {
+        throw new RuntimeException("Implement");
+    }
+
+    /**
+     * <p>
+     * Checks to see if the two matrices are the negative of each other:<br>
+     * <br>
+     * a<sub>ij</sub> = -b<sub>ij</sub>
+     * </p>
+     *
+     * @param a First matrix.  Not modified.
+     * @param b Second matrix.  Not modified.
+     * @param tol Numerical tolerance.
+     * @return True if they are the negative of each other within tolerance.
+     */
+    public static boolean isNegative(D1Matrix64F a, D1Matrix64F b, double tol) {
+        if( a.numRows != b.numRows || a.numCols != b.numCols )
+            throw new IllegalArgumentException("Matrix dimensions must match");
+
+        int length = a.getNumElements();
+
+        for( int i = 0; i < length; i++ ) {
+            if( !(Math.abs(a.get(i)+b.get(i)) <= tol) )
+                return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * <p>
+     * Checks to see if a matrix is upper triangular or Hessenberg. A Hessenberg matrix of degree N
+     * has the following property:<br>
+     * <br>
+     * a<sub>ij</sub> ≤ 0 for all i < j+N<br>
+     * <br>
+     * A triangular matrix is a Hessenberg matrix of degree 0.
+     * </p>
+     * @param A Matrix being tested.  Not modified.
+     * @param hessenberg The degree of being hessenberg.
+     * @param tol How close to zero the lower left elements need to be.
+     * @return If it is an upper triangular/hessenberg matrix or not.
+     */
+    public static boolean isUpperTriangle(DenseMatrix64F A , int hessenberg , double tol ) {
+        if( A.numRows != A.numCols )
+            return false;
+
+        for( int i = hessenberg+1; i < A.numRows; i++ ) {
+            for( int j = 0; j < i-hessenberg; j++ ) {
+                if( !(Math.abs(A.get(i,j)) <= tol) ) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Computes the rank of a matrix using a default tolerance.
+     *
+     * @param A Matrix whose rank is to be calculated.  Not modified.
+     * @return The matrix's rank.
+     */
+    public static int rank( DenseMatrix64F A ) {
+        return rank(A, UtilEjml.EPS*100);
+    }
+
+    /**
+     * Computes the rank of a matrix using the specified tolerance.
+     *
+     * @param A Matrix whose rank is to be calculated.  Not modified.
+     * @param threshold The numerical threshold used to determine a singular value.
+     * @return The matrix's rank.
+     */
+    public static int rank( DenseMatrix64F A , double threshold ) {
+        SingularValueDecomposition<DenseMatrix64F> svd = DecompositionFactory.svd(A.numRows,A.numCols,false,false,true);
+
+        if( svd.inputModified() )
+            A = A.copy();
+
+        if( !svd.decompose(A) )
+            throw new RuntimeException("Decomposition failed");
+
+        return SingularOps.rank(svd, threshold);
+    }
+
+    /**
+     * Computes the nullity of a matrix using the default tolerance. 
+     *
+     * @param A Matrix whose rank is to be calculated.  Not modified.
+     * @return The matrix's nullity.
+     */
+    public static int nullity( DenseMatrix64F A ) {
+        return nullity(A, UtilEjml.EPS*100);
+    }
+
+    /**
+     * Computes the nullity of a matrix using the specified tolerance.
+     *
+     * @param A Matrix whose rank is to be calculated.  Not modified.
+     * @param threshold The numerical threshold used to determine a singular value.
+     * @return The matrix's nullity.
+     */
+    public static int nullity( DenseMatrix64F A , double threshold ) {
+        SingularValueDecomposition<DenseMatrix64F> svd = DecompositionFactory.svd(A.numRows,A.numCols,false,false,true);
+
+        if( svd.inputModified() )
+            A = A.copy();
+
+        if( !svd.decompose(A) )
+            throw new RuntimeException("Decomposition failed");
+
+        return SingularOps.nullity(svd,threshold);
+    }
+}
diff --git a/main/dense64/src/org/ejml/ops/MatrixVisualization.java b/main/dense64/src/org/ejml/ops/MatrixVisualization.java
new file mode 100644
index 0000000..c7dc67d
--- /dev/null
+++ b/main/dense64/src/org/ejml/ops/MatrixVisualization.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.data.D1Matrix64F;
+
+import javax.swing.*;
+import java.awt.*;
+
+
+/**
+ * <p>
+ * Functions for visualizing matrices in a GUI matrices.
+ * </p>
+ *
+ * <p>
+ * NOTE: In some embedded applications there is no GUI or AWT is not supported (like in Android) so excluding
+ * this class is necessary.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class MatrixVisualization {
+    /**
+     * Creates a window visually showing the matrix's state.  Block means an element is zero.
+     * Red positive and blue negative.  More intense the color larger the element's absolute value
+     * is.
+     *
+     * @param A A matrix.
+     * @param title Name of the window.
+     */
+    public static void show( D1Matrix64F A , String title ) {
+        JFrame frame = new JFrame(title);
+
+        int width = 300;
+        int height = 300;
+
+        if( A.numRows > A.numCols) {
+            width = width*A.numCols/A.numRows;
+        } else {
+            height = height*A.numRows/A.numCols;
+        }
+
+        MatrixComponent panel = new MatrixComponent(width,height);
+        panel.setMatrix(A);
+
+        frame.add(panel, BorderLayout.CENTER);
+
+        frame.pack();
+        frame.setVisible(true);
+
+    }
+}
diff --git a/main/dense64/src/org/ejml/ops/NormOps.java b/main/dense64/src/org/ejml/ops/NormOps.java
new file mode 100644
index 0000000..acabccb
--- /dev/null
+++ b/main/dense64/src/org/ejml/ops/NormOps.java
@@ -0,0 +1,462 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.UtilEjml;
+import org.ejml.data.D1Matrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.data.RowD1Matrix64F;
+import org.ejml.factory.DecompositionFactory;
+import org.ejml.interfaces.decomposition.SingularValueDecomposition;
+
+
+/**
+ * <p>
+ * Norms are a measure of the size of a vector or a matrix.  One typical application is in error analysis.
+ * </p>
+ * <p>
+ * Vector norms have the following properties:
+ * <ol>
+ * <li>||x|| > 0 if x ≠ 0 and ||0|| = 0</li>
+ * <li>||αx|| = |α| ||x||</li>
+ * <li>||x+y|| ≤ ||x|| + ||y||</li>
+ * </ol>
+ * </p>
+ *
+ * <p>
+ * Matrix norms have the following properties:
+ * <ol>
+ * <li>||A|| > 0 if A ≠ 0 where A ∈ ℜ <sup>m × n</sup></li>
+ * <li> || α A || = |α| ||A|| where A ∈ ℜ <sup>m × n</sup></li>
+ * <li>||A+B|| ≤ ||A|| + ||B|| where A and B are ∈ ℜ <sup>m × n</sup></li>
+ * <li>||AB|| ≤ ||A|| ||B|| where A and B are ∈ ℜ <sup>m × m</sup></li>
+ * </ol>
+ * Note that the last item in the list only applies to square matrices.
+ * </p>
+ *
+ * <p>
+ * Matrix norms can be induced from vector norms as is shown below:<br>
+ * <br>
+ * ||A||<sub>M</sub> = max<sub>x≠0</sub>||Ax||<sub>v</sub>/||x||<sub>v</sub><br>
+ * <br>
+ * where ||.||<sub>M</sub> is the induced matrix norm for the vector norm ||.||<sub>v</sub>.
+ * </p>
+ *
+ * <p>
+ * By default implementations that try to mitigate overflow/underflow are used.  If the word fast is
+ * found before a function's name that means it does not mitigate those issues, but runs a bit faster.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class NormOps {
+
+    /**
+     * Normalizes the matrix such that the Frobenius norm is equal to one.
+     *
+     * @param A The matrix that is to be normalized.
+     */
+    public static void normalizeF( DenseMatrix64F A ) {
+        double val = normF(A);
+
+        if( val == 0 )
+            return;
+
+        int size = A.getNumElements();
+
+        for( int i = 0; i < size; i++) {
+            A.div(i , val);
+        }
+    }
+
+    /**
+     * <p>
+     * The condition number of a matrix is used to measure the sensitivity of the linear
+     * system <b>Ax=b</b>.  A value near one indicates that it is a well conditioned matrix.<br>
+     * <br>
+     * κ<sub>p</sub> = ||A||<sub>p</sub>||A<sup>-1</sup>||<sub>p</sub>
+     * </p>
+     * <p>
+     * If the matrix is not square then the condition of either A<sup>T</sup>A or AA<sup>T</sup> is computed. 
+     * <p>
+     * @param A The matrix.
+     * @param p p-norm
+     * @return The condition number.
+     */
+    public static double conditionP( DenseMatrix64F A , double p )
+    {
+        if( p == 2 ) {
+            return conditionP2(A);
+        } else if( A.numRows == A.numCols ){
+            // square matrices are the typical case
+
+            DenseMatrix64F A_inv = new DenseMatrix64F(A.numRows,A.numCols);
+
+            if( !CommonOps.invert(A,A_inv) )
+                throw new IllegalArgumentException("A can't be inverted.");
+
+            return normP(A,p) * normP(A_inv,p);
+        } else  {
+            DenseMatrix64F pinv = new DenseMatrix64F(A.numCols,A.numRows);
+            CommonOps.pinv(A,pinv);
+
+            return normP(A,p) * normP(pinv,p);
+        }
+    }
+
+    /**
+     * <p>
+     * The condition p = 2 number of a matrix is used to measure the sensitivity of the linear
+     * system <b>Ax=b</b>.  A value near one indicates that it is a well conditioned matrix.<br>
+     * <br>
+     * κ<sub>2</sub> = ||A||<sub>2</sub>||A<sup>-1</sup>||<sub>2</sub>
+     * </p>
+     * <p>
+     * This is also known as the spectral condition number.
+     * </p>
+     *
+     * @param A The matrix.
+     * @return The condition number.
+     */
+    public static double conditionP2( DenseMatrix64F A )
+    {
+        SingularValueDecomposition<DenseMatrix64F> svd = DecompositionFactory.svd(A.numRows,A.numCols,false,false,true);
+
+        svd.decompose(A);
+
+        double[] singularValues = svd.getSingularValues();
+
+        int n = SingularOps.rank(svd,1e-12);
+
+        if( n == 0 ) return 0;
+
+        double smallest = Double.MAX_VALUE;
+        double largest = Double.MIN_VALUE;
+
+        for( double s : singularValues ) {
+            if( s < smallest )
+                smallest = s;
+            if( s > largest )
+                largest = s;
+        }
+
+        return largest/smallest;
+    }
+
+    /**
+     * <p>
+     * This implementation of the Frobenius norm is a straight forward implementation and can
+     * be susceptible for overflow/underflow issues.  A more resilient implementation is
+     * {@link #normF}.
+     * </p>
+     *
+     * @param a The matrix whose norm is computed.  Not modified.
+     */
+    public static double fastNormF( D1Matrix64F a ) {
+        double total = 0;
+
+        int size = a.getNumElements();
+
+        for( int i = 0; i < size; i++ ) {
+            double val = a.get(i);
+            total += val*val;
+        }
+
+        return Math.sqrt(total);
+    }
+
+    /**
+     * <p>
+     * Computes the Frobenius matrix norm:<br>
+     * <br>
+     * normF = Sqrt{  ∑<sub>i=1:m</sub> ∑<sub>j=1:n</sub> { a<sub>ij</sub><sup>2</sup>}   }
+     * </p>
+     * <p>
+     * This is equivalent to the element wise p=2 norm.  See {@link #fastNormF} for another implementation
+     * that is faster, but more prone to underflow/overflow errors.
+     * </p>
+     *
+     * @param a The matrix whose norm is computed.  Not modified.
+     * @return The norm's value.
+     */
+    public static double normF( D1Matrix64F a ) {
+        double total = 0;
+
+        double scale = CommonOps.elementMaxAbs(a);
+
+        if( scale == 0.0 )
+            return 0.0;
+
+        final int size = a.getNumElements();
+
+        for( int i = 0; i < size; i++ ) {
+            double val = a.get(i)/scale;
+            total += val*val;
+        }
+
+        return scale*Math.sqrt(total);
+    }
+
+    /**
+     * <p>
+     * Element wise p-norm:<br>
+     * <br>
+     * norm = {∑<sub>i=1:m</sub> ∑<sub>j=1:n</sub> { |a<sub>ij</sub>|<sup>p</sup>}}<sup>1/p</sup>
+     * </p>
+     *
+     * <p>
+     * This is not the same as the induced p-norm used on matrices, but is the same as the vector p-norm.
+     * </p>
+     *
+     * @param A Matrix. Not modified.
+     * @param p p value.
+     * @return The norm's value.
+     */
+    public static double elementP( RowD1Matrix64F A , double p ) {
+        if( p == 1 ) {
+            return CommonOps.elementSumAbs(A);
+        } if( p == 2 ) {
+            return normF(A);
+        } else {
+            double max = CommonOps.elementMaxAbs(A);
+
+            if( max == 0.0 )
+                return 0.0;
+
+            double total = 0;
+
+            int size = A.getNumElements();
+
+            for( int i = 0; i < size; i++ ) {
+                double a = A.get(i)/max;
+
+                total += Math.pow(Math.abs(a),p);
+            }
+
+            return max*Math.pow(total,1.0/p);
+        }
+    }
+
+    /**
+     * Same as {@link #elementP} but runs faster by not mitigating overflow/underflow related problems.
+     *
+     * @param A Matrix. Not modified.
+     * @param p p value.
+     * @return The norm's value.
+     */
+    public static double fastElementP( D1Matrix64F A , double p ) {
+        if( p == 2 ) {
+            return fastNormF(A);
+        } else {
+            double total = 0;
+
+            int size = A.getNumElements();
+
+            for( int i = 0; i < size; i++ ) {
+                double a = A.get(i);
+
+                total += Math.pow(Math.abs(a),p);
+            }
+
+            return Math.pow(total,1.0/p);
+        }
+    }
+
+    /**
+     * Computes either the vector p-norm or the induced matrix p-norm depending on A
+     * being a vector or a matrix respectively.
+     *
+     * @param A Vector or matrix whose norm is to be computed.
+     * @param p The p value of the p-norm.
+     * @return The computed norm.
+     */
+    public static double normP( DenseMatrix64F A , double p ) {
+        if( p == 1 ) {
+            return normP1(A);
+        } else if( p == 2 ) {
+            return normP2(A);
+        } else if( Double.isInfinite(p)) {
+            return normPInf(A);
+        }
+        if( MatrixFeatures.isVector(A) ) {
+            return elementP(A,p);
+        } else {
+            throw new IllegalArgumentException("Doesn't support induced norms yet.");
+        }
+    }
+
+    /**
+     * An unsafe but faster version of {@link #normP} that calls routines which are faster
+     * but more prone to overflow/underflow problems.
+     *
+     * @param A Vector or matrix whose norm is to be computed.
+     * @param p The p value of the p-norm.
+     * @return The computed norm.
+     */
+    public static double fastNormP( DenseMatrix64F A , double p ) {
+        if( p == 1 ) {
+            return normP1(A);
+        } else if( p == 2 ) {
+            return fastNormP2(A);
+        } else if( Double.isInfinite(p)) {
+            return normPInf(A);
+        }
+        if( MatrixFeatures.isVector(A) ) {
+            return fastElementP(A,p);
+        } else {
+            throw new IllegalArgumentException("Doesn't support induced norms yet.");
+        }
+    }
+
+    /**
+     * Computes the p=1 norm.  If A is a matrix then the induced norm is computed.
+     *
+     * @param A Matrix or vector.
+     * @return The norm.
+     */
+    public static double normP1( DenseMatrix64F A ) {
+        if( MatrixFeatures.isVector(A)) {
+            return CommonOps.elementSumAbs(A);
+        } else {
+            return inducedP1(A);
+        }
+    }
+
+    /**
+     * Computes the p=2 norm.  If A is a matrix then the induced norm is computed.
+     *
+     * @param A Matrix or vector.
+     * @return The norm.
+     */
+    public static double normP2( DenseMatrix64F A ) {
+        if( MatrixFeatures.isVector(A)) {
+            return normF(A);
+        } else {
+            return inducedP2(A);
+        }
+    }
+
+    /**
+     * Computes the p=2 norm.  If A is a matrix then the induced norm is computed. This
+     * implementation is faster, but more prone to buffer overflow or underflow problems.
+     *
+     * @param A Matrix or vector.
+     * @return The norm.
+     */
+    public static double fastNormP2( DenseMatrix64F A ) {
+        if( MatrixFeatures.isVector(A)) {
+            return fastNormF(A);
+        } else {
+            return inducedP2(A);
+        }
+    }
+
+    /**
+     * Computes the p=∞ norm.  If A is a matrix then the induced norm is computed.
+     *
+     * @param A Matrix or vector.
+     * @return The norm.
+     */
+    public static double normPInf( DenseMatrix64F A ) {
+        if( MatrixFeatures.isVector(A)) {
+            return CommonOps.elementMaxAbs(A);
+        } else {
+            return inducedPInf(A);
+        }
+    }
+
+    /**
+     * <p>
+     * Computes the induced p = 1 matrix norm.<br>
+     * <br>
+     * ||A||<sub>1</sub>= max(j=1 to n; sum(i=1 to m; |a<sub>ij</sub>|))
+     * </p>
+     *
+     * @param A Matrix. Not modified.
+     * @return The norm.
+     */
+    public static double inducedP1( DenseMatrix64F A ) {
+        double max = 0;
+
+        int m = A.numRows;
+        int n = A.numCols;
+
+        for( int j = 0; j < n; j++ ) {
+            double total = 0;
+            for( int i = 0; i < m; i++ ) {
+                total += Math.abs(A.get(i,j));
+            }
+            if( total > max ) {
+                max = total;
+            }
+        }
+
+        return max;
+    }
+
+    /**
+     * <p>
+     * Computes the induced p = 2 matrix norm, which is the largest singular value.
+     * </p>
+     *
+     * @param A Matrix. Not modified.
+     * @return The norm.
+     */
+    public static double inducedP2( DenseMatrix64F A ) {
+        SingularValueDecomposition<DenseMatrix64F> svd = DecompositionFactory.svd(A.numRows,A.numCols,false,false,true);
+
+        if( !svd.decompose(A) )
+            throw new RuntimeException("Decomposition failed");
+
+        double[] singularValues = svd.getSingularValues();
+
+        // the largest singular value is the induced p2 norm
+        return UtilEjml.max(singularValues,0,singularValues.length);
+    }
+
+    /**
+     * <p>
+     * Induced matrix p = infinity norm.<br>
+     * <br>
+     * ||A||<sub>∞</sub> = max(i=1 to m; sum(j=1 to n; |a<sub>ij</sub>|))
+     * </p>
+     *
+     * @param A A matrix.
+     * @return the norm.
+     */
+    public static double inducedPInf( DenseMatrix64F A ) {
+        double max = 0;
+
+        int m = A.numRows;
+        int n = A.numCols;
+
+        for( int i = 0; i < m; i++ ) {
+            double total = 0;
+            for( int j = 0; j < n; j++ ) {
+                total += Math.abs(A.get(i,j));
+            }
+            if( total > max ) {
+                max = total;
+            }
+        }
+
+        return max;
+    }
+
+}
diff --git a/main/dense64/src/org/ejml/ops/RandomMatrices.java b/main/dense64/src/org/ejml/ops/RandomMatrices.java
new file mode 100644
index 0000000..30ee1de
--- /dev/null
+++ b/main/dense64/src/org/ejml/ops/RandomMatrices.java
@@ -0,0 +1,485 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.alg.dense.mult.SubmatrixOps;
+import org.ejml.alg.dense.mult.VectorVectorMult;
+import org.ejml.data.D1Matrix64F;
+import org.ejml.data.DenseMatrix64F;
+
+import java.util.Random;
+
+
+/**
+ * Contains a list of functions for creating random dense real matrices and vectors with different structures.
+ *
+ * @author Peter Abeles
+ */
+public class RandomMatrices {
+
+    /**
+     * <p>
+     * Creates a randomly generated set of orthonormal vectors.  At most it can generate the same
+     * number of vectors as the dimension of the vectors.
+     * </p>
+     *
+     * <p>
+     * This is done by creating random vectors then ensuring that they are orthogonal
+     * to all the ones previously created with reflectors.
+     * </p>
+     *
+     * <p>
+     * NOTE: This employs a brute force O(N<sup>3</sup>) algorithm.
+     * </p>
+     *
+     * @param dimen dimension of the space which the vectors will span.
+     * @param numVectors How many vectors it should generate.
+     * @param rand Used to create random vectors.
+     * @return Array of N random orthogonal vectors of unit length.
+     */
+    // is there a faster algorithm out there? This one is a bit sluggish
+    public static DenseMatrix64F[] createSpan( int dimen, int numVectors , Random rand ) {
+        if( dimen < numVectors )
+            throw new IllegalArgumentException("The number of vectors must be less than or equal to the dimension");
+
+        DenseMatrix64F u[] = new DenseMatrix64F[numVectors];
+
+        u[0] = RandomMatrices.createRandom(dimen,1,-1,1,rand);
+        NormOps.normalizeF(u[0]);
+
+        for( int i = 1; i < numVectors; i++ ) {
+//            System.out.println(" i = "+i);
+            DenseMatrix64F a = new DenseMatrix64F(dimen,1);
+            DenseMatrix64F r=null;
+
+            for( int j = 0; j < i; j++ ) {
+//                System.out.println("j = "+j);
+                if( j == 0 )
+                    r = RandomMatrices.createRandom(dimen,1,-1,1,rand);
+
+                // find a vector that is normal to vector j
+                // u[i] = (1/2)*(r + Q[j]*r)
+                a.set(r);
+                VectorVectorMult.householder(-2.0,u[j],r,a);
+                CommonOps.add(r,a,a);
+                CommonOps.scale(0.5,a);
+
+//                UtilEjml.print(a);
+
+                DenseMatrix64F t = a;
+                a = r;
+                r = t;
+
+                // normalize it so it doesn't get too small
+                double val = NormOps.normF(r);
+                if( val == 0 || Double.isNaN(val) || Double.isInfinite(val))
+                    throw new RuntimeException("Failed sanity check");
+                CommonOps.divide(r,val);
+            }
+
+            u[i] = r;
+        }
+
+        return u;
+    }
+
+    /**
+     * Creates a random vector that is inside the specified span.
+     *
+     * @param span The span the random vector belongs in.
+     * @param rand RNG
+     * @return A random vector within the specified span.
+     */
+    public static DenseMatrix64F createInSpan( DenseMatrix64F[] span , double min , double max , Random rand ) {
+        DenseMatrix64F A = new DenseMatrix64F(span.length,1);
+
+        DenseMatrix64F B = new DenseMatrix64F(span[0].getNumElements(),1);
+
+        for( int i = 0; i < span.length; i++ ) {
+            B.set(span[i]);
+            double val = rand.nextDouble()*(max-min)+min;
+            CommonOps.scale(val,B);
+
+            CommonOps.add(A,B,A);
+
+        }
+
+        return A;
+    }
+
+    /**
+     * <p>
+     * Creates a random orthogonal or isometric matrix, depending on the number of rows and columns.
+     * The number of rows must be more than or equal to the number of columns.
+     * </p>
+     *
+     * @param numRows Number of rows in the generated matrix.
+     * @param numCols Number of columns in the generated matrix.
+     * @param rand Random number generator used to create matrices.
+     * @return A new isometric matrix.
+     */
+    public static DenseMatrix64F createOrthogonal( int numRows , int numCols , Random rand ) {
+        if( numRows < numCols ) {
+            throw new IllegalArgumentException("The number of rows must be more than or equal to the number of columns");
+        }
+
+        DenseMatrix64F u[] = createSpan(numRows,numCols,rand);
+
+        DenseMatrix64F ret = new DenseMatrix64F(numRows,numCols);
+        for( int i = 0; i < numCols; i++ ) {
+            SubmatrixOps.setSubMatrix(u[i],ret,0,0,0,i,numRows,1);
+        }
+
+        return ret;
+    }
+
+    /**
+     * Creates a random diagonal matrix where the diagonal elements are selected from a uniform
+     * distribution that goes from min to max.
+     *
+     * @param N Dimension of the matrix.
+     * @param min Minimum value of a diagonal element.
+     * @param max Maximum value of a diagonal element.
+     * @param rand Random number generator.
+     * @return A random diagonal matrix.
+     */
+    public static DenseMatrix64F createDiagonal( int N , double min , double max , Random rand ) {
+        return createDiagonal(N,N,min,max,rand);
+    }
+
+    /**
+     * Creates a random matrix where all elements are zero but diagonal elements.  Diagonal elements
+     * randomly drawn from a uniform distribution from min to max, inclusive.
+     *
+     * @param numRows Number of rows in the returned matrix..
+     * @param numCols Number of columns in the returned matrix.
+     * @param min Minimum value of a diagonal element.
+     * @param max Maximum value of a diagonal element.
+     * @param rand Random number generator.
+     * @return A random diagonal matrix.
+     */
+    public static DenseMatrix64F createDiagonal( int numRows , int numCols , double min , double max , Random rand ) {
+        if( max < min )
+            throw new IllegalArgumentException("The max must be >= the min");
+
+        DenseMatrix64F ret = new DenseMatrix64F(numRows,numCols);
+
+        int N = Math.min(numRows,numCols);
+
+        double r = max-min;
+
+        for( int i = 0; i < N; i++ ) {
+            ret.set(i,i, rand.nextDouble()*r+min);
+        }
+
+        return ret;
+    }
+
+    /**
+     * <p>
+     * Creates a random matrix which will have the provided singular values.  The length of sv
+     * is assumed to be the rank of the matrix.  This can be useful for testing purposes when one
+     * needs to ensure that a matrix is not singular but randomly generated.
+     * </p>
+     * 
+     * @param numRows Number of rows in generated matrix.
+     * @param numCols NUmber of columns in generated matrix.
+     * @param rand Random number generator.
+     * @param sv Singular values of the matrix.
+     * @return A new matrix with the specified singular values.
+     */
+    public static DenseMatrix64F createSingularValues(int numRows, int numCols,
+                                                      Random rand, double ...sv) {
+        DenseMatrix64F U = RandomMatrices.createOrthogonal(numRows,numRows,rand);
+        DenseMatrix64F V = RandomMatrices.createOrthogonal(numCols,numCols,rand);
+
+        DenseMatrix64F S = new DenseMatrix64F(numRows,numCols);
+
+        int min = Math.min(numRows,numCols);
+        min = Math.min(min,sv.length);
+        
+        for( int i = 0; i < min; i++ ) {
+            S.set(i,i,sv[i]);
+        }
+
+        DenseMatrix64F tmp = new DenseMatrix64F(numRows,numCols);
+        CommonOps.mult(U,S,tmp);
+        CommonOps.multTransB(tmp,V,S);
+
+        return S;
+    }
+
+    /**
+     * Creates a new random symmetric matrix that will have the specified real eigenvalues.
+     *
+     * @param num Dimension of the resulting matrix.
+     * @param rand Random number generator.
+     * @param eigenvalues Set of real eigenvalues that the matrix will have.
+     * @return A random matrix with the specified eigenvalues.
+     */
+    public static DenseMatrix64F createEigenvaluesSymm( int num,  Random rand , double ...eigenvalues ) {
+        DenseMatrix64F V = RandomMatrices.createOrthogonal(num,num,rand);
+        DenseMatrix64F D = CommonOps.diag(eigenvalues);
+
+        DenseMatrix64F temp = new DenseMatrix64F(num,num);
+
+        CommonOps.mult(V,D,temp);
+        CommonOps.multTransB(temp,V,D);
+
+        return D;
+    }
+
+    /**
+     * Returns a matrix where all the elements are selected independently from
+     * a uniform distribution between 0 and 1 inclusive.
+     *
+     * @param numRow Number of rows in the new matrix.
+     * @param numCol Number of columns in the new matrix.
+     * @param rand Random number generator used to fill the matrix.
+     * @return The randomly generated matrix.
+     */
+    public static DenseMatrix64F createRandom( int numRow , int numCol , Random rand ) {
+        DenseMatrix64F mat = new DenseMatrix64F(numRow,numCol);
+
+        setRandom(mat,0,1,rand);
+
+        return mat;
+    }
+
+    /**
+     * <p>
+     * Adds random values to each element in the matrix from an uniform distribution.<br>
+     * <br>
+     * a<sub>ij</sub> = a<sub>ij</sub> + U(min,max)<br>
+     * </p>
+     *
+     * @param A The matrix who is to be randomized. Modified
+     * @param min The minimum value each element can be.
+     * @param max The maximum value each element can be..
+     * @param rand Random number generator used to fill the matrix.
+     */
+    public static void addRandom( DenseMatrix64F A , double min , double max , Random rand ) {
+        double d[] = A.getData();
+        int size = A.getNumElements();
+
+        double r = max-min;
+
+        for( int i = 0; i < size; i++ ) {
+            d[i] += r*rand.nextDouble()+min;
+        }
+    }
+
+    /**
+     * <p>
+     * Returns a matrix where all the elements are selected independently from
+     * a uniform distribution between 'min' and 'max' inclusive.
+     * </p>
+     *
+     * @param numRow Number of rows in the new matrix.
+     * @param numCol Number of columns in the new matrix.
+     * @param min The minimum value each element can be.
+     * @param max The maximum value each element can be.
+     * @param rand Random number generator used to fill the matrix.
+     * @return The randomly generated matrix.
+     */
+    public static DenseMatrix64F createRandom( int numRow , int numCol , double min , double max , Random rand ) {
+        DenseMatrix64F mat = new DenseMatrix64F(numRow,numCol);
+
+        setRandom(mat,min,max,rand);
+
+        return mat;
+    }
+
+    /**
+     * <p>
+     * Sets each element in the matrix to a value drawn from an uniform distribution from 0 to 1 inclusive.
+     * </p>
+     *
+     * @param mat The matrix who is to be randomized. Modified.
+     * @param rand Random number generator used to fill the matrix.
+     */
+    public static void setRandom( DenseMatrix64F mat , Random rand )
+    {
+        setRandom(mat,0,1,rand);
+    }
+
+    /**
+     * <p>
+     * Sets each element in the matrix to a value drawn from an uniform distribution from 'min' to 'max' inclusive.
+     * </p>
+     *
+     * @param min The minimum value each element can be.
+     * @param max The maximum value each element can be.
+     * @param mat The matrix who is to be randomized. Modified.
+     * @param rand Random number generator used to fill the matrix.
+     */
+    public static void setRandom( D1Matrix64F mat , double min , double max , Random rand )
+    {
+        double d[] = mat.getData();
+        int size = mat.getNumElements();
+
+        double r = max-min;
+
+        for( int i = 0; i < size; i++ ) {
+            d[i] = r*rand.nextDouble()+min;
+        }
+    }
+
+    /**
+     * <p>
+     * Sets each element in the matrix to a value drawn from an Gaussian distribution with the specified mean and
+     * standard deviation
+     * </p>
+     *
+     *
+     * @param numRow Number of rows in the new matrix.
+     * @param numCol Number of columns in the new matrix.
+     * @param mean Mean value in the distribution
+     * @param stdev Standard deviation in the distribution
+     * @param rand Random number generator used to fill the matrix.
+     */
+    public static DenseMatrix64F createGaussian( int numRow , int numCol , double mean , double stdev , Random rand )
+    {
+        DenseMatrix64F m = new DenseMatrix64F(numRow,numCol);
+        setGaussian(m,mean,stdev,rand);
+        return m;
+    }
+
+    /**
+     * <p>
+     * Sets each element in the matrix to a value drawn from an Gaussian distribution with the specified mean and
+     * standard deviation
+     * </p>
+     *
+     * @param mat The matrix who is to be randomized. Modified.
+     * @param mean Mean value in the distribution
+     * @param stdev Standard deviation in the distribution
+     * @param rand Random number generator used to fill the matrix.
+     */
+    public static void setGaussian( D1Matrix64F mat , double mean , double stdev , Random rand )
+    {
+        double d[] = mat.getData();
+        int size = mat.getNumElements();
+
+        for( int i = 0; i < size; i++ ) {
+            d[i] = mean + stdev*rand.nextGaussian();
+        }
+    }
+
+    /**
+     * Creates a random symmetric positive definite matrix.
+     *
+     * @param width The width of the square matrix it returns.
+     * @param rand Random number generator used to make the matrix.
+     * @return The random symmetric  positive definite matrix.
+     */
+    public static DenseMatrix64F createSymmPosDef(int width, Random rand) {
+        // This is not formally proven to work.  It just seems to work.
+        DenseMatrix64F a = new DenseMatrix64F(width,1);
+        DenseMatrix64F b = new DenseMatrix64F(width,width);
+
+        for( int i = 0; i < width; i++ ) {
+            a.set(i,0,rand.nextDouble());
+        }
+
+        CommonOps.multTransB(a,a,b);
+
+        for( int i = 0; i < width; i++ ) {
+            b.add(i,i,1);
+        }
+
+        return b;
+    }
+
+    /**
+     * Creates a random symmetric matrix whose values are selected from an uniform distribution
+     * from min to max, inclusive.
+     *
+     * @param length Width and height of the matrix.
+     * @param min Minimum value an element can have.
+     * @param max Maximum value an element can have.
+     * @param rand Random number generator.
+     * @return A symmetric matrix.
+     */
+    public static DenseMatrix64F createSymmetric(int length, double min, double max, Random rand) {
+        DenseMatrix64F A = new DenseMatrix64F(length,length);
+
+        createSymmetric(A,min,max,rand);
+
+        return A;
+    }
+
+    /**
+     * Sets the provided square matrix to be a random symmetric matrix whose values are selected from an uniform distribution
+     * from min to max, inclusive.
+     *
+     * @param A The matrix that is to be modified.  Must be square.  Modified.
+     * @param min Minimum value an element can have.
+     * @param max Maximum value an element can have.
+     * @param rand Random number generator.
+     */
+    public static void createSymmetric(DenseMatrix64F A, double min, double max, Random rand) {
+        if( A.numRows != A.numCols )
+            throw new IllegalArgumentException("A must be a square matrix");
+
+        double range = max-min;
+
+        int length = A.numRows;
+
+        for( int i = 0; i < length; i++ ) {
+            for( int j = i; j < length; j++ ) {
+                double val = rand.nextDouble()*range + min;
+                A.set(i,j,val);
+                A.set(j,i,val);
+            }
+        }
+    }
+
+    /**
+     * Creates an upper triangular matrix whose values are selected from a uniform distribution.  If hessenberg
+     * is greater than zero then a hessenberg matrix of the specified degree is created instead.
+     *
+     * @param dimen Number of rows and columns in the matrix..
+     * @param hessenberg 0 for triangular matrix and > 0 for hessenberg matrix.
+     * @param min minimum value an element can be.
+     * @param max maximum value an element can be.
+     * @param rand random number generator used.
+     * @return The randomly generated matrix.
+     */
+    public static DenseMatrix64F createUpperTriangle( int dimen , int hessenberg , double min , double max , Random rand )
+    {
+        if( hessenberg < 0 )
+            throw new RuntimeException("hessenberg must be more than or equal to 0");
+
+        double range = max-min;
+
+        DenseMatrix64F A = new DenseMatrix64F(dimen,dimen);
+
+        for( int i = 0; i < dimen; i++ ) {
+            int start = i <= hessenberg ? 0 : i-hessenberg;
+
+            for( int j = start; j < dimen; j++ ) {
+                A.set(i,j, rand.nextDouble()*range+min);
+            }
+
+        }
+
+        return A;
+    }
+}
diff --git a/main/dense64/src/org/ejml/ops/SingularOps.java b/main/dense64/src/org/ejml/ops/SingularOps.java
new file mode 100644
index 0000000..92f6b2e
--- /dev/null
+++ b/main/dense64/src/org/ejml/ops/SingularOps.java
@@ -0,0 +1,429 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.UtilEjml;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.SingularValueDecomposition;
+
+
+/**
+ * Operations related to singular value decomposition.
+ *
+ * @author Peter Abeles
+ */
+public class SingularOps {
+
+    /**
+     * <p>
+     * Adjusts the matrices so that the singular values are in descending order.
+     * </p>
+     *
+     * <p>
+     * In most implementations of SVD the singular values are automatically arranged in in descending
+     * order.  In EJML this is not the case since it is often not needed and some computations can
+     * be saved by not doing that.
+     * </p>
+     *
+     * @param U Matrix. Modified.
+     * @param tranU is U transposed or not.
+     * @param W Diagonal matrix with singular values. Modified.
+     * @param V Matrix. Modified.
+     * @param tranV is V transposed or not.
+     */
+    // TODO the number of copies can probably be reduced here
+    public static void descendingOrder( DenseMatrix64F U , boolean tranU ,
+                                        DenseMatrix64F W ,
+                                        DenseMatrix64F V , boolean tranV )
+    {
+        int numSingular = Math.min(W.numRows,W.numCols);
+
+        checkSvdMatrixSize(U, tranU, W, V, tranV);
+
+        for( int i = 0; i < numSingular; i++ ) {
+            double bigValue=-1;
+            int bigIndex=-1;
+
+            // find the smallest singular value in the submatrix
+            for( int j = i; j < numSingular; j++ ) {
+                double v = W.get(j,j);
+
+                if( v > bigValue ) {
+                    bigValue = v;
+                    bigIndex = j;
+                }
+            }
+
+            // only swap if the current index is not the smallest
+            if( bigIndex == i)
+                continue;
+
+            if( bigIndex == -1 ) {
+                // there is at least one uncountable singular value.  just stop here
+                break;
+            }
+
+            double tmp = W.get(i,i);
+            W.set(i,i,bigValue);
+            W.set(bigIndex,bigIndex,tmp);
+
+            if( V != null ) {
+                swapRowOrCol(V, tranV, i, bigIndex);
+            }
+
+            if( U != null ) {
+                swapRowOrCol(U, tranU, i, bigIndex);
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Similar to {@link #descendingOrder(org.ejml.data.DenseMatrix64F, boolean, org.ejml.data.DenseMatrix64F, org.ejml.data.DenseMatrix64F, boolean)}
+     * but takes in an array of singular values instead.
+     * </p>
+     *
+     * @param U Matrix. Modified.
+     * @param tranU is U transposed or not.
+     * @param singularValues Array of singular values. Modified.
+     * @param numSingularValues Number of elements in singularValues array
+     * @param V Matrix. Modified.
+     * @param tranV is V transposed or not.
+     */
+    public static void descendingOrder( DenseMatrix64F U , boolean tranU ,
+                                        double singularValues[] ,
+                                        int numSingularValues ,
+                                        DenseMatrix64F V , boolean tranV )
+    {
+//        checkSvdMatrixSize(U, tranU, W, V, tranV);
+
+        for( int i = 0; i < numSingularValues; i++ ) {
+            double bigValue=-1;
+            int bigIndex=-1;
+
+            // find the smallest singular value in the submatrix
+            for( int j = i; j < numSingularValues; j++ ) {
+                double v = singularValues[j];
+
+                if( v > bigValue ) {
+                    bigValue = v;
+                    bigIndex = j;
+                }
+            }
+
+            // only swap if the current index is not the smallest
+            if( bigIndex == i)
+                continue;
+
+            if( bigIndex == -1 ) {
+                // there is at least one uncountable singular value.  just stop here
+                break;
+            }
+
+            double tmp = singularValues[i];
+            singularValues[i] = bigValue;
+            singularValues[bigIndex] = tmp;
+
+            if( V != null ) {
+                swapRowOrCol(V, tranV, i, bigIndex);
+            }
+
+            if( U != null ) {
+                swapRowOrCol(U, tranU, i, bigIndex);
+            }
+        }
+    }
+
+    /**
+     * Checks to see if all the provided matrices are the expected size for an SVD.  If an error is encountered
+     * then an exception is thrown.  This automatically handles compact and non-compact formats
+     */
+    public static void checkSvdMatrixSize(DenseMatrix64F U, boolean tranU, DenseMatrix64F W, DenseMatrix64F V, boolean tranV ) {
+        int numSingular = Math.min(W.numRows,W.numCols);
+        boolean compact = W.numRows == W.numCols;
+
+        if( compact ) {
+            if( U != null ) {
+                if( tranU && U.numRows != numSingular )
+                    throw new IllegalArgumentException("Unexpected size of matrix U");
+                else if( !tranU && U.numCols != numSingular )
+                    throw new IllegalArgumentException("Unexpected size of matrix U");
+            }
+
+            if( V != null ) {
+            if( tranV && V.numRows != numSingular )
+                throw new IllegalArgumentException("Unexpected size of matrix V");
+            else if( !tranV && V.numCols != numSingular )
+                throw new IllegalArgumentException("Unexpected size of matrix V");
+            }
+        } else {
+            if( U != null && U.numRows != U.numCols )
+                throw new IllegalArgumentException("Unexpected size of matrix U");
+            if( V != null && V.numRows != V.numCols )
+                throw new IllegalArgumentException("Unexpected size of matrix V");
+            if( U != null && U.numRows != W.numRows )
+                throw new IllegalArgumentException("Unexpected size of W");
+            if( V != null && V.numRows != W.numCols )
+                throw new IllegalArgumentException("Unexpected size of W");
+        }
+    }
+
+    private static void swapRowOrCol(DenseMatrix64F M, boolean tran, int i, int bigIndex) {
+        double tmp;
+        if( tran ) {
+            // swap the rows
+            for( int col = 0; col < M.numCols; col++ ) {
+                tmp = M.get(i,col);
+                M.set(i,col,M.get(bigIndex,col));
+                M.set(bigIndex,col,tmp);
+            }
+        } else {
+            // swap the columns
+            for( int row = 0; row < M.numRows; row++ ) {
+                tmp = M.get(row,i);
+                M.set(row,i,M.get(row,bigIndex));
+                M.set(row,bigIndex,tmp);
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Returns the null-space from the singular value decomposition. The null space is a set of non-zero vectors that
+     * when multiplied by the original matrix return zero.
+     * </p>
+     *
+     * <p>
+     * The null space is found by extracting the columns in V that are associated singular values less than
+     * or equal to the threshold. In some situations a non-compact SVD is required.
+     * </p>
+     *
+     * @param svd A precomputed decomposition.  Not modified.
+     * @param nullSpace Storage for null space.  Will be reshaped as needed.  Modified.
+     * @param tol Threshold for selecting singular values.  Try UtilEjml.EPS.
+     * @return The null space.
+     */
+    public static DenseMatrix64F nullSpace( SingularValueDecomposition<DenseMatrix64F> svd ,
+                                            DenseMatrix64F nullSpace , double tol )
+    {
+        int N = svd.numberOfSingularValues();
+        double s[] = svd.getSingularValues();
+
+        DenseMatrix64F V = svd.getV(null,true);
+
+        if( V.numRows != svd.numCols() ) {
+            throw new IllegalArgumentException("Can't compute the null space using a compact SVD for a matrix of this size.");
+        }
+
+        // first determine the size of the null space
+        int numVectors = svd.numCols()-N;
+
+        for( int i = 0; i < N; i++ ) {
+            if( s[i] <= tol ) {
+                numVectors++;
+            }
+        }
+
+        // declare output data
+        if( nullSpace == null ) {
+            nullSpace = new DenseMatrix64F(numVectors,svd.numCols());
+        } else {
+            nullSpace.reshape(numVectors,svd.numCols());
+        }
+
+        // now extract the vectors
+        int count = 0;
+        for( int i = 0; i < N; i++ ) {
+            if( s[i] <= tol ) {
+                CommonOps.extract(V, i,i+1,0, V.numCols,nullSpace,count++,0);
+            }
+        }
+        for( int i = N; i < svd.numCols(); i++ ) {
+            CommonOps.extract(V, i,i+1,0, V.numCols,nullSpace,count++,0);
+        }
+
+        CommonOps.transpose(nullSpace);
+
+        return nullSpace;
+    }
+
+    /**
+     * <p>
+     * The vector associated will the smallest singular value is returned as the null space
+     * of the decomposed system.  A right null space is returned if 'isRight' is set to true,
+     * and a left null space if false.
+     * </p>
+     *
+     * @param svd A precomputed decomposition.  Not modified.
+     * @param isRight true for right null space and false for left null space.  Right is more commonly used.
+     * @param nullVector Optional storage for a vector for the null space.  Modified.
+     * @return Vector in V associated with smallest singular value..
+     */
+    public static DenseMatrix64F nullVector( SingularValueDecomposition<DenseMatrix64F> svd ,
+                                             boolean isRight ,
+                                             DenseMatrix64F nullVector )
+    {
+        int N = svd.numberOfSingularValues();
+        double s[] = svd.getSingularValues();
+
+        DenseMatrix64F A = isRight ? svd.getV(null,true) : svd.getU(null,false);
+
+        if( isRight ) {
+            if( A.numRows != svd.numCols() ) {
+                throw new IllegalArgumentException("Can't compute the null space using a compact SVD for a matrix of this size.");
+            }
+
+            if( nullVector == null ) {
+                nullVector = new DenseMatrix64F(svd.numCols(),1);
+            }
+        } else {
+            if( A.numCols != svd.numRows() ) {
+                throw new IllegalArgumentException("Can't compute the null space using a compact SVD for a matrix of this size.");
+            }
+
+            if( nullVector == null ) {
+                nullVector = new DenseMatrix64F(svd.numRows(),1);
+            }
+        }
+
+        int smallestIndex = -1;
+
+        if( isRight && svd.numCols() > svd.numRows() )
+            smallestIndex = svd.numCols()-1;
+        else if( !isRight && svd.numCols() < svd.numRows() )
+            smallestIndex = svd.numRows()-1;
+        else {
+            // find the smallest singular value
+            double smallestValue = Double.MAX_VALUE;
+
+            for( int i = 0; i < N; i++ ) {
+                if( s[i] < smallestValue ) {
+                    smallestValue = s[i];
+                    smallestIndex = i;
+                }
+            }
+        }
+
+        // extract the null space
+        if( isRight )
+            SpecializedOps.subvector(A,smallestIndex,0,A.numRows,true,0,nullVector);
+        else
+            SpecializedOps.subvector(A,0,smallestIndex,A.numRows,false,0,nullVector);
+
+        return nullVector;
+    }
+
+    /**
+     * Returns a reasonable threshold for singular values.<br><br>
+     *
+     * tol = max (size (A)) * largest sigma * eps;
+     *
+     * @param svd A precomputed decomposition.  Not modified.
+     * @return threshold for singular values
+     */
+    public static double singularThreshold( SingularValueDecomposition svd ) {
+        double largest = 0;
+        double w[]= svd.getSingularValues();
+
+        int N = svd.numberOfSingularValues();
+
+        for( int j = 0; j < N; j++ ) {
+            if( w[j] > largest)
+                largest = w[j];
+        }
+
+        int M = Math.max(svd.numCols(),svd.numRows());
+        return M*largest* UtilEjml.EPS;
+    }
+
+    /**
+     * Extracts the rank of a matrix using a preexisting decomposition and default threshold.
+     *
+     * @see #singularThreshold(org.ejml.interfaces.decomposition.SingularValueDecomposition)
+     *
+     * @param svd A precomputed decomposition.  Not modified.
+     * @return The rank of the decomposed matrix.
+     */
+    public static int rank( SingularValueDecomposition svd ) {
+        double threshold = singularThreshold(svd);
+        return rank(svd,threshold);
+    }
+
+    /**
+     * Extracts the rank of a matrix using a preexisting decomposition.
+     *
+     * @see #singularThreshold(org.ejml.interfaces.decomposition.SingularValueDecomposition)
+     *
+     * @param svd A precomputed decomposition.  Not modified.
+     * @param threshold Tolerance used to determine of a singular value is singular.
+     * @return The rank of the decomposed matrix.
+     */
+    public static int rank( SingularValueDecomposition svd , double threshold ) {
+        int numRank=0;
+
+        double w[]= svd.getSingularValues();
+
+        int N = svd.numberOfSingularValues();
+
+        for( int j = 0; j < N; j++ ) {
+            if( w[j] > threshold)
+                numRank++;
+        }
+
+        return numRank;
+    }
+
+    /**
+     * Extracts the nullity of a matrix using a preexisting decomposition and default threshold.
+     *
+     * @see #singularThreshold(org.ejml.interfaces.decomposition.SingularValueDecomposition)
+     *
+     * @param svd A precomputed decomposition.  Not modified.
+     * @return The nullity of the decomposed matrix.
+     */
+    public static int nullity( SingularValueDecomposition svd  ) {
+        double threshold = singularThreshold(svd);
+        return nullity(svd, threshold);
+    }
+
+    /**
+     * Extracts the nullity of a matrix using a preexisting decomposition.
+     *
+     * @see #singularThreshold(org.ejml.interfaces.decomposition.SingularValueDecomposition)
+     *
+     * @param svd A precomputed decomposition.  Not modified.
+     * @param threshold Tolerance used to determine of a singular value is singular.
+     * @return The nullity of the decomposed matrix.
+     */
+    public static int nullity( SingularValueDecomposition svd , double threshold ) {
+        int ret = 0;
+
+        double w[]= svd.getSingularValues();
+
+        int N = svd.numberOfSingularValues();
+
+        int numCol = svd.numCols();
+
+        for( int j = 0; j < N; j++ ) {
+            if( w[j] <= threshold) ret++;
+        }
+        return ret + numCol-N;
+    }
+
+}
diff --git a/main/dense64/src/org/ejml/ops/SpecializedOps.java b/main/dense64/src/org/ejml/ops/SpecializedOps.java
new file mode 100644
index 0000000..6d06642
--- /dev/null
+++ b/main/dense64/src/org/ejml/ops/SpecializedOps.java
@@ -0,0 +1,444 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.data.D1Matrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.data.RowD1Matrix64F;
+
+
+/**
+ * This contains less common or more specialized matrix operations.
+ *
+ * @author Peter Abeles
+ */
+public class SpecializedOps {
+
+    /**
+     * <p>
+     * Creates a reflector from the provided vector.<br>
+     * <br>
+     * Q = I - γ u u<sup>T</sup><br>
+     * γ = 2/||u||<sup>2</sup>
+     * </p>
+     *
+     * <p>
+     * In practice {@link org.ejml.alg.dense.mult.VectorVectorMult#householder(double, org.ejml.data.D1Matrix64F, org.ejml.data.D1Matrix64F, org.ejml.data.D1Matrix64F)}  multHouseholder}
+     * should be used for performance reasons since there is no need to calculate Q explicitly.
+     * </p>
+     *
+     * @param u A vector. Not modified.
+     * @return An orthogonal reflector.
+     */
+    public static DenseMatrix64F createReflector( RowD1Matrix64F u ) {
+        if( !MatrixFeatures.isVector(u))
+            throw new IllegalArgumentException("u must be a vector");
+
+        double norm = NormOps.fastNormF(u);
+        double gamma = -2.0/(norm*norm);
+
+        DenseMatrix64F Q = CommonOps.identity(u.getNumElements());
+        CommonOps.multAddTransB(gamma,u,u,Q);
+
+        return Q;
+    }
+
+    /**
+     * <p>
+     * Creates a reflector from the provided vector and gamma.<br>
+     * <br>
+     * Q = I - γ u u<sup>T</sup><br>
+     * </p>
+     *
+     * <p>
+     * In practice {@link org.ejml.alg.dense.mult.VectorVectorMult#householder(double, org.ejml.data.D1Matrix64F, org.ejml.data.D1Matrix64F, org.ejml.data.D1Matrix64F)}  multHouseholder}
+     * should be used for performance reasons since there is no need to calculate Q explicitly.
+     * </p>
+     *
+     * @param u A vector.  Not modified.
+     * @param gamma To produce a reflector gamma needs to be equal to 2/||u||.
+     * @return An orthogonal reflector.
+     */
+    public static DenseMatrix64F createReflector( DenseMatrix64F u , double gamma) {
+        if( !MatrixFeatures.isVector(u))
+            throw new IllegalArgumentException("u must be a vector");
+
+        DenseMatrix64F Q = CommonOps.identity(u.getNumElements());
+        CommonOps.multAddTransB(-gamma,u,u,Q);
+
+        return Q;
+    }
+
+    /**
+     * Creates a copy of a matrix but swaps the rows as specified by the order array.
+     *
+     * @param order Specifies which row in the dest corresponds to a row in the src. Not modified.
+     * @param src The original matrix. Not modified.
+     * @param dst A Matrix that is a row swapped copy of src. Modified.
+     */
+    public static DenseMatrix64F copyChangeRow( int order[] , DenseMatrix64F src , DenseMatrix64F dst )
+    {
+        if( dst == null ) {
+            dst = new DenseMatrix64F(src.numRows,src.numCols);
+        } else if( src.numRows != dst.numRows || src.numCols != dst.numCols ) {
+            throw new IllegalArgumentException("src and dst must have the same dimensions.");
+        }
+
+        for( int i = 0; i < src.numRows; i++ ) {
+            int indexDst = i*src.numCols;
+            int indexSrc = order[i]*src.numCols;
+
+            System.arraycopy(src.data,indexSrc,dst.data,indexDst,src.numCols);
+        }
+
+        return dst;
+    }
+
+    /**
+     * Copies just the upper or lower triangular portion of a matrix.
+     *
+     * @param src Matrix being copied. Not modified.
+     * @param dst Where just a triangle from src is copied.  If null a new one will be created. Modified.
+     * @param upper If the upper or lower triangle should be copied.
+     * @return The copied matrix.
+     */
+    public static DenseMatrix64F copyTriangle( DenseMatrix64F src , DenseMatrix64F dst , boolean upper ) {
+        if( dst == null ) {
+            dst = new DenseMatrix64F(src.numRows,src.numCols);
+        } else if( src.numRows != dst.numRows || src.numCols != dst.numCols ) {
+            throw new IllegalArgumentException("src and dst must have the same dimensions.");
+        }
+
+        if( upper ) {
+            int N = Math.min(src.numRows,src.numCols);
+            for( int i = 0; i < N; i++ ) {
+                int index = i*src.numCols+i;
+                System.arraycopy(src.data,index,dst.data,index,src.numCols-i);
+            }
+        } else {
+            for( int i = 0; i < src.numRows; i++ ) {
+                int length = Math.min(i+1,src.numCols);
+                int index = i*src.numCols;
+                System.arraycopy(src.data,index,dst.data,index,length);
+            }
+        }
+
+        return dst;
+    }
+
+    /**
+     * <p>
+     * Computes the F norm of the difference between the two Matrices:<br>
+     * <br>
+     * Sqrt{∑<sub>i=1:m</sub> ∑<sub>j=1:n</sub> ( a<sub>ij</sub> - b<sub>ij</sub>)<sup>2</sup>}
+     * </p>
+     * <p>
+     * This is often used as a cost function.
+     * </p>
+     *
+     * @see NormOps#fastNormF
+     *
+     * @param a m by n matrix. Not modified.
+     * @param b m by n matrix. Not modified.
+     *
+     * @return The F normal of the difference matrix.
+     */
+    public static double diffNormF( D1Matrix64F a , D1Matrix64F b )
+    {
+        if( a.numRows != b.numRows || a.numCols != b.numCols ) {
+            throw new IllegalArgumentException("Both matrices must have the same shape.");
+        }
+
+        final int size = a.getNumElements();
+
+        DenseMatrix64F diff = new DenseMatrix64F(size,1);
+
+        for( int i = 0; i < size; i++ ) {
+            diff.set(i , b.get(i) - a.get(i));
+        }
+        return NormOps.normF(diff);
+    }
+
+    public static double diffNormF_fast( D1Matrix64F a , D1Matrix64F b )
+    {
+        if( a.numRows != b.numRows || a.numCols != b.numCols ) {
+            throw new IllegalArgumentException("Both matrices must have the same shape.");
+        }
+
+        final int size = a.getNumElements();
+
+        double total=0;
+        for( int i = 0; i < size; i++ ) {
+            double diff = b.get(i) - a.get(i);
+            total += diff*diff;
+        }
+        return Math.sqrt(total);
+    }
+
+    /**
+     * <p>
+     * Computes the p=1 p-norm of the difference between the two Matrices:<br>
+     * <br>
+     * ∑<sub>i=1:m</sub> ∑<sub>j=1:n</sub> | a<sub>ij</sub> - b<sub>ij</sub>| <br>
+     * <br>
+     * where |x| is the absolute value of x.
+     * </p>
+     * <p>
+     * This is often used as a cost function.
+     * </p>
+     *
+     * @param a m by n matrix. Not modified.
+     * @param b m by n matrix. Not modified.
+     *
+     * @return The p=1 p-norm of the difference matrix.
+     */
+    public static double diffNormP1( D1Matrix64F a , D1Matrix64F b )
+    {
+        if( a.numRows != b.numRows || a.numCols != b.numCols ) {
+            throw new IllegalArgumentException("Both matrices must have the same shape.");
+        }
+
+        final int size = a.getNumElements();
+
+        double total=0;
+        for( int i = 0; i < size; i++ ) {
+            total += Math.abs(b.get(i) - a.get(i));
+        }
+        return total;
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * B = A + αI
+     * <p> 
+     *
+     * @param A A square matrix.  Not modified.
+     * @param B A square matrix that the results are saved to.  Modified.
+     * @param alpha Scaling factor for the identity matrix.
+     */
+    public static void addIdentity( RowD1Matrix64F A , RowD1Matrix64F B , double alpha )
+    {
+        if( A.numCols != A.numRows )
+            throw new IllegalArgumentException("A must be square");
+        if( B.numCols != A.numCols || B.numRows != A.numRows )
+            throw new IllegalArgumentException("B must be the same shape as A");
+
+        int n = A.numCols;
+
+        int index = 0;
+        for( int i = 0; i < n; i++ ) {
+            for( int j = 0; j < n; j++ , index++) {
+                if( i == j ) {
+                    B.set( index , A.get(index) + alpha);
+                } else {
+                    B.set( index , A.get(index) );
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Extracts a row or column vector from matrix A.  The first element in the matrix is at element (rowA,colA).
+     * The next 'length' elements are extracted along a row or column.  The results are put into vector 'v'
+     * start at its element v0.
+     * </p>
+     *
+     * @param A Matrix that the vector is being extracted from.  Not modified.
+     * @param rowA Row of the first element that is extracted.
+     * @param colA Column of the first element that is extracted.
+     * @param length Length of the extracted vector.
+     * @param row If true a row vector is extracted, otherwise a column vector is extracted.
+     * @param offsetV First element in 'v' where the results are extracted to.
+     * @param v Vector where the results are written to. Modified.
+     */
+    public static void subvector(RowD1Matrix64F A, int rowA, int colA, int length , boolean row, int offsetV, RowD1Matrix64F v) {
+        if( row ) {
+            for( int i = 0; i < length; i++ ) {
+                v.set( offsetV +i , A.get(rowA,colA+i) );
+            }
+        } else {
+            for( int i = 0; i < length; i++ ) {
+                v.set( offsetV +i , A.get(rowA+i,colA));
+            }
+        }
+    }
+
+    /**
+     * Takes a matrix and splits it into a set of row or column vectors.
+     *
+     * @param A original matrix.
+     * @param column If true then column vectors will be created.
+     * @return Set of vectors.
+     */
+    public static DenseMatrix64F[] splitIntoVectors( RowD1Matrix64F A , boolean column )
+    {
+        int w = column ? A.numCols : A.numRows;
+
+        int M = column ? A.numRows : 1;
+        int N = column ? 1 : A.numCols;
+
+        int o = Math.max(M,N);
+
+        DenseMatrix64F[] ret  = new DenseMatrix64F[w];
+
+        for( int i = 0; i < w; i++ ) {
+            DenseMatrix64F a = new DenseMatrix64F(M,N);
+
+            if( column )
+                subvector(A,0,i,o,false,0,a);
+            else
+                subvector(A,i,0,o,true,0,a);
+
+            ret[i] = a;
+        }
+
+        return ret;
+    }
+
+    /**
+     * <p>
+     * Creates a pivot matrix that exchanges the rows in a matrix:
+     * <br>
+     * A' = P*A<br>
+     * </p>
+     * <p>
+     * For example, if element 0 in 'pivots' is 2 then the first row in A' will be the 3rd row in A.
+     * </p>
+     *
+     * @param ret If null then a new matrix is declared otherwise the results are written to it.  Is modified.
+     * @param pivots Specifies the new order of rows in a matrix.
+     * @param numPivots How many elements in pivots are being used.
+     * @param transposed If the transpose of the matrix is returned.
+     * @return A pivot matrix.
+     */
+    public static DenseMatrix64F pivotMatrix(DenseMatrix64F ret, int pivots[], int numPivots, boolean transposed ) {
+
+        if( ret == null ) {
+            ret = new DenseMatrix64F(numPivots, numPivots);
+        } else {
+            if( ret.numCols != numPivots || ret.numRows != numPivots )
+                throw new IllegalArgumentException("Unexpected matrix dimension");
+            CommonOps.fill(ret, 0);
+        }
+
+        if( transposed ) {
+            for( int i = 0; i < numPivots; i++ ) {
+                ret.set(pivots[i],i,1);
+            }
+        } else {
+            for( int i = 0; i < numPivots; i++ ) {
+                ret.set(i,pivots[i],1);
+            }
+        }
+
+        return ret;
+    }
+
+    /**
+     * Computes the product of the diagonal elements.  For a diagonal or triangular
+     * matrix this is the determinant.
+     *
+     * @param T A matrix.
+     * @return product of the diagonal elements.
+     */
+    public static double diagProd( RowD1Matrix64F T )
+    {
+        double prod = 1.0;
+        int N = Math.min(T.numRows,T.numCols);
+        for( int i = 0; i < N; i++ ) {
+            prod *= T.unsafe_get(i,i);
+        }
+
+        return prod;
+    }
+
+    /**
+     * <p>
+     * Returns the absolute value of the digonal element in the matrix that has the largest absolute value.<br>
+     * <br>
+     * Max{ |a<sub>ij</sub>| } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max abs element value of the matrix.
+     */
+    public static double elementDiagonalMaxAbs( D1Matrix64F a ) {
+        final int size = Math.min(a.numRows,a.numCols);
+
+        double max = 0;
+        for( int i = 0; i < size; i++ ) {
+            double val = Math.abs(a.get( i,i ));
+            if( val > max ) {
+                max = val;
+            }
+        }
+
+        return max;
+    }
+
+    /**
+     * Computes the quality of a triangular matrix, where the quality of a matrix
+     * is defined in {@link org.ejml.interfaces.linsol.LinearSolver#quality()}.  In
+     * this situation the quality os the absolute value of the product of
+     * each diagonal element divided by the magnitude of the largest diagonal element.
+     * If all diagonal elements are zero then zero is returned.
+     *
+     * @param T A matrix.  @return product of the diagonal elements.
+     * @return the quality of the system.
+     */
+    public static double qualityTriangular(D1Matrix64F T)
+    {
+        int N = Math.min(T.numRows,T.numCols);
+
+        // TODO make faster by just checking the upper triangular portion
+        double max = elementDiagonalMaxAbs(T);
+
+        if( max == 0.0d )
+            return 0.0d;
+
+        double quality = 1.0;
+        for( int i = 0; i < N; i++ ) {
+            quality *= T.unsafe_get(i,i)/max;
+        }
+
+        return Math.abs(quality);
+    }
+
+    /**
+     * Sums up the square of each element in the matrix.  This is equivalent to the
+     * Frobenius norm squared.
+     *
+     * @param m Matrix.
+     * @return Sum of elements squared.
+     */
+    public static double elementSumSq( D1Matrix64F m  ) {
+        double total = 0;
+        
+        int N = m.getNumElements();
+        for( int i = 0; i < N; i++ ) {
+            double d = m.data[i];
+            total += d*d;
+        }
+
+        return total;
+    }
+}
diff --git a/main/dense64/src/overview.html b/main/dense64/src/overview.html
new file mode 100644
index 0000000..79756b2
--- /dev/null
+++ b/main/dense64/src/overview.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<body>
+    A fast and easy to use dense matrix linear algebra library written in Java.
+
+    Efficient Java Matrix Library (EJML) is a linear algebra library for manipulating dense matrices. Its design goals are; 1) to be as computationally and memory efficient as possible for both small and large matrices, and 2) to be accessible to both novices and experts. These goals are accomplished by dynamically selecting the best algorithms to use at runtime, clean API, and multiple interfaces. EJML is free, written in 100% Java and has been released under an Apache v2.0 license.
+
+</body>
+</html>
\ No newline at end of file
diff --git a/main/dense64/test/org/ejml/alg/block/TestBlockInnerMultiplication.java b/main/dense64/test/org/ejml/alg/block/TestBlockInnerMultiplication.java
new file mode 100644
index 0000000..38557a7
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/block/TestBlockInnerMultiplication.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestBlockInnerMultiplication {
+
+    private static Random rand = new Random(234234);
+
+    private static final int BLOCK_LENGTH = 4;
+
+    /**
+     * Check the inner block multiplication functions against various shapes of inputs
+     */
+    @Test
+    public void testAllBlockMult()
+    {
+        checkBlockMultCase(BLOCK_LENGTH, BLOCK_LENGTH, BLOCK_LENGTH);
+        checkBlockMultCase(BLOCK_LENGTH -1, BLOCK_LENGTH, BLOCK_LENGTH);
+        checkBlockMultCase(BLOCK_LENGTH -1, BLOCK_LENGTH -1, BLOCK_LENGTH);
+        checkBlockMultCase(BLOCK_LENGTH -1, BLOCK_LENGTH -1, BLOCK_LENGTH -1);
+        checkBlockMultCase(BLOCK_LENGTH,   BLOCK_LENGTH -1, BLOCK_LENGTH -1);
+        checkBlockMultCase(BLOCK_LENGTH, BLOCK_LENGTH,   BLOCK_LENGTH -1);
+    }
+
+    /**
+     * Searches for all inner block matrix operations and tests their correctness.
+     */
+    private void checkBlockMultCase(final int heightA, final int widthA, final int widthB) {
+        Method methods[] = BlockInnerMultiplication.class.getDeclaredMethods();
+
+        int numFound = 0;
+        for( Method m : methods) {
+            String name = m.getName();
+
+//            System.out.println("name = "+name);
+
+            boolean transA = false;
+            boolean transB = false;
+
+            if( name.contains("TransA"))
+                transA = true;
+
+            if( name.contains("TransB"))
+                transB = true;
+
+            // See if the results are added, subtracted, or set to the output matrix
+            int operationType = 0;
+            if( name.contains("Plus")) operationType = 1;
+            else if ( name.contains("Minus")) operationType = -1;
+            else if( name.contains("Set")) operationType = 0;
+
+            checkBlockMult(operationType,transA,transB,m,heightA,widthA,widthB);
+            numFound++;
+        }
+
+        // make sure all the functions were in fact tested
+        assertEquals(15,numFound);
+    }
+
+    /**
+     * The inner block multiplication is in a row major format.  Test it against
+     * operations for DenseMatrix64F
+     */
+    private void checkBlockMult( int operationType , boolean transA , boolean transB , Method method,
+                                 final int heightA, final int widthA, final int widthB )
+    {
+        boolean hasAlpha = method.getParameterTypes().length == 10;
+
+        if( hasAlpha && operationType == -1 )
+            fail("No point to minus and alpha");
+
+        DenseMatrix64F A = RandomMatrices.createRandom(heightA,widthA,rand);
+        DenseMatrix64F B = RandomMatrices.createRandom(widthA,widthB,rand);
+        DenseMatrix64F C = new DenseMatrix64F(heightA,widthB);
+
+        if( operationType == -1 )
+            CommonOps.mult(-1,A,B,C);
+        else
+            CommonOps.mult(A,B,C);
+
+        DenseMatrix64F C_found = new DenseMatrix64F(heightA,widthB);
+        // if it is set then it should overwrite everything just fine
+        if( operationType == 0)
+            RandomMatrices.setRandom(C_found,rand);
+
+        if( transA )
+            CommonOps.transpose(A);
+        if( transB )
+            CommonOps.transpose(B);
+
+        double alpha = 2.0;
+
+        if( hasAlpha ) {
+            CommonOps.scale(alpha,C);
+        }
+
+        invoke(method,alpha,A.data,B.data,C_found.data,0,0,0,A.numRows,A.numCols,C_found.numCols);
+
+        if( !MatrixFeatures.isIdentical(C,C_found,1e-10) ) {
+            C.print();
+            C_found.print();
+            System.out.println("Method "+method.getName());
+            System.out.println("transA " +transA);
+            System.out.println("transB " +transB);
+            System.out.println("type   " +operationType);
+            System.out.println("alpha  " +hasAlpha);
+            fail("Not identical");
+        }
+    }
+
+    public static void invoke(Method func,
+                              double alpha ,
+                              double[] dataA, double []dataB, double []dataC,
+                              int indexA, int indexB, int indexC,
+                              final int heightA, final int widthA, final int widthB )
+    {
+        try {
+            if( func.getParameterTypes().length == 9 ) {
+                func.invoke(null, dataA, dataB, dataC,
+                        indexA,indexB,indexC,
+                        heightA,widthA,widthB);
+            } else {
+                func.invoke(null, alpha , dataA, dataB, dataC,
+                        indexA,indexB,indexC,
+                        heightA,widthA,widthB);
+            }
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException(e);
+        } catch (InvocationTargetException e) {
+            throw new RuntimeException(e);
+        }
+
+    }
+
+}
diff --git a/main/dense64/test/org/ejml/alg/block/TestBlockInnerRankUpdate.java b/main/dense64/test/org/ejml/alg/block/TestBlockInnerRankUpdate.java
new file mode 100644
index 0000000..2ea7117
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/block/TestBlockInnerRankUpdate.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block;
+
+import org.ejml.alg.generic.GenericMatrixOps;
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.D1Submatrix64F;
+import org.ejml.ops.RandomMatrices;
+import org.ejml.simple.SimpleMatrix;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestBlockInnerRankUpdate {
+
+    Random rand = new Random(234234);
+
+    int N = 4;
+
+    /**
+     * Tests rankNUpdate with various sized input matrices
+     */
+    @Test
+    public void rankNUpdate() {
+        // the matrix being updated is a whole block
+        checkRankNUpdate(N, N-2);
+
+        // the matrix being updated is multiple blocks + a fraction
+        checkRankNUpdate(N*2+1, N-2);
+
+        // matrix being updated is less than a block
+        checkRankNUpdate(N-1, N-2);
+
+    }
+
+    private void checkRankNUpdate(int lengthA, int heightB) {
+        double alpha = -2.0;
+        SimpleMatrix origA = SimpleMatrix.random(lengthA,lengthA,-1,1,rand);
+        SimpleMatrix origB = SimpleMatrix.random(heightB,lengthA,-1,1,rand);
+
+        BlockMatrix64F blockA = BlockMatrixOps.convert(origA.getMatrix(),N);
+        BlockMatrix64F blockB = BlockMatrixOps.convert(origB.getMatrix(),N);
+
+        D1Submatrix64F subA = new D1Submatrix64F(blockA,0, origA.numRows(), 0, origA.numCols());
+        D1Submatrix64F subB = new D1Submatrix64F(blockB,0, origB.numRows(), 0, origB.numCols());
+
+        SimpleMatrix expected = origA.plus(origB.transpose().mult(origB).scale(alpha));
+        BlockInnerRankUpdate.rankNUpdate(N,alpha,subA,subB);
+
+        assertTrue(GenericMatrixOps.isEquivalent(expected.getMatrix(),blockA,1e-8));
+    }
+
+    /**
+     * Tests symmRankNMinus_U with various sized input matrices
+     */
+    @Test
+    public void symmRankNMinus_U() {
+        // the matrix being updated is a whole block
+        checkSymmRankNMinus_U(N, N-2);
+
+        // the matrix being updated is multiple blocks + a fraction
+        checkSymmRankNMinus_U(N*2+1, N-2);
+
+        // matrix being updated is less than a block
+        checkSymmRankNMinus_U(N-1, N-2);
+    }
+
+    private void checkSymmRankNMinus_U(int lengthA, int heightB) {
+        SimpleMatrix origA = SimpleMatrix.wrap(RandomMatrices.createSymmPosDef(lengthA,rand));
+        SimpleMatrix origB = SimpleMatrix.random(heightB,lengthA,-1,1,rand);
+
+        BlockMatrix64F blockA = BlockMatrixOps.convert(origA.getMatrix(),N);
+        BlockMatrix64F blockB = BlockMatrixOps.convert(origB.getMatrix(),N);
+
+        D1Submatrix64F subA = new D1Submatrix64F(blockA,0, origA.numRows(), 0, origA.numCols());
+        D1Submatrix64F subB = new D1Submatrix64F(blockB,0, origB.numRows(), 0, origB.numCols());
+
+        SimpleMatrix expected = origA.plus(origB.transpose().mult(origB).scale(-1));
+        BlockInnerRankUpdate.symmRankNMinus_U(N,subA,subB);
+
+        assertTrue(GenericMatrixOps.isEquivalentTriangle(true,expected.getMatrix(),blockA,1e-8));
+    }
+
+    @Test
+    public void symmRankNMinus_L() {
+        // the matrix being updated is a whole block
+        checkSymmRankNMinus_L(N, N-2);
+
+        // the matrix being updated is multiple blocks + a fraction
+        checkSymmRankNMinus_L(N*2+1, N-2);
+
+        // matrix being updated is less than a block
+        checkSymmRankNMinus_L(N-1, N-2);
+    }
+
+    private void checkSymmRankNMinus_L(int lengthA, int widthB) {
+        SimpleMatrix origA = SimpleMatrix.wrap(RandomMatrices.createSymmPosDef(lengthA,rand));
+        SimpleMatrix origB = SimpleMatrix.random(lengthA,widthB,-1,1,rand);
+
+        BlockMatrix64F blockA = BlockMatrixOps.convert(origA.getMatrix(),N);
+        BlockMatrix64F blockB = BlockMatrixOps.convert(origB.getMatrix(),N);
+
+        D1Submatrix64F subA = new D1Submatrix64F(blockA,0, origA.numRows(), 0, origA.numCols());
+        D1Submatrix64F subB = new D1Submatrix64F(blockB,0, origB.numRows(), 0, origB.numCols());
+
+        SimpleMatrix expected = origA.plus(origB.mult(origB.transpose()).scale(-1));
+        BlockInnerRankUpdate.symmRankNMinus_L(N,subA,subB);
+
+//        expected.print();
+//        blockA.print();
+
+        assertTrue(GenericMatrixOps.isEquivalentTriangle(false,expected.getMatrix(),blockA,1e-8));
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/block/TestBlockInnerTriangularSolver.java b/main/dense64/test/org/ejml/alg/block/TestBlockInnerTriangularSolver.java
new file mode 100644
index 0000000..2b6994f
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/block/TestBlockInnerTriangularSolver.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block;
+
+import org.ejml.alg.dense.misc.UnrolledInverseFromMinor;
+import org.ejml.alg.generic.GenericMatrixOps;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestBlockInnerTriangularSolver {
+
+    Random rand = new Random(234534);
+
+
+    @Test
+    public void testInvertLower_two() {
+        DenseMatrix64F A = RandomMatrices.createUpperTriangle(5,0,-1,1,rand);
+        CommonOps.transpose(A);
+
+        DenseMatrix64F A_inv = A.copy();
+
+        BlockInnerTriangularSolver.invertLower(A.data,A_inv.data,5,0,0);
+
+        DenseMatrix64F S = new DenseMatrix64F(5,5);
+        CommonOps.mult(A,A_inv,S);
+
+        assertTrue(GenericMatrixOps.isIdentity(S,1e-8));
+
+        // see if it works with the same input matrix
+        BlockInnerTriangularSolver.invertLower(A.data,A.data,5,0,0);
+
+        assertTrue(MatrixFeatures.isIdentical(A,A_inv,1e-8));
+    }
+
+    @Test
+    public void testInvertLower_one() {
+        DenseMatrix64F A = RandomMatrices.createUpperTriangle(5,0,-1,1,rand);
+        CommonOps.transpose(A);
+
+        DenseMatrix64F A_inv = A.copy();
+
+        BlockInnerTriangularSolver.invertLower(A_inv.data,5,0);
+
+        DenseMatrix64F S = new DenseMatrix64F(5,5);
+        CommonOps.mult(A,A_inv,S);
+
+        assertTrue(GenericMatrixOps.isIdentity(S,1e-8));
+    }
+
+    /**
+     * Test all inner block solvers using reflections to look up the functions
+     */
+    @Test
+    public void testSolveArray() {
+        Method methods[] = BlockInnerTriangularSolver.class.getMethods();
+
+        int numFound = 0;
+        for( Method m : methods) {
+            String name = m.getName();
+
+            if( !name.contains("solve") || name.compareTo("solve") == 0 || name.compareTo("solveBlock") == 0 )
+                continue;
+
+//            System.out.println("name = "+name);
+
+            boolean solveL = name.contains("L");
+            boolean transT;
+            boolean transB = name.contains("TransB");
+
+            if( solveL )
+                transT = name.contains("TransL");
+            else
+                transT = name.contains("TransU");
+
+            check_solve_array(m,solveL,transT,transB);
+
+            numFound++;
+        }
+
+        // make sure all the functions were in fact tested
+        assertEquals(5,numFound);
+    }
+
+    /**
+     * Checks to see if solve functions that use arrays as input work correctly.
+     */
+    private void check_solve_array(Method m,
+                                   boolean solveL, boolean transT, boolean transB) {
+        int offsetL = 2;
+        int offsetB = 3;
+
+        DenseMatrix64F L = createRandomLowerTriangular(3);
+
+        if( !solveL ) {
+            CommonOps.transpose(L);
+        }
+
+        if( transT ) {
+            CommonOps.transpose(L);
+        }
+
+        DenseMatrix64F L_inv = L.copy();
+        UnrolledInverseFromMinor.inv(L_inv,L_inv);
+
+        DenseMatrix64F B = RandomMatrices.createRandom(3,4,rand);
+        DenseMatrix64F expected = RandomMatrices.createRandom(3,4,rand);
+        DenseMatrix64F found = B.copy();
+
+        // compute the expected solution
+        CommonOps.mult(L_inv,B,expected);
+
+        if( transT ) {
+            CommonOps.transpose(L);
+        }
+
+        if( transB ) {
+            CommonOps.transpose(found);
+            CommonOps.transpose(expected);
+        }
+
+        // create arrays that are offset from the original
+        // use two different offsets to make sure it doesn't confuse them internally
+        double dataL[] = offsetArray(L.data,offsetL);
+        double dataB[] = offsetArray(found.data,offsetB);
+
+        try {
+            m.invoke(null,dataL,dataB,3,4,3,offsetL,offsetB);
+        } catch (IllegalAccessException e) {
+            fail("invoke failed");
+        } catch (InvocationTargetException e) {
+            throw new RuntimeException(e.getCause());
+        }
+
+        // put the solution into B, minus the offset
+        System.arraycopy(dataB,offsetB,found.data,0,found.data.length);
+
+        assertTrue(MatrixFeatures.isIdentical(expected,found,1e-8));
+    }
+
+    private DenseMatrix64F createRandomLowerTriangular( int N ) {
+        DenseMatrix64F U = RandomMatrices.createUpperTriangle(N,0,-1,1,rand);
+
+        CommonOps.transpose(U);
+
+        return U;
+    }
+
+    private double[] offsetArray( double[] orig , int offset )
+    {
+        double[] ret = new double[ orig.length + offset ];
+
+        System.arraycopy(orig,0,ret,offset,orig.length);
+
+        return ret;
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/block/TestBlockMatrixOps.java b/main/dense64/test/org/ejml/alg/block/TestBlockMatrixOps.java
new file mode 100644
index 0000000..1c27244
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/block/TestBlockMatrixOps.java
@@ -0,0 +1,559 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block;
+
+import org.ejml.alg.generic.GenericMatrixOps;
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.D1Submatrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.RandomMatrices;
+import org.ejml.simple.SimpleMatrix;
+import org.ejml.simple.UtilSimpleMatrix;
+import org.junit.Test;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestBlockMatrixOps {
+
+    final static int BLOCK_LENGTH = 10;
+
+    Random rand = new Random(234);
+
+    @Test
+    public void convert_dense_to_block() {
+        checkConvert_dense_to_block(10,10);
+        checkConvert_dense_to_block(5,8);
+        checkConvert_dense_to_block(12,16);
+        checkConvert_dense_to_block(16,12);
+        checkConvert_dense_to_block(21,27);
+        checkConvert_dense_to_block(28,5);
+        checkConvert_dense_to_block(5,28);
+        checkConvert_dense_to_block(20,20);
+    }
+
+    private void checkConvert_dense_to_block( int m , int n ) {
+        DenseMatrix64F A = RandomMatrices.createRandom(m,n,rand);
+        BlockMatrix64F B = new BlockMatrix64F(A.numRows,A.numCols,BLOCK_LENGTH);
+
+        BlockMatrixOps.convert(A,B);
+
+        assertTrue( GenericMatrixOps.isEquivalent(A,B,1e-8));
+    }
+
+    @Test
+    public void convertInline_dense_to_block() {
+        for( int i = 2; i < 30; i += 5 ) {
+            for( int j = 2; j < 30; j += 5 ) {
+                checkConvertInline_dense_to_block(i,j);
+            }
+        }
+    }
+
+    private void checkConvertInline_dense_to_block( int m , int n ) {
+        double tmp[] = new double[BLOCK_LENGTH*n];
+        DenseMatrix64F A = RandomMatrices.createRandom(m,n,rand);
+        DenseMatrix64F A_orig = A.copy();
+
+        BlockMatrixOps.convertRowToBlock(m,n,BLOCK_LENGTH,A.data,tmp);
+        BlockMatrix64F B = BlockMatrix64F.wrap(A.data,A.numRows,A.numCols,BLOCK_LENGTH);
+
+        assertTrue( GenericMatrixOps.isEquivalent(A_orig,B,1e-8));
+    }
+
+    @Test
+    public void convert_block_to_dense() {
+        checkBlockToDense(10,10);
+        checkBlockToDense(5,8);
+        checkBlockToDense(12,16);
+        checkBlockToDense(16,12);
+        checkBlockToDense(21,27);
+        checkBlockToDense(28,5);
+        checkBlockToDense(5,28);
+        checkBlockToDense(20,20);
+    }
+
+    private void checkBlockToDense( int m , int n ) {
+        DenseMatrix64F A = new DenseMatrix64F(m,n);
+        BlockMatrix64F B = BlockMatrixOps.createRandom(m,n,-1,1,rand);
+
+        BlockMatrixOps.convert(B,A);
+
+        assertTrue( GenericMatrixOps.isEquivalent(A,B,1e-8));
+    }
+
+    @Test
+    public void convertInline_block_to_dense() {
+        for( int i = 2; i < 30; i += 5 ) {
+            for( int j = 2; j < 30; j += 5 ) {
+                checkConvertInline_block_to_dense(i,j);
+            }
+        }
+    }
+
+    private void checkConvertInline_block_to_dense( int m , int n ) {
+        double tmp[] = new double[BLOCK_LENGTH*n];
+        BlockMatrix64F A = BlockMatrixOps.createRandom(m,n,-1,1,rand,BLOCK_LENGTH);
+        BlockMatrix64F A_orig = A.copy();
+
+        BlockMatrixOps.convertBlockToRow(m,n,BLOCK_LENGTH,A.data,tmp);
+        DenseMatrix64F B = DenseMatrix64F.wrap(A.numRows,A.numCols,A.data);
+
+        assertTrue( GenericMatrixOps.isEquivalent(A_orig,B,1e-8));
+    }
+
+    /**
+     * Makes sure the bounds check on input matrices for mult() is done correctly
+     */
+    @Test
+    public void testMultInputChecks() {
+        Method methods[] = BlockMatrixOps.class.getDeclaredMethods();
+
+        int numFound = 0;
+        for( Method m : methods) {
+            String name = m.getName();
+
+            if( !name.contains("mult"))
+                continue;
+
+            boolean transA = false;
+            boolean transB = false;
+
+            if( name.contains("TransA"))
+                transA = true;
+
+            if( name.contains("TransB"))
+                transB = true;
+
+            checkMultInput(m,transA,transB);
+            numFound++;
+        }
+
+        // make sure all the functions were in fact tested
+        assertEquals(3,numFound);
+    }
+
+    /**
+     * Makes sure exceptions are thrown for badly shaped input matrices.
+     */
+    private void checkMultInput( Method func, boolean transA , boolean transB ) {
+        // bad block size
+        BlockMatrix64F A = new BlockMatrix64F(5,4,3);
+        BlockMatrix64F B = new BlockMatrix64F(4,6,3);
+        BlockMatrix64F C = new BlockMatrix64F(5,6,4);
+
+        invokeErrorCheck(func, transA , transB , A, B, C);
+        C.blockLength = 3;
+        B.blockLength = 4;
+        invokeErrorCheck(func, transA , transB ,A, B, C);
+        B.blockLength = 3;
+        A.blockLength = 4;
+        invokeErrorCheck(func, transA , transB , A, B, C);
+        A.blockLength = 3;
+
+        // check for bad size C
+        C.numCols = 7;
+        invokeErrorCheck(func,transA , transB ,A,B,C);
+        C.numCols = 6;
+        C.numRows = 4;
+        invokeErrorCheck(func,transA , transB ,A,B,C);
+
+        // make A and B incompatible
+        A.numCols = 3;
+        invokeErrorCheck(func,transA , transB ,A,B,C);
+    }
+
+    private void invokeErrorCheck(Method func, boolean transA , boolean transB ,
+                                  BlockMatrix64F a, BlockMatrix64F b, BlockMatrix64F c) {
+
+        if( transA )
+            a = BlockMatrixOps.transpose(a,null);
+        if( transB )
+            b = BlockMatrixOps.transpose(b,null);
+
+        try {
+            func.invoke(null, a, b, c);
+            fail("No exception");
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException(e);
+        } catch (InvocationTargetException e) {
+            if( !(e.getCause() instanceof IllegalArgumentException) )
+                fail("Unexpected exception: "+e.getCause().getMessage());
+        }
+    }
+
+    /**
+     * Tests for correctness multiplication of an entire matrix for all multiplication operations.
+     */
+    @Test
+    public void testMultSolution() {
+        Method methods[] = BlockMatrixOps.class.getDeclaredMethods();
+
+        int numFound = 0;
+        for( Method m : methods) {
+            String name = m.getName();
+
+            if( !name.contains("mult"))
+                continue;
+
+//            System.out.println("name = "+name);
+
+            boolean transA = false;
+            boolean transB = false;
+
+            if( name.contains("TransA"))
+                transA = true;
+
+            if( name.contains("TransB"))
+                transB = true;
+
+            checkMult(m,transA,transB);
+            numFound++;
+        }
+
+        // make sure all the functions were in fact tested
+        assertEquals(3,numFound);
+    }
+
+    /**
+     * Test the method against various matrices of different sizes and shapes which have partial
+     * blocks.
+     */
+    private void checkMult( Method func, boolean transA , boolean transB ) {
+        // trivial case
+        checkMult(func,transA,transB,BLOCK_LENGTH, BLOCK_LENGTH, BLOCK_LENGTH);
+
+        // stuff larger than the block size
+        checkMult(func,transA,transB,BLOCK_LENGTH+1, BLOCK_LENGTH, BLOCK_LENGTH);
+        checkMult(func,transA,transB,BLOCK_LENGTH, BLOCK_LENGTH+1, BLOCK_LENGTH);
+        checkMult(func,transA,transB,BLOCK_LENGTH, BLOCK_LENGTH, BLOCK_LENGTH+1);
+        checkMult(func,transA,transB,BLOCK_LENGTH+1, BLOCK_LENGTH+1, BLOCK_LENGTH+1);
+
+        // stuff smaller than the block size
+        checkMult(func,transA,transB,BLOCK_LENGTH-1, BLOCK_LENGTH, BLOCK_LENGTH);
+        checkMult(func,transA,transB,BLOCK_LENGTH, BLOCK_LENGTH-1, BLOCK_LENGTH);
+        checkMult(func,transA,transB,BLOCK_LENGTH, BLOCK_LENGTH, BLOCK_LENGTH-1);
+        checkMult(func,transA,transB,BLOCK_LENGTH-1, BLOCK_LENGTH-1, BLOCK_LENGTH-1);
+
+        // stuff multiple blocks
+        checkMult(func,transA,transB,BLOCK_LENGTH*2, BLOCK_LENGTH, BLOCK_LENGTH);
+        checkMult(func,transA,transB,BLOCK_LENGTH, BLOCK_LENGTH*2, BLOCK_LENGTH);
+        checkMult(func,transA,transB,BLOCK_LENGTH, BLOCK_LENGTH, BLOCK_LENGTH*2);
+        checkMult(func,transA,transB,BLOCK_LENGTH*2, BLOCK_LENGTH*2, BLOCK_LENGTH*2);
+        checkMult(func,transA,transB,BLOCK_LENGTH*2+4, BLOCK_LENGTH*2+3, BLOCK_LENGTH*2+2);
+    }
+
+    private void checkMult( Method func, boolean transA , boolean transB ,
+                            int m, int n, int o) {
+        DenseMatrix64F A_d = RandomMatrices.createRandom(m, n,rand);
+        DenseMatrix64F B_d = RandomMatrices.createRandom(n, o,rand);
+        DenseMatrix64F C_d = new DenseMatrix64F(m, o);
+
+        BlockMatrix64F A_b = BlockMatrixOps.convert(A_d,BLOCK_LENGTH);
+        BlockMatrix64F B_b = BlockMatrixOps.convert(B_d,BLOCK_LENGTH);
+        BlockMatrix64F C_b = BlockMatrixOps.createRandom(m, o, -1 , 1 , rand , BLOCK_LENGTH);
+
+        if( transA )
+            A_b=BlockMatrixOps.transpose(A_b,null);
+
+        if( transB )
+            B_b=BlockMatrixOps.transpose(B_b,null);
+
+        CommonOps.mult(A_d,B_d,C_d);
+        try {
+            func.invoke(null,A_b,B_b,C_b);
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException(e);
+        } catch (InvocationTargetException e) {
+            throw new RuntimeException(e);
+        }
+
+//        C_d.print();
+//        C_b.print();
+        assertTrue( GenericMatrixOps.isEquivalent(C_d,C_b,1e-8));
+    }
+
+
+    @Test
+    public void convertTranSrc_block_to_dense() {
+        checkTranSrcBlockToDense(10,10);
+        checkTranSrcBlockToDense(5,8);
+        checkTranSrcBlockToDense(12,16);
+        checkTranSrcBlockToDense(16,12);
+        checkTranSrcBlockToDense(21,27);
+        checkTranSrcBlockToDense(28,5);
+        checkTranSrcBlockToDense(5,28);
+        checkTranSrcBlockToDense(20,20);
+    }
+
+    private void checkTranSrcBlockToDense( int m , int n ) {
+        DenseMatrix64F A = RandomMatrices.createRandom(m,n,rand);
+        DenseMatrix64F A_t = new DenseMatrix64F(n,m);
+        BlockMatrix64F B = new BlockMatrix64F(n,m,BLOCK_LENGTH);
+
+        CommonOps.transpose(A,A_t);
+        BlockMatrixOps.convertTranSrc(A,B);
+
+        assertTrue( GenericMatrixOps.isEquivalent(A_t,B,1e-8));
+    }
+
+    @Test
+    public void transpose() {
+        checkTranspose(10,10);
+        checkTranspose(5,8);
+        checkTranspose(12,16);
+        checkTranspose(16,12);
+        checkTranspose(21,27);
+        checkTranspose(28,5);
+        checkTranspose(5,28);
+        checkTranspose(20,20);
+    }
+
+    private void checkTranspose( int m , int n ) {
+        DenseMatrix64F A = RandomMatrices.createRandom(m,n,rand);
+        DenseMatrix64F A_t = new DenseMatrix64F(n,m);
+
+        BlockMatrix64F B = new BlockMatrix64F(A.numRows,A.numCols,BLOCK_LENGTH);
+        BlockMatrix64F B_t = new BlockMatrix64F(n,m,BLOCK_LENGTH);
+
+        BlockMatrixOps.convert(A,B);
+
+        CommonOps.transpose(A,A_t);
+        BlockMatrixOps.transpose(B,B_t);
+
+        assertTrue( GenericMatrixOps.isEquivalent(A_t,B_t,1e-8));
+    }
+
+    @Test
+    public void zeroTriangle_upper() {
+        int r = 3;
+
+        for( int numRows = 2; numRows <= 6; numRows += 2 ){
+            for( int numCols = 2; numCols <= 6; numCols += 2 ){
+                BlockMatrix64F B = BlockMatrixOps.createRandom(numRows,numCols,-1,1,rand,r);
+                BlockMatrixOps.zeroTriangle(true,B);
+
+                for( int i = 0; i < B.numRows; i++ ) {
+                    for( int j = 0; j < B.numCols; j++ ) {
+                        if( j <= i )
+                            assertTrue(B.get(i,j) != 0 );
+                        else
+                            assertTrue(B.get(i,j) == 0 );
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    public void zeroTriangle_lower() {
+
+        int r = 3;
+
+        for( int numRows = 2; numRows <= 6; numRows += 2 ){
+            for( int numCols = 2; numCols <= 6; numCols += 2 ){
+                BlockMatrix64F B = BlockMatrixOps.createRandom(numRows,numCols,-1,1,rand,r);
+
+                BlockMatrixOps.zeroTriangle(false,B);
+
+                for( int i = 0; i < B.numRows; i++ ) {
+                    for( int j = 0; j < B.numCols; j++ ) {
+                        if( j >= i )
+                            assertTrue(B.get(i,j) != 0 );
+                        else
+                            assertTrue(B.get(i,j) == 0 );
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    public void copyTriangle() {
+
+        int r = 3;
+
+        // test where src and dst are the same size
+        for( int numRows = 2; numRows <= 6; numRows += 2 ){
+            for( int numCols = 2; numCols <= 6; numCols += 2 ){
+                BlockMatrix64F A = BlockMatrixOps.createRandom(numRows,numCols,-1,1,rand,r);
+                BlockMatrix64F B = new BlockMatrix64F(numRows,numCols,r);
+
+                BlockMatrixOps.copyTriangle(true,A,B);
+
+                for( int i = 0; i < numRows; i++) {
+                    for( int j = 0; j < numCols; j++ ) {
+                        if( j >= i )
+                            assertTrue(A.get(i,j) == B.get(i,j));
+                        else
+                            assertTrue( 0 == B.get(i,j));
+                    }
+                }
+
+                CommonOps.fill(B, 0);
+                BlockMatrixOps.copyTriangle(false,A,B);
+                
+                for( int i = 0; i < numRows; i++) {
+                    for( int j = 0; j < numCols; j++ ) {
+                        if( j <= i )
+                            assertTrue(A.get(i,j) == B.get(i,j));
+                        else
+                            assertTrue( 0 == B.get(i,j));
+                    }
+                }
+            }
+        }
+
+        // now the dst will be smaller than the source
+        BlockMatrix64F B = new BlockMatrix64F(r+1,r+1,r);
+        for( int numRows = 4; numRows <= 6; numRows += 1 ){
+            for( int numCols = 4; numCols <= 6; numCols += 1 ){
+                BlockMatrix64F A = BlockMatrixOps.createRandom(numRows,numCols,-1,1,rand,r);
+                CommonOps.fill(B, 0);
+
+                BlockMatrixOps.copyTriangle(true,A,B);
+
+                for( int i = 0; i < B.numRows; i++) {
+                    for( int j = 0; j < B.numCols; j++ ) {
+                        if( j >= i )
+                            assertTrue(A.get(i,j) == B.get(i,j));
+                        else
+                            assertTrue( 0 == B.get(i,j));
+                    }
+                }
+
+                CommonOps.fill(B, 0);
+                BlockMatrixOps.copyTriangle(false,A,B);
+
+                for( int i = 0; i < B.numRows; i++) {
+                    for( int j = 0; j < B.numCols; j++ ) {
+                        if( j <= i )
+                            assertTrue(A.get(i,j) == B.get(i,j));
+                        else
+                            assertTrue( 0 == B.get(i,j));
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    public void setIdentity() {
+        int r = 3;
+
+        for( int numRows = 2; numRows <= 6; numRows += 2 ){
+            for( int numCols = 2; numCols <= 6; numCols += 2 ){
+                BlockMatrix64F A = BlockMatrixOps.createRandom(numRows,numCols,-1,1,rand,r);
+
+                BlockMatrixOps.setIdentity(A);
+
+                for( int i = 0; i < numRows; i++ ) {
+                    for( int j = 0; j < numCols; j++ ) {
+                        if( i == j )
+                            assertEquals(1.0,A.get(i,j),1e-8);
+                        else
+                            assertEquals(0.0,A.get(i,j),1e-8);
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    public void convertSimple() {
+        BlockMatrix64F A = BlockMatrixOps.createRandom(4,6,-1,1,rand,3);
+
+        SimpleMatrix S = UtilSimpleMatrix.convertSimple(A);
+
+        assertEquals(A.numRows,S.numRows());
+        assertEquals(A.numCols,S.numCols());
+
+        for( int i = 0; i < A.numRows; i++ ) {
+            for( int j = 0; j < A.numCols; j++ ) {
+                assertEquals(A.get(i,j),S.get(i,j),1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void identity() {
+        // test square
+        BlockMatrix64F A = BlockMatrixOps.identity(4,4,3);
+        assertTrue(GenericMatrixOps.isIdentity(A,1e-8));
+
+        // test wide
+        A = BlockMatrixOps.identity(4,5,3);
+        assertTrue(GenericMatrixOps.isIdentity(A,1e-8));
+
+        // test tall
+        A = BlockMatrixOps.identity(5,4,3);
+        assertTrue(GenericMatrixOps.isIdentity(A,1e-8));
+    }
+
+    @Test
+    public void extractAligned() {
+        BlockMatrix64F A = BlockMatrixOps.createRandom(10,11,-1,1,rand,3);
+        BlockMatrix64F B = new BlockMatrix64F(9,11,3);
+
+        BlockMatrixOps.extractAligned(A,B);
+
+        for( int i = 0; i < B.numRows; i++ ) {
+            for( int j = 0; j < B.numCols; j++ ) {
+                assertEquals(A.get(i,j),B.get(i,j),1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void blockAligned() {
+        int r = 3;
+        BlockMatrix64F A = BlockMatrixOps.createRandom(10,11,-1,1,rand,r);
+
+        D1Submatrix64F S = new D1Submatrix64F(A);
+
+        assertTrue(BlockMatrixOps.blockAligned(r,S));
+
+        S.row0 = r;
+        S.col0 = 2*r;
+
+        assertTrue(BlockMatrixOps.blockAligned(r,S));
+
+        // test negative cases
+        S.row0 = r-1;
+        assertFalse(BlockMatrixOps.blockAligned(r,S));
+        S.row0 = 0;
+        S.col0 = 1;
+        assertFalse(BlockMatrixOps.blockAligned(r,S));
+        S.col0 = 0;
+        S.row1 = 8;
+        assertFalse(BlockMatrixOps.blockAligned(r,S));
+        S.row1 = 10;
+        S.col0 = 10;
+        assertFalse(BlockMatrixOps.blockAligned(r,S));
+    }
+
+}
diff --git a/main/dense64/test/org/ejml/alg/block/TestBlockMultiplication.java b/main/dense64/test/org/ejml/alg/block/TestBlockMultiplication.java
new file mode 100644
index 0000000..2e66630
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/block/TestBlockMultiplication.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block;
+
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.D1Submatrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.simple.SimpleMatrix;
+import org.junit.Test;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestBlockMultiplication {
+
+    private static Random rand = new Random(234234);
+
+    private static final int BLOCK_LENGTH = 4;
+    
+    private static final int numRows = 10;
+    private static final int numCols = 13;
+
+    /**
+     * Checks to see if matrix multiplication variants handles submatrices correctly
+     */
+    @Test
+    public void mult_submatrix() {
+        Method methods[] = BlockMultiplication.class.getDeclaredMethods();
+
+        int numFound = 0;
+        for( Method m : methods) {
+            String name = m.getName();
+
+            if( name.contains("Block") || !name.contains("mult") )
+                continue;
+
+//            System.out.println("name = "+name);
+
+            boolean transA = name.contains("TransA");
+            boolean transB = name.contains("TransB");
+
+            int operationType = 0;
+            if( name.contains("Plus")) operationType = 1;
+            else if ( name.contains("Minus")) operationType = -1;
+            else if( name.contains("Set")) operationType = 0;
+
+            checkMult_submatrix(m,operationType,transA,transB);
+            numFound++;
+        }
+
+        // make sure all the functions were in fact tested
+        assertEquals(7,numFound);
+    }
+
+    private static void checkMult_submatrix( Method func , int operationType , boolean transA , boolean transB )
+    {
+        // the submatrix is the same size as the originals
+        checkMult_submatrix( func , operationType , transA , transB , sub(0,0,numRows,numCols),sub(0,0,numCols,numRows));
+
+        // submatrix has a size in multiples of the block
+        checkMult_submatrix( func , operationType , transA , transB , sub(BLOCK_LENGTH, BLOCK_LENGTH, BLOCK_LENGTH *2, BLOCK_LENGTH *2),
+                sub(BLOCK_LENGTH, BLOCK_LENGTH, BLOCK_LENGTH *2, BLOCK_LENGTH *2));
+
+        // submatrix row and column ends at a fraction of a block
+        checkMult_submatrix( func , operationType , transA , transB , sub(BLOCK_LENGTH, BLOCK_LENGTH,numRows,numCols),
+                sub(BLOCK_LENGTH, BLOCK_LENGTH,numCols,numRows));
+
+        // the previous tests have some symmetry in it which can mask errors
+        checkMult_submatrix( func , operationType , transA , transB , sub(0, BLOCK_LENGTH,BLOCK_LENGTH,2*BLOCK_LENGTH),
+                sub(0, BLOCK_LENGTH,BLOCK_LENGTH,numRows));
+    }
+
+    /**
+     * Multiplies the two sub-matrices together.  Checks to see if the same result
+     * is found when multiplied using the normal algorithm versus the submatrix one.
+     */
+    private static void checkMult_submatrix( Method func , int operationType , boolean transA , boolean transB ,
+                                             D1Submatrix64F A , D1Submatrix64F B ) {
+        if( A.col0 % BLOCK_LENGTH != 0 || A.row0 % BLOCK_LENGTH != 0)
+            throw new IllegalArgumentException("Submatrix A is not block aligned");
+        if( B.col0 % BLOCK_LENGTH != 0 || B.row0 % BLOCK_LENGTH != 0)
+            throw new IllegalArgumentException("Submatrix B is not block aligned");
+
+        BlockMatrix64F origA = BlockMatrixOps.createRandom(numRows,numCols,-1,1, rand, BLOCK_LENGTH);
+        BlockMatrix64F origB = BlockMatrixOps.createRandom(numCols,numRows,-1,1, rand, BLOCK_LENGTH);
+
+        A.original = origA;
+        B.original = origB;
+        int w = B.col1-B.col0;
+        int h = A.row1-A.row0;
+
+        // offset it to make the test harder
+        // randomize to see if its set or adding
+        BlockMatrix64F subC = BlockMatrixOps.createRandom(BLOCK_LENGTH +h, BLOCK_LENGTH +w, -1,1,rand, BLOCK_LENGTH);
+        D1Submatrix64F C = new D1Submatrix64F(subC, BLOCK_LENGTH, subC.numRows, BLOCK_LENGTH, subC.numCols);
+
+        DenseMatrix64F rmC = multByExtract(operationType,A,B,C);
+
+        if( transA ) {
+            origA = BlockMatrixOps.transpose(origA,null);
+            transposeSub(A);
+            A.original = origA;
+        }
+
+        if( transB ) {
+            origB = BlockMatrixOps.transpose(origB,null);
+            transposeSub(B);
+            B.original = origB;
+        }
+
+        try {
+            func.invoke(null,BLOCK_LENGTH,A,B,C);
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException(e);
+        } catch (InvocationTargetException e) {
+            throw new RuntimeException(e);
+        }
+
+
+
+        for( int i = C.row0; i < C.row1; i++ ) {
+            for( int j = C.col0; j < C.col1; j++ ) {
+//                System.out.println(i+" "+j);
+                double diff = Math.abs(subC.get(i,j) - rmC.get(i-C.row0,j-C.col0));
+//                System.out.println(subC.get(i,j)+" "+rmC.get(i-C.row0,j-C.col0));
+                if( diff >= 1e-12) {
+                    subC.print();
+                    rmC.print();
+                    System.out.println(func.getName());
+                    System.out.println("transA    "+transA);
+                    System.out.println("transB    "+transB);
+                    System.out.println("type      "+operationType);
+                    fail("Error too large");
+                }
+            }
+        }
+    }
+
+    public static void transposeSub(D1Submatrix64F A) {
+        int temp = A.col0;
+        A.col0 = A.row0;
+        A.row0 = temp;
+        temp = A.col1;
+        A.col1 = A.row1;
+        A.row1 = temp;
+    }
+
+    private static D1Submatrix64F sub( int row0 , int col0 , int row1 , int col1 ) {
+        return new D1Submatrix64F(null,row0, row1, col0, col1);
+    }
+
+    private static DenseMatrix64F multByExtract( int operationType ,
+                                                 D1Submatrix64F subA , D1Submatrix64F subB ,
+                                                 D1Submatrix64F subC )
+    {
+        SimpleMatrix A = SimpleMatrix.wrap(subA.extract());
+        SimpleMatrix B = SimpleMatrix.wrap(subB.extract());
+        SimpleMatrix C = SimpleMatrix.wrap(subC.extract());
+
+        if( operationType > 0 )
+            return A.mult(B).plus(C).getMatrix();
+        else if( operationType < 0 )
+            return C.minus(A.mult(B)).getMatrix();
+        else
+            return A.mult(B).getMatrix();
+    }
+
+
+}
diff --git a/main/dense64/test/org/ejml/alg/block/TestBlockTriangularSolver.java b/main/dense64/test/org/ejml/alg/block/TestBlockTriangularSolver.java
new file mode 100644
index 0000000..4b73926
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/block/TestBlockTriangularSolver.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block;
+
+import org.ejml.alg.generic.GenericMatrixOps;
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.D1Submatrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestBlockTriangularSolver {
+
+    Random rand = new Random(234534);
+
+    @Test
+    public void invert_two() {
+        // block size
+        int r = 3;
+
+        double temp[] = new double[r*r];
+
+        for( int size = 1; size <= 9; size++ ) {
+            BlockMatrix64F T = BlockMatrixOps.createRandom(size,size,-1,1,rand,r);
+            BlockMatrixOps.zeroTriangle(true,T);
+
+            BlockMatrix64F T_inv = T.copy();
+
+            BlockTriangularSolver.invert(r,false,new D1Submatrix64F(T),new D1Submatrix64F(T_inv),temp);
+
+            BlockMatrix64F C = new BlockMatrix64F(size,size,r);
+
+            BlockMatrixOps.mult(T,T_inv,C);
+
+            assertTrue(GenericMatrixOps.isIdentity(C,1e-8));
+
+            // see if passing in the same matrix instance twice messes it up or not
+            BlockTriangularSolver.invert(r,false,new D1Submatrix64F(T),new D1Submatrix64F(T),temp);
+
+            assertTrue(BlockMatrixOps.isEquals(T,T_inv,1e-8));
+        }
+    }
+
+    @Test
+    public void invert_one() {
+        // block size
+        int r = 3;
+
+        double temp[] = new double[r*r];
+
+        for( int size = 1; size <= 9; size++ ) {
+            BlockMatrix64F T = BlockMatrixOps.createRandom(size,size,-1,1,rand,r);
+            BlockMatrixOps.zeroTriangle(true,T);
+
+            BlockMatrix64F T_inv = T.copy();
+
+            BlockTriangularSolver.invert(r,false,new D1Submatrix64F(T_inv),temp);
+
+            BlockMatrix64F C = new BlockMatrix64F(size,size,r);
+
+            BlockMatrixOps.mult(T,T_inv,C);
+
+            assertTrue(GenericMatrixOps.isIdentity(C,1e-8));
+        }
+    }
+
+
+    /**
+     * Test solving several different triangular systems with different sizes.
+     * All matrices begin and end along block boundaries.
+     */
+    @Test
+    public void testSolve() {
+        // block size
+        int r = 3;
+
+        for( int dir = 0; dir < 2; dir++ ) {
+            boolean upper = dir == 0;
+            for( int triangleSize = 1; triangleSize <= 9; triangleSize++ ) {
+                for( int cols = 1; cols <= 9; cols++ ) {
+//                System.out.println("triangle "+triangleSize+" cols "+cols);
+                    BlockMatrix64F T = BlockMatrixOps.createRandom(triangleSize,triangleSize,-1,1,rand,r);
+                    BlockMatrixOps.zeroTriangle(true,T);
+
+                    if( upper ) {
+                        T=BlockMatrixOps.transpose(T,null);
+                    }
+
+                    BlockMatrix64F B = BlockMatrixOps.createRandom(triangleSize,cols,-1,1,rand,r);
+                    BlockMatrix64F Y = new BlockMatrix64F(B.numRows,B.numCols,r);
+
+                    checkSolve(T,B,Y,r,upper,false);
+                    checkSolve(T,B,Y,r,upper,true);
+
+                    // test cases where the submatrix is not aligned with the inner
+                    // blocks
+                    checkSolveUnaligned(T,B,Y,r,upper,false);
+                    checkSolveUnaligned(T,B,Y,r,upper,true);
+                }
+            }
+        }
+    }
+
+    /**
+     * Checks to see if BlockTriangularSolver.solve produces the expected output given
+     * these inputs.  The solution is computed directly.
+     */
+    private void checkSolve( BlockMatrix64F T , BlockMatrix64F B , BlockMatrix64F Y ,
+                             int r , boolean upper , boolean transT )
+    {
+        if( transT ) {
+            BlockMatrix64F T_tran = BlockMatrixOps.transpose(T,null);
+
+            // Compute Y directly from the expected result B
+            BlockMatrixOps.mult(T_tran,B,Y);
+        } else {
+            // Compute Y directly from the expected result B
+            BlockMatrixOps.mult(T,B,Y);
+        }
+
+        // Y is overwritten with the solution
+        BlockTriangularSolver.solve(r,upper,new D1Submatrix64F(T),new D1Submatrix64F(Y),transT);
+
+        assertTrue( BlockMatrixOps.isEquals(B,Y,1e-8));
+    }
+
+    /**
+     * Checks to see if BlockTriangularSolver.solve produces the expected output given
+     * these inputs.  The solution is computed directly.
+     */
+    private void checkSolveUnaligned( BlockMatrix64F T , BlockMatrix64F B , BlockMatrix64F Y ,
+                                      int r , boolean upper , boolean transT )
+    {
+        BlockMatrix64F T2;
+
+        if( upper )
+            T2 = BlockMatrixOps.createRandom(T.numRows+1,T.numCols,-1,1,rand,T.blockLength);
+        else
+            T2 = BlockMatrixOps.createRandom(T.numRows,T.numCols+1,-1,1,rand,T.blockLength);
+
+        CommonOps.insert(T,T2,0,0);
+
+        if( transT ) {
+            BlockMatrix64F T_tran = BlockMatrixOps.transpose(T,null);
+
+            // Compute Y directly from the expected result B
+            BlockMatrixOps.mult(T_tran,B,Y);
+        } else {
+            // Compute Y directly from the expected result B
+            BlockMatrixOps.mult(T,B,Y);
+        }
+
+        int size = T.numRows;
+
+        // Y is overwritten with the solution
+        BlockTriangularSolver.solve(r,upper,new D1Submatrix64F(T2,0,size,0,size),new D1Submatrix64F(Y),transT);
+
+        assertTrue( "Failed upper = "+upper+" transT = "+transT+" T.length "+T.numRows+" B.cols "+B.numCols,
+                BlockMatrixOps.isEquals(B,Y,1e-8));
+    }
+
+
+    /**
+     * Check all permutations of solve for submatrices
+     */
+    @Test
+    public void testSolveBlock() {
+        check_solveBlock_submatrix(false,false,false);
+        check_solveBlock_submatrix(true,false,false);
+        check_solveBlock_submatrix(false,true,false);
+        check_solveBlock_submatrix(true,true,false);
+//        check_solveBlock_submatrix(false,false,true);
+        check_solveBlock_submatrix(true,false,true);
+//        check_solveBlock_submatrix(false,true,true);
+//        check_solveBlock_submatrix(false,true,true);
+    }
+
+    /**
+     * Checks to see if solve functions that use sub matrices as input work correctly
+     */
+    private void check_solveBlock_submatrix( boolean solveL , boolean transT , boolean transB ) {
+        // compute expected solution
+        DenseMatrix64F L = createRandomLowerTriangular(3);
+        DenseMatrix64F B = RandomMatrices.createRandom(3,5,rand);
+        DenseMatrix64F X = new DenseMatrix64F(3,5);
+
+        if( !solveL ) {
+            CommonOps.transpose(L);
+        }
+
+        if( transT ) {
+           CommonOps.transpose(L);
+        }
+
+        CommonOps.solve(L,B,X);
+
+        // do it again using block matrices
+        BlockMatrix64F b_L = BlockMatrixOps.convert(L,3);
+        BlockMatrix64F b_B = BlockMatrixOps.convert(B,3);
+
+        D1Submatrix64F sub_L = new D1Submatrix64F(b_L,0, 3, 0, 3);
+        D1Submatrix64F sub_B = new D1Submatrix64F(b_B,0, 3, 0, 5);
+
+        if( transT ) {
+            sub_L.original = BlockMatrixOps.transpose((BlockMatrix64F)sub_L.original,null);
+            TestBlockMultiplication.transposeSub(sub_L);
+        }
+
+        if( transB ) {
+            sub_B.original = b_B = BlockMatrixOps.transpose((BlockMatrix64F)sub_B.original,null);
+            TestBlockMultiplication.transposeSub(sub_B);
+            CommonOps.transpose(X);
+        }
+
+//        sub_L.original.print();
+//        sub_B.original.print();
+
+        BlockTriangularSolver.solveBlock(3,!solveL,sub_L,sub_B,transT,transB);
+
+        assertTrue(GenericMatrixOps.isEquivalent(X,b_B,1e-10));
+    }
+
+    private DenseMatrix64F createRandomLowerTriangular( int N ) {
+        DenseMatrix64F U = RandomMatrices.createUpperTriangle(N,0,-1,1,rand);
+
+        CommonOps.transpose(U);
+
+        return U;
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/block/TestBlockVectorOps.java b/main/dense64/test/org/ejml/alg/block/TestBlockVectorOps.java
new file mode 100644
index 0000000..04651df
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/block/TestBlockVectorOps.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block;
+
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.D1Submatrix64F;
+import org.ejml.simple.SimpleMatrix;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestBlockVectorOps {
+
+    Random rand = new Random(234);
+    int r = 3;
+
+    @Test
+    public void scale_row() {
+        int rowA=0;
+        int rowB=1;
+
+        double alpha = 1.5;
+
+        for( int width = 1; width <= 3*r; width++ ) {
+//            System.out.println("width "+width);
+            int end = width;
+            int offset = width > 1 ? 1 : 0;
+
+            SimpleMatrix A = SimpleMatrix.random(r,width,-1,1,rand);
+            BlockMatrix64F Ab = BlockMatrixOps.convert(A.getMatrix(),r);
+            BlockMatrix64F Bb = Ab.copy();
+
+            SimpleMatrix b = A.extractVector(true,rowA).scale(alpha);
+
+            BlockVectorOps.scale_row(r,new D1Submatrix64F(Ab),rowA, alpha, new D1Submatrix64F(Bb),rowB,offset,end);
+
+            checkVector_row(rowB, end, offset, A, Bb, b);
+        }
+    }
+
+    @Test
+    public void div_row() {
+        int rowA=0;
+        int rowB=1;
+
+        double alpha = 1.5;
+
+        for( int width = 1; width <= 3*r; width++ ) {
+//            System.out.println("width "+width);
+            int end = width;
+            int offset = width > 1 ? 1 : 0;
+
+            SimpleMatrix A = SimpleMatrix.random(r,width,-1,1,rand);
+            BlockMatrix64F Ab = BlockMatrixOps.convert(A.getMatrix(),r);
+            BlockMatrix64F Bb = Ab.copy();
+
+            SimpleMatrix b = A.extractVector(true,rowA).divide(alpha);
+
+            BlockVectorOps.div_row(r,new D1Submatrix64F(Ab),rowA, alpha, new D1Submatrix64F(Bb),rowB,offset,end);
+
+            checkVector_row(rowB, end, offset, A, Bb, b);
+        }
+    }
+
+    @Test
+    public void add_row() {
+        int rowA=0;
+        int rowB=1;
+        int rowC=2;
+
+        double alpha = 1.5;
+        double beta = -0.7;
+
+        for( int width = 1; width <= 3*r; width++ ) {
+//            System.out.println("width "+width);
+            int end = width;
+            int offset = width > 1 ? 1 : 0;
+
+            SimpleMatrix A = SimpleMatrix.random(r,width,-1,1,rand);
+            SimpleMatrix B = SimpleMatrix.random(r,width,-1,1,rand);
+            BlockMatrix64F Ab = BlockMatrixOps.convert(A.getMatrix(),r);
+            BlockMatrix64F Bb = BlockMatrixOps.convert(B.getMatrix(),r);
+            BlockMatrix64F Cb = Ab.copy();
+
+            SimpleMatrix a = A.extractVector(true,rowA).scale(alpha);
+            SimpleMatrix b = B.extractVector(true,rowB).scale(beta);
+            SimpleMatrix c = a.plus(b);
+
+            BlockVectorOps.add_row(r,
+                    new D1Submatrix64F(Ab),rowA, alpha,
+                    new D1Submatrix64F(Bb),rowB, beta ,
+                    new D1Submatrix64F(Cb),rowC, offset,end);
+
+            checkVector_row(rowC, end, offset, A, Cb, c);
+        }
+    }
+
+    @Test
+    public void dot_row() {
+        int rowA=0;
+        int rowB=1;
+
+
+        for( int width = 1; width <= 3*r; width++ ) {
+//            System.out.println("width "+width);
+            int end = width;
+            int offset = width > 1 ? 1 : 0;
+
+            SimpleMatrix A = SimpleMatrix.random(r,width,-1,1,rand);
+            SimpleMatrix a = A.extractMatrix(rowA,rowA+1,offset,SimpleMatrix.END);
+            SimpleMatrix B = SimpleMatrix.random(r,width,-1,1,rand);
+            SimpleMatrix b = B.extractMatrix(rowB,rowB+1,offset,SimpleMatrix.END);
+
+            BlockMatrix64F Ab = BlockMatrixOps.convert(A.getMatrix(),r);
+            BlockMatrix64F Bb = BlockMatrixOps.convert(B.getMatrix(),r);
+
+            double expected = a.dot(b);
+
+            double found = BlockVectorOps.dot_row(r,new D1Submatrix64F(Ab),rowA, new D1Submatrix64F(Bb),rowB,offset,end);
+
+            assertEquals(expected,found,1e-8);
+        }
+    }
+
+    @Test
+    public void dot_row_col() {
+        int rowA=0;
+        int colB=1;
+
+
+        for( int width = 1; width <= 3*r; width++ ) {
+//            System.out.println("width "+width);
+            int end = width;
+            int offset = width > 1 ? 1 : 0;
+            if( colB >= width) colB = 0;
+
+            SimpleMatrix A = SimpleMatrix.random(width,width,-1,1,rand);
+            SimpleMatrix a = A.extractMatrix(rowA,rowA+1,offset,SimpleMatrix.END);
+            SimpleMatrix B = SimpleMatrix.random(width,width,-1,1,rand);
+            SimpleMatrix b = B.extractMatrix(offset,SimpleMatrix.END,colB,colB+1);
+
+            BlockMatrix64F Ab = BlockMatrixOps.convert(A.getMatrix(),r);
+            BlockMatrix64F Bb = BlockMatrixOps.convert(B.getMatrix(),r);
+
+            double expected = a.dot(b);
+
+            double found = BlockVectorOps.dot_row_col(r,
+                    new D1Submatrix64F(Ab),rowA,
+                    new D1Submatrix64F(Bb),colB,
+                    offset,end);
+
+            assertEquals(expected,found,1e-8);
+        }
+    }
+
+    /**
+     * Checks to see if only the anticipated parts of the matrix have been modified and that
+     * they are the anticipated values.
+     *
+     * @param row The row modified in modMatrix.
+     * @param end end of the vector.
+     * @param offset start of the vector.
+     * @param untouched Original values of the modMatrix.
+     * @param modMatrix The matrix which have been modified by the function being tested.
+     * @param modVector Vector that contains the anticipated values.
+     */
+    public static void checkVector_row(int row, int end, int offset,
+                                       SimpleMatrix untouched,
+                                       BlockMatrix64F modMatrix, SimpleMatrix modVector) {
+        for( int i = 0; i < modMatrix.numRows; i++ ) {
+            if( i == row ) {
+                for( int j = 0; j < offset; j++ ) {
+                    assertEquals(untouched.get(i,j), modMatrix.get(i,j),1e-8);
+                }
+                for( int j = offset; j < end; j++ ) {
+                    assertEquals(modVector.get(j), modMatrix.get(i,j),1e-8);
+                }
+                for( int j = end; j < modMatrix.numCols; j++ ) {
+                    assertEquals(untouched.get(i,j), modMatrix.get(i,j),1e-8);
+                }
+            } else {
+                for( int j = 0; j < modMatrix.numCols; j++ ) {
+                    assertEquals(i+" "+j, untouched.get(i,j), modMatrix.get(i,j),1e-8);
+                }
+            }
+        }
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/block/decomposition/bidiagonal/TestBidiagonalHelper.java b/main/dense64/test/org/ejml/alg/block/decomposition/bidiagonal/TestBidiagonalHelper.java
new file mode 100644
index 0000000..c32d8ab
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/block/decomposition/bidiagonal/TestBidiagonalHelper.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block.decomposition.bidiagonal;
+
+import org.junit.Test;
+
+import java.util.Random;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestBidiagonalHelper {
+
+    final static int r = 3;
+    Random rand = new Random(234);
+
+
+    @Test
+    public void bidiagOuterBlocks() {
+//        SimpleMatrix A = SimpleMatrix.random(r*2+r-1,r*2+r-1,-1,1,rand);
+//        BlockMatrix64F Ab = BlockMatrixOps.convert(A.getMatrix(),r);
+//
+//        A.print();
+//        BidiagonalDecompositionRow decompTest = new BidiagonalDecompositionRow();
+//        assertTrue( decompTest.decompose(A.getMatrix()) );
+//
+//        double gammasU[] = new double[ r*3 ];
+//        double gammasV[] = new double[ r*3 ];
+//
+//        BidiagonalHelper.bidiagOuterBlocks(r,new D1Submatrix64F(Ab),gammasU,gammasV);
+//
+//        for( int i = 0; i < r; i++ ) {
+//            assertEquals(decompTest.getGammasU()[i],gammasU[i],1e-8);
+//            assertEquals(decompTest.getGammasV()[i],gammasV[i],1e-8);
+//        }
+//
+//        for( int i = 0; i < A.numRows(); i++ ) {
+//            for( int j = 0; j < A.numCols(); j++ ) {
+//                if( i < r && j < r ) {
+//                    assertEquals(A.get(i,j),Ab.get(i,j),1e-8);
+//                }
+//            }
+//        }
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/block/decomposition/chol/TestCholeskyOuterForm_B64.java b/main/dense64/test/org/ejml/alg/block/decomposition/chol/TestCholeskyOuterForm_B64.java
new file mode 100644
index 0000000..8ff3c8b
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/block/decomposition/chol/TestCholeskyOuterForm_B64.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block.decomposition.chol;
+
+import org.ejml.alg.block.BlockMatrixOps;
+import org.ejml.alg.generic.GenericMatrixOps;
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.DecompositionFactory;
+import org.ejml.interfaces.decomposition.CholeskyDecomposition;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestCholeskyOuterForm_B64 {
+
+    Random rand = new Random(1231);
+
+    // size of a block
+    int bl = 5;
+
+    /**
+     * Test upper cholesky decomposition for upper triangular.
+     */
+    @Test
+    public void testUpper() {
+        // test against various different sizes
+        for( int N = bl-2; N <= 13; N += 2 ) {
+            DenseMatrix64F A = RandomMatrices.createSymmPosDef(N,rand);
+
+            CholeskyDecomposition<DenseMatrix64F> chol = DecompositionFactory.chol(1,false);
+            assertTrue(DecompositionFactory.decomposeSafe(chol,A));
+
+            DenseMatrix64F expectedT = chol.getT(null);
+
+            BlockMatrix64F blockA = BlockMatrixOps.convert(A,bl);
+
+            CholeskyOuterForm_B64 blockChol = new CholeskyOuterForm_B64(false);
+
+            assertTrue(DecompositionFactory.decomposeSafe(blockChol,blockA));
+
+            assertTrue(GenericMatrixOps.isEquivalent(expectedT,blockChol.getT(null),1e-8));
+
+            double blockDet = blockChol.computeDeterminant().real;
+            double expectedDet = chol.computeDeterminant().real;
+
+            assertEquals(expectedDet,blockDet,1e-8);
+        }
+    }
+
+    /**
+     * Test upper cholesky decomposition for upper triangular.
+     */
+    @Test
+    public void testLower() {
+        // test against various different sizes
+        for( int N = bl-2; N <= 13; N += 2 ) {
+
+            DenseMatrix64F A = RandomMatrices.createSymmPosDef(N,rand);
+
+            CholeskyDecomposition<DenseMatrix64F> chol = DecompositionFactory.chol(1,true);
+            assertTrue(DecompositionFactory.decomposeSafe(chol, A));
+
+            DenseMatrix64F expectedT = chol.getT(null);
+
+            BlockMatrix64F blockA = BlockMatrixOps.convert(A,bl);
+
+            CholeskyOuterForm_B64 blockChol = new CholeskyOuterForm_B64(true);
+
+            assertTrue(DecompositionFactory.decomposeSafe(blockChol,blockA));
+
+            assertTrue(GenericMatrixOps.isEquivalent(expectedT,blockChol.getT(null),1e-8));
+
+            double blockDet = blockChol.computeDeterminant().real;
+            double expectedDet = chol.computeDeterminant().real;
+
+            assertEquals(expectedDet,blockDet,1e-8);
+        }
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/block/decomposition/chol/TestInnerCholesky_B64.java b/main/dense64/test/org/ejml/alg/block/decomposition/chol/TestInnerCholesky_B64.java
new file mode 100644
index 0000000..89e17e6
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/block/decomposition/chol/TestInnerCholesky_B64.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block.decomposition.chol;
+
+import org.ejml.alg.generic.GenericMatrixOps;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.DecompositionFactory;
+import org.ejml.interfaces.decomposition.CholeskyDecomposition;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestInnerCholesky_B64 {
+    Random rand = new Random(234234);
+
+    @Test
+    public void upper() {
+        checkDecompose(5, false);
+        checkNotPositiveDefinite(5,true);
+    }
+
+    @Test
+    public void lower() {
+        checkDecompose(5, true);
+        checkNotPositiveDefinite(5,true);
+    }
+
+    /**
+     * Test a positive case where it should be able to decompose the matrix
+     */
+    private void checkDecompose(int n, boolean lower) {
+        DenseMatrix64F A = RandomMatrices.createSymmPosDef(n,rand);
+
+        // decompose a DenseMatrix64F to find expected solution
+        CholeskyDecomposition<DenseMatrix64F> chol = DecompositionFactory.chol(n,lower);
+
+        assertTrue(DecompositionFactory.decomposeSafe(chol,A));
+
+        DenseMatrix64F expected = chol.getT(null);
+
+        // copy the original data by an offset
+        double data[] = new double[ A.getNumElements() + 2 ];
+        System.arraycopy(A.data,0,data,2,A.getNumElements());
+
+        // decompose using the algorithm
+        if( lower )
+            assertTrue(InnerCholesky_B64.lower(data, 2, n));
+        else
+            assertTrue(InnerCholesky_B64.upper(data, 2, n));
+
+        DenseMatrix64F found = new DenseMatrix64F(n, n);
+        System.arraycopy(data,2,found.data,0,found.data.length);
+
+        // set lower triangular potion to be zero so that it is exactly the same
+        assertTrue(GenericMatrixOps.isEquivalentTriangle(!lower,expected,found,1e-10));
+    }
+
+    /**
+     * See if it fails when the matrix is not positive definite.
+     */
+    private void checkNotPositiveDefinite(int n, boolean lower) {
+        DenseMatrix64F A = new DenseMatrix64F(n,n);
+        for( int i = 0; i < n; i++ ) {
+            for( int j = 0; j < n; j++ ) {
+                A.set(i,j,1);
+            }
+        }
+
+        // copy the original data by an offset
+        double data[] = new double[ A.getNumElements() + 2 ];
+        System.arraycopy(A.data,0,data,2,A.getNumElements());
+
+        // decompose using the algorithm
+        if( lower )
+            assertFalse(InnerCholesky_B64.lower(data, 2, n));
+        else
+            assertFalse(InnerCholesky_B64.upper(data, 2, n));
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/block/decomposition/hessenberg/TestTridiagonalDecompositionHouseholder_B64.java b/main/dense64/test/org/ejml/alg/block/decomposition/hessenberg/TestTridiagonalDecompositionHouseholder_B64.java
new file mode 100644
index 0000000..b1739bb
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/block/decomposition/hessenberg/TestTridiagonalDecompositionHouseholder_B64.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block.decomposition.hessenberg;
+
+import org.ejml.alg.block.BlockMatrixOps;
+import org.ejml.alg.dense.decomposition.hessenberg.TridiagonalDecompositionHouseholderOrig_D64;
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.D1Submatrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.EjmlUnitTests;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.ejml.simple.SimpleMatrix;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestTridiagonalDecompositionHouseholder_B64 {
+
+    Random rand = new Random(23423);
+    int r = 3;
+    
+    @Test
+    public void compareToSimple() {
+
+        for( int width = 1; width <= r*3; width++ ) {
+//            System.out.println("width = "+width);
+            
+            DenseMatrix64F A = RandomMatrices.createSymmetric(width,-1,1,rand);
+            BlockMatrix64F Ab = BlockMatrixOps.convert(A,r);
+
+            TridiagonalDecompositionHouseholderOrig_D64 decomp = new TridiagonalDecompositionHouseholderOrig_D64();
+            decomp.decompose(A);
+
+            DenseMatrix64F expected = decomp.getQT();
+
+            TridiagonalDecompositionHouseholder_B64 decompB = new TridiagonalDecompositionHouseholder_B64();
+            assertTrue(decompB.decompose(Ab));
+
+//            expected.print();
+//            Ab.print();
+
+            // see if the decomposed matrix is the same
+            for( int i = 0; i < width; i++ ) {
+                for( int j = i; j < width; j++ ) {
+                    assertEquals(i+" "+j,expected.get(i,j),Ab.get(i,j),1e-8);
+                }
+            }
+            // check the gammas
+            for( int i = 0; i < width-1; i++ ) {
+                assertEquals(decomp.getGamma(i+1),decompB.gammas[i],1e-8);
+            }
+
+            DenseMatrix64F Q = decomp.getQ(null);
+            BlockMatrix64F Qb = decompB.getQ(null,false);
+
+            EjmlUnitTests.assertEquals(Q,Qb,1e-8);
+        }
+    }
+
+    @Test
+    public void fullTest() {
+        for( int width = 1; width <= r*3; width++ ) {
+            SimpleMatrix A = SimpleMatrix.wrap(RandomMatrices.createSymmetric(width,-1,1,rand));
+            BlockMatrix64F Ab = BlockMatrixOps.convert(A.getMatrix(),r);
+
+            TridiagonalDecompositionHouseholder_B64 alg = new TridiagonalDecompositionHouseholder_B64();
+
+            assertTrue(alg.decompose(Ab));
+
+            BlockMatrix64F Qb = alg.getQ(null,false);
+            BlockMatrix64F Tb = alg.getT(null);
+
+            SimpleMatrix Q = new SimpleMatrix(Qb);
+            SimpleMatrix T = new SimpleMatrix(Tb);
+
+            // reconstruct the original matrix
+            SimpleMatrix A_found = Q.mult(T).mult(Q.transpose());
+
+            assertTrue(MatrixFeatures.isIdentical(A.getMatrix(),A_found.getMatrix(),1e-8));
+        }
+    }
+
+    @Test
+    public void multPlusTransA() {
+        for( int width = r+1; width <= r*3; width++ ) {
+            SimpleMatrix A = SimpleMatrix.random(width,width,-1,1,rand);
+            SimpleMatrix U = SimpleMatrix.random(r,width,-1,1,rand);
+            SimpleMatrix V = SimpleMatrix.random(r,width,-1,1,rand);
+
+            BlockMatrix64F Ab = BlockMatrixOps.convert(A.getMatrix(),r);
+            BlockMatrix64F Ub = BlockMatrixOps.convert(U.getMatrix(),r);
+            BlockMatrix64F Vb = BlockMatrixOps.convert(V.getMatrix(),r);
+
+            SimpleMatrix expected = A.plus(U.transpose().mult(V));
+
+            TridiagonalDecompositionHouseholder_B64.multPlusTransA(r, new D1Submatrix64F(Ub)
+                    , new D1Submatrix64F(Vb), new D1Submatrix64F(Ab));
+
+
+            for( int i = r; i < width; i++ ) {
+                for( int j = i; j < width; j++ ) {
+                    assertEquals(i+" "+j,expected.get(i,j),Ab.get(i,j),1e-8);
+                }
+            }
+        }
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/block/decomposition/hessenberg/TestTridiagonalHelper_B64.java b/main/dense64/test/org/ejml/alg/block/decomposition/hessenberg/TestTridiagonalHelper_B64.java
new file mode 100644
index 0000000..9ea243b
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/block/decomposition/hessenberg/TestTridiagonalHelper_B64.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block.decomposition.hessenberg;
+
+import org.ejml.alg.block.BlockMatrixOps;
+import org.ejml.alg.dense.decomposition.hessenberg.TridiagonalDecompositionHouseholderOrig_D64;
+import org.ejml.alg.generic.GenericMatrixOps;
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.D1Submatrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.RandomMatrices;
+import org.ejml.simple.SimpleMatrix;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestTridiagonalHelper_B64 {
+
+    Random rand = new Random(234324);
+    int r = 3;
+
+    /**
+     * Compare against a simple tridiagonalization implementation
+     */
+    @Test
+    public void tridiagUpperRow() {
+
+        int offX = 0;
+        int offY = 0;
+
+        // test it out on a variety of sizes
+        for( int width = 1; width <= 3*r; width++ ) {
+//            System.out.println("********* width "+width);
+
+            // create a random symmetric matrix
+            SimpleMatrix A = SimpleMatrix.wrap(RandomMatrices.createSymmetric(width,-1,1,rand));
+
+            TridiagonalDecompositionHouseholderOrig_D64 decomp = new TridiagonalDecompositionHouseholderOrig_D64();
+            decomp.decompose(A.getMatrix());
+
+            D1Submatrix64F Ab = insertIntoBlock(offX,offY,A,r);
+            D1Submatrix64F V = new D1Submatrix64F(new BlockMatrix64F(r,offX+A.numCols(),r));
+            V.col0 = offX;
+            V.row1 = Ab.row1-Ab.row0;
+            int gammaOffset = offX;
+            double gammas[] = new double[gammaOffset+A.numCols()];
+            TridiagonalHelper_B64.tridiagUpperRow(r, Ab, gammas, V);
+
+            DenseMatrix64F expected = decomp.getQT();
+
+            // see if the decomposed matrix is the same
+            for( int i = 0; i < r; i++ ) {
+                for( int j = i; j < width; j++ ) {
+                    assertEquals(i+" "+j,expected.get(i,j),Ab.get(i,j),1e-8);
+                }
+            }
+            // check the gammas
+            for( int i = 0; i < Math.min(width-1,r); i++ ) {
+                assertEquals(decomp.getGamma(i+1),gammas[i+gammaOffset],1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void computeW_row() {
+
+        for( int width = r; width <= 3*r; width++ ) {
+//            System.out.println("width!!!  "+width);
+            double betas[] = new double[ r ];
+            for( int i = 0; i < r; i++ )
+                betas[i] = i + 0.5;
+
+            SimpleMatrix A = SimpleMatrix.random(r,width,-1,1,rand);
+
+            // Compute W directly using SimpleMatrix
+            SimpleMatrix v = A.extractVector(true,0);
+            v.set(0,0);
+            v.set(1,1);
+            SimpleMatrix Y = v;
+            SimpleMatrix W = v.scale(-betas[0]);
+
+            for( int i = 1; i < A.numRows(); i++ ) {
+                v = A.extractVector(true,i);
+
+                for( int j = 0; j <= i; j++ )
+                    v.set(j,0);
+                if( i+1 < A.numCols())
+                    v.set(i+1,1);
+
+                SimpleMatrix z = v.transpose().plus(W.transpose().mult(Y.mult(v.transpose()))).scale(-betas[i]);
+
+                W = W.combine(i,0,z.transpose());
+                Y = Y.combine(i,0,v);
+            }
+
+            // now compute it using the block matrix stuff
+            BlockMatrix64F Ab = BlockMatrixOps.convert(A.getMatrix(),r);
+            BlockMatrix64F Wb = new BlockMatrix64F(Ab.numRows,Ab.numCols,r);
+
+            D1Submatrix64F Ab_sub = new D1Submatrix64F(Ab);
+            D1Submatrix64F Wb_sub = new D1Submatrix64F(Wb);
+
+            TridiagonalHelper_B64.computeW_row(r, Ab_sub, Wb_sub, betas, 0);
+
+            // see if the result is the same
+            assertTrue(GenericMatrixOps.isEquivalent(Wb,W.getMatrix(),1e-8));
+        }
+    }
+
+    @Test
+    public void applyReflectorsToRow() {
+
+        // try different offsets to make sure there are no implicit assumptions
+        for( int offX = 0; offX <= r; offX += r) {
+            for( int offY = 0; offY <= r; offY += r) {
+                SimpleMatrix A = SimpleMatrix.random(2*r+2,2*r+2,-1,1,rand);
+                A = A.mult(A.transpose());
+                SimpleMatrix A_orig = A.copy();
+                SimpleMatrix V = SimpleMatrix.random(r,A.numCols(),-1,1,rand);
+
+                D1Submatrix64F Ab = insertIntoBlock(offY,offX,A,r);
+                D1Submatrix64F Vb = insertIntoBlock(0,offX,V,r);
+
+                int row = r-1;
+
+                // manually apply "reflectors" to A
+                for( int i = 0; i < row; i++ ) {
+                    SimpleMatrix u = A_orig.extractVector(true,i).transpose();
+                    SimpleMatrix v = V.extractVector(true,i).transpose();
+
+                    for( int j = 0; j <= i; j++ ) {
+                        u.set(j,0.0);
+                    }
+                    u.set(i+1,1.0);
+
+                    A = A.plus(u.mult(v.transpose())).plus(v.mult(u.transpose()));
+                }
+                // apply the reflector to that row
+                TridiagonalHelper_B64.applyReflectorsToRow(r, Ab, Vb, row);
+
+                // compare to manually computed solution
+                for( int i = row; i < A.numCols(); i++ ) {
+                    assertEquals(A.get(row,i),Ab.get(row,i),1e-8);
+                }
+            }
+        }
+    }
+
+    private static D1Submatrix64F insertIntoBlock( int offRow , int offCol , SimpleMatrix A , int r )
+    {
+        DenseMatrix64F B = new DenseMatrix64F(offRow+A.numRows(),offCol+A.numCols());
+        CommonOps.insert(A.getMatrix(),B,offRow,offCol);
+
+        BlockMatrix64F C = BlockMatrixOps.convert(B,r);
+        return new D1Submatrix64F(C,offRow,C.numRows,offCol,C.numCols);
+    }
+
+    @Test
+    public void multA_u() {
+        SimpleMatrix A = SimpleMatrix.random(2*r+2,2*r+2,-1,1,rand);
+        // make a symmetric so that this mult will work
+        A = A.transpose().mult(A);
+
+        BlockMatrix64F Ab = BlockMatrixOps.convert(A.getMatrix(),r);
+        BlockMatrix64F V = new BlockMatrix64F(r,Ab.numCols,r);
+
+        int row = 1;
+
+        SimpleMatrix u = A.extractVector(true,row).transpose();
+        for( int i = 0; i <= row; i++ ) {
+            u.set(i,0);
+        }
+        u.set(row+1,1);
+
+        SimpleMatrix v = A.mult(u).transpose();
+
+        TridiagonalHelper_B64.multA_u(r, new D1Submatrix64F(Ab),
+                new D1Submatrix64F(V), row);
+
+        for( int i = row+1; i < A.numCols(); i++ ) {
+            assertEquals(v.get(i),V.get(row,i),1e-8);
+        }
+    }
+
+    /**
+     * Check by performing the calculation manually
+     */
+    @Test
+    public void computeY() {
+        SimpleMatrix A = SimpleMatrix.random(2*r+2,2*r+2,-1,1,rand);
+        A = A.transpose().mult(A); // needs to be symmetric to pass
+        SimpleMatrix Vo = SimpleMatrix.random(r,A.numCols(),-1,1,rand);
+
+        for( int row = 0; row < r; row++ ) {
+            SimpleMatrix AA = A.copy();
+            SimpleMatrix u = A.extractVector(true,row).transpose();
+            SimpleMatrix y;
+
+            double gamma = 1.3;
+
+            // zero elements that should already be zero
+            for( int i = 0; i < row; i++ ) {
+                u.set(i,0);
+                for( int j = i+2; j < A.numRows(); j++ ) {
+                    AA.set(i,j,0);
+                    AA.set(j,i,0);
+                }
+            }
+            u.set(row,0);
+            u.set(row+1,1);
+
+            if( row > 0 ) {
+                SimpleMatrix U = A.extractMatrix(0,row,0,A.numCols()).transpose();
+                SimpleMatrix V = Vo.extractMatrix(0,row,0,A.numCols()).transpose();
+
+                for( int i = 0; i < row; i++ ) {
+                    for( int j = 0; j <= i; j++ ) {
+                        U.set(j,i,0);
+                    }
+                    U.set(i+1,i,1);
+                }
+
+                y = AA.plus(U.mult(V.transpose())).plus(V.mult(U.transpose())).mult(u);
+            } else {
+                y = AA.mult(u);
+            }
+
+            y = y.scale(-gamma);
+
+            BlockMatrix64F Ab = BlockMatrixOps.convert(A.getMatrix(),r);
+            BlockMatrix64F Vb = BlockMatrixOps.convert(Vo.getMatrix(),r);
+
+            TridiagonalHelper_B64.computeY(r, new D1Submatrix64F(Ab), new D1Submatrix64F(Vb), row, gamma);
+
+            for( int i = row+1; i < A.numCols(); i++ ) {
+                assertEquals(Vb.get(row,i),y.get(i),1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void computeRowOfV() {
+        SimpleMatrix A = SimpleMatrix.random(2*r+2,2*r+2,-1,1,rand);
+        SimpleMatrix V = SimpleMatrix.random(r,A.numCols(),-1,1,rand);
+
+        double gamma = 2.3;
+
+        for( int row = 0; row < r; row++ ) {
+            SimpleMatrix u = A.extractVector(true,row).transpose();
+            SimpleMatrix y = V.extractVector(true,row).transpose();
+
+            for( int i = 0; i <= row; i++ ) {
+                u.set(i,0);
+            }
+            u.set(row+1,1);
+
+            SimpleMatrix v = y.plus(u.scale(-(gamma/2.0)*u.dot(y)));
+
+            BlockMatrix64F Ab = BlockMatrixOps.convert(A.getMatrix(),r);
+            BlockMatrix64F Vb = BlockMatrixOps.convert(V.getMatrix(),r);
+
+            TridiagonalHelper_B64.computeRowOfV(r, new D1Submatrix64F(Ab), new D1Submatrix64F(Vb),
+                    row, gamma);
+
+            for( int i = row+1; i < A.numCols(); i++ ) {
+                assertEquals(Vb.get(row,i),v.get(i),1e-8);
+            }
+        }
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/block/decomposition/qr/GenericBlock64QrDecompositionTests.java b/main/dense64/test/org/ejml/alg/block/decomposition/qr/GenericBlock64QrDecompositionTests.java
new file mode 100644
index 0000000..c1a074f
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/block/decomposition/qr/GenericBlock64QrDecompositionTests.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block.decomposition.qr;
+
+import org.ejml.alg.block.BlockMatrixOps;
+import org.ejml.alg.dense.decomposition.qr.QRDecompositionHouseholderTran_D64;
+import org.ejml.alg.generic.GenericMatrixOps;
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.EjmlUnitTests;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * Generic tests that test the compliance of an implementation of QRDecomposition(BlockMatrix64F).
+ *
+ * @author Peter Abeles
+ */
+public class GenericBlock64QrDecompositionTests {
+    Random rand = new Random(324);
+
+    int r = 3;
+
+    QRDecompositionHouseholder_B64 alg;
+
+    public GenericBlock64QrDecompositionTests(QRDecompositionHouseholder_B64 alg) {
+        this.alg = alg;
+    }
+
+    /**
+     * Runs all the tests.
+     */
+    public void allTests() {
+        applyQ();
+        applyQTran();
+        checkInternalData();
+        fullDecomposition();
+    }
+
+    /**
+     * Test applyQTran() by explicitly computing Q and compare the results of multiplying
+     * a matrix by Q<sup>T</sup> and applying Q to it.
+     */
+    public void applyQTran() {
+        for( int i = 1; i <= 3*r; i++ ) {
+            for( int j = 1; j <= 3*r; j++ ) {
+                BlockMatrix64F A = BlockMatrixOps.createRandom(i,j,-1,1,rand,r);
+
+                assertTrue(alg.decompose(A.copy()));
+
+                BlockMatrix64F Q = alg.getQ(null,false);
+
+                BlockMatrix64F B = BlockMatrixOps.createRandom(i,j,-1,1,rand,r);
+                BlockMatrix64F expected = new BlockMatrix64F(i,j,r);
+
+                BlockMatrixOps.multTransA(Q,B,expected);
+                alg.applyQTran(B);
+
+                assertTrue(MatrixFeatures.isIdentical(expected,B,1e-8));
+            }
+        }
+    }
+
+    /**
+     * Test applyQ() by explicitly computing Q and compare the results of multiplying
+     * a matrix by Q and applying Q to it.
+     */
+    public void applyQ() {
+        for( int i = 1; i <= 3*r; i++ ) {
+            for( int j = 1; j <= 3*r; j++ ) {
+                BlockMatrix64F A = BlockMatrixOps.createRandom(i,j,-1,1,rand,r);
+
+                assertTrue(alg.decompose(A.copy()));
+
+                BlockMatrix64F Q = alg.getQ(null,false);
+
+                BlockMatrix64F B = BlockMatrixOps.createRandom(i,j,-1,1,rand,r);
+                BlockMatrix64F expected = new BlockMatrix64F(i,j,r);
+
+                BlockMatrixOps.mult(Q,B,expected);
+                alg.applyQ(B);
+
+                assertTrue(MatrixFeatures.isIdentical(expected,B,1e-8));
+            }
+        }
+    }
+
+    /**
+     * Decomposes the matrix and checks the internal data structure for correctness.
+     */
+    public void checkInternalData() {
+        for( int i = 1; i <= 3*r; i++ ) {
+            for( int j = 1; j <= 3*r; j++ ) {
+//                System.out.println("i = "+i+" j = "+j);
+                checkSize(i,j);
+            }
+        }
+    }
+
+    private void checkSize( int numRows , int numCols ) {
+        DenseMatrix64F A = RandomMatrices.createRandom(numRows,numCols,-1,1,rand);
+        BlockMatrix64F Ab = BlockMatrixOps.convert(A,r);
+
+        QRDecompositionHouseholderTran_D64 algCheck = new QRDecompositionHouseholderTran_D64();
+        assertTrue(algCheck.decompose(A));
+
+        assertTrue(alg.decompose(Ab));
+
+        DenseMatrix64F expected = CommonOps.transpose(algCheck.getQR(),null);
+//        expected.print();
+//        Ab.print();
+
+        EjmlUnitTests.assertEquals(expected,Ab,1e-8);
+    }
+
+    /**
+     * Decomposes the matrix and computes Q and R.  Verifies the results by
+     * multiplying Q and R together and seeing if it gets A.
+     */
+    public void fullDecomposition() {
+        for( int i = 1; i <= 3*r; i++ ) {
+            for( int j = 1; j <= 3*r; j++ ) {
+//                i=4;j=4;
+//                System.out.println("i = "+i+" j = "+j);
+                checkFullDecomposition(i,j,true);
+                checkFullDecomposition(i,j,false);
+            }
+        }
+    }
+
+    private void checkFullDecomposition( int numRows , int numCols , boolean compact ) {
+        BlockMatrix64F A = BlockMatrixOps.createRandom(numRows,numCols,-1,1,rand,r);
+
+        assertTrue(alg.decompose(A.copy()));
+
+        BlockMatrix64F Q = alg.getQ(null,compact);
+        BlockMatrix64F R = alg.getR(null,compact);
+
+        BlockMatrix64F found = new BlockMatrix64F(numRows,numCols,r);
+
+        BlockMatrixOps.mult(Q,R,found);
+
+        assertTrue(GenericMatrixOps.isEquivalent(A,found,1e-8));
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/block/decomposition/qr/TestBlockHouseHolder.java b/main/dense64/test/org/ejml/alg/block/decomposition/qr/TestBlockHouseHolder.java
new file mode 100644
index 0000000..3804907
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/block/decomposition/qr/TestBlockHouseHolder.java
@@ -0,0 +1,504 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block.decomposition.qr;
+
+import org.ejml.alg.block.BlockMatrixOps;
+import org.ejml.alg.dense.decomposition.qr.QRDecompositionHouseholderTran_D64;
+import org.ejml.alg.dense.mult.VectorVectorMult;
+import org.ejml.alg.generic.GenericMatrixOps;
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.D1Submatrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.RandomMatrices;
+import org.ejml.simple.SimpleMatrix;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestBlockHouseHolder {
+
+    Random rand = new Random(234);
+
+    // the block length
+    int r = 3;
+
+    SimpleMatrix A, Y,V,W;
+
+    @Test
+    public void decomposeQR_block_col() {
+        DenseMatrix64F A = RandomMatrices.createRandom(r*2+r-1,r,-1,1,rand);
+        BlockMatrix64F Ab = BlockMatrixOps.convert(A,r);
+
+        QRDecompositionHouseholderTran_D64 algTest = new QRDecompositionHouseholderTran_D64();
+        assertTrue(algTest.decompose(A));
+
+        double gammas[] = new double[A.numCols];
+        BlockHouseHolder.decomposeQR_block_col(r,new D1Submatrix64F(Ab),gammas);
+
+        DenseMatrix64F expected = CommonOps.transpose(algTest.getQR(),null);
+
+        assertTrue(GenericMatrixOps.isEquivalent(expected,Ab,1e-8));
+    }
+
+    @Test
+    public void rank1UpdateMultR_Col() {
+
+        // check various sized matrices
+        double gamma = 2.5;
+        A = SimpleMatrix.random(r*2+r-1,r*2-1,-1,1,rand);
+
+        SimpleMatrix U = A.extractMatrix(0,A.numRows(),1,2);
+        U.set(0,0,0);
+        U.set(1,0,1);
+
+        SimpleMatrix V = A.extractMatrix(0,A.numRows(),2,3);
+        SimpleMatrix expected = V.minus(U.mult(U.transpose().mult(V)).scale(gamma));
+
+        BlockMatrix64F Ab = BlockMatrixOps.convert(A.getMatrix(),r);
+
+        BlockHouseHolder.rank1UpdateMultR_Col(r,new D1Submatrix64F(Ab),1,gamma);
+
+        for( int i = 1; i < expected.numRows(); i++ ) {
+            assertEquals(expected.get(i,0),Ab.get(i,2),1e-8);
+        }
+    }
+
+    @Test
+    public void rank1UpdateMultR_TopRow() {
+        double gamma = 2.5;
+        A = SimpleMatrix.random(r*2+r-1,r*2-1,-1,1,rand);
+
+        SimpleMatrix U = A.extractMatrix(0,A.numRows(),1,2);
+        U.set(0,0,0);
+        U.set(1,0,1);
+
+        BlockMatrix64F Ab = BlockMatrixOps.convert(A.getMatrix(),r);
+
+        BlockHouseHolder.rank1UpdateMultR_TopRow(r,new D1Submatrix64F(Ab),1,gamma);
+
+        // check all the columns now
+        for( int i = 0; i < r; i++ ) {
+            for( int j = r; j < A.numCols(); j++ ) {
+                SimpleMatrix V = A.extractMatrix(0,A.numRows(),j,j+1);
+                SimpleMatrix expected = V.minus(U.mult(U.transpose().mult(V)).scale(gamma));
+
+                assertEquals(i+" "+j,expected.get(i,0),Ab.get(i,j),1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void rank1UpdateMultL_Row() {
+        double gamma = 2.5;
+        A = SimpleMatrix.random(r*2+r-1,r*2+r-1,-1,1,rand);
+
+        SimpleMatrix U = A.extractMatrix(1,2,0,A.numCols()).transpose();
+        U.set(0,0);
+        U.set(1,1);
+
+        SimpleMatrix expected = A.minus( A.mult(U).mult(U.transpose()).scale(gamma) );
+
+        BlockMatrix64F Ab = BlockMatrixOps.convert(A.getMatrix(),r);
+
+        BlockHouseHolder.rank1UpdateMultL_Row(r,new D1Submatrix64F(Ab),1,1,gamma);
+
+        for( int j = 1; j < expected.numCols(); j++ ) {
+            assertEquals(expected.get(2,j),Ab.get(2,j),1e-8);
+        }
+    }
+
+    @Test
+    public void rank1UpdateMultL_LeftCol() {
+        double gamma = 2.5;
+        A = SimpleMatrix.random(r*2+r-1,r*2+r-1,-1,1,rand);
+
+        int row = 0;
+        int zeroOffset = 1;
+        SimpleMatrix U = A.extractMatrix(row,row+1,0,A.numCols()).transpose();
+        for( int i = 0; i < row+zeroOffset; i++ )
+            U.set(i,0);
+        U.set(row+zeroOffset,1);
+
+        SimpleMatrix expected = A.minus( A.mult(U).mult(U.transpose()).scale(gamma) );
+
+        BlockMatrix64F Ab = BlockMatrixOps.convert(A.getMatrix(),r);
+
+        BlockHouseHolder.rank1UpdateMultL_LeftCol(r,new D1Submatrix64F(Ab),row,gamma,zeroOffset);
+
+        for( int i = r; i < A.numRows(); i++ ) {
+            for( int j = 0; j < r; j++ ) {
+                assertEquals(expected.get(i,j),Ab.get(i,j),1e-8);
+            }
+        }
+    }
+
+    /**
+     * Check inner product when column blocks have two different widths
+     */
+    @Test
+    public void innerProdCol() {
+        DenseMatrix64F A = RandomMatrices.createRandom(r*2+r-1,r*3-1,-1,1,rand);
+        BlockMatrix64F Ab = BlockMatrixOps.convert(A,r);
+
+        int row = 0;
+        int innerCol = 1;
+        for( int colBlock = 0; colBlock < r*2; colBlock+=r) {
+            int colA = colBlock+innerCol;
+            int colB = colA+innerCol+1;
+            int widthA = Math.min(r,A.numCols - (colA-colA%r));
+            int widthB = Math.min(r,A.numCols - (colB-colB%r));
+
+            DenseMatrix64F v0 = CommonOps.extract(A,row,A.numRows,colA,colA+1);
+            DenseMatrix64F v1 = CommonOps.extract(A,row,A.numRows,colB,colB+1);
+            for( int j = 0; j < innerCol; j++ ) {
+                v0.set(j,0.0);
+            }
+            v0.set(innerCol,1.0);
+
+            double expected = VectorVectorMult.innerProd(v0,v1);
+
+            D1Submatrix64F subAb = new D1Submatrix64F(Ab,row,A.numRows,colBlock,A.numCols);
+
+            double found = BlockHouseHolder.innerProdCol(r,subAb,colA-colBlock,widthA,colB-colBlock,widthB);
+
+            assertEquals(expected,found,1e-8);
+        }
+    }
+
+
+    @Test
+    public void innerProdRow() {
+        DenseMatrix64F A = RandomMatrices.createRandom(r*3-1,r*2+r-1,-1,1,rand);
+        BlockMatrix64F Ab = BlockMatrixOps.convert(A,r);
+
+        int zeroOffset = 1;
+        for( int rowBlock = 0; rowBlock < r*2; rowBlock+=r) {
+            int rowA = 2;
+            int rowB = 1;
+
+            DenseMatrix64F v0 = CommonOps.extract(A,rowBlock+rowA,rowBlock+rowA+1,0,A.numCols);
+            DenseMatrix64F v1 = CommonOps.extract(A,rowBlock+rowB,rowBlock+rowB+1,0,A.numCols);
+            for( int j = 0; j < rowA+zeroOffset; j++ ) {
+                v0.set(j,0.0);
+            }
+            v0.set(rowA+zeroOffset,1.0);
+
+            double expected = VectorVectorMult.innerProd(v0,v1);
+
+            D1Submatrix64F subAb = new D1Submatrix64F(Ab,rowBlock,A.numRows,0,A.numCols);
+
+            double found = BlockHouseHolder.innerProdRow(r, subAb,rowA,subAb,rowB,zeroOffset);
+
+            assertEquals(expected,found,1e-8);
+        }
+    }
+
+    @Test
+    public void divideElementsCol() {
+
+        double div = 1.5;
+        int col = 1;
+        BlockMatrix64F A = BlockMatrixOps.createRandom(r*2+r-1,r,-1,1,rand,r);
+        BlockMatrix64F A_orig = A.copy();
+
+        BlockHouseHolder.divideElementsCol(r,new D1Submatrix64F(A),col,div);
+
+        for( int i = col+1; i < A.numRows; i++ ) {
+            assertEquals(A_orig.get(i,col)/div , A.get(i,col),1e-8);
+        }
+    }
+
+    @Test
+    public void scale_row() {
+
+        double div = 1.5;
+        int row = 1;
+        BlockMatrix64F A = BlockMatrixOps.createRandom(r*2+r-1,r*2+1,-1,1,rand,r);
+        BlockMatrix64F A_orig = A.copy();
+
+        BlockHouseHolder.scale_row(r,new D1Submatrix64F(A),new D1Submatrix64F(A),row,1,div);
+
+        // check the one
+        assertEquals(div,A.get(row,row+1),1e-8);
+        // check the rest
+        for( int i = row+2; i < A.numCols; i++ ) {
+            assertEquals(A_orig.get(row,i)*div , A.get(row,i),1e-8);
+        }
+    }
+
+    @Test
+    public void add_row() {
+        int rowA=0;
+        int rowB=1;
+        int rowC=2;
+
+        double alpha = 1.5;
+        double beta = -0.7;
+
+        for( int width = 1; width <= 3*r; width++ ) {
+//            System.out.println("width "+width);
+            int end = width;
+
+            SimpleMatrix A = SimpleMatrix.random(r,width,-1,1,rand);
+            SimpleMatrix B = SimpleMatrix.random(r,width,-1,1,rand);
+            BlockMatrix64F Ab = BlockMatrixOps.convert(A.getMatrix(),r);
+            BlockMatrix64F Bb = BlockMatrixOps.convert(B.getMatrix(),r);
+            BlockMatrix64F Cb = Ab.copy();
+
+            // turn A into householder vectors
+            for( int i = 0; i < A.numRows(); i++ ) {
+                for( int j = 0; j <= i; j++ ) {
+                    if( A.isInBounds(i,j))
+                        A.set(i,j,0);
+                }
+                if( A.isInBounds(i,i+1) )
+                    A.set(i,i+1,1);
+            }
+
+            SimpleMatrix a = A.extractVector(true,rowA).scale(alpha);
+            SimpleMatrix b = B.extractVector(true,rowB).scale(beta);
+            SimpleMatrix c = a.plus(b);
+
+            BlockHouseHolder.add_row(r,
+                    new D1Submatrix64F(Ab),rowA, alpha,
+                    new D1Submatrix64F(Bb),rowB, beta ,
+                    new D1Submatrix64F(Cb),rowC, 1,end);
+
+            // skip over the zeros
+            for( int j = rowA+1; j < end; j++ ) {
+                assertEquals(c.get(j), Cb.get(rowC,j),1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void computeTauAndDivideCol() {
+
+        double max = 1.5;
+        int col = 1;
+        BlockMatrix64F A = BlockMatrixOps.createRandom(r*2+r-1,r,-1,1,rand,r);
+        BlockMatrix64F A_orig = A.copy();
+
+        // manual alg
+        double expected = 0;
+        for( int i = col; i < A.numRows; i++ ) {
+            double val = A.get(i,col)/max;
+            expected += val*val;
+        }
+        expected = Math.sqrt(expected);
+
+        double found = BlockHouseHolder.computeTauAndDivideCol(r,new D1Submatrix64F(A),col,max);
+
+        assertEquals(expected,found,1e-8);
+
+        for( int i = col; i < A.numRows; i++ ) {
+            assertEquals(A_orig.get(i,col)/max , A.get(i,col),1e-8);
+        }
+
+    }
+
+    @Test
+    public void computeTauAndDivideRow() {
+        double max = 1.5;
+        int row = 1;
+        int colStart = row+1;
+        BlockMatrix64F A = BlockMatrixOps.createRandom(r*2+r-1,r*2+1,-1,1,rand,r);
+        BlockMatrix64F A_orig = A.copy();
+
+        // manual alg
+        double expected = 0;
+        for( int j = colStart; j < A.numCols; j++ ) {
+            double val = A.get(row,j)/max;
+            expected += val*val;
+        }
+        expected = Math.sqrt(expected);
+
+        double found = BlockHouseHolder.computeTauAndDivideRow(r,new D1Submatrix64F(A),row,colStart,max);
+
+        assertEquals(expected,found,1e-8);
+
+        for( int j = colStart; j < A.numCols; j++ ) {
+            assertEquals(A_orig.get(row,j)/max , A.get(row,j),1e-8);
+        }
+    }
+
+    @Test
+    public void testFindMaxCol() {
+        BlockMatrix64F A = BlockMatrixOps.createRandom(r*2+r-1,r,-1,1,rand,r);
+
+        // make sure it ignores the first element
+        A.set(0,1,100000);
+        A.set(5,1,-2346);
+
+        double max = BlockHouseHolder.findMaxCol(r,new D1Submatrix64F(A),1);
+
+        assertEquals(2346,max,1e-8);
+    }
+
+    @Test
+    public void testFindMaxRow() {
+        BlockMatrix64F A = BlockMatrixOps.createRandom(r*2+r-1,r*2-1,-1,1,rand,r);
+
+        // make sure it ignores the first element
+        A.set(1,1,100000);
+        A.set(1,4,-2346);
+
+        double max = BlockHouseHolder.findMaxRow(r,new D1Submatrix64F(A),1,2);
+
+        assertEquals(2346,max,1e-8);
+    }
+
+    @Test
+    public void computeW_Column() {
+        double betas[] = new double[]{1.2,2,3};
+
+        A = SimpleMatrix.random(r*2+r-1,r,-1,1,rand);
+
+        // Compute W directly using SimpleMatrix
+        SimpleMatrix V = A.extractMatrix(0,A.numRows(),0,1);
+        V.set(0,0,1);
+        SimpleMatrix Y = V;
+        SimpleMatrix W = V.scale(-betas[0]);
+
+        for( int i = 1; i < A.numCols(); i++ ) {
+            V = A.extractMatrix(0,A.numRows(),i,i+1);
+
+            for( int j = 0; j < i; j++ )
+                V.set(j,0,0);
+            V.set(i,0,1);
+
+            SimpleMatrix z = V.plus(W.mult(Y.transpose().mult(V))).scale(-betas[i]);
+            W = W.combine(0,i,z);
+            Y = Y.combine(0,i,V);
+        }
+
+        // now compute it using the block matrix stuff
+        double temp[] = new double[ r ];
+
+        BlockMatrix64F Ab = BlockMatrixOps.convert(A.getMatrix(),r);
+        BlockMatrix64F Wb = new BlockMatrix64F(Ab.numRows,Ab.numCols,r);
+
+        D1Submatrix64F Ab_sub = new D1Submatrix64F(Ab);
+        D1Submatrix64F Wb_sub = new D1Submatrix64F(Wb);
+
+        BlockHouseHolder.computeW_Column(r,Ab_sub,Wb_sub,temp,betas,0);
+
+        // see if the result is the same
+        assertTrue(GenericMatrixOps.isEquivalent(Wb,W.getMatrix(),1e-8));
+    }
+
+    @Test
+    public void initializeW() {
+        initMatrices(r-1);
+
+        double beta = 1.5;
+
+        BlockMatrix64F Wb = BlockMatrixOps.convert(W.getMatrix(),r);
+        BlockMatrix64F Ab = BlockMatrixOps.convert(A.getMatrix(),r);
+
+        D1Submatrix64F Wb_sub = new D1Submatrix64F(Wb,0, W.numRows(), 0, r);
+        D1Submatrix64F Yb_sub = new D1Submatrix64F(Ab,0, A.numRows(), 0, r);
+
+        BlockHouseHolder.initializeW(r,Wb_sub,Yb_sub,r,beta);
+
+        assertEquals(-beta,Wb.get(0,0),1e-8);
+
+        for( int i = 1; i < Wb.numRows; i++ ) {
+            assertEquals(-beta*Ab.get(i,0),Wb.get(i,0),1e-8);
+        }
+    }
+
+    @Test
+    public void computeZ() {
+        int M = r-1;
+        initMatrices(M);
+
+        double beta = 2.5;
+
+        BlockMatrix64F Ab = BlockMatrixOps.convert(A.getMatrix(),r);
+        BlockMatrix64F Aw = BlockMatrixOps.convert(W.getMatrix(),r);
+
+        // need to extract only the elements in W that are currently being used when
+        // computing the expected Z
+        W = W.extractMatrix(0,W.numRows(),0,M);
+        SimpleMatrix T = SimpleMatrix.random(M,1,-1,1,rand);
+
+        // -beta * (V + W*T)
+        SimpleMatrix expected = V.plus(W.mult(T)).scale(-beta);
+
+        BlockHouseHolder.computeZ(r,new D1Submatrix64F(Ab,0, A.numRows(), 0, r),new D1Submatrix64F(Aw,0, A.numRows(), 0, r),
+                M,T.getMatrix().data,beta);
+
+        for( int i = 0; i < A.numRows(); i++ ) {
+            assertEquals(expected.get(i),Aw.get(i,M),1e-8);
+        }
+    }
+
+    @Test
+    public void computeY_t_V() {
+        int M = r-2;
+        initMatrices(M);
+
+        // Y'*V
+        SimpleMatrix expected = Y.transpose().mult(V);
+
+        BlockMatrix64F Ab = BlockMatrixOps.convert(A.getMatrix(),r);
+        double found[] = new double[ M ];
+
+        BlockHouseHolder.computeY_t_V(r,new D1Submatrix64F(Ab,0, A.numRows(), 0, r),M,found);
+
+        for( int i = 0; i < M; i++ ) {
+            assertEquals(expected.get(i),found[i],1e-8);
+        }
+    }
+
+    private void initMatrices( int M ) {
+        A = SimpleMatrix.random(r*2+r-1,r,-1,1,rand);
+
+        // create matrices that are used to test
+        Y = A.extractMatrix(0,A.numRows(),0,M);
+        V = A.extractMatrix(0,A.numRows(),M,M+1);
+
+        // add in zeros and ones
+        setZerosY();
+        for( int i = 0; i < M; i++ ) {
+            V.set(i,0);
+        }
+        V.set(M,1);
+
+        W = SimpleMatrix.random(r*2+r-1,r,-1,1,rand);
+    }
+
+    private void setZerosY() {
+        for( int j = 0; j < Y.numCols(); j++ ) {
+            for( int i = 0; i < j; i++ ) {
+                Y.set(i,j,0);
+            }
+            Y.set(j,j,1);
+        }
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/block/decomposition/qr/TestQRDecompositionHouseholder_B64.java b/main/dense64/test/org/ejml/alg/block/decomposition/qr/TestQRDecompositionHouseholder_B64.java
new file mode 100644
index 0000000..3d1b0c1
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/block/decomposition/qr/TestQRDecompositionHouseholder_B64.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block.decomposition.qr;
+
+import org.junit.Test;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestQRDecompositionHouseholder_B64 {
+
+    @Test
+    public void generic() {
+        QRDecompositionHouseholder_B64 decomp = new QRDecompositionHouseholder_B64();
+
+        GenericBlock64QrDecompositionTests tests;
+        tests = new GenericBlock64QrDecompositionTests(decomp);
+
+        tests.allTests();
+    }
+
+    @Test
+    public void genericSaveW() {
+        QRDecompositionHouseholder_B64 decomp = new QRDecompositionHouseholder_B64();
+        decomp.setSaveW(true);
+
+        GenericBlock64QrDecompositionTests tests;
+        tests = new GenericBlock64QrDecompositionTests(decomp);
+
+        tests.allTests();
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/block/linsol/chol/TestBlockCholeskyOuterSolver.java b/main/dense64/test/org/ejml/alg/block/linsol/chol/TestBlockCholeskyOuterSolver.java
new file mode 100644
index 0000000..d07cecb
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/block/linsol/chol/TestBlockCholeskyOuterSolver.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block.linsol.chol;
+
+import org.ejml.alg.block.BlockMatrixOps;
+import org.ejml.alg.block.linsol.qr.BlockQrHouseHolderSolver;
+import org.ejml.alg.generic.GenericMatrixOps;
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestBlockCholeskyOuterSolver {
+    protected Random rand = new Random(234234);
+
+    protected int r = 3;
+
+    /**
+     * Test positive examples against a variety of different inputs shapes.
+     */
+    @Test
+    public void testPositiveSolve() {
+        BlockCholeskyOuterSolver solver = new BlockCholeskyOuterSolver();
+
+        for( int i = 1; i <= r*3; i++ ) {
+            for( int j = 1; j <= r*3; j++ ) {
+                BlockMatrix64F A = createMatrixSPD(i);
+                BlockMatrix64F X = BlockMatrixOps.createRandom(i,j,-1,1,rand,r);
+                BlockMatrix64F Y = new BlockMatrix64F(i,j,r);
+                BlockMatrix64F X_found = new BlockMatrix64F(i,j,r);
+
+                // compute the expected solution directly
+                BlockMatrixOps.mult(A,X,Y);
+
+                assertTrue(solver.setA(A.copy()));
+
+                solver.solve(Y,X_found);
+
+                assertTrue(BlockMatrixOps.isEquals(X,X_found,1e-8));
+            }
+        }
+    }
+
+    /**
+     * Give it a matrix which is not SPD and see if it fails
+     */
+    @Test
+    public void testNegativeSolve() {
+        BlockCholeskyOuterSolver solver = new BlockCholeskyOuterSolver();
+
+        BlockMatrix64F X = BlockMatrixOps.createRandom(7,7,-1,1,rand,r);
+
+        assertFalse(solver.setA(X));
+    }
+
+    @Test
+    public void testInvert() {
+        BlockCholeskyOuterSolver solver = new BlockCholeskyOuterSolver();
+
+        for( int i = 1; i <= r*3; i++ ) {
+            BlockMatrix64F A = createMatrixSPD(i);
+            BlockMatrix64F A_inv = BlockMatrixOps.createRandom(i,i,-1,1,rand,r);
+
+            assertTrue(solver.setA(A.copy()));
+
+            solver.invert(A_inv);
+
+            BlockMatrix64F B = new BlockMatrix64F(i,i,r);
+
+            BlockMatrixOps.mult(A,A_inv,B);
+
+            assertTrue(GenericMatrixOps.isIdentity(B,1e-8));
+        }
+    }
+
+    @Test
+    public void testQuality() {
+        BlockCholeskyOuterSolver solver = new BlockCholeskyOuterSolver();
+
+        DenseMatrix64F A = CommonOps.diag(5,3,2,1);
+        DenseMatrix64F B = CommonOps.diag(5,3,2,0.001);
+
+        assertTrue(solver.setA(BlockMatrixOps.convert(A,r)));
+        double qualityA = solver.quality();
+
+        assertTrue(solver.setA(BlockMatrixOps.convert(B,r)));
+        double qualityB = solver.quality();
+
+        assertTrue(qualityB < qualityA);
+        assertTrue(qualityB*10.0 < qualityA);
+    }
+
+    @Test
+    public void testQuality_scale() {
+        BlockCholeskyOuterSolver solver = new BlockCholeskyOuterSolver();
+
+        DenseMatrix64F A = CommonOps.diag(5,3,2,1);
+        DenseMatrix64F B = A.copy();
+        CommonOps.scale(0.001,B);
+
+        assertTrue(solver.setA(BlockMatrixOps.convert(A,r)));
+        double qualityA = solver.quality();
+
+        assertTrue(solver.setA(BlockMatrixOps.convert(B,r)));
+        double qualityB = solver.quality();
+
+        assertEquals(qualityB,qualityA,1e-8);
+    }
+
+    @Test
+    public void testPositiveSolveNull() {
+        BlockCholeskyOuterSolver solver = new BlockCholeskyOuterSolver();
+
+        for( int i = 1; i <= r*3; i++ ) {
+            for( int j = 1; j <= r*3; j++ ) {
+                BlockMatrix64F A = createMatrixSPD(i);
+                BlockMatrix64F X = BlockMatrixOps.createRandom(i,j,-1,1,rand,r);
+                BlockMatrix64F Y = new BlockMatrix64F(i,j,r);
+                BlockMatrix64F X_found = new BlockMatrix64F(i,j,r);
+
+                // compute the expected solution directly
+                BlockMatrixOps.mult(A,X,Y);
+
+                assertTrue(solver.setA(A.copy()));
+
+                solver.solve(Y,null);
+
+                assertTrue(BlockMatrixOps.isEquals(X,Y,1e-8));
+            }
+        }
+    }
+
+    @Test
+    public void modifiesA(){
+        BlockMatrix64F A = createMatrixSPD(4);
+        BlockMatrix64F A_orig = A.copy();
+
+        BlockQrHouseHolderSolver solver = new BlockQrHouseHolderSolver();
+
+        assertTrue(solver.setA(A));
+
+        boolean modified = !MatrixFeatures.isEquals(A,A_orig);
+
+        assertTrue(modified == solver.modifiesA());
+    }
+
+    @Test
+    public void modifiesB(){
+        BlockMatrix64F A = createMatrixSPD(4);
+
+        BlockQrHouseHolderSolver solver = new BlockQrHouseHolderSolver();
+
+        assertTrue(solver.setA(A));
+
+        BlockMatrix64F B = BlockMatrixOps.createRandom(4,2,-1,1,rand,3);
+        BlockMatrix64F B_orig = B.copy();
+        BlockMatrix64F X = new BlockMatrix64F(A.numRows,B.numCols,3);
+
+        solver.solve(B,X);
+
+        boolean modified = !MatrixFeatures.isEquals(B_orig,B);
+
+        assertTrue(modified == solver.modifiesB());
+    }
+
+    protected BlockMatrix64F createMatrixSPD( int width ) {
+        DenseMatrix64F A = RandomMatrices.createSymmPosDef(width,rand);
+
+        return BlockMatrixOps.convert(A,r);
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/block/linsol/qr/TestBlockQrHouseHolderSolver.java b/main/dense64/test/org/ejml/alg/block/linsol/qr/TestBlockQrHouseHolderSolver.java
new file mode 100644
index 0000000..b4ab9d8
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/block/linsol/qr/TestBlockQrHouseHolderSolver.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block.linsol.qr;
+
+import org.ejml.alg.block.BlockMatrixOps;
+import org.ejml.alg.generic.GenericMatrixOps;
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.MatrixFeatures;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestBlockQrHouseHolderSolver {
+
+    Random rand = new Random(23423);
+
+    /**
+     * Test positive examples against a variety of different inputs shapes.
+     */
+    @Test
+    public void testPositiveSolve() {
+        int r = 3;
+        BlockQrHouseHolderSolver solver = new BlockQrHouseHolderSolver();
+
+        for( int i = 1; i <= r*3; i++ ) {
+            for( int j = i; j <= r*3; j++ ) {
+                for( int k = 1; k <= r*3; k++ ) {
+//                    System.out.println("i = "+i+" j = "+j+" k = "+k);
+                    BlockMatrix64F A = BlockMatrixOps.createRandom(j,i,-1,1,rand,r);
+                    BlockMatrix64F X = BlockMatrixOps.createRandom(i,k,-1,1,rand,r);
+                    BlockMatrix64F Y = new BlockMatrix64F(j,k,r);
+                    BlockMatrix64F X_found = new BlockMatrix64F(i,k,r);
+
+                    // compute the expected solution directly
+                    BlockMatrixOps.mult(A,X,Y);
+
+                    assertTrue(solver.setA(A.copy()));
+
+                    solver.solve(Y,X_found);
+
+                    assertTrue(BlockMatrixOps.isEquals(X,X_found,1e-8));
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testInvert() {
+        int r = 3;
+        BlockQrHouseHolderSolver solver = new BlockQrHouseHolderSolver();
+
+        for( int i = 1; i <= r*3; i++ ) {
+            BlockMatrix64F A = BlockMatrixOps.createRandom(i,i,-1,1,rand,r);
+
+            BlockMatrix64F A_orig = A.copy();
+            BlockMatrix64F I = new BlockMatrix64F(i,i,r);
+
+            assertTrue(solver.setA(A.copy()));
+
+            solver.invert(A);
+
+            // A times its inverse is an identity matrix
+            BlockMatrixOps.mult(A,A_orig,I);
+
+            assertTrue(GenericMatrixOps.isIdentity(I,1e-8));
+        }
+    }
+
+    @Test
+    public void testQuality() {
+        BlockMatrix64F A = BlockMatrixOps.convert(CommonOps.diag(4,3,2,1),3);
+        BlockMatrix64F B = BlockMatrixOps.convert(CommonOps.diag(4,3,2,0.1),3);
+
+        // see if a matrix with smaller singular value has a worse quality
+        BlockQrHouseHolderSolver solver = new BlockQrHouseHolderSolver();
+        assertTrue(solver.setA(A.copy()));
+        double qualityA = solver.quality();
+
+        assertTrue(solver.setA(B.copy()));
+        double qualityB = solver.quality();
+
+        assertTrue(qualityB<qualityA);
+        assertEquals(qualityB*10.0,qualityA,1e-8);
+    }
+
+    /**
+     * Checks to see if quality is scale invariant.
+     */
+    @Test
+    public void testQuality_scale() {
+        BlockMatrix64F A = BlockMatrixOps.convert(CommonOps.diag(4,3,2,1),3);
+        BlockMatrix64F B = A.copy();
+        CommonOps.scale(2,B);
+
+        // see if a matrix with smaller singular value has a worse quality
+        BlockQrHouseHolderSolver solver = new BlockQrHouseHolderSolver();
+        assertTrue(solver.setA(A.copy()));
+        double qualityA = solver.quality();
+
+        assertTrue(solver.setA(B.copy()));
+        double qualityB = solver.quality();
+
+        assertEquals(qualityA,qualityB,1e-8);
+    }
+
+    @Test
+    public void modifiesA(){
+        BlockMatrix64F A = BlockMatrixOps.createRandom(4,4,-1,1,rand,3);
+        BlockMatrix64F A_orig = A.copy();
+
+        BlockQrHouseHolderSolver solver = new BlockQrHouseHolderSolver();
+
+        assertTrue(solver.setA(A));
+
+        boolean modified = !MatrixFeatures.isEquals(A,A_orig);
+
+        assertTrue(modified == solver.modifiesA());
+    }
+
+    @Test
+    public void modifiesB(){
+        BlockMatrix64F A = BlockMatrixOps.createRandom(4,4,-1,1,rand,3);
+
+        BlockQrHouseHolderSolver solver = new BlockQrHouseHolderSolver();
+
+        assertTrue(solver.setA(A));
+
+        BlockMatrix64F B = BlockMatrixOps.createRandom(4,2,-1,1,rand,3);
+        BlockMatrix64F B_orig = B.copy();
+        BlockMatrix64F X = new BlockMatrix64F(A.numRows,B.numCols,3);
+
+        solver.solve(B,X);
+
+        boolean modified = !MatrixFeatures.isEquals(B_orig,B);
+
+        assertTrue(modified == solver.modifiesB());
+    }
+
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/CheckDecompositionInterface.java b/main/dense64/test/org/ejml/alg/dense/decomposition/CheckDecompositionInterface.java
new file mode 100644
index 0000000..6261b43
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/CheckDecompositionInterface.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.DecompositionInterface;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class CheckDecompositionInterface {
+
+    /**
+     * Performs a decomposition and makes sure the input matrix is not modified.
+     */
+    public static boolean safeDecomposition( DecompositionInterface decomp , DenseMatrix64F A ) {
+
+        DenseMatrix64F A_orig = decomp.inputModified() ? A.copy() : A;
+
+        return decomp.decompose(A_orig);
+    }
+
+
+    /**
+     * Checks to see if the matrix is or is not modified as according to the modified
+     * flag.
+     *
+     * @param decomp
+     */
+    public static void checkModifiedInput( DecompositionInterface decomp ) {
+        DenseMatrix64F A = RandomMatrices.createSymmPosDef(4,new Random(0x434));
+        DenseMatrix64F A_orig = A.copy();
+
+        assertTrue(decomp.decompose(A));
+
+        boolean modified = !MatrixFeatures.isEquals(A,A_orig);
+
+        assertTrue(modified+" "+decomp.inputModified(),decomp.inputModified()==modified);
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/TestBaseDecomposition_B64_to_D64.java b/main/dense64/test/org/ejml/alg/dense/decomposition/TestBaseDecomposition_B64_to_D64.java
new file mode 100644
index 0000000..eb5b654
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/TestBaseDecomposition_B64_to_D64.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition;
+
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.DecompositionInterface;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestBaseDecomposition_B64_to_D64 {
+
+
+
+
+    /**
+     * Make sure the input is never modified.  Also checks to see if the matrix was correctly converted from
+     * row into block format and back
+     */
+    @Test
+    public void inputModified() {
+
+        DenseMatrix64F A = new DenseMatrix64F(25,20);
+        for (int i = 0; i < A.data.length; i++) {
+            A.data[i] = i;
+        }
+
+        // Should not modify the input in this case
+        BaseDecomposition_B64_to_D64 alg = new BaseDecomposition_B64_to_D64(new DoNotModifyBlock(),10);
+
+        assertFalse(alg.inputModified());
+        assertTrue(alg.decompose(A));
+        for (int i = 0; i < A.data.length; i++) {
+            assertEquals(i,A.data[i],1e-8);
+        }
+
+        // test it with a decomposition which modifies the input
+        alg = new BaseDecomposition_B64_to_D64(new ModifyBlock(),10);
+
+        assertTrue(alg.inputModified());
+        assertTrue(alg.decompose(A));
+    }
+
+    private static class ModifyBlock implements DecompositionInterface<BlockMatrix64F> {
+
+        @Override
+        public boolean decompose(BlockMatrix64F orig) {
+
+            // see if the input was correctly converted
+            int val = 0;
+            for (int i = 0; i < orig.numRows; i++) {
+                for (int j = 0; j < orig.numCols; j++,val++) {
+                    assertEquals(val,orig.get(i,j),1e-8);
+                }
+            }
+
+            // modify it now
+            Arrays.fill(orig.data,1);
+
+            return true;
+        }
+
+        @Override
+        public boolean inputModified() {
+            return true;
+        }
+    }
+
+    private static class DoNotModifyBlock implements DecompositionInterface<BlockMatrix64F> {
+
+        @Override
+        public boolean decompose(BlockMatrix64F orig) {
+            return true;
+        }
+
+        @Override
+        public boolean inputModified() {
+            return false;
+        }
+    }
+
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/TestTriangularSolver.java b/main/dense64/test/org/ejml/alg/dense/decomposition/TestTriangularSolver.java
new file mode 100644
index 0000000..5d56547
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/TestTriangularSolver.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition;
+
+import org.ejml.alg.dense.misc.UnrolledInverseFromMinor;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestTriangularSolver {
+
+    Random rand = new Random(0xff);
+
+
+    @Test
+    public void invert_inplace() {
+        DenseMatrix64F L = createRandomLowerTriangular();
+
+        DenseMatrix64F L_inv = L.copy();
+
+        TriangularSolver.invertLower(L_inv.data,L.numRows);
+
+        DenseMatrix64F I = new DenseMatrix64F(L.numRows,L.numCols);
+
+        CommonOps.mult(L,L_inv,I);
+
+        assertTrue(MatrixFeatures.isIdentity(I,1e-8));
+    }
+
+    @Test
+    public void invert() {
+        DenseMatrix64F L = createRandomLowerTriangular();
+
+        DenseMatrix64F L_inv = L.copy();
+
+        TriangularSolver.invertLower(L.data,L_inv.data,L.numRows);
+
+        DenseMatrix64F I = new DenseMatrix64F(L.numRows,L.numCols);
+
+        CommonOps.mult(L,L_inv,I);
+
+        assertTrue(MatrixFeatures.isIdentity(I,1e-8));
+    }
+
+    @Test
+    public void solveL_vector() {
+        DenseMatrix64F L = createRandomLowerTriangular();
+
+        DenseMatrix64F L_inv = L.copy();
+        UnrolledInverseFromMinor.inv(L_inv,L_inv);
+
+        DenseMatrix64F B = RandomMatrices.createRandom(3,1,rand);
+        DenseMatrix64F expected = RandomMatrices.createRandom(3,1,rand);
+        DenseMatrix64F found = B.copy();
+
+        TriangularSolver.solveL(L.data,found.data,3);
+        CommonOps.mult(L_inv,B,expected);
+
+
+        assertTrue(MatrixFeatures.isIdentical(expected,found,1e-8));
+    }
+
+    private DenseMatrix64F createRandomLowerTriangular() {
+        DenseMatrix64F L = RandomMatrices.createRandom(3,3,rand);
+        for( int i = 0; i < L.numRows; i++ ) {
+            for( int j = i+1; j < L.numCols; j++ ) {
+                L.set(i,j,0);
+            }
+        }
+        return L;
+    }
+
+    @Test
+    public void solveL_matrix() {
+        DenseMatrix64F L = createRandomLowerTriangular();
+
+        DenseMatrix64F L_inv = L.copy();
+        UnrolledInverseFromMinor.inv(L_inv,L_inv);
+
+        DenseMatrix64F B = RandomMatrices.createRandom(3,4,rand);
+        DenseMatrix64F expected = RandomMatrices.createRandom(3,4,rand);
+        DenseMatrix64F found = B.copy();
+
+        TriangularSolver.solveL(L.data,found.data,3,4);
+        CommonOps.mult(L_inv,B,expected);
+
+        assertTrue(MatrixFeatures.isIdentical(expected,found,1e-8));
+    }
+
+    @Test
+    public void solveTranL() {
+        DenseMatrix64F L = createRandomLowerTriangular();
+
+        DenseMatrix64F B = RandomMatrices.createRandom(3,1,rand);
+        DenseMatrix64F expected = RandomMatrices.createRandom(3,1,rand);
+        DenseMatrix64F found = B.copy();
+
+        TriangularSolver.solveTranL(L.data,found.data,3);
+
+        CommonOps.transpose(L);
+        DenseMatrix64F L_inv = L.copy();
+        UnrolledInverseFromMinor.inv(L_inv,L_inv);
+        CommonOps.mult(L_inv,B,expected);
+
+        assertTrue(MatrixFeatures.isIdentical(expected,found,1e-8));
+    }
+
+    @Test
+    public void solveU() {
+        DenseMatrix64F U = RandomMatrices.createRandom(3,3,rand);
+        for( int i = 0; i < U.numRows; i++ ) {
+            for( int j = 0; j < i; j++ ) {
+                U.set(i,j,0);
+            }
+        }
+
+        DenseMatrix64F U_inv = U.copy();
+        UnrolledInverseFromMinor.inv(U_inv,U_inv);
+
+        DenseMatrix64F B = RandomMatrices.createRandom(3,1,rand);
+        DenseMatrix64F expected = RandomMatrices.createRandom(3,1,rand);
+        DenseMatrix64F found = B.copy();
+
+        TriangularSolver.solveU(U.data,found.data,3);
+        CommonOps.mult(U_inv,B,expected);
+
+        assertTrue(MatrixFeatures.isIdentical(expected,found,1e-8));
+    }
+
+    @Test
+    public void solveU_submatrix() {
+
+        // create U and B.  Insert into a larger matrix
+        DenseMatrix64F U_orig = RandomMatrices.createRandom(3,3,rand);
+        for( int i = 0; i < U_orig.numRows; i++ ) {
+            for( int j = 0; j < i; j++ ) {
+                U_orig.set(i,j,0);
+            }
+        }
+        DenseMatrix64F U = new DenseMatrix64F(6,7);
+        CommonOps.insert(U_orig,U,2,3);
+        
+        
+        DenseMatrix64F B_orig = RandomMatrices.createRandom(3,2,rand);
+
+        DenseMatrix64F B = new DenseMatrix64F(4,5);
+        CommonOps.insert(B_orig,B,1,2);
+        
+        // compute expected solution
+        DenseMatrix64F U_inv = U_orig.copy();
+        UnrolledInverseFromMinor.inv(U_inv,U_inv);
+
+        DenseMatrix64F expected = RandomMatrices.createRandom(3,2,rand);
+
+        int startU = 2*U.numCols+3;
+        int strideU = U.numCols;
+        int widthU = U_orig.numCols;
+        int startB = 1*B.numCols+2;
+        int strideB = B.numCols;
+        int widthB = B_orig.numCols;
+        TriangularSolver.solveU(U.data,startU,strideU,widthU,B.data,startB,strideB,widthB);
+
+        DenseMatrix64F found = CommonOps.extract(B,1,4,2,4);
+        CommonOps.mult(U_inv,B_orig,expected);
+
+        assertTrue(MatrixFeatures.isIdentical(expected,found,1e-8));
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/bidiagonal/GenericBidiagonalCheck.java b/main/dense64/test/org/ejml/alg/dense/decomposition/bidiagonal/GenericBidiagonalCheck.java
new file mode 100644
index 0000000..dc4e126
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/bidiagonal/GenericBidiagonalCheck.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.bidiagonal;
+
+import org.ejml.alg.dense.decomposition.CheckDecompositionInterface;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.BidiagonalDecomposition;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.ejml.simple.SimpleMatrix;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public abstract class GenericBidiagonalCheck {
+    protected Random rand = new Random(0xff);
+
+    abstract protected BidiagonalDecomposition<DenseMatrix64F> createQRDecomposition();
+
+    @Test
+    public void testModifiedInput() {
+        CheckDecompositionInterface.checkModifiedInput(createQRDecomposition());
+    }
+
+    @Test
+    public void testRandomMatrices() {
+        BidiagonalDecomposition<DenseMatrix64F> decomp = createQRDecomposition();
+
+        for( int i = 0; i < 10; i++ ) {
+            for( int N = 2;  N <= 10; N++ ) {
+                for( int tall = 0; tall <= 2; tall++ ) {
+                    DenseMatrix64F A = RandomMatrices.createRandom(N+tall,N,rand);
+
+                    assertTrue(decomp.decompose(A.copy()));
+
+                    checkGeneric(A, decomp);
+                }
+                for( int wide = 1; wide <= 2; wide++ ) {
+                    DenseMatrix64F A = RandomMatrices.createRandom(N,N+wide,rand);
+
+                    assertTrue(decomp.decompose(A.copy()));
+
+                    checkGeneric(A, decomp);
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testIdentity() {
+        SimpleMatrix A = SimpleMatrix.identity(5);
+
+        BidiagonalDecomposition<DenseMatrix64F> decomp = createQRDecomposition();
+
+        assertTrue(decomp.decompose(A.getMatrix().copy()));
+
+        checkGeneric(A.getMatrix(), decomp);
+    }
+
+    @Test
+    public void testZero() {
+        SimpleMatrix A = new SimpleMatrix(5,5);
+
+        BidiagonalDecomposition<DenseMatrix64F> decomp = createQRDecomposition();
+
+        assertTrue(decomp.decompose(A.getMatrix().copy()));
+
+        checkGeneric(A.getMatrix(), decomp);
+    }
+
+    /**
+     * Checks to see if the decomposition will reconstruct the original input matrix
+     */
+    protected void checkGeneric(DenseMatrix64F a,
+                                BidiagonalDecomposition<DenseMatrix64F> decomp) {
+        // check the full version
+        SimpleMatrix U = SimpleMatrix.wrap(decomp.getU(null,false,false));
+        SimpleMatrix B = SimpleMatrix.wrap(decomp.getB(null,false));
+        SimpleMatrix V = SimpleMatrix.wrap(decomp.getV(null,false,false));
+
+        DenseMatrix64F foundA = U.mult(B).mult(V.transpose()).getMatrix();
+
+        assertTrue(MatrixFeatures.isIdentical(a,foundA,1e-8));
+
+        //       check with transpose
+        SimpleMatrix Ut = SimpleMatrix.wrap(decomp.getU(null,true,false));
+
+        assertTrue(U.transpose().isIdentical(Ut,1e-8));
+
+        SimpleMatrix Vt = SimpleMatrix.wrap(decomp.getV(null,true,false));
+
+        assertTrue(V.transpose().isIdentical(Vt,1e-8));
+
+//        U.print();
+//        V.print();
+//        B.print();
+//        System.out.println("------------------------");
+
+        // now test compact
+        U = SimpleMatrix.wrap(decomp.getU(null,false,true));
+        B = SimpleMatrix.wrap(decomp.getB(null,true));
+        V = SimpleMatrix.wrap(decomp.getV(null,false,true));
+
+//        U.print();
+//        V.print();
+//        B.print();
+
+        foundA = U.mult(B).mult(V.transpose()).getMatrix();
+
+        assertTrue(MatrixFeatures.isIdentical(a,foundA,1e-8));
+
+        //       check with transpose
+        Ut = SimpleMatrix.wrap(decomp.getU(null,true,true));
+        Vt = SimpleMatrix.wrap(decomp.getV(null,true,true));
+
+        assertTrue(U.transpose().isIdentical(Ut,1e-8));
+        assertTrue(V.transpose().isIdentical(Vt,1e-8));
+    }
+
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/bidiagonal/TestBidiagonalDecompositionRow_D64.java b/main/dense64/test/org/ejml/alg/dense/decomposition/bidiagonal/TestBidiagonalDecompositionRow_D64.java
new file mode 100644
index 0000000..0ceb929
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/bidiagonal/TestBidiagonalDecompositionRow_D64.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.bidiagonal;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.data.RowD1Matrix64F;
+import org.ejml.interfaces.decomposition.BidiagonalDecomposition;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.ejml.ops.SpecializedOps;
+import org.ejml.simple.SimpleMatrix;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestBidiagonalDecompositionRow_D64 extends GenericBidiagonalCheck {
+
+
+    /**
+     * See if the naive implementation and this version produce the same results.
+     */
+    @Test
+    public void testAgainstNaive() {
+        for( int i = 1; i <= 5; i++ ) {
+            for( int j = 1; j <= 5; j++ ) {
+                checkNaive(i,j);
+            }
+        }
+    }
+
+    private void checkNaive(int m, int n) {
+        SimpleMatrix A = SimpleMatrix.wrap(RandomMatrices.createRandom(m,n,rand));
+
+        BidiagonalDecompositionRow_D64 decomp = new BidiagonalDecompositionRow_D64();
+        BidiagonalDecompositionNaive_D64 naive = new BidiagonalDecompositionNaive_D64();
+
+        assertTrue(decomp.decompose(A.getMatrix().copy()));
+        assertTrue(naive.decompose(A.getMatrix()));
+
+        SimpleMatrix U = SimpleMatrix.wrap(decomp.getU(null,false,false));
+        SimpleMatrix B = SimpleMatrix.wrap(decomp.getB(null,false));
+        SimpleMatrix V = SimpleMatrix.wrap(decomp.getV(null,false,false));
+
+//        U.print();
+//        B.print();
+//        naive.getB().print();
+//        V.print();
+//        naive.getV().print();
+
+//        naive.getVTran().print();
+
+        assertTrue(naive.getB().isIdentical(B,1e-8));
+        assertTrue(naive.getU().isIdentical(U,1e-8));
+        assertTrue(naive.getV().isIdentical(V,1e-8));
+
+        // check the decomposition
+        DenseMatrix64F foundA = U.mult(B).mult(V.transpose()).getMatrix();
+
+//        A.print();
+//        foundA.print();
+
+        assertTrue(MatrixFeatures.isIdentical(A.getMatrix(),foundA,1e-8));
+    }
+
+    @Test
+    public void testComputeU()
+    {
+        int m = 7;
+        int n = 5;
+
+        DenseMatrix64F A = RandomMatrices.createRandom(m,n,rand);
+
+        DebugBidiagonal alg = new DebugBidiagonal(A);
+
+        DenseMatrix64F B = new DenseMatrix64F(A);
+
+        DenseMatrix64F C = new DenseMatrix64F(m,n);
+        DenseMatrix64F u = new DenseMatrix64F(m,1);
+
+        RowD1Matrix64F UBV = alg.getUBV();
+
+        for( int i = 0; i < n; i++ ) {
+            alg.computeU(i);
+
+            SpecializedOps.subvector(UBV,i+1,i,m-i-1,false,i+1,u);
+            u.data[i] = 1;
+
+            DenseMatrix64F Q = SpecializedOps.createReflector(u,alg.getGammasU()[i]);
+
+            CommonOps.mult(Q,B,C);
+
+//            u.print();
+//            B.print();
+//            UBV.print();
+//            C.print();
+
+            B.set(C);
+
+            // make sure everything is as expected
+            for( int j = i+1; j < m; j++ ) {
+                assertEquals(0,C.get(j,i),1e-8);
+            }
+
+            for( int j = i+1; j < n; j++ ) {
+                assertEquals(UBV.get(i,j),C.get(i,j),1e-8);
+            }
+            u.data[i] = 0;
+        }
+    }
+
+    @Test
+    public void testComputeV()
+    {
+        int m = 7;
+        int n = 5;
+
+        DenseMatrix64F A = RandomMatrices.createRandom(m,n,rand);
+
+        DebugBidiagonal alg = new DebugBidiagonal(A);
+
+        DenseMatrix64F B = new DenseMatrix64F(A);
+
+        DenseMatrix64F C = new DenseMatrix64F(m,n);
+        DenseMatrix64F u = new DenseMatrix64F(n,1);
+
+        RowD1Matrix64F UBV = alg.getUBV();
+
+//        A.print();
+
+        for( int i = 0; i < n-2; i++ ) {
+            alg.computeV(i);
+
+            u.zero();
+            SpecializedOps.subvector(UBV,i,i+2,n-i-2,true,i+2,u);
+            u.data[i+1] = 1;
+
+            DenseMatrix64F Q = SpecializedOps.createReflector(u,alg.getGammasV()[i]);
+
+//            Q.print();
+
+            CommonOps.mult(B,Q,C);
+
+//            u.print();
+//            B.print();
+//            UBV.print();
+//            C.print();
+
+            B.set(C);
+
+            // make sure everything is as expected
+            for( int j = i+2; j < n; j++ ) {
+                assertEquals(0,C.get(i,j),1e-8);
+            }
+
+            for( int j = i+2; j < m; j++ ) {
+                assertEquals(UBV.get(j,i),C.get(j,i),1e-8);
+            }
+            u.data[i] = 0;
+        }
+
+    }
+
+    @Override
+    protected BidiagonalDecomposition<DenseMatrix64F> createQRDecomposition() {
+        return new BidiagonalDecompositionRow_D64();
+    }
+
+    private static class DebugBidiagonal extends BidiagonalDecompositionRow_D64 {
+
+
+        public DebugBidiagonal( DenseMatrix64F A ) {
+            init(A.copy());
+        }
+
+        @Override
+        protected void computeU(int k) {
+            super.computeU(k);
+        }
+
+        @Override
+        protected void computeV(int k) {
+            super.computeV(k);
+        }
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/bidiagonal/TestBidiagonalDecompositionTall_D64.java b/main/dense64/test/org/ejml/alg/dense/decomposition/bidiagonal/TestBidiagonalDecompositionTall_D64.java
new file mode 100644
index 0000000..d4996e7
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/bidiagonal/TestBidiagonalDecompositionTall_D64.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.bidiagonal;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.BidiagonalDecomposition;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestBidiagonalDecompositionTall_D64 extends GenericBidiagonalCheck {
+    @Override
+    protected BidiagonalDecomposition<DenseMatrix64F> createQRDecomposition() {
+        return new BidiagonalDecompositionTall_D64();
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/chol/GenericCholeskyTests_D64.java b/main/dense64/test/org/ejml/alg/dense/decomposition/chol/GenericCholeskyTests_D64.java
new file mode 100644
index 0000000..6ce5dc4
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/chol/GenericCholeskyTests_D64.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.chol;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.DecompositionFactory;
+import org.ejml.interfaces.decomposition.CholeskyDecomposition;
+import org.ejml.interfaces.decomposition.LUDecomposition;
+import org.ejml.ops.EjmlUnitTests;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.ejml.simple.SimpleMatrix;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * @author Peter Abeles
+ */
+public abstract class GenericCholeskyTests_D64 {
+    Random rand = new Random(0x45478);
+
+    boolean canL = true;
+    boolean canR = true;
+
+    public abstract CholeskyDecomposition<DenseMatrix64F> create( boolean lower );
+
+    @Test
+    public void testDecomposeL() {
+        if( !canL ) return;
+
+        DenseMatrix64F A = new DenseMatrix64F(3,3, true, 1, 2, 4, 2, 13, 23, 4, 23, 90);
+
+        DenseMatrix64F L = new DenseMatrix64F(3,3, true, 1, 0, 0, 2, 3, 0, 4, 5, 7);
+
+        CholeskyDecomposition<DenseMatrix64F> cholesky = create(true);
+        assertTrue(cholesky.decompose(A));
+
+        DenseMatrix64F foundL = cholesky.getT(null);
+
+        EjmlUnitTests.assertEquals(L,foundL,1e-8);
+    }
+
+    @Test
+    public void testDecomposeR() {
+        if( !canR ) return;
+
+        DenseMatrix64F A = new DenseMatrix64F(3,3, true, 1, 2, 4, 2, 13, 23, 4, 23, 90);
+
+        DenseMatrix64F R = new DenseMatrix64F(3,3, true, 1, 2, 4, 0, 3, 5, 0, 0, 7);
+
+        CholeskyDecomposition<DenseMatrix64F> cholesky = create(false);
+        assertTrue(cholesky.decompose(A));
+
+        DenseMatrix64F foundR = cholesky.getT(null);
+
+        EjmlUnitTests.assertEquals(R,foundR,1e-8);
+    }
+
+    /**
+     * If it is not positive definate it should fail
+     */
+    @Test
+    public void testNotPositiveDefinite() {
+        DenseMatrix64F A = new DenseMatrix64F(2,2, true, 1, -1, -1, -2);
+
+        CholeskyDecomposition<DenseMatrix64F> alg = create(true);
+        assertFalse(alg.decompose(A));
+    }
+
+    /**
+     * The correctness of getT(null) has been tested else where effectively.  This
+     * checks to see if it handles the case where an input is provided correctly.
+     */
+    @Test
+    public void getT() {
+        DenseMatrix64F A = new DenseMatrix64F(3,3, true, 1, 2, 4, 2, 13, 23, 4, 23, 90);
+
+        CholeskyDecomposition<DenseMatrix64F> cholesky = create(true);
+
+        assertTrue(cholesky.decompose(A));
+
+        DenseMatrix64F L_null = cholesky.getT(null);
+        DenseMatrix64F L_provided = RandomMatrices.createRandom(3,3,rand);
+        assertTrue( L_provided == cholesky.getT(L_provided));
+
+        assertTrue(MatrixFeatures.isEquals(L_null,L_provided));
+    }
+
+    /**
+     * Test across several different matrix sizes and upper/lower decompositions using
+     * the definition of cholesky.
+     */
+    @Test
+    public void checkWithDefinition() {
+        for( int i = 0; i < 2; i++ ) {
+            boolean lower = i == 0;
+            if( lower && !canL )
+                continue;
+            if( !lower && !canR )
+                continue;
+
+            for( int size = 1; size < 10; size++ ) {
+                checkWithDefinition(lower, size);
+            }
+        }
+    }
+
+    private void checkWithDefinition(boolean lower, int size) {
+        SimpleMatrix A = SimpleMatrix.wrap( RandomMatrices.createSymmPosDef(size,rand));
+
+        CholeskyDecomposition<DenseMatrix64F> cholesky = create(lower);
+        assertTrue(DecompositionFactory.decomposeSafe(cholesky,A.getMatrix()));
+
+        SimpleMatrix T = SimpleMatrix.wrap(cholesky.getT(null));
+        SimpleMatrix found;
+
+        if( lower ) {
+            found = T.mult(T.transpose());
+        } else {
+            found = T.transpose().mult(T);
+        }
+
+        assertTrue(A.isIdentical(found,1e-8));
+    }
+
+    @Test
+    public void checkDeterminant() {
+        for( int i = 0; i < 2; i++ ) {
+            boolean lower = i == 0;
+            if( lower && !canL )
+                continue;
+            if( !lower && !canR )
+                continue;
+
+            for( int size = 1; size < 20; size += 2 ) {
+                checkDeterminant(lower, size);
+            }
+        }
+    }
+
+    public void checkDeterminant( boolean lower , int size ) {
+
+        LUDecomposition<DenseMatrix64F> lu = DecompositionFactory.lu(size,size);
+        CholeskyDecomposition<DenseMatrix64F> cholesky = create(lower);
+
+        DenseMatrix64F A = RandomMatrices.createSymmPosDef(size,rand);
+
+        assertTrue(DecompositionFactory.decomposeSafe(lu,A));
+        assertTrue(DecompositionFactory.decomposeSafe(cholesky,A));
+
+        double expected = lu.computeDeterminant().real;
+        double found = cholesky.computeDeterminant().real;
+
+        assertEquals(expected,found,1e-8);
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/chol/TestCholeskyDecompositionBlock_D64.java b/main/dense64/test/org/ejml/alg/dense/decomposition/chol/TestCholeskyDecompositionBlock_D64.java
new file mode 100644
index 0000000..e406a18
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/chol/TestCholeskyDecompositionBlock_D64.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.chol;
+
+import org.ejml.alg.dense.linsol.LinearSolverSafe;
+import org.ejml.alg.dense.linsol.chol.LinearSolverChol_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.CholeskyDecomposition;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.ejml.ops.EjmlUnitTests;
+import org.junit.Test;
+
+import static org.ejml.alg.dense.decomposition.CheckDecompositionInterface.checkModifiedInput;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestCholeskyDecompositionBlock_D64 extends GenericCholeskyTests_D64 {
+
+    public TestCholeskyDecompositionBlock_D64(){
+        canR = false;
+    }
+
+    @Test
+    public void checkModifyInput() {
+        checkModifiedInput(new CholeskyDecompositionBlock_D64(2));
+    }
+
+    @Override
+    public CholeskyDecomposition<DenseMatrix64F> create(boolean lower) {
+        if( !lower )
+            throw new IllegalArgumentException("Doesn't support upper form");
+
+        return new CholeskyDecompositionBlock_D64(1);
+    }
+
+    /**
+     *
+     * L =
+     *
+     * 2   0   0   0
+     * 1   5   0   0
+     * 3   2   4   0
+     * 7   1   6   3
+     *
+     */
+    @Test
+    public void testWithBlocks() {
+        int W = 4;
+        int B = 2;
+        checkBlockMatrix(W, B);
+    }
+
+    /**
+     * The block size and the matrix width are not perfectly divisible.  see if this is handled correctly.
+     */
+    @Test
+    public void testWithBlocksNotDivisible() {
+        int W = 4;
+        int B = 3;
+        checkBlockMatrix(W, B);
+    }
+
+    /**
+     * The block size is bigger than the matrix.
+     */
+    @Test
+    public void testWithBlocksBiggerThanMatrix() {
+        int W = 4;
+        int B = 10;
+        checkBlockMatrix(W, B);
+    }
+
+    private void checkBlockMatrix(int w, int b) {
+        DenseMatrix64F A = new DenseMatrix64F(w, w, true, 4, 2, 6, 14, 2, 26, 13, 12, 6, 13, 29, 47, 14, 12, 47, 95);
+
+        DenseMatrix64F A_inv = new DenseMatrix64F(w, w);
+        DenseMatrix64F A_inv_block = new DenseMatrix64F(w, w);
+
+        CholeskyDecompositionBlock_D64 algBlock = new CholeskyDecompositionBlock_D64(b);
+        LinearSolver<DenseMatrix64F> solver = new LinearSolverChol_D64(algBlock);
+        solver = new LinearSolverSafe<DenseMatrix64F>(solver);
+        assertTrue(solver.setA(A));
+        solver.invert(A_inv_block);
+
+        CholeskyDecompositionInner_D64 alg = new CholeskyDecompositionInner_D64(true);
+        solver = new LinearSolverChol_D64(alg);
+        solver = new LinearSolverSafe<DenseMatrix64F>(solver);
+        assertTrue(solver.setA(A));
+        solver.invert(A_inv);
+
+        EjmlUnitTests.assertEquals(A_inv,A_inv_block,1e-5);
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/chol/TestCholeskyDecompositionInner_D64.java b/main/dense64/test/org/ejml/alg/dense/decomposition/chol/TestCholeskyDecompositionInner_D64.java
new file mode 100644
index 0000000..8f966f7
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/chol/TestCholeskyDecompositionInner_D64.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.chol;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.CholeskyDecomposition;
+import org.junit.Test;
+
+import static org.ejml.alg.dense.decomposition.CheckDecompositionInterface.checkModifiedInput;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestCholeskyDecompositionInner_D64 extends GenericCholeskyTests_D64 {
+
+    @Override
+    public CholeskyDecomposition<DenseMatrix64F> create(boolean lower) {
+        return new CholeskyDecompositionInner_D64(lower);
+    }
+
+    @Test
+    public void checkModifyInput() {
+        checkModifiedInput(new CholeskyDecompositionInner_D64(true));
+        checkModifiedInput(new CholeskyDecompositionInner_D64(false));
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/chol/TestCholeskyDecompositionLDL_D64.java b/main/dense64/test/org/ejml/alg/dense/decomposition/chol/TestCholeskyDecompositionLDL_D64.java
new file mode 100644
index 0000000..b337d3e
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/chol/TestCholeskyDecompositionLDL_D64.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.chol;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.EjmlUnitTests;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.ejml.alg.dense.decomposition.CheckDecompositionInterface.checkModifiedInput;
+import static org.junit.Assert.*;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestCholeskyDecompositionLDL_D64 {
+
+    Random rand = new Random(0x45478);
+
+        @Test
+    public void checkModifyInput() {
+        checkModifiedInput(new CholeskyDecompositionLDL_D64());
+    }
+
+    @Test
+    public void testDecompose() {
+        DenseMatrix64F A = new DenseMatrix64F(3,3, true, 1, 2, 4, 2, 7, 23, 4, 23, 98);
+
+
+        DenseMatrix64F L = new DenseMatrix64F(3,3, true, 1, 0, 0, 2, 1, 0, 4, 5, 1);
+
+        double D[] = new double[]{1,3,7};
+
+        CholeskyDecompositionLDL_D64 cholesky = new CholeskyDecompositionLDL_D64();
+        assertTrue(cholesky.decompose(A));
+
+        DenseMatrix64F foundL = cholesky.getL();
+
+        EjmlUnitTests.assertEquals(L,foundL,1e-8);
+        for( int i = 0; i < D.length; i++ ) {
+            assertEquals(D[i],cholesky.getDiagonal()[i],1e-8);
+        }
+    }
+
+    /**
+     * If it is not positive definate it should fail
+     */
+    @Test
+    public void testNotPositiveDefinate() {
+        DenseMatrix64F A = new DenseMatrix64F(2,2, true, 1, -1, -1, -2);
+
+        CholeskyDecompositionLDL_D64 alg = new CholeskyDecompositionLDL_D64();
+        assertFalse(alg.decompose(A));
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/chol/TestCholeskyDecomposition_B64_to_D64.java b/main/dense64/test/org/ejml/alg/dense/decomposition/chol/TestCholeskyDecomposition_B64_to_D64.java
new file mode 100644
index 0000000..0d1751b
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/chol/TestCholeskyDecomposition_B64_to_D64.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.chol;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.CholeskyDecomposition;
+import org.junit.Test;
+
+import static org.ejml.alg.dense.decomposition.CheckDecompositionInterface.checkModifiedInput;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestCholeskyDecomposition_B64_to_D64 extends GenericCholeskyTests_D64 {
+    @Test
+    public void checkModifyInput() {
+        checkModifiedInput(new CholeskyDecomposition_B64_to_D64(true));
+        checkModifiedInput(new CholeskyDecomposition_B64_to_D64(false));
+    }
+
+    @Override
+    public CholeskyDecomposition<DenseMatrix64F> create(boolean lower) {
+        return new CholeskyDecomposition_B64_to_D64(lower);
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/eig/GeneralEigenDecompositionCheck.java b/main/dense64/test/org/ejml/alg/dense/decomposition/eig/GeneralEigenDecompositionCheck.java
new file mode 100644
index 0000000..d5edffd
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/eig/GeneralEigenDecompositionCheck.java
@@ -0,0 +1,626 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.eig;
+
+import org.ejml.UtilEjml;
+import org.ejml.data.Complex64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.data.Eigenpair64F;
+import org.ejml.interfaces.decomposition.EigenDecomposition;
+import org.ejml.ops.*;
+import org.ejml.simple.SimpleMatrix;
+
+import java.util.Random;
+
+import static org.ejml.alg.dense.decomposition.CheckDecompositionInterface.safeDecomposition;
+import static org.junit.Assert.*;
+
+
+/**
+ * @author Peter Abeles
+ */
+public abstract class GeneralEigenDecompositionCheck {
+
+    Random rand = new Random(895723);
+
+    public abstract EigenDecomposition createDecomposition();
+
+    boolean computeVectors;
+
+    public void allTests() {
+        computeVectors = true;
+
+        checkSizeZero();
+        checkRandom();
+        checkKnownReal();
+        checkKnownComplex();
+        checkCompanionMatrix();
+        checkRandomSymmetric();
+        checkExceptional();
+        checkIdentity();
+        checkAllZeros();
+        rand = new Random(2934);
+        checkWithSomeRepeatedValuesSymm();
+        checkWithSingularSymm();
+        checkSmallValue(false);
+        checkSmallValue(true);
+        checkLargeValue(false);
+        checkLargeValue(true);
+    }
+
+    /**
+     * Tests for when it just computes eigenvalues
+     */
+    public void justEigenValues() {
+        computeVectors = false;
+
+        checkKnownReal_JustValue();
+        checkKnownSymmetric_JustValue();
+        checkCompanionMatrix();
+    }
+
+    public void checkSizeZero() {
+        EigenDecomposition alg = createDecomposition();
+
+        assertFalse(alg.decompose(new DenseMatrix64F(0,0)));
+    }
+
+    /**
+     * Create a variety of different random matrices of different sizes and sees if they pass the standard
+     * eigen decompositions tests.
+     */
+    public void checkRandom() {
+        int sizes[] = new int[]{1,2,5,10,20,50,100,200};
+
+        EigenDecomposition alg = createDecomposition();
+
+        for( int s = 2; s < sizes.length; s++ ) {
+            int N = sizes[s];
+//            System.out.println("N = "+N);
+
+            for( int i = 0; i < 2; i++ ) {
+                DenseMatrix64F A = RandomMatrices.createRandom(N,N,-1,1,rand);
+
+                assertTrue(safeDecomposition(alg,A));
+
+                performStandardTests(alg,A,-1);
+            }
+        }
+    }
+
+    /**
+     * Compare results against a simple matrix with known results where all the eigenvalues
+     * are real.  Octave was used to test the known values.
+     */
+    public void checkKnownReal() {
+        DenseMatrix64F A = new DenseMatrix64F(3,3, true, 0.907265, 0.832472, 0.255310, 0.667810, 0.871323, 0.612657, 0.025059, 0.126475, 0.427002);
+
+        EigenDecomposition alg = createDecomposition();
+
+        assertTrue(safeDecomposition(alg,A));
+        performStandardTests(alg,A,-1);
+
+        testForEigenpair(alg,1.686542,0,-0.739990,-0.667630,-0.081761);
+        testForEigenpair(alg,0.079014,0,-0.658665,0.721163,-0.214673);
+        testForEigenpair(alg,0.440034,0,-0.731422,0.211711,0.648229);
+    }
+
+    /**
+     * Found to be a stressing case that broke a version of the general EVD algorithm.  It is a companion matrix
+     * for a polynomial used to find the zeros.
+     *
+     * Discovered by exratt at googlema*l.com
+     */
+    public void checkCompanionMatrix() {
+//        double[] polynomial = {
+//                5.392104631674957e7,
+//                -7.717841412372049e8,
+//                -1.4998803087543774e7,
+//                -30110.074181432814,
+//                -16.0
+//        };
+//
+////        double polynomial[] = new double[]{
+////                0.0817011296749115,
+////                -0.8100357949733734,
+////                -0.8667608685791492,
+////                2.2995666563510895,
+////                0.8879469335079193,
+////                -4.16266793012619,
+////                -1.527034044265747,
+////                2.201415002346039,
+////                0.5391231775283813,
+////                -0.41334158182144165};
+//
+//        // build companion matrix
+//        int n = polynomial.length - 1;
+//        DenseMatrix64F companion = new DenseMatrix64F(n, n);
+//        for (int i = 0; i < n; i++) {
+//            companion.set(i, n - 1, -polynomial[i] / polynomial[n]);
+//        }
+//        for (int i = 1; i < n; i++) {
+//            companion.set(i, i - 1, 1);
+//        }
+//
+//        // the eigenvalues of the companion matrix matches the roots of the polynomial
+//        EigenDecomposition alg = createDecomposition();
+//        assertTrue(safeDecomposition(alg,companion));
+//
+//        // see if the roots are zero
+//        for( int i = 0; i < alg.getNumberOfEigenvalues(); i++ ) {
+//            Complex64F c = alg.getEigenvalue(i);
+//
+//            if( !c.isReal() ) {
+//                continue;
+//            }
+//
+//            double total = 0;
+//            for( int j = 0; j < polynomial.length; j++ ) {
+//                total += polynomial[j]*Math.pow(c.real,j);
+//            }
+//
+//            assertEquals(0,total,1e-12);
+//        }
+//
+//
+//        performStandardTests(alg,companion,n);
+    }
+
+    /**
+     * Sees if it correctly computed the eigenvalues.  Does not check eigenvectors.
+     */
+    public void checkKnownReal_JustValue() {
+        DenseMatrix64F A = new DenseMatrix64F(3,3, true, 0.907265, 0.832472, 0.255310, 0.667810, 0.871323, 0.612657, 0.025059, 0.126475, 0.427002);
+
+        EigenDecomposition alg = createDecomposition();
+
+        assertTrue(safeDecomposition(alg,A));
+
+        testForEigenvalue(alg,A,1.686542,0,1);
+        testForEigenvalue(alg,A,0.079014,0,1);
+        testForEigenvalue(alg,A,0.440034,0,1);
+    }
+
+    /**
+     * Sees if it correctly computed the eigenvalues.  Does not check eigenvectors.
+     */
+    public void checkKnownSymmetric_JustValue() {
+        DenseMatrix64F A = new DenseMatrix64F(3,3, true,
+                0.98139,   0.78650,   0.78564,
+                0.78650,   1.03207,   0.29794,
+                0.78564,   0.29794,   0.91926);
+        EigenDecomposition alg = createDecomposition();
+
+        assertTrue(safeDecomposition(alg,A));
+
+        testForEigenvalue(alg,A,0.00426,0,1);
+        testForEigenvalue(alg,A,0.67856,0,1);
+        testForEigenvalue(alg,A,2.24989,0,1);
+    }
+
+    /**
+     * Compare results against a simple matrix with known results where some the eigenvalues
+     * are real and some are complex.
+     */
+    public void checkKnownComplex() {
+        DenseMatrix64F A = new DenseMatrix64F(3,3, true, -0.418284, 0.279875, 0.452912, -0.093748, -0.045179, 0.310949, 0.250513, -0.304077, -0.031414);
+
+        EigenDecomposition alg = createDecomposition();
+
+        assertTrue(safeDecomposition(alg,A));
+        performStandardTests(alg,A,-1);
+
+        testForEigenpair(alg,-0.39996,0,0.87010,0.43425,-0.23314);
+        testForEigenpair(alg,-0.04746,0.02391);
+        testForEigenpair(alg,-0.04746,-0.02391);
+    }
+
+    /**
+     * Check results against symmetric matrices that are randomly generated
+     */
+    public void checkRandomSymmetric() {
+        for( int N = 1; N <= 15; N++ ) {
+            for( int i = 0; i < 20; i++ ) {
+                DenseMatrix64F A = RandomMatrices.createSymmetric(N,-1,1,rand);
+
+                EigenDecomposition alg = createDecomposition();
+
+                assertTrue(safeDecomposition(alg,A));
+
+                performStandardTests(alg,A,N);
+            }
+        }
+    }
+
+    /**
+     * For some eigenvector algorithms this is a difficult matrix that requires a special
+     * check for.  If it fails that check it will either loop forever or exit before converging.
+     */
+    public void checkExceptional() {
+        DenseMatrix64F A = new DenseMatrix64F(5,5, true, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0);
+
+        EigenDecomposition alg = createDecomposition();
+
+        assertTrue(safeDecomposition(alg,A));
+
+        performStandardTests(alg,A,1);
+    }
+
+    public void checkIdentity() {
+        DenseMatrix64F I = CommonOps.identity(4);
+
+        EigenDecomposition alg = createDecomposition();
+
+        assertTrue(safeDecomposition(alg,I));
+
+        performStandardTests(alg,I,4);
+
+        testForEigenpair(alg,1,0,1,0,0,0);
+        testForEigenpair(alg,1,0,0,1,0,0);
+        testForEigenpair(alg,1,0,0,0,1,0);
+        testForEigenpair(alg,1,0,0,0,0,1);
+    }
+
+    public void checkAllZeros() {
+        DenseMatrix64F A = new DenseMatrix64F(5,5);
+
+        EigenDecomposition alg = createDecomposition();
+
+        assertTrue(safeDecomposition(alg,A));
+
+        performStandardTests(alg,A,5);
+        testEigenvalues(alg,0);
+    }
+
+    public void checkWithSomeRepeatedValuesSymm() {
+        EigenDecomposition alg = createDecomposition();
+
+        checkSymmetricMatrix(alg,2,-3,-3,-3);
+        checkSymmetricMatrix(alg,2,-3,2,2);
+        checkSymmetricMatrix(alg,1,1,1,2);
+    }
+
+    public void checkWithSingularSymm() {
+
+        EigenDecomposition alg = createDecomposition();
+
+        checkSymmetricMatrix(alg,1,0,1,2);
+    }
+
+    /**
+     * Creates a random symmetric matrix with the specified eigenvalues.  It then
+     * checks to see if it has the expected results.
+     */
+    private void checkSymmetricMatrix(EigenDecomposition alg , double ...ev ) {
+        int numRepeated[] = new int[ ev.length ];
+
+        for( int i = 0; i < ev.length ; i++ ) {
+            int num = 0;
+
+            for (double anEv : ev) {
+                if (ev[i] == anEv)
+                    num++;
+            }
+            numRepeated[i] = num;
+        }
+
+        for( int i = 0; i < 200; i++ ) {
+            DenseMatrix64F A = RandomMatrices.createEigenvaluesSymm(ev.length,rand,ev);
+
+            assertTrue(safeDecomposition(alg,A));
+
+            performStandardTests(alg,A,ev.length);
+
+            for( int j = 0; j < ev.length; j++ ) {
+                testForEigenvalue(alg,A,ev[j],0,numRepeated[j]);
+            }
+        }
+    }
+
+    public void checkSmallValue( boolean symmetric) {
+
+//        System.out.println("Symmetric = "+symmetric);
+        EigenDecomposition alg = createDecomposition();
+
+        for( int i = 0; i < 20; i++ ) {
+            DenseMatrix64F A = symmetric ?
+                    RandomMatrices.createSymmetric(4,-1,1,rand) :
+                    RandomMatrices.createRandom(4,4,-1,1,rand);
+
+            CommonOps.scale(1e-200,A);
+
+            assertTrue(safeDecomposition(alg,A));
+
+//        A.print("%15.13e");
+
+            performStandardTests(alg,A,-1);
+        }
+    }
+
+    public void checkLargeValue( boolean symmetric) {
+
+        EigenDecomposition alg = createDecomposition();
+
+        for( int i = 0; i < 20; i++ ) {
+            DenseMatrix64F A = symmetric ?
+                    RandomMatrices.createSymmetric(4,-1,1,rand) :
+                    RandomMatrices.createRandom(4,4,-1,1,rand);
+
+            CommonOps.scale(1e100,A);
+
+            assertTrue(safeDecomposition(alg,A));
+
+            performStandardTests(alg,A,-1);
+        }
+    }
+
+    /**
+     * If the eigenvalues are all known, real, and the same this can be used to check them.
+     */
+    public void testEigenvalues( EigenDecomposition alg , double expected ) {
+
+        for( int i = 0; i < alg.getNumberOfEigenvalues(); i++ ) {
+            Complex64F c = alg.getEigenvalue(i);
+
+            assertTrue(c.isReal());
+
+            assertEquals(expected,c.real,1e-8);
+        }
+    }
+
+    /**
+     * Preforms standard tests that can be performed on any decomposition without prior knowledge of
+     * what the results should be.
+     */
+    public void performStandardTests( EigenDecomposition alg , DenseMatrix64F A , int numReal )
+    {
+
+        // basic sanity tests
+        assertEquals(A.numRows,alg.getNumberOfEigenvalues());
+
+        if( numReal >= 0 ) {
+            for( int i = 0; i < A.numRows; i++ ) {
+                Complex64F v = alg.getEigenvalue(i);
+
+                assertFalse( Double.isNaN(v.getReal() ));
+                if( v.isReal() )
+                    numReal--;
+                else if( Math.abs(v.getImaginary()) < 10*UtilEjml.EPS)
+                    numReal--;
+            }
+
+            // if there are more than the expected number of real eigenvalues this will
+            // be negative
+            assertEquals(0,numReal);
+        }
+
+//        checkCharacteristicEquation(alg,A);
+        if( computeVectors ) {
+            testPairsConsistent(alg,A);
+            testVectorsLinearlyIndependent(alg);
+        } else {
+            testEigenvalueConsistency(alg,A);
+        }
+    }
+
+    /**
+     * Checks to see if an eigenvalue is complex then the eigenvector is null.  If it is real it
+     * then checks to see if the equation A*v = lambda*v holds true.
+     */
+    public void testPairsConsistent( EigenDecomposition<DenseMatrix64F> alg , DenseMatrix64F A )
+    {
+//        System.out.println("-------------------------------------------------------------------------");
+        int N = alg.getNumberOfEigenvalues();
+
+        DenseMatrix64F tempA = new DenseMatrix64F(N,1);
+        DenseMatrix64F tempB = new DenseMatrix64F(N,1);
+        
+        for( int i = 0; i < N; i++ ) {
+            Complex64F c = alg.getEigenvalue(i);
+            DenseMatrix64F v = alg.getEigenVector(i);
+
+            if( Double.isInfinite(c.real) || Double.isNaN(c.real) ||
+                    Double.isInfinite(c.imaginary) || Double.isNaN(c.imaginary))
+                fail("Uncountable eigenvalue");
+
+            if( !c.isReal() ) {
+                assertTrue(v==null);
+            } else {
+                assertTrue(v != null );
+//                if( MatrixFeatures.hasUncountable(v)) {
+//                    throw new RuntimeException("Egads");
+//                }
+                assertFalse(MatrixFeatures.hasUncountable(v));
+
+                CommonOps.mult(A,v,tempA);
+                CommonOps.scale(c.real,v,tempB);
+
+                double max = NormOps.normPInf(A);
+                if( max == 0 ) max = 1;
+
+                double error = SpecializedOps.diffNormF(tempA,tempB)/max;
+
+                if( error > 1e-12 ) {
+                    System.out.println("Original matrix:");
+                    A.print();
+                    System.out.println("Eigenvalue = "+c.real);
+                    Eigenpair64F p = EigenOps.computeEigenVector(A,c.real);
+                    p.vector.print();
+                    v.print();
+
+
+                    CommonOps.mult(A,p.vector,tempA);
+                    CommonOps.scale(c.real,p.vector,tempB);
+
+                    max = NormOps.normPInf(A);
+
+                    System.out.println("error before = "+error);
+                    error = SpecializedOps.diffNormF(tempA,tempB)/max;
+                    System.out.println("error after = "+error);
+                    A.print("%f");
+                    System.out.println();
+                    fail("Error was too large");
+                }
+
+                assertTrue(error <= 1e-12);
+            }
+        }
+    }
+
+    /**
+     * Takes a real eigenvalue and computes its eigenvector.  then sees if it is similar to the adjusted
+     * eigenvalue
+     */
+    public void testEigenvalueConsistency( EigenDecomposition alg ,
+                                           DenseMatrix64F A )
+    {
+        int N = alg.getNumberOfEigenvalues();
+
+        DenseMatrix64F AV = new DenseMatrix64F(N,1);
+        DenseMatrix64F LV = new DenseMatrix64F(N,1);
+
+        for( int i = 0; i < N; i++ ) {
+            Complex64F c = alg.getEigenvalue(i);
+
+            if( c.isReal() ) {
+                Eigenpair64F p = EigenOps.computeEigenVector(A,c.getReal());
+
+                if( p != null ) {
+                    CommonOps.mult(A,p.vector,AV);
+                    CommonOps.scale(c.getReal(),p.vector,LV);
+                    double error = SpecializedOps.diffNormF(AV,LV);
+//                    System.out.println("error = "+error);
+                    assertTrue(error<1e-12);
+                }
+            }
+        }
+    }
+
+    /**
+     * See if eigenvalues cause the characteristic equation to have a value of zero
+     */
+    public void checkCharacteristicEquation( EigenDecomposition alg ,
+                                             DenseMatrix64F A ) {
+        int N = alg.getNumberOfEigenvalues();
+
+        SimpleMatrix a = SimpleMatrix.wrap(A);
+
+        for( int i = 0; i < N; i++ ) {
+            Complex64F c = alg.getEigenvalue(i);
+
+            if( c.isReal() ) {
+                // test using the characteristic equation
+                double det = SimpleMatrix.identity(A.numCols).scale(c.real).minus(a).determinant();
+
+                // extremely crude test.  given perfect data this is probably considered a failure...  However,
+                // its hard to tell what a good test value actually is.
+                assertEquals(0, det, 0.1);
+            }
+        }
+    }
+
+    /**
+     * Checks to see if all the real eigenvectors are linearly independent of each other.
+     */
+    public void testVectorsLinearlyIndependent( EigenDecomposition<DenseMatrix64F> alg ) {
+        int N = alg.getNumberOfEigenvalues();
+
+        // create a matrix out of the eigenvectors
+        DenseMatrix64F A = new DenseMatrix64F(N,N);
+
+        int off = 0;
+        for( int i = 0; i < N; i++ ) {
+            DenseMatrix64F v = alg.getEigenVector(i);
+
+            // it can only handle real eigenvectors
+            if( v == null )
+                off++;
+            else {
+                for( int j = 0; j < N; j++ ) {
+                    A.set(i-off,j,v.get(j));
+                }
+            }
+        }
+
+        // see if there are any real eigenvectors
+        if( N == off )
+            return;
+
+        A.reshape(N-off,N, false);
+
+        assertTrue(MatrixFeatures.isRowsLinearIndependent(A));
+    }
+
+    /**
+     * Sees if the pair of eigenvalue and eigenvector was found in the decomposition.
+     */
+    public void testForEigenpair( EigenDecomposition<DenseMatrix64F> alg , double valueReal ,
+                                  double valueImg , double... vector )
+    {
+        int N = alg.getNumberOfEigenvalues();
+
+        int numMatched = 0;
+        for( int i = 0; i < N; i++ ) {
+            Complex64F c = alg.getEigenvalue(i);
+
+            if( Math.abs(c.real-valueReal) < 1e-4 && Math.abs(c.imaginary-valueImg) < 1e-4) {
+
+                if( c.isReal() ) {
+                    if( vector.length > 0 ) {
+                        DenseMatrix64F v = alg.getEigenVector(i);
+                        DenseMatrix64F e = new DenseMatrix64F(N,1, true, vector);
+
+                        double error = SpecializedOps.diffNormF(e,v);
+                        CommonOps.changeSign(e);
+                        double error2 = SpecializedOps.diffNormF(e,v);
+
+
+                        if(error < 1e-3 || error2 < 1e-3)
+                            numMatched++;
+                    } else {
+                        numMatched++;
+                    }
+                } else if( !c.isReal() ) {
+                    numMatched++;
+                }
+            }
+        }
+
+        assertEquals(1,numMatched);
+    }
+
+    public void testForEigenvalue( EigenDecomposition alg ,
+                                   DenseMatrix64F A,
+                                   double valueReal ,
+                                   double valueImg , int numMatched )
+    {
+        int N = alg.getNumberOfEigenvalues();
+
+        int numFound = 0;
+        for( int i = 0; i < N; i++ ) {
+            Complex64F c = alg.getEigenvalue(i);
+
+            if( Math.abs(c.real-valueReal) < 1e-4 && Math.abs(c.imaginary-valueImg) < 1e-4) {
+                numFound++;
+            }
+        }
+
+        assertEquals(numMatched,numFound);
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/eig/TestEigenPowerMethod.java b/main/dense64/test/org/ejml/alg/dense/decomposition/eig/TestEigenPowerMethod.java
new file mode 100644
index 0000000..8cc98bf
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/eig/TestEigenPowerMethod.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.eig;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.NormOps;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestEigenPowerMethod {
+
+    Random rand = new Random(0x34234);
+
+    /**
+     * Test it against a case
+     */
+    @Test
+    public void computeDirect() {
+        double dataA[] = new double[]{
+                0.499765 ,  0.626231 ,  0.759554,
+                0.850879 ,  0.104374 ,  0.247645 ,
+                0.069614 ,  0.155754  , 0.380435 };
+
+        DenseMatrix64F A = new DenseMatrix64F(3,3, true, dataA);
+
+        EigenPowerMethod power = new EigenPowerMethod(3);
+        power.setOptions(100,1e-10);
+
+        assertTrue(power.computeDirect(A));
+
+        DenseMatrix64F v = power.getEigenVector();
+
+        NormOps.normalizeF(v);
+
+        assertEquals(0.75678,v.get(0,0),1e-6);
+        assertEquals(0.62755,v.get(1,0),1e-5);
+        assertEquals(0.18295,v.get(2,0),1e-5);
+    }
+
+    @Test
+    public void computeShiftDirect() {
+        double dataA[] = new double[]{
+                0.499765 ,  0.626231 ,  0.759554,
+                0.850879 ,  0.104374 ,  0.247645 ,
+                0.069614 ,  0.155754  , 0.380435 };
+
+        DenseMatrix64F A = new DenseMatrix64F(3,3, true, dataA);
+
+        EigenPowerMethod power = new EigenPowerMethod(3);
+        power.setOptions(100,1e-10);
+
+        assertTrue(power.computeShiftDirect(A,0.2));
+
+        DenseMatrix64F v = power.getEigenVector();
+
+        NormOps.normalizeF(v);
+
+        assertEquals(0.75678,v.get(0,0),1e-6);
+        assertEquals(0.62755,v.get(1,0),1e-5);
+        assertEquals(0.18295,v.get(2,0),1e-5);
+    }
+
+    @Test
+    public void computeShiftInvert() {
+        double dataA[] = new double[]{
+                0.499765 ,  0.626231 ,  0.759554,
+                0.850879 ,  0.104374 ,  0.247645 ,
+                0.069614 ,  0.155754  , 0.380435 };
+
+        DenseMatrix64F A = new DenseMatrix64F(3,3, true, dataA);
+
+        EigenPowerMethod power = new EigenPowerMethod(3);
+        power.setOptions(100,1e-10);
+
+        // a tried a few values for psi until I found one that converged
+        assertTrue(power.computeShiftInvert(A,1.1));
+
+        DenseMatrix64F v = power.getEigenVector();
+
+        NormOps.normalizeF(v);
+
+        assertEquals(0.75678,v.get(0,0),1e-6);
+        assertEquals(0.62755,v.get(1,0),1e-5);
+        assertEquals(0.18295,v.get(2,0),1e-5);
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/eig/TestSwitchingEigenDecomposition.java b/main/dense64/test/org/ejml/alg/dense/decomposition/eig/TestSwitchingEigenDecomposition.java
new file mode 100644
index 0000000..8b033cc
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/eig/TestSwitchingEigenDecomposition.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.eig;
+
+import org.ejml.interfaces.decomposition.EigenDecomposition;
+import org.junit.Test;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestSwitchingEigenDecomposition extends GeneralEigenDecompositionCheck {
+    @Override
+    public EigenDecomposition createDecomposition() {
+        return new SwitchingEigenDecomposition(0,computeVectors,1e-8);
+    }
+
+    @Test
+    public void allTests() {
+        super.allTests();
+        super.justEigenValues();
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/eig/TestSymmetricQRAlgorithmDecomposition_D64.java b/main/dense64/test/org/ejml/alg/dense/decomposition/eig/TestSymmetricQRAlgorithmDecomposition_D64.java
new file mode 100644
index 0000000..7a2d54f
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/eig/TestSymmetricQRAlgorithmDecomposition_D64.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.eig;
+
+import org.ejml.interfaces.decomposition.EigenDecomposition;
+import org.junit.Test;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestSymmetricQRAlgorithmDecomposition_D64 extends GeneralEigenDecompositionCheck {
+    boolean together;
+
+    @Override
+    public EigenDecomposition createDecomposition() {
+        SymmetricQRAlgorithmDecomposition_D64 alg = new SymmetricQRAlgorithmDecomposition_D64(computeVectors);
+        if( computeVectors )
+            alg.setComputeVectorsWithValues(together);
+
+        return alg;
+    }
+
+    @Test
+    public void justSymmetricTests_separate() {
+        together = false;
+        computeVectors = true;
+
+        checkSizeZero();
+        checkRandomSymmetric();
+        checkIdentity();
+        checkAllZeros();
+        checkWithSomeRepeatedValuesSymm();
+        checkWithSingularSymm();
+        checkSmallValue(true);
+        checkLargeValue(true);
+
+        computeVectors = false;
+        checkKnownSymmetric_JustValue();
+    }
+
+    @Test
+    public void justSymmetricTests_together() {
+        together = true;
+        computeVectors = true;
+
+        checkSizeZero();
+        checkRandomSymmetric();
+        checkIdentity();
+        checkAllZeros();
+        checkWithSomeRepeatedValuesSymm();
+        checkWithSingularSymm();
+        checkSmallValue(true);
+        checkLargeValue(true);
+
+        computeVectors = false;
+        checkKnownSymmetric_JustValue();
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/eig/TestWatchedDoubleStepQRDecomposition_D64.java b/main/dense64/test/org/ejml/alg/dense/decomposition/eig/TestWatchedDoubleStepQRDecomposition_D64.java
new file mode 100644
index 0000000..4f17794
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/eig/TestWatchedDoubleStepQRDecomposition_D64.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.eig;
+
+import org.ejml.interfaces.decomposition.EigenDecomposition;
+import org.junit.Test;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestWatchedDoubleStepQRDecomposition_D64 extends GeneralEigenDecompositionCheck {
+    @Override
+    public EigenDecomposition createDecomposition() {
+        return new WatchedDoubleStepQRDecomposition_D64(computeVectors);
+    }
+
+    @Test
+    public void allTests() {
+        super.allTests();
+        super.justEigenValues();
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/eig/symm/TestSymmetricQrAlgorithm.java b/main/dense64/test/org/ejml/alg/dense/decomposition/eig/symm/TestSymmetricQrAlgorithm.java
new file mode 100644
index 0000000..567fb5f
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/eig/symm/TestSymmetricQrAlgorithm.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.eig.symm;
+
+import org.ejml.alg.dense.decomposition.hessenberg.TridiagonalDecompositionHouseholder_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestSymmetricQrAlgorithm {
+
+    /**
+     * There should no need to do anything in this case.
+     */
+    @Test
+    public void shouldNotChange() {
+        double diag[] = new double[]{2,3,4,5,6};
+        double off[] = new double[diag.length-1];
+
+        SymmetricQrAlgorithm alg = new SymmetricQrAlgorithm();
+
+        assertTrue(alg.process(diag.length,diag,off));
+
+        for( int i = 0; i < diag.length; i++ ) {
+            assertEquals(1,countNumFound(alg,diag[i],1e-4));
+        }
+    }
+
+    /**
+     * The tridiagonal matrix has off diagonal terms now
+     */
+    @Test
+    public void hasOffDiagonal() {
+        double diag[] = new double[]{2,3,4,5,6};
+        double off[] = new double[diag.length-1];
+
+        for( int i = 1; i < diag.length; i++ ) {
+            off[i-1] = i+0.5;
+        }
+
+        SymmetricQrAlgorithm alg = new SymmetricQrAlgorithm();
+
+        assertTrue(alg.process(diag.length,diag,off));
+
+        assertEquals(1,countNumFound(alg,-1.26677,1e-4));
+        assertEquals(1,countNumFound(alg,0.93171,1e-4));
+        assertEquals(1,countNumFound(alg,3.11320,1e-4));
+        assertEquals(1,countNumFound(alg,6.20897,1e-4));
+        assertEquals(1,countNumFound(alg,11.01290,1e-4));
+
+    }
+
+    /**
+     * Test it against a matrix that has zeros along the diagonal but non zero values along
+     * the off diagonal elements.
+     */
+    @Test
+    public void zeroDiagonalNotZeroOff() {
+        int N = 5;
+        double diag[] = new double[N];
+        double off[] = new double[N-1];
+
+        for( int i = 0; i < N-1; i++ ) {
+            off[i] = i+0.5;
+        }
+
+//        A.print();
+
+        SymmetricQrAlgorithm alg = new SymmetricQrAlgorithm();
+
+        assertTrue(alg.process(N,diag,off));
+
+        assertEquals(1,countNumFound(alg,-4.39719,1e-4));
+        assertEquals(1,countNumFound(alg,-1.29023,1e-4));
+        assertEquals(1,countNumFound(alg,0,1e-4));
+        assertEquals(1,countNumFound(alg,1.29023,1e-4));
+        assertEquals(1,countNumFound(alg,4.39719,1e-4));
+    }
+
+    /**
+     * Provide a test case where the same eigenvalue is repeated a few times
+     */
+    @Test
+    public void multipleEigenvalues() {
+        DenseMatrix64F A = new DenseMatrix64F(5,5, true, 2.191140, -0.098491, -0.397037, 0.367426, -0.208338, -0.098491, 2.776741, 0.623341, 0.624798, 0.401906, -0.397037, 0.623341, 3.571302, -0.239631, -0.264573, 0.367426, 0.624798, -0.239631, 3.625034, -0.162896, -0.208338, 0.401906, -0.264573, -0.162896, 3.835783);
+
+        TridiagonalDecompositionHouseholder_D64 tridiag = new TridiagonalDecompositionHouseholder_D64();
+        tridiag.decompose(A);
+
+        double diag[] = new double[5];
+        double off[] = new double[4];
+
+        tridiag.getDiagonal(diag,off);
+
+        SymmetricQrAlgorithm alg = new SymmetricQrAlgorithm();
+
+        assertTrue(alg.process(5,diag,off));
+
+        assertEquals(3,countNumFound(alg,4,1e-4));
+        assertEquals(2,countNumFound(alg,2,1e-4));
+    }
+
+    /**
+     * Counts the number of times the specified eigenvalue appears.
+     */
+    public int countNumFound( SymmetricQrAlgorithm alg , double val , double tol ) {
+        int total = 0;
+
+        for( int i = 0; i < alg.getNumberOfEigenvalues(); i++ ) {
+            double a = alg.getEigenvalue(i);
+
+            if( Math.abs(a-val) <= tol ) {
+                total++;
+            }
+        }
+
+        return total;
+    }
+
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/hessenberg/StandardTridiagonalTests.java b/main/dense64/test/org/ejml/alg/dense/decomposition/hessenberg/StandardTridiagonalTests.java
new file mode 100644
index 0000000..14891b0
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/hessenberg/StandardTridiagonalTests.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.hessenberg;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.TridiagonalSimilarDecomposition;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.ejml.simple.SimpleMatrix;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.ejml.alg.dense.decomposition.CheckDecompositionInterface.safeDecomposition;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public abstract class StandardTridiagonalTests {
+
+    protected Random rand = new Random(2344);
+
+    protected abstract TridiagonalSimilarDecomposition<DenseMatrix64F> createDecomposition();
+
+    @Test
+    public void fullTest() {
+
+        for( int width = 1; width < 20; width += 2 ) {
+
+            SimpleMatrix A = SimpleMatrix.wrap(RandomMatrices.createSymmetric(width,-1,1,rand));
+
+            TridiagonalSimilarDecomposition<DenseMatrix64F> alg = createDecomposition();
+
+
+            assertTrue(safeDecomposition(alg,A.getMatrix()));
+
+            // test the results using the decomposition's definition
+            SimpleMatrix Q = SimpleMatrix.wrap(alg.getQ(null,false));
+            SimpleMatrix T = SimpleMatrix.wrap(alg.getT(null));
+
+            SimpleMatrix A_found = Q.mult(T).mult(Q.transpose());
+
+            assertTrue("width = "+width,MatrixFeatures.isIdentical(A.getMatrix(),A_found.getMatrix(),1e-8));
+        }
+    }
+
+    @Test
+    public void getDiagonal() {
+        for( int width = 1; width < 20; width += 2 ) {
+
+            DenseMatrix64F A = RandomMatrices.createSymmetric(width,-1,1,rand);
+
+            TridiagonalSimilarDecomposition<DenseMatrix64F> alg = createDecomposition();
+
+            assertTrue(safeDecomposition(alg,A));
+
+            DenseMatrix64F T = alg.getT(null);
+
+            double diag[] = new double[width];
+            double off[] = new double[width];
+
+            alg.getDiagonal(diag,off);
+            assertEquals(T.get(0,0),diag[0],1e-8);
+            for( int i = 1; i < width; i++ ) {
+                assertEquals(T.get(i,i),diag[i],1e-8);
+                assertEquals(T.get(i-1,i),off[i-1],1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void transposeFlagForQ() {
+        for( int width = 1; width < 20; width += 2 ) {
+
+            DenseMatrix64F A = RandomMatrices.createSymmetric(width,-1,1,rand);
+
+            TridiagonalSimilarDecomposition<DenseMatrix64F> alg = createDecomposition();
+
+            assertTrue(safeDecomposition(alg,A));
+
+            DenseMatrix64F Q = alg.getQ(null,false);
+            DenseMatrix64F Q_t = alg.getQ(null,true);
+
+            for( int i = 0; i < Q.numRows; i++ ) {
+                for( int j = 0; j < Q.numCols; j++ ) {
+                    assertEquals(Q.get(i,j),Q_t.get(j,i),1e-8);
+                }
+            }
+        }
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/hessenberg/TestHessenbergSimilarDecomposition_D64.java b/main/dense64/test/org/ejml/alg/dense/decomposition/hessenberg/TestHessenbergSimilarDecomposition_D64.java
new file mode 100644
index 0000000..d6ab25c
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/hessenberg/TestHessenbergSimilarDecomposition_D64.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.hessenberg;
+
+import org.ejml.UtilEjml;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.ejml.ops.SpecializedOps;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.ejml.alg.dense.decomposition.CheckDecompositionInterface.safeDecomposition;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class TestHessenbergSimilarDecomposition_D64 {
+
+    Random rand = new Random(5745784);
+
+    /**
+     * Decomposes the matrix, extracts H and Q, then sees if it can recompute A using similar matrix stuff.
+     */
+    @Test
+    public void testItAllTogether() {
+        DenseMatrix64F A = RandomMatrices.createRandom(5,5,rand);
+
+        checkItAll(A);
+    }
+
+    private void checkItAll(DenseMatrix64F A) {
+        HessenbergSimilarDecomposition_D64 decomp = new HessenbergSimilarDecomposition_D64(A.numRows);
+
+        assertTrue(safeDecomposition(decomp,A));
+
+        DenseMatrix64F Q = decomp.getQ(null);
+        DenseMatrix64F H = decomp.getH(null);
+//        System.out.println("-------- H ---------");
+//        UtilEjml.print(H,"%8.2e");
+//        System.out.println("-------- Q ---------");
+//        UtilEjml.print(Q,"%8.2e");
+        assertTrue(MatrixFeatures.isOrthogonal(Q, UtilEjml.TOLERANCE));
+
+        DenseMatrix64F temp0 = new DenseMatrix64F(5,5);
+
+        CommonOps.mult(Q,H,temp0);
+        CommonOps.multTransB(temp0,Q,H);
+
+//        System.out.println("------- A ----------");
+//        UtilEjml.print(A,"%8.2e");
+//        System.out.println("----- Found A ------");
+//        UtilEjml.print(H,"%8.2e");
+
+        assertTrue(!MatrixFeatures.hasUncountable(H));
+
+        assertTrue(MatrixFeatures.isIdentical(A,H,UtilEjml.TOLERANCE));
+    }
+
+    /**
+     * Make sure it doesn't change the input
+     */
+    @Test
+    public void testInputUnmodified() {
+        DenseMatrix64F A = RandomMatrices.createRandom(4,4,rand);
+        DenseMatrix64F B = A.copy();
+
+        HessenbergSimilarDecomposition_D64 decomp = new HessenbergSimilarDecomposition_D64(A.numRows);
+
+        assertTrue(safeDecomposition(decomp,A));
+
+        assertTrue(MatrixFeatures.isIdentical(A,B,0));
+    }
+
+    /**
+     * Give it a matrix that is already a Hessenberg matrix and see if its comes out the same.
+     */
+//    @Test
+//    public void testNoChange() {
+//        DenseMatrix64F A = RandomMatrices.createUpperTriangle(4,1,-1,1,rand);
+//
+//        HessenbergSimilarDecomposition decomp = new HessenbergSimilarDecomposition(A.numRows);
+//
+//        assertTrue(decomp.decompose(A));
+//
+//        DenseMatrix64F H = decomp.getH(null);
+//
+//        assertTrue(MatrixFeatures.isIdentical(A,H,0));
+//    }
+
+    /**
+     * This checks to see the gammas and if the householder vectors stored in QH are correct. This
+     * is done by extracting the vectors, computing reflectors, and multipling them by A and seeing
+     * if it has the expected response.
+     */
+    @Test
+    public void testHouseholderVectors()
+    {
+        int N = 5;
+        DenseMatrix64F A = RandomMatrices.createRandom(N,N,rand);
+        DenseMatrix64F B = new DenseMatrix64F(N,N);
+
+        HessenbergSimilarDecomposition_D64 decomp = new HessenbergSimilarDecomposition_D64(A.numRows);
+
+        assertTrue(safeDecomposition(decomp,A));
+        
+        DenseMatrix64F QH = decomp.getQH();
+//        System.out.println("------------ QH -----------");
+//        UtilEjml.print(QH);
+
+        double gammas[] = decomp.getGammas();
+
+        DenseMatrix64F u = new DenseMatrix64F(N,1);
+
+//        UtilEjml.print(A);
+//        System.out.println("-------------------");
+
+        for( int i = 0; i < N-1; i++ ) {
+            u.zero();
+            u.data[i+1] = 1;
+            for( int j = i+2; j < N; j++ ) {
+                u.data[j] = QH.get(j,i);
+            }
+
+            DenseMatrix64F Q = SpecializedOps.createReflector(u,gammas[i]);
+            CommonOps.mult(Q,A,B);
+//            System.out.println("----- u ------");
+//            UtilEjml.print(u);
+//            System.out.println("----- Q ------");
+//            UtilEjml.print(Q);
+//            System.out.println("----- B ------");
+//            UtilEjml.print(B);
+
+            for( int j = 0; j < i+2; j++ ) {
+                assertTrue(Math.abs(B.get(j,i))>UtilEjml.TOLERANCE);
+            }
+            for( int j = i+2; j < N; j++ ) {
+                assertEquals(0,B.get(j,i),UtilEjml.TOLERANCE);
+            }
+            CommonOps.mult(B,Q,A);
+
+//            System.out.println("-------------------");
+//            UtilEjml.print(A);
+//            System.out.println("-------------------");
+        }
+    }
+
+    /**
+     * Compute the overall Q matrix from the stored u vectors.  See if the extract H is the same as the expected H.
+     */
+    @Test
+    public void testH() {
+        int N = 5;
+        DenseMatrix64F A = RandomMatrices.createRandom(N,N,rand);
+
+        HessenbergSimilarDecomposition_D64 decomp = new HessenbergSimilarDecomposition_D64(A.numRows);
+
+        assertTrue(safeDecomposition(decomp,A));
+
+        DenseMatrix64F QH = decomp.getQH();
+
+        double gammas[] = decomp.getGammas();
+
+        DenseMatrix64F u = new DenseMatrix64F(N,1);
+
+
+        DenseMatrix64F Q = CommonOps.identity(N);
+        DenseMatrix64F temp = new DenseMatrix64F(N,N);
+
+        for( int i = N-2; i >= 0; i-- ) {
+            u.zero();
+            u.data[i+1] = 1;
+            for( int j = i+2; j < N; j++ ) {
+                u.data[j] = QH.get(j,i);
+            }
+
+            DenseMatrix64F Qi = SpecializedOps.createReflector(u,gammas[i]);
+
+            CommonOps.mult(Qi,Q,temp);
+            Q.set(temp);
+        }
+        DenseMatrix64F expectedH = new DenseMatrix64F(N,N);
+
+        CommonOps.multTransA(Q,A,temp);
+        CommonOps.mult(temp,Q,expectedH);
+
+//        UtilEjml.print(expectedH);
+
+        DenseMatrix64F foundH = decomp.getH(null);
+
+//        UtilEjml.print(foundH);
+
+        assertTrue(MatrixFeatures.isIdentical(expectedH,foundH,UtilEjml.TOLERANCE));
+
+        System.out.println();
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/hessenberg/TestTridiagonalDecompositionHouseholder_D64.java b/main/dense64/test/org/ejml/alg/dense/decomposition/hessenberg/TestTridiagonalDecompositionHouseholder_D64.java
new file mode 100644
index 0000000..2ed1645
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/hessenberg/TestTridiagonalDecompositionHouseholder_D64.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.hessenberg;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.TridiagonalSimilarDecomposition;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestTridiagonalDecompositionHouseholder_D64 extends StandardTridiagonalTests {
+
+
+    @Override
+    protected TridiagonalSimilarDecomposition<DenseMatrix64F> createDecomposition() {
+        return new TridiagonalDecompositionHouseholder_D64();
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/hessenberg/TestTridiagonalDecomposition_B64_to_D64.java b/main/dense64/test/org/ejml/alg/dense/decomposition/hessenberg/TestTridiagonalDecomposition_B64_to_D64.java
new file mode 100644
index 0000000..d5a90d6
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/hessenberg/TestTridiagonalDecomposition_B64_to_D64.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.hessenberg;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.TridiagonalSimilarDecomposition;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestTridiagonalDecomposition_B64_to_D64 extends StandardTridiagonalTests {
+
+
+    @Override
+    protected TridiagonalSimilarDecomposition<DenseMatrix64F> createDecomposition() {
+        return new TridiagonalDecomposition_B64_to_D64(3);
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/lu/GeneralLuDecompositionChecks.java b/main/dense64/test/org/ejml/alg/dense/decomposition/lu/GeneralLuDecompositionChecks.java
new file mode 100644
index 0000000..525d28a
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/lu/GeneralLuDecompositionChecks.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.lu;
+
+import org.ejml.alg.dense.decomposition.CheckDecompositionInterface;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.LUDecomposition;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.EjmlUnitTests;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.ejml.simple.SimpleMatrix;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public abstract class GeneralLuDecompositionChecks {
+
+    Random rand = new Random(0xff);
+
+    public abstract LUDecomposition<DenseMatrix64F> create( int numRows , int numCols );
+
+    @Test
+    public void testModifiedInput() {
+        CheckDecompositionInterface.checkModifiedInput(create(0,0));
+    }
+
+    /**
+     * Uses the decomposition returned from octave, which uses LAPACK
+     */
+    @Test
+    public void testDecomposition()
+    {
+        DenseMatrix64F A = new DenseMatrix64F(3,3, true, 5, 2, 3, 1.5, -2, 8, -3, 4.7, -0.5);
+
+        DenseMatrix64F octLower = new DenseMatrix64F(3,3, true, 1, 0, 0, -0.6, 1, 0, 0.3, -0.44068, 1);
+        DenseMatrix64F octUpper = new DenseMatrix64F(3,3, true, 5, 2, 3, 0, 5.9, 1.3, 0, 0, 7.67288);
+
+        LUDecomposition<DenseMatrix64F> alg = create(3,3);
+        assertTrue(alg.decompose(A));
+
+        assertFalse(alg.isSingular());
+
+        SimpleMatrix L = SimpleMatrix.wrap(alg.getLower(null));
+        SimpleMatrix U = SimpleMatrix.wrap(alg.getUpper(null));
+        SimpleMatrix P = SimpleMatrix.wrap(alg.getPivot(null));
+
+        EjmlUnitTests.assertEquals(octLower,L.getMatrix(),1e-5);
+        EjmlUnitTests.assertEquals(octUpper,U.getMatrix(),1e-5);
+
+        DenseMatrix64F A_found = P.mult(L).mult(U).getMatrix();
+        assertTrue(MatrixFeatures.isIdentical(A_found,A,1e-8));
+    }
+
+    @Test
+    public void testDecomposition2()
+    {
+        for( int i = 2; i <= 20; i++ ) {
+            DenseMatrix64F A = RandomMatrices.createRandom(i,i,-1,1,rand);
+
+            LUDecomposition<DenseMatrix64F> alg = create(i,i);
+            assertTrue(alg.decompose(A));
+
+            assertFalse(alg.isSingular());
+
+            SimpleMatrix L = SimpleMatrix.wrap(alg.getLower(null));
+            SimpleMatrix U = SimpleMatrix.wrap(alg.getUpper(null));
+            SimpleMatrix P = SimpleMatrix.wrap(alg.getPivot(null));
+
+            DenseMatrix64F A_found = P.transpose().mult(L).mult(U).getMatrix();
+            assertTrue(MatrixFeatures.isIdentical(A_found,A,1e-8));
+        }
+    }
+
+    @Test
+    public void zeroMatrix() {
+        DenseMatrix64F A = new DenseMatrix64F(3,3);
+
+        LUDecomposition<DenseMatrix64F> alg = create(3,3);
+
+        assertTrue(alg.decompose(A));
+        assertTrue(alg.isSingular());
+
+        DenseMatrix64F L = alg.getLower(null);
+        DenseMatrix64F U = alg.getUpper(null);
+
+        DenseMatrix64F A_found = new DenseMatrix64F(3,3);
+        CommonOps.mult(L,U,A_found);
+
+        assertFalse(MatrixFeatures.hasUncountable(A_found));
+        assertTrue(MatrixFeatures.isIdentical(A_found,A,1e-8));
+    }
+
+    @Test
+    public void testSingular(){
+        DenseMatrix64F A = new DenseMatrix64F(3,3, true, 1, 2, 3, 2, 4, 6, 4, 4, 0);
+
+        LUDecomposition alg = create(3,3);
+        assertTrue(alg.decompose(A));
+        assertTrue(alg.isSingular());
+    }
+
+    @Test
+    public void testNearlySingular(){
+        DenseMatrix64F A = new DenseMatrix64F(3,3, true, 1, 2, 3, 2, 4, 6.1, 4, 4, 0);
+
+        LUDecomposition alg = create(3,3);
+        assertTrue(alg.decompose(A));
+        assertFalse(alg.isSingular());
+    }
+
+    /**
+     * Checks to see how it handles getLower getUpper functions with and without
+     * a matrix being provided.
+     */
+    @Test
+    public void getLower_getUpper() {
+        DenseMatrix64F A = new DenseMatrix64F(3,3, true, 5, 2, 3, 1.5, -2, 8, -3, 4.7, -0.5);
+
+        LUDecomposition<DenseMatrix64F> alg = create(3,3);
+
+        alg.decompose(A);
+
+        DenseMatrix64F L_provided = RandomMatrices.createRandom(3,3,rand);
+        DenseMatrix64F U_provided = RandomMatrices.createRandom(3,3,rand);
+
+        assertTrue(L_provided == alg.getLower(L_provided));
+        assertTrue(U_provided == alg.getUpper(U_provided));
+
+        DenseMatrix64F L_ret = alg.getLower(null);
+        DenseMatrix64F U_ret = alg.getUpper(null);
+
+        assertTrue(MatrixFeatures.isEquals(L_provided,L_ret));
+        assertTrue(MatrixFeatures.isEquals(U_provided,U_ret));
+    }
+
+    @Test
+    public void testFat() {
+        DenseMatrix64F A = new DenseMatrix64F(2,3, true, 1, 2, 3, 2, 4, 6.1);
+
+        LUDecomposition<DenseMatrix64F> alg = create(2,3);
+
+        assertTrue(alg.decompose(A));
+//        assertFalse(alg.isSingular());
+
+        SimpleMatrix L = SimpleMatrix.wrap(alg.getLower(null));
+        SimpleMatrix U = SimpleMatrix.wrap(alg.getUpper(null));
+        SimpleMatrix P = SimpleMatrix.wrap(alg.getPivot(null));
+
+        DenseMatrix64F A_found = P.mult(L).mult(U).getMatrix();
+
+        assertTrue(MatrixFeatures.isIdentical(A_found,A,1e-8));
+    }
+
+    @Test
+    public void testTall() {
+        DenseMatrix64F A = new DenseMatrix64F(3,2, true, 1, 2, 3, 2, 4, 6.1);
+
+        LUDecomposition<DenseMatrix64F> alg = create(3,2);
+
+        assertTrue(alg.decompose(A));
+//        assertFalse(alg.isSingular());
+
+        SimpleMatrix L = SimpleMatrix.wrap(alg.getLower(null));
+        SimpleMatrix U = SimpleMatrix.wrap(alg.getUpper(null));
+        SimpleMatrix P = SimpleMatrix.wrap(alg.getPivot(null));
+
+        DenseMatrix64F A_found = P.transpose().mult(L).mult(U).getMatrix();
+
+        assertTrue(MatrixFeatures.isIdentical(A_found,A,1e-8));
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/lu/TestLUDecompositionAlt_D64.java b/main/dense64/test/org/ejml/alg/dense/decomposition/lu/TestLUDecompositionAlt_D64.java
new file mode 100644
index 0000000..278bc04
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/lu/TestLUDecompositionAlt_D64.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.lu;
+
+import java.util.Random;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestLUDecompositionAlt_D64 extends GeneralLuDecompositionChecks {
+    Random rand = new Random(0x3344);
+
+    @Override
+    public LUDecompositionBase_D64 create(int numRows, int numCols) {
+        return new LUDecompositionAlt_D64();
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/lu/TestLUDecompositionBase_D64.java b/main/dense64/test/org/ejml/alg/dense/decomposition/lu/TestLUDecompositionBase_D64.java
new file mode 100644
index 0000000..08a90e5
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/lu/TestLUDecompositionBase_D64.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.lu;
+
+import org.ejml.alg.dense.misc.DeterminantFromMinor;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestLUDecompositionBase_D64 {
+    Random rand = new Random(0x3344);
+
+    /**
+     * Compare the determinant computed from LU to the value computed from the minor
+     * matrix method.
+     */
+    @Test
+    public void testDeterminant()
+    {
+        Random rand = new Random(0xfff);
+
+        int width = 10;
+
+        DenseMatrix64F A = RandomMatrices.createRandom(width,width,rand);
+
+        DeterminantFromMinor minor = new DeterminantFromMinor(width);
+        double minorVal = minor.compute(A);
+
+        LUDecompositionAlt_D64 alg = new LUDecompositionAlt_D64();
+        alg.decompose(A);
+        double luVal = alg.computeDeterminant().real;
+
+        assertEquals(minorVal,luVal,1e-6);
+    }
+
+    @Test
+    public void _solveVectorInternal() {
+        int width = 10;
+        DenseMatrix64F LU = RandomMatrices.createRandom(width,width,rand);
+
+        DenseMatrix64F L = new DenseMatrix64F(width,width);
+        DenseMatrix64F U = new DenseMatrix64F(width,width);
+
+        for (int i = 0; i < width; i++) {
+            for (int j = 0; j < width; j++) {
+                double real = LU.get(i, j);
+                if( j <= i ) {
+                    if( j == i )
+                        L.set(i,j,1);
+                    else
+                        L.set(i,j,real);
+                }
+                if( i <= j ) {
+                    U.set(i,j,real);
+                }
+            }
+        }
+
+        DenseMatrix64F x = RandomMatrices.createRandom(width, 1, -1, 1, rand);
+        DenseMatrix64F tmp = new DenseMatrix64F(width,1);
+        DenseMatrix64F b = new DenseMatrix64F(width,1);
+
+        CommonOps.mult(U, x, tmp);
+        CommonOps.mult(L,tmp,b);
+
+
+        DebugDecompose alg = new DebugDecompose(width);
+        for( int i = 0; i < width; i++ ) alg.getIndx()[i] = i;
+        alg.setLU(LU);
+
+        alg._solveVectorInternal(b.data);
+
+        for( int i = 0; i < width; i++ ) {
+            assertEquals(x.data[i],b.data[i],1e-6);
+        }
+    }
+
+    private static class DebugDecompose extends LUDecompositionBase_D64
+    {
+        public DebugDecompose(int width) {
+            setExpectedMaxSize(width, width);
+            m = n = width;
+        }
+
+        void setLU( DenseMatrix64F LU ) {
+            this.LU = LU;
+            this.dataLU = LU.data;
+        }
+
+        @Override
+        public boolean decompose(DenseMatrix64F orig) {
+            return false;
+        }
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/qr/GenericQrCheck_D64.java b/main/dense64/test/org/ejml/alg/dense/decomposition/qr/GenericQrCheck_D64.java
new file mode 100644
index 0000000..95a660c
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/qr/GenericQrCheck_D64.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.qr;
+
+import org.ejml.alg.dense.decomposition.CheckDecompositionInterface;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.QRDecomposition;
+import org.ejml.ops.EjmlUnitTests;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.ejml.simple.SimpleMatrix;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+
+/**
+ * @author Peter Abeles
+ */
+public abstract class GenericQrCheck_D64 {
+    Random rand = new Random(0xff);
+
+    abstract protected QRDecomposition<DenseMatrix64F> createQRDecomposition();
+
+    @Test
+    public void testModifiedInput() {
+        CheckDecompositionInterface.checkModifiedInput(createQRDecomposition());
+    }
+
+    /**
+     * See if it correctly decomposes a square, tall, or wide matrix.
+     */
+    @Test
+    public void decompositionShape() {
+        checkDecomposition(5, 5 ,false);
+        checkDecomposition(10, 5,false);
+        checkDecomposition(5, 10,false);
+        checkDecomposition(5, 5 ,true);
+        checkDecomposition(10, 5,true);
+        checkDecomposition(5, 10,true);
+    }
+
+    private void checkDecomposition(int height, int width, boolean compact ) {
+        QRDecomposition<DenseMatrix64F> alg = createQRDecomposition();
+
+        SimpleMatrix A = new SimpleMatrix(height,width);
+        RandomMatrices.setRandom(A.getMatrix(),rand);
+
+        assertTrue(alg.decompose(A.copy().getMatrix()));
+
+        int minStride = Math.min(height,width);
+
+        SimpleMatrix Q = new SimpleMatrix(height,compact ? minStride : height);
+        alg.getQ(Q.getMatrix(), compact);
+        SimpleMatrix R = new SimpleMatrix(compact ? minStride : height,width);
+        alg.getR(R.getMatrix(), compact);
+
+
+        // see if Q has the expected properties
+        assertTrue(MatrixFeatures.isOrthogonal(Q.getMatrix(),1e-6));
+
+//        UtilEjml.print(alg.getQR());
+//        Q.print();
+//        R.print();
+
+        // see if it has the expected properties
+        DenseMatrix64F A_found = Q.mult(R).getMatrix();
+
+        EjmlUnitTests.assertEquals(A.getMatrix(),A_found,1e-6);
+        assertTrue(Q.transpose().mult(A).isIdentical(R,1e-6));
+    }
+
+    /**
+     * See if passing in a matrix or not providing one to getQ and getR functions
+     * has the same result
+     */
+    @Test
+    public void checkGetNullVersusNot() {
+        int width = 5;
+        int height = 10;
+
+        QRDecomposition<DenseMatrix64F> alg = createQRDecomposition();
+
+        SimpleMatrix A = new SimpleMatrix(height,width);
+        RandomMatrices.setRandom(A.getMatrix(),rand);
+
+        alg.decompose(A.getMatrix());
+
+        // get the results from a provided matrix
+        DenseMatrix64F Q_provided = RandomMatrices.createRandom(height,height,rand);
+        DenseMatrix64F R_provided = RandomMatrices.createRandom(height,width,rand);
+        
+        assertTrue(R_provided == alg.getR(R_provided, false));
+        assertTrue(Q_provided == alg.getQ(Q_provided, false));
+
+        // get the results when no matrix is provided
+        DenseMatrix64F Q_null = alg.getQ(null, false);
+        DenseMatrix64F R_null = alg.getR(null,false);
+
+        // see if they are the same
+        assertTrue(MatrixFeatures.isEquals(Q_provided,Q_null));
+        assertTrue(MatrixFeatures.isEquals(R_provided,R_null));
+    }
+
+    /**
+     * Depending on if setZero being true or not the size of the R matrix changes
+     */
+    @Test
+    public void checkGetRInputSize()
+    {
+        int width = 5;
+        int height = 10;
+
+        QRDecomposition<DenseMatrix64F> alg = createQRDecomposition();
+
+        SimpleMatrix A = new SimpleMatrix(height,width);
+        RandomMatrices.setRandom(A.getMatrix(),rand);
+
+        alg.decompose(A.getMatrix());
+
+        // check the case where it creates the matrix first
+        assertTrue(alg.getR(null,true).numRows == width);
+        assertTrue(alg.getR(null,false).numRows == height);
+
+        // check the case where a matrix is provided
+        alg.getR(new DenseMatrix64F(width,width),true);
+        alg.getR(new DenseMatrix64F(height,width),false);
+
+        // check some negative cases
+        try {
+            alg.getR(new DenseMatrix64F(height,width),true);
+            fail("Should have thrown an exception");
+        } catch( IllegalArgumentException e ) {}
+
+        try {
+            alg.getR(new DenseMatrix64F(width-1,width),false);
+            fail("Should have thrown an exception");
+        } catch( IllegalArgumentException e ) {}
+    }
+
+    /**
+     * See if the compact format for Q works
+     */
+    @Test
+    public void checkCompactFormat()
+    {
+        int height = 10;
+        int width = 5;
+
+        QRDecomposition<DenseMatrix64F> alg = createQRDecomposition();
+
+        SimpleMatrix A = new SimpleMatrix(height,width);
+        RandomMatrices.setRandom(A.getMatrix(),rand);
+
+        alg.decompose(A.getMatrix());
+
+        SimpleMatrix Q = new SimpleMatrix(height,width);
+        alg.getQ(Q.getMatrix(), true);
+
+        // see if Q has the expected properties
+        assertTrue(MatrixFeatures.isOrthogonal(Q.getMatrix(),1e-6));
+
+        // try to extract it with the wrong dimensions
+        Q = new SimpleMatrix(height,height);
+        try {
+            alg.getQ(Q.getMatrix(), true);
+            fail("Didn't fail");
+        } catch( RuntimeException e ) {}
+    }
+
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/qr/TestQRColPivDecompositionHouseholderColumn_D64.java b/main/dense64/test/org/ejml/alg/dense/decomposition/qr/TestQRColPivDecompositionHouseholderColumn_D64.java
new file mode 100644
index 0000000..d22fdaa
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/qr/TestQRColPivDecompositionHouseholderColumn_D64.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.qr;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.ejml.simple.SimpleMatrix;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestQRColPivDecompositionHouseholderColumn_D64 {
+
+    Random rand = new Random(234);
+
+    /**
+     * Test it against a specially created matrix which should not require pivots
+     * and is full rank.
+     */
+    @Test
+    public void noPivot() {
+        DenseMatrix64F A = RandomMatrices.createOrthogonal(6, 3, rand);
+
+        // make sure the columns have norms in descending magnitude
+        for( int i = 0; i < A.numCols; i++ ) {
+
+            double scale = (A.numCols-i)*3;
+
+            for( int j = 0; j < A.numRows; j++ ) {
+                A.set(j,i,scale*A.get(j,i));
+            }
+        }
+
+        // because no pivots are happening this should be equivalent of the normal QR
+        QRColPivDecompositionHouseholderColumn_D64 alg = new QRColPivDecompositionHouseholderColumn_D64();
+        assertTrue(alg.decompose(A));
+
+        DenseMatrix64F Q = alg.getQ(null, false);
+        DenseMatrix64F R = alg.getR(null, false);
+
+        DenseMatrix64F found = new DenseMatrix64F(A.numRows,A.numCols);
+        CommonOps.mult(Q,R,found);
+
+        assertTrue(MatrixFeatures.isIdentical(A,found,1e-8));
+
+        // check the pivots
+        int pivots[] = alg.getPivots();
+        for( int i = 0; i < A.numCols; i++ ) {
+            assertEquals(i,pivots[i]);
+        }
+
+        DenseMatrix64F P = alg.getPivotMatrix(null);
+        assertTrue(MatrixFeatures.isIdentity(P, 1e-8));
+    }
+
+    /**
+     * Test it against a rank deficient matrix
+     */
+    @Test
+    public void testRankDeficient() {
+        int numRows = 10;
+
+        for( int numSingular = 0; numSingular < numRows-1; numSingular++ )  {
+
+            // construct a singular matrix from its SVD decomposition
+            SimpleMatrix U = SimpleMatrix.wrap(RandomMatrices.createOrthogonal(numRows,numRows,rand));
+            SimpleMatrix S = SimpleMatrix.diag(1,2,3,4,5,6,7,8,9,10);
+            SimpleMatrix V = SimpleMatrix.wrap(RandomMatrices.createOrthogonal(numRows,numRows,rand));
+
+            for( int i = 0; i < numSingular; i++ ) {
+                S.set(i,i,0);
+            }
+
+            SimpleMatrix A = U.mult(S).mult(V.transpose());
+
+            QRColPivDecompositionHouseholderColumn_D64 alg = new QRColPivDecompositionHouseholderColumn_D64();
+            assertTrue(alg.decompose(A.getMatrix()));
+
+            checkDecomposition(false,A.getMatrix(),alg);
+        }
+    }
+
+    /**
+     * See how the decomposition goes against various matrices of different sizes
+     */
+    @Test
+    public void testRandomMatrix() {
+        checkDecomposition(5, 5 ,false);
+        checkDecomposition(10, 5,false);
+        checkDecomposition(5, 10,false);
+        checkDecomposition(5, 5 ,true);
+        checkDecomposition(10, 5,true);
+        checkDecomposition(5, 10,true);
+    }
+
+    /**
+     * See if a zero matrix is gracefully handled
+     */
+    @Test
+    public void testZeroMatrix() {
+        DenseMatrix64F A = new DenseMatrix64F(5,5);
+
+        QRColPivDecompositionHouseholderColumn_D64 alg = new QRColPivDecompositionHouseholderColumn_D64();
+        assertTrue(alg.decompose(A));
+
+        checkDecomposition(false, A, alg);
+        checkDecomposition(true, A, alg);
+    }
+
+    private void checkDecomposition( int numRows , int numCols , boolean compact )
+    {
+
+        for( int i = 0; i < 10; i++ ) {
+            DenseMatrix64F A = RandomMatrices.createRandom(numRows, numCols, rand);
+
+            QRColPivDecompositionHouseholderColumn_D64 alg = new QRColPivDecompositionHouseholderColumn_D64();
+            assertTrue(alg.decompose(A));
+
+            checkDecomposition(compact, A, alg);
+        }
+    }
+
+    private void checkDecomposition(boolean compact, DenseMatrix64F a,
+                                    QRColPivDecompositionHouseholderColumn_D64 alg) {
+        SimpleMatrix Q = SimpleMatrix.wrap(alg.getQ(null, compact));
+        SimpleMatrix R = SimpleMatrix.wrap(alg.getR(null, compact));
+        SimpleMatrix P = SimpleMatrix.wrap(alg.getPivotMatrix(null));
+
+        SimpleMatrix AA = SimpleMatrix.wrap(a);
+
+        SimpleMatrix expected = AA.mult(P);
+        SimpleMatrix found = Q.mult(R);
+
+//        Q.print();
+//        R.print();
+//        P.print();
+//        System.out.println("asdfasdf");
+//        expected.print();
+//        found.print();
+        assertTrue(expected.isIdentical(found,1e-8));
+    }
+
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/qr/TestQRDecompositionHouseholderColumn_D64.java b/main/dense64/test/org/ejml/alg/dense/decomposition/qr/TestQRDecompositionHouseholderColumn_D64.java
new file mode 100644
index 0000000..5618cc9
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/qr/TestQRDecompositionHouseholderColumn_D64.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.qr;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.QRDecomposition;
+import org.ejml.ops.RandomMatrices;
+import org.ejml.simple.SimpleMatrix;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestQRDecompositionHouseholderColumn_D64 extends GenericQrCheck_D64 {
+
+    Random rand = new Random(0xff);
+
+
+    @Override
+    protected QRDecomposition<DenseMatrix64F> createQRDecomposition() {
+        return new QRDecompositionHouseholderColumn_D64();
+    }
+
+    /**
+     * Internal several householder operations are performed.  This
+     * checks to see if the householder operations and the expected result for all the
+     * submatrices.
+     */
+    @Test
+    public void householder() {
+        int width = 5;
+
+        for( int i = 0; i < width; i++ ) {
+            checkSubHouse(i , width);
+        }
+    }
+
+    private void checkSubHouse(int w , int width) {
+        DebugQR qr = new DebugQR(width,width);
+
+        SimpleMatrix A = new SimpleMatrix(width,width);
+        RandomMatrices.setRandom(A.getMatrix(),rand);
+
+        qr.householder(w,A.getMatrix());
+
+        SimpleMatrix U = new SimpleMatrix(width,1, true, qr.getQR()[w]).extractMatrix(w,width,0,1);
+        U.set(0,0,1); // this is not explicity set and is assumed to be 1
+        SimpleMatrix I = SimpleMatrix.identity(width-w);
+        SimpleMatrix Q = I.minus(U.mult(U.transpose()).scale(qr.getGamma()));
+
+
+        // check the expected properties of Q
+        assertTrue(Q.isIdentical(Q.transpose(),1e-6));
+        assertTrue(Q.isIdentical(Q.invert(),1e-6));
+
+        SimpleMatrix result = Q.mult(A.extractMatrix(w,width,w,width));
+
+        for( int i = 1; i < width-w; i++ ) {
+            assertEquals(0,result.get(i,0),1e-5);
+        }
+    }
+
+    /**
+     * Check the results of this function against basic matrix operations
+     * which are equivalent.
+     */
+    @Test
+    public void updateA() {
+        int width = 5;
+
+        for( int i = 0; i < width; i++ )
+            checkSubMatrix(width,i);
+    }
+
+    private void checkSubMatrix(int width , int w ) {
+        DebugQR qr = new DebugQR(width,width);
+
+        double gamma = 0.2;
+        double tau = 0.75;
+
+        SimpleMatrix U = new SimpleMatrix(width,1);
+        SimpleMatrix A = new SimpleMatrix(width,width);
+
+        RandomMatrices.setRandom(U.getMatrix(),rand);
+        RandomMatrices.setRandom(A.getMatrix(),rand);
+
+        qr.convertToColumnMajor(A.getMatrix());
+
+        // compute the results using standard matrix operations
+        SimpleMatrix I = SimpleMatrix.identity(width-w);
+
+        SimpleMatrix u_sub = U.extractMatrix(w,width,0,1);
+        u_sub.set(0,0,1);// assumed to be 1 in the algorithm
+        SimpleMatrix A_sub = A.extractMatrix(w,width,w,width);
+        SimpleMatrix expected = I.minus(u_sub.mult(u_sub.transpose()).scale(gamma)).mult(A_sub);
+
+        qr.updateA(w,U.getMatrix().getData(),gamma,tau);
+
+        double[][] found = qr.getQR();
+
+        for( int i = w+1; i < width; i++ ) {
+            assertEquals(U.get(i,0),found[w][i],1e-8);
+        }
+
+        // the right should be the same
+        for( int i = w; i < width; i++ ) {
+            for( int j = w+1; j < width; j++ ) {
+                double a = expected.get(i-w,j-w);
+                double b = found[j][i];
+
+                assertEquals(a,b,1e-6);
+            }
+        }
+    }
+
+    private static class DebugQR extends QRDecompositionHouseholderColumn_D64
+    {
+
+        public DebugQR( int numRows , int numCols ) {
+            setExpectedMaxSize(numRows,numCols);
+            this.numCols = numCols;
+            this.numRows = numRows;
+        }
+
+        public void householder( int j , DenseMatrix64F A ) {
+            convertToColumnMajor(A);
+
+            super.householder(j);
+        }
+
+        protected void convertToColumnMajor(DenseMatrix64F A) {
+            super.convertToColumnMajor(A);
+        }
+
+        public void updateA( int w , double u[] , double gamma , double tau ) {
+            System.arraycopy(u,0,this.dataQR[w],0,u.length);
+            this.gamma = gamma;
+            this.tau = tau;
+
+            super.updateA(w);
+        }
+
+        public double getGamma() {
+            return gamma;
+        }
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/qr/TestQRDecompositionHouseholderTran_D64.java b/main/dense64/test/org/ejml/alg/dense/decomposition/qr/TestQRDecompositionHouseholderTran_D64.java
new file mode 100644
index 0000000..ec31277
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/qr/TestQRDecompositionHouseholderTran_D64.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.qr;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.QRDecomposition;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.ejml.simple.SimpleMatrix;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestQRDecompositionHouseholderTran_D64 extends GenericQrCheck_D64 {
+
+    Random rand = new Random(0xff);
+
+    @Override
+    protected QRDecomposition<DenseMatrix64F> createQRDecomposition() {
+        return new QRDecompositionHouseholderTran_D64();
+    }
+
+    /**
+     * Sees if computing Q explicitly and applying Q produces the same results
+     */
+    @Test
+    public void applyQ() {
+        DenseMatrix64F A = RandomMatrices.createRandom(5,4,rand);
+
+        QRDecompositionHouseholderTran_D64 alg = new QRDecompositionHouseholderTran_D64();
+
+        assertTrue(alg.decompose(A));
+
+        DenseMatrix64F Q = alg.getQ(null,false);
+        DenseMatrix64F B = RandomMatrices.createRandom(5,2,rand);
+
+        DenseMatrix64F expected = new DenseMatrix64F(B.numRows,B.numCols);
+        CommonOps.mult(Q,B,expected);
+
+        alg.applyQ(B);
+
+        assertTrue(MatrixFeatures.isIdentical(expected,B,1e-8));
+    }
+
+    /**
+     * Sees if computing Q^T explicitly and applying Q^T produces the same results
+     */
+    @Test
+    public void applyTranQ() {
+        DenseMatrix64F A = RandomMatrices.createRandom(5,4,rand);
+
+        QRDecompositionHouseholderTran_D64 alg = new QRDecompositionHouseholderTran_D64();
+
+        assertTrue(alg.decompose(A));
+
+        DenseMatrix64F Q = alg.getQ(null,false);
+        DenseMatrix64F B = RandomMatrices.createRandom(5,2,rand);
+
+        DenseMatrix64F expected = new DenseMatrix64F(B.numRows,B.numCols);
+        CommonOps.multTransA(Q,B,expected);
+
+        alg.applyTranQ(B);
+
+        assertTrue(MatrixFeatures.isIdentical(expected,B,1e-8));
+    }
+
+    /**
+     * A focused check to see if the internal house holder operations are performed correctly.
+     */
+    @Test
+    public void householder() {
+        int width = 5;
+
+        for( int i = 0; i < width; i++ ) {
+            checkSubHouse(i , width);
+        }
+    }
+
+    private void checkSubHouse(int w , int width) {
+        DebugQR qr = new DebugQR(width,width);
+
+        SimpleMatrix A = new SimpleMatrix(width,width);
+        RandomMatrices.setRandom(A.getMatrix(),rand);
+
+        qr.householder(w,A.getMatrix());
+
+        SimpleMatrix U = new SimpleMatrix(width,1, true, qr.getU(w)).extractMatrix(w,width,0,1);
+
+        SimpleMatrix I = SimpleMatrix.identity(width-w);
+        SimpleMatrix Q = I.minus(U.mult(U.transpose()).scale(qr.getGamma()));
+
+
+        // check the expected properties of Q
+        assertTrue(Q.isIdentical(Q.transpose(),1e-6));
+        assertTrue(Q.isIdentical(Q.invert(),1e-6));
+
+        SimpleMatrix result = Q.mult(A.extractMatrix(w,width,w,width));
+
+        for( int i = 1; i < width-w; i++ ) {
+            assertEquals(0,result.get(i,0),1e-5);
+        }
+    }
+
+    /**
+     * Check the results of this function against basic matrix operations
+     * which are equivalent.
+     */
+    @Test
+    public void updateA() {
+        int width = 5;
+
+        for( int i = 0; i < width; i++ )
+            checkSubMatrix(width,i);
+    }
+
+    private void checkSubMatrix(int width , int w ) {
+        DebugQR qr = new DebugQR(width,width);
+
+        double gamma = 0.2;
+        double tau = 0.75;
+
+        SimpleMatrix U = new SimpleMatrix(width,1);
+        SimpleMatrix A = new SimpleMatrix(width,width);
+
+        RandomMatrices.setRandom(U.getMatrix(),rand);
+        RandomMatrices.setRandom(A.getMatrix(),rand);
+
+        CommonOps.transpose(A.getMatrix(),qr.getQR());
+
+        // compute the results using standard matrix operations
+        SimpleMatrix I = SimpleMatrix.identity(width-w);
+
+        SimpleMatrix u_sub = U.extractMatrix(w,width,0,1);
+        u_sub.set(0,0,1);// assumed to be 1 in the algorithm
+        SimpleMatrix A_sub = A.extractMatrix(w,width,w,width);
+        SimpleMatrix expected = I.minus(u_sub.mult(u_sub.transpose()).scale(gamma)).mult(A_sub);
+
+        qr.updateA(w,U.getMatrix().getData(),gamma,tau);
+
+        DenseMatrix64F found = qr.getQR();
+
+        for( int i = w+1; i < width; i++ ) {
+            assertEquals(U.get(i,0),found.get(w,i),1e-8);
+        }
+
+        // the right should be the same
+        for( int i = w; i < width; i++ ) {
+            for( int j = w+1; j < width; j++ ) {
+                double a = expected.get(i-w,j-w);
+                double b = found.get(j,i);
+
+                assertEquals(a,b,1e-6);
+            }
+        }
+    }
+
+    private static class DebugQR extends QRDecompositionHouseholderTran_D64
+    {
+
+        public DebugQR(int numRows, int numCols) {
+            setExpectedMaxSize(numRows,numCols);
+            this.numRows = numRows;
+            this.numCols = numCols;
+        }
+
+        public void householder( int j , DenseMatrix64F A ) {
+            CommonOps.transpose(A,QR);
+
+            super.householder(j);
+        }
+
+        public void updateA( int w , double u[] , double gamma , double tau ) {
+            System.arraycopy(u,0,this.QR.data,w*QR.numRows,u.length);
+            this.gamma = gamma;
+            this.tau = tau;
+
+            super.updateA(w);
+        }
+
+        public double[] getU( int w ) {
+            System.arraycopy(QR.data,w*numRows,v,0,numRows);
+            v[w] = 1;
+            return v;
+        }
+
+        public double getGamma() {
+            return gamma;
+        }
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/qr/TestQRDecompositionHouseholder_D64.java b/main/dense64/test/org/ejml/alg/dense/decomposition/qr/TestQRDecompositionHouseholder_D64.java
new file mode 100644
index 0000000..951124f
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/qr/TestQRDecompositionHouseholder_D64.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.qr;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.QRDecomposition;
+import org.ejml.ops.RandomMatrices;
+import org.ejml.simple.SimpleMatrix;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestQRDecompositionHouseholder_D64 extends GenericQrCheck_D64 {
+
+    Random rand = new Random(0xff);
+
+
+    @Override
+    protected QRDecomposition<DenseMatrix64F> createQRDecomposition() {
+        return new QRDecompositionHouseholder_D64();
+    }
+
+    /**
+     * Internall several house holder operations are performed.  This
+     * checks to see if the householder operations and the expected result for all the
+     * submatrices.
+     */
+    @Test
+    public void householder() {
+        int width = 5;
+
+        for( int i = 0; i < width; i++ ) {
+            checkSubHouse(i , width);
+        }
+    }
+
+    private void checkSubHouse(int w , int width) {
+        DebugQR qr = new DebugQR(width,width);
+
+        SimpleMatrix A = new SimpleMatrix(width,width);
+        RandomMatrices.setRandom(A.getMatrix(),rand);
+
+        qr.householder(w,A.getMatrix());
+
+        SimpleMatrix U = new SimpleMatrix(width,1, true, qr.getU()).extractMatrix(w,width,0,1);
+
+        SimpleMatrix I = SimpleMatrix.identity(width-w);
+        SimpleMatrix Q = I.minus(U.mult(U.transpose()).scale(qr.getGamma()));
+
+
+        // check the expected properties of Q
+        assertTrue(Q.isIdentical(Q.transpose(),1e-6));
+        assertTrue(Q.isIdentical(Q.invert(),1e-6));
+
+        SimpleMatrix result = Q.mult(A.extractMatrix(w,width,w,width));
+
+        assertEquals(-qr.tau,result.get(0,0),1e-8);
+        for( int i = 1; i < width-w; i++ ) {
+            assertEquals(0,result.get(i,0),1e-5);
+        }
+    }
+
+    /**
+     * Check the results of this function against basic matrix operations
+     * which are equivalent.
+     */
+    @Test
+    public void updateA() {
+        int width = 5;
+
+        for( int i = 0; i < width; i++ )
+            checkSubMatrix(width,i);
+    }
+
+    private void checkSubMatrix(int width , int w ) {
+        DebugQR qr = new DebugQR(width,width);
+
+        double gamma = 0.2;
+        double tau = 0.75;
+
+        SimpleMatrix U = new SimpleMatrix(width,1);
+        SimpleMatrix A = new SimpleMatrix(width,width);
+
+        RandomMatrices.setRandom(U.getMatrix(),rand);
+        RandomMatrices.setRandom(A.getMatrix(),rand);
+
+        qr.getQR().set(A.getMatrix());
+
+        // compute the results using standard matrix operations
+        SimpleMatrix I = SimpleMatrix.identity(width-w);
+
+        SimpleMatrix u_sub = U.extractMatrix(w,width,0,1);
+        SimpleMatrix A_sub = A.extractMatrix(w,width,w,width);
+        SimpleMatrix expected = I.minus(u_sub.mult(u_sub.transpose()).scale(gamma)).mult(A_sub);
+
+        qr.updateA(w,U.getMatrix().getData(),gamma,tau);
+
+        DenseMatrix64F found = qr.getQR();
+
+        assertEquals(-tau,found.get(w,w),1e-8);
+
+        for( int i = w+1; i < width; i++ ) {
+            assertEquals(U.get(i,0),found.get(i,w),1e-8);
+        }
+
+        // the right should be the same
+        for( int i = w; i < width; i++ ) {
+            for( int j = w+1; j < width; j++ ) {
+                double a = expected.get(i-w,j-w);
+                double b = found.get(i,j);
+
+                assertEquals(a,b,1e-6);
+            }
+        }
+    }
+
+    private static class DebugQR extends QRDecompositionHouseholder_D64
+    {
+
+        public DebugQR(int numRows, int numCols) {
+            setExpectedMaxSize(numRows,numCols);
+            this.numRows = numRows;
+            this.numCols = numCols;
+        }
+
+        public void householder( int j , DenseMatrix64F A ) {
+            this.QR.set(A);
+
+            super.householder(j);
+        }
+
+        public void updateA( int w , double u[] , double gamma , double tau ) {
+            System.arraycopy(u,0,this.u,0,this.u.length);
+            this.gamma = gamma;
+            this.tau = tau;
+
+            super.updateA(w);
+        }
+
+        public double[] getU() {
+            return u;
+        }
+
+        public double getGamma() {
+            return gamma;
+        }
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/qr/TestQRDecomposition_B64_to_D64.java b/main/dense64/test/org/ejml/alg/dense/decomposition/qr/TestQRDecomposition_B64_to_D64.java
new file mode 100644
index 0000000..ce3aac1
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/qr/TestQRDecomposition_B64_to_D64.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.qr;
+
+import org.ejml.interfaces.decomposition.QRDecomposition;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestQRDecomposition_B64_to_D64 extends GenericQrCheck_D64 {
+
+    @Override
+    protected QRDecomposition createQRDecomposition() {
+        return new QRDecomposition_B64_to_D64();
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/qr/TestQrUpdate.java b/main/dense64/test/org/ejml/alg/dense/decomposition/qr/TestQrUpdate.java
new file mode 100644
index 0000000..e0b54f5
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/qr/TestQrUpdate.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.qr;
+
+import org.ejml.alg.dense.mult.SubmatrixOps;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.QRDecomposition;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestQrUpdate {
+
+    Random rand = new Random(0x345345);
+
+    /**
+     * Adds a row to a matrix at various points and updates the QR decomposition.
+     * This is then checked by multiplying Q by R and seeing if the augmented A matrix
+     * is the result
+     */
+    @Test
+    public void testInsertRow() {
+        int n = 3;
+
+        for( int m = 3; m < 6; m++ ) {
+            for( int insert = 0; insert < m; insert++ ) {
+                checkInsert(m, n, insert);
+            }
+        }
+    }
+
+    @Test
+    public void testRemoveRow() {
+        int n = 3;
+
+        for( int m = 4; m < 6; m++ ) {
+            for( int remove = 0; remove < m; remove++ ) {
+                checkRemove(m, n, remove);
+            }
+        }
+    }
+
+    private void checkRemove(int m, int n, int remove) {
+        DenseMatrix64F A = RandomMatrices.createRandom(m,n,rand);
+        DenseMatrix64F Q = RandomMatrices.createRandom(m,m,rand);
+        DenseMatrix64F R = new DenseMatrix64F(m,n);
+
+        // compute what the A matrix would look like without the row
+        DenseMatrix64F A_e = RandomMatrices.createRandom(m-1,n,rand);
+        SubmatrixOps.setSubMatrix(A,A_e,0,0,0,0,remove,n);
+        SubmatrixOps.setSubMatrix(A,A_e,remove+1,0,remove,0,m-remove-1,n);
+
+        QRDecomposition decomp = new QRDecompositionHouseholderColumn_D64();
+
+        // now compute the results by removing elements from A
+        decomp.decompose(A);
+        Q.reshape(m,m, false);
+        decomp.getQ(Q,false);
+        decomp.getR(R,false);
+
+        QrUpdate update = new QrUpdate(m,n);
+
+        update.deleteRow(Q,R,remove,true);
+
+        assertTrue(MatrixFeatures.isOrthogonal(update.getU_tran(),1e-6));
+
+        DenseMatrix64F A_r = RandomMatrices.createRandom(m-1,n,rand);
+        CommonOps.mult(Q,R,A_r);
+
+
+        // see if the augmented A matrix is correct extracted from the adjusted Q and R matrices
+        assertTrue(MatrixFeatures.isIdentical(A_e,A_r,1e-6));
+    }
+
+    private void checkInsert(int m, int n, int insert) {
+        DenseMatrix64F A = RandomMatrices.createRandom(m,n,rand);
+        DenseMatrix64F Q = RandomMatrices.createRandom(m+1,m+1,rand);
+        DenseMatrix64F R = new DenseMatrix64F(m+1,n);
+
+        // the row that is to be inserted
+        double row[] = new double[]{1,2,3};
+
+        // create the modified A
+        DenseMatrix64F A_e = RandomMatrices.createRandom(m+1,n,rand);
+        SubmatrixOps.setSubMatrix(A,A_e,0,0,0,0,insert,n);
+        System.arraycopy(row, 0, A_e.data, insert * n, n);
+        SubmatrixOps.setSubMatrix(A,A_e,insert,0,insert+1,0,m-insert,n);
+
+        QRDecomposition decomp = new QRDecompositionHouseholderColumn_D64();
+
+        decomp.decompose(A);
+        Q.reshape(m,m, false);
+        decomp.getQ(Q,false);
+        R.reshape(m,n,false);
+        decomp.getR(R,false);
+
+        DenseMatrix64F Qmod = createQMod(Q,insert);
+
+        QrUpdate update = new QrUpdate(m+1,n);
+
+        update.addRow(Q,R,row,insert,true);
+
+        DenseMatrix64F Z = new DenseMatrix64F(m+1,m+1);
+        CommonOps.multTransB(Qmod,update.getU_tran(),Z);
+        // see if the U matrix has the expected features
+        assertTrue(MatrixFeatures.isOrthogonal(Z,1e-6));
+
+        // see if the process that updates Q from U is valid
+        assertTrue(MatrixFeatures.isIdentical(Q,Z,1e-6));
+
+        DenseMatrix64F A_r = RandomMatrices.createRandom(m+1,n,rand);
+        CommonOps.mult(Q,R,A_r);
+
+        // see if the augmented A matrix is correct extracted from the adjusted Q and R matrices
+        assertTrue(MatrixFeatures.isIdentical(A_e,A_r,1e-6));
+    }
+
+    public static DenseMatrix64F createQMod( DenseMatrix64F Q , int insertRow ) {
+        DenseMatrix64F Qmod = new DenseMatrix64F(Q.numRows+1,Q.numCols+1);
+
+        SubmatrixOps.setSubMatrix(Q,Qmod,0,0,0,1,insertRow,Q.numCols);
+        Qmod.set(insertRow,0,1);
+        SubmatrixOps.setSubMatrix(Q,Qmod,insertRow,0,insertRow+1,1,Q.numRows-insertRow,Q.numCols);
+
+        return Qmod;
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/svd/StandardSvdChecks.java b/main/dense64/test/org/ejml/alg/dense/decomposition/svd/StandardSvdChecks.java
new file mode 100644
index 0000000..d60cadc
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/svd/StandardSvdChecks.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.svd;
+
+import org.ejml.UtilEjml;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.data.UtilTestMatrix;
+import org.ejml.interfaces.decomposition.SingularValueDecomposition;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.ejml.ops.SingularOps;
+import org.ejml.simple.SimpleMatrix;
+
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * @author Peter Abeles
+ */
+public abstract class StandardSvdChecks {
+
+    Random rand = new Random(73675);
+
+    public abstract SingularValueDecomposition<DenseMatrix64F> createSvd();
+
+    boolean omitVerySmallValues = false;
+
+    public void allTests() {
+        testSizeZero();
+        testDecompositionOfTrivial();
+        testWide();
+        testTall();
+        checkGetU_Transpose();
+        checkGetU_Storage();
+        checkGetV_Transpose();
+        checkGetV_Storage();
+
+        if( !omitVerySmallValues )
+            testVerySmallValue();
+        testZero();
+        testLargeToSmall();
+        testIdentity();
+        testLarger();
+        testLots();
+    }
+
+    public void testSizeZero() {
+        SingularValueDecomposition<DenseMatrix64F> alg = createSvd();
+
+        assertFalse(alg.decompose(new DenseMatrix64F(0, 0)));
+        assertFalse(alg.decompose(new DenseMatrix64F(0,2)));
+        assertFalse(alg.decompose(new DenseMatrix64F(2,0)));
+    }
+
+    public void testDecompositionOfTrivial()
+    {
+        DenseMatrix64F A = new DenseMatrix64F(3,3, true, 5, 2, 3, 1.5, -2, 8, -3, 4.7, -0.5);
+
+        SingularValueDecomposition<DenseMatrix64F> alg = createSvd();
+        assertTrue(alg.decompose(A));
+
+        assertEquals(3, SingularOps.rank(alg, UtilEjml.EPS));
+        assertEquals(0, SingularOps.nullity(alg, UtilEjml.EPS));
+
+        double []w = alg.getSingularValues();
+        UtilTestMatrix.checkNumFound(1,1e-5,9.59186,w);
+        UtilTestMatrix.checkNumFound(1,1e-5,5.18005,w);
+        UtilTestMatrix.checkNumFound(1,1e-5,4.55558,w);
+
+        checkComponents(alg,A);
+    }
+
+    public void testWide() {
+        DenseMatrix64F A = RandomMatrices.createRandom(5,20,-1,1,rand);
+
+        SingularValueDecomposition<DenseMatrix64F> alg = createSvd();
+        assertTrue(alg.decompose(A));
+
+        checkComponents(alg,A);
+    }
+
+    public void testTall() {
+        DenseMatrix64F A = RandomMatrices.createRandom(21,5,-1,1,rand);
+
+        SingularValueDecomposition<DenseMatrix64F> alg = createSvd();
+        assertTrue(alg.decompose(A));
+
+        checkComponents(alg,A);
+    }
+
+    public void testZero() {
+
+        for( int i = 1; i <= 16; i += 5 ) {
+            for( int j = 1; j <= 16; j += 5 ) {
+                DenseMatrix64F A = new DenseMatrix64F(i,j);
+
+                SingularValueDecomposition<DenseMatrix64F> alg = createSvd();
+                assertTrue(alg.decompose(A));
+
+                int min = Math.min(i,j);
+
+                assertEquals(min,checkOccurrence(0,alg.getSingularValues(),min),UtilEjml.EPS);
+
+                checkComponents(alg,A);
+            }
+        }
+    }
+
+    public void testIdentity() {
+        DenseMatrix64F A = CommonOps.identity(6,6);
+
+        SingularValueDecomposition<DenseMatrix64F> alg = createSvd();
+        assertTrue(alg.decompose(A));
+
+        assertEquals(6,checkOccurrence(1,alg.getSingularValues(),6),1e-5);
+
+        checkComponents(alg,A);
+    }
+
+    public void testLarger() {
+        DenseMatrix64F A = RandomMatrices.createRandom(200,200,-1,1,rand);
+
+        SingularValueDecomposition<DenseMatrix64F> alg = createSvd();
+        assertTrue(alg.decompose(A));
+
+        checkComponents(alg,A);
+    }
+
+    /**
+     * See if it can handle very small values and not blow up.  This can some times
+     * cause a zero to appear unexpectedly and thus a divided by zero.
+     */
+    public void testVerySmallValue() {
+        DenseMatrix64F A = RandomMatrices.createRandom(5,5,-1,1,rand);
+
+        CommonOps.scale(1e-200,A);
+
+        SingularValueDecomposition<DenseMatrix64F> alg = createSvd();
+        assertTrue(alg.decompose(A));
+
+        checkComponents(alg,A);
+    }
+
+
+    public void testLots() {
+        SingularValueDecomposition<DenseMatrix64F> alg = createSvd();
+
+        for( int i = 1; i < 10; i++ ) {
+            for( int j = 1; j < 10; j++ ) {
+                DenseMatrix64F A = RandomMatrices.createRandom(i,j,-1,1,rand);
+
+                assertTrue(alg.decompose(A));
+
+                checkComponents(alg,A);
+            }
+        }
+    }
+
+    /**
+     * Makes sure transposed flag is correctly handled.
+     */
+    public void checkGetU_Transpose() {
+        DenseMatrix64F A = RandomMatrices.createRandom(5, 7, -1, 1, rand);
+
+        SingularValueDecomposition<DenseMatrix64F> alg = createSvd();
+        assertTrue(alg.decompose(A));
+
+        DenseMatrix64F U = alg.getU(null,false);
+        DenseMatrix64F Ut = alg.getU(null,true);
+
+        DenseMatrix64F found = new DenseMatrix64F(U.numCols,U.numRows);
+
+        CommonOps.transpose(U,found);
+
+        assertTrue( MatrixFeatures.isEquals(Ut,found));
+    }
+
+    /**
+     * Makes sure the optional storage parameter is handled correctly
+     */
+    public void checkGetU_Storage() {
+        DenseMatrix64F A = RandomMatrices.createRandom(5,7,-1,1,rand);
+
+        SingularValueDecomposition<DenseMatrix64F> alg = createSvd();
+        assertTrue(alg.decompose(A));
+
+        // test positive cases
+        DenseMatrix64F U = alg.getU(null,false);
+        DenseMatrix64F storage = new DenseMatrix64F(U.numRows,U.numCols);
+
+        alg.getU(storage,false);
+
+        assertTrue( MatrixFeatures.isEquals(U,storage));
+
+        U = alg.getU(null,true);
+        storage = new DenseMatrix64F(U.numRows,U.numCols);
+
+        alg.getU(storage,true);
+        assertTrue( MatrixFeatures.isEquals(U,storage));
+
+        // give it an incorrect sign
+        try {
+            alg.getU(new DenseMatrix64F(10,20),true);
+            fail("Exception should have been thrown");
+        } catch( RuntimeException e ){}
+        try {
+            alg.getU(new DenseMatrix64F(10,20),false);
+            fail("Exception should have been thrown");
+        } catch( RuntimeException e ){}
+    }
+
+    /**
+     * Makes sure transposed flag is correctly handled.
+     */
+    public void checkGetV_Transpose() {
+        DenseMatrix64F A = RandomMatrices.createRandom(5,7,-1,1,rand);
+
+        SingularValueDecomposition<DenseMatrix64F> alg = createSvd();
+        assertTrue(alg.decompose(A));
+
+        DenseMatrix64F V = alg.getV(null,false);
+        DenseMatrix64F Vt = alg.getV(null,true);
+
+        DenseMatrix64F found = new DenseMatrix64F(V.numCols,V.numRows);
+
+        CommonOps.transpose(V,found);
+
+        assertTrue( MatrixFeatures.isEquals(Vt,found));
+    }
+
+    /**
+     * Makes sure the optional storage parameter is handled correctly
+     */
+    public void checkGetV_Storage() {
+        DenseMatrix64F A = RandomMatrices.createRandom(5,7,-1,1,rand);
+
+        SingularValueDecomposition<DenseMatrix64F> alg = createSvd();
+        assertTrue(alg.decompose(A));
+
+        // test positive cases
+        DenseMatrix64F V = alg.getV(null, false);
+        DenseMatrix64F storage = new DenseMatrix64F(V.numRows,V.numCols);
+
+        alg.getV(storage, false);
+
+        assertTrue(MatrixFeatures.isEquals(V, storage));
+
+        V = alg.getV(null, true);
+        storage = new DenseMatrix64F(V.numRows,V.numCols);
+
+        alg.getV(storage, true);
+        assertTrue( MatrixFeatures.isEquals(V,storage));
+
+        // give it an incorrect sign
+        try {
+            alg.getV(new DenseMatrix64F(10, 20), true);
+            fail("Exception should have been thrown");
+        } catch( RuntimeException e ){}
+        try {
+            alg.getV(new DenseMatrix64F(10, 20), false);
+            fail("Exception should have been thrown");
+        } catch( RuntimeException e ){}
+    }
+
+    /**
+     * Makes sure arrays are correctly set when it first computers a larger matrix
+     * then a smaller one.  When going from small to large its often forces to declare
+     * new memory, this way it actually uses memory.
+     */
+    public void testLargeToSmall() {
+        SingularValueDecomposition<DenseMatrix64F> alg = createSvd();
+
+        // first the larger one
+        DenseMatrix64F A = RandomMatrices.createRandom(10,10,-1,1,rand);
+        assertTrue(alg.decompose(A));
+        checkComponents(alg,A);
+
+        // then the smaller one
+        A = RandomMatrices.createRandom(5,5,-1,1,rand);
+        assertTrue(alg.decompose(A));
+        checkComponents(alg,A);
+    }
+
+    private int checkOccurrence( double check , double[]values , int numSingular ) {
+        int num = 0;
+
+        for( int i = 0; i < numSingular; i++ ) {
+            if( Math.abs(values[i]-check)<1e-8)
+                num++;
+        }
+
+        return num;
+    }
+
+    private void checkComponents( SingularValueDecomposition<DenseMatrix64F> svd , DenseMatrix64F expected )
+    {
+        SimpleMatrix U = SimpleMatrix.wrap(svd.getU(null,false));
+        SimpleMatrix Vt = SimpleMatrix.wrap(svd.getV(null,true));
+        SimpleMatrix W = SimpleMatrix.wrap(svd.getW(null));
+
+        assertTrue( !U.hasUncountable() );
+        assertTrue( !Vt.hasUncountable() );
+        assertTrue( !W.hasUncountable() );
+
+        if( svd.isCompact() ) {
+            assertEquals(W.numCols(),W.numRows());
+            assertEquals(U.numCols(),W.numRows());
+            assertEquals(Vt.numRows(),W.numCols());
+        } else {
+            assertEquals(U.numCols(),W.numRows());
+            assertEquals(W.numCols(),Vt.numRows());
+            assertEquals(U.numCols(),U.numRows());
+            assertEquals(Vt.numCols(),Vt.numRows());
+        }
+
+        DenseMatrix64F found = U.mult(W).mult(Vt).getMatrix();
+
+//        found.print();
+//        expected.print();
+
+        assertTrue(MatrixFeatures.isIdentical(expected,found,1e-8));
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/svd/TestSafeSvd.java b/main/dense64/test/org/ejml/alg/dense/decomposition/svd/TestSafeSvd.java
new file mode 100644
index 0000000..eb43331
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/svd/TestSafeSvd.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.svd;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.data.RealMatrix64F;
+import org.ejml.interfaces.decomposition.SingularValueDecomposition;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestSafeSvd {
+
+    @Test
+    public void getSafety() {
+        DenseMatrix64F A = new DenseMatrix64F(3,4);
+
+        // it will need to create a copy in this case
+        Dummy dummy = new Dummy(2,true,true,2,3);
+
+        SingularValueDecomposition decomp = new SafeSvd(dummy);
+        assertFalse(decomp.inputModified());
+
+        decomp.decompose(A);
+
+        assertTrue(A != dummy.passedInMatrix);
+
+        // now no need to make a copy
+        dummy = new Dummy(2,true,false,2,3);
+        decomp = new SafeSvd(dummy);
+        assertFalse(decomp.inputModified());
+
+        decomp.decompose(A);
+
+        assertTrue(A == dummy.passedInMatrix);
+    }
+
+    @Test
+    public void checkOtherFunctions() {
+        Dummy dummy = new Dummy(2,true,true,2,3);
+
+        SingularValueDecomposition decomp = new SafeSvd(dummy);
+
+        assertTrue(decomp.isCompact());
+        assertEquals(2, decomp.numberOfSingularValues());
+
+        assertFalse(dummy.getU_called);
+        assertFalse(dummy.getV_called);
+        assertFalse(dummy.getW_called);
+
+        decomp.getU(null,false);
+        assertTrue(dummy.getU_called);
+        decomp.getV(null, false);
+        assertTrue(dummy.getV_called);
+        decomp.getW(null);
+        assertTrue(dummy.getW_called);
+
+        assertEquals(2,decomp.numCols());
+        assertEquals(3,decomp.numRows());
+    }
+
+    protected static class Dummy implements SingularValueDecomposition<DenseMatrix64F> {
+
+        RealMatrix64F passedInMatrix;
+
+        boolean compact;
+        double singular[];
+        boolean getU_called;
+        boolean getV_called;
+        boolean getW_called;
+
+        int numRow,numCol;
+        boolean inputModified;
+
+        private Dummy( int numSingular ,
+                       boolean compact,
+                       boolean inputModified,
+                       int numCol, int numRow) {
+            singular = new double[ numSingular ];
+            this.compact = compact;
+            this.inputModified = inputModified;
+            this.numCol = numCol;
+            this.numRow = numRow;
+        }
+
+        @Override
+        public double[] getSingularValues() {
+            return singular;
+        }
+
+        @Override
+        public int numberOfSingularValues() {
+            return singular.length;
+        }
+
+        @Override
+        public boolean isCompact() {
+            return compact;
+        }
+
+        @Override
+        public DenseMatrix64F getU(DenseMatrix64F U, boolean transposed) {
+            getU_called = true;
+            return null;
+        }
+
+        @Override
+        public DenseMatrix64F getV(DenseMatrix64F V, boolean transposed) {
+            getV_called = true;
+            return null;
+        }
+
+        @Override
+        public DenseMatrix64F getW(DenseMatrix64F W) {
+            getW_called = true;
+            return null;
+        }
+
+        @Override
+        public int numRows() {
+            return numRow;
+        }
+
+        @Override
+        public int numCols() {
+            return numCol;
+        }
+
+        @Override
+        public boolean decompose(DenseMatrix64F orig) {
+            this.passedInMatrix = orig;
+            return true;
+        }
+
+        @Override
+        public boolean inputModified() {
+            return inputModified;
+        }
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/svd/TestSvdImplicitQrDecompose_D64.java b/main/dense64/test/org/ejml/alg/dense/decomposition/svd/TestSvdImplicitQrDecompose_D64.java
new file mode 100644
index 0000000..c13f87d
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/svd/TestSvdImplicitQrDecompose_D64.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.svd;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.data.UtilTestMatrix;
+import org.ejml.interfaces.decomposition.SingularValueDecomposition;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestSvdImplicitQrDecompose_D64 extends StandardSvdChecks {
+
+    boolean compact;
+    boolean needU;
+    boolean needV;
+
+    @Override
+    public SingularValueDecomposition createSvd() {
+        return new SvdImplicitQrDecompose_D64(compact,needU,needV,false);
+    }
+
+    @Test
+    public void checkCompact() {
+        compact = true;
+        needU = true;
+        needV = true;
+        allTests();
+    }
+
+    @Test
+    public void checkNotCompact() {
+        compact = false;
+        needU = true;
+        needV = true;
+        allTests();
+    }
+
+    /**
+     * This SVD can be configured to compute or not compute different components
+     * Checks to see if it has the expected behavior no matter how it is configured
+     */
+    @Test
+    public void checkAllPermutations() {
+        // test matrices with different shapes.
+        // this ensure that transposed and non-transposed are handled correctly
+        checkAllPermutations(5, 5);
+        checkAllPermutations(7, 5);
+        checkAllPermutations(5, 7);
+//        // for much taller or wider matrices different algs might be used
+        checkAllPermutations(30, 5);
+        checkAllPermutations(5, 30);
+    }
+
+    private void checkAllPermutations(int numRows, int numCols) {
+
+        for( int a = 0; a < 2; a++ ) {
+            boolean singular = a == 0;
+
+            for( int k = 0; k < 2; k++ ) {
+                compact = k == 0;
+
+                SingularValueDecomposition<DenseMatrix64F> alg = new SvdImplicitQrDecompose_D64(compact,true,true,false);
+
+                DenseMatrix64F A;
+
+                if( singular ) {
+                    double sv[] = new double[ Math.min(numRows,numCols)];
+//                    for( int i = 0; i < sv.length; i++ )
+//                        sv[i] = rand.nextDouble()*2;
+//                    sv[0] = 0;
+
+                    A = RandomMatrices.createSingularValues(numRows,numCols,rand,sv);
+//                    A = new DenseMatrix64F(numRows,numCols);
+                } else {
+                    A = RandomMatrices.createRandom(numRows,numCols,-1,1,rand);
+                }
+
+                assertTrue(alg.decompose(A.copy()));
+
+                DenseMatrix64F origU = alg.getU(null,false);
+                double sv[] = alg.getSingularValues();
+                DenseMatrix64F origV = alg.getV(null,false);
+
+                for( int i = 0; i < 2; i++ ) {
+                    needU = i == 0;
+                    for( int j = 0; j < 2; j++ ) {
+                        needV = j==0;
+
+                        testPartial(A,origU,sv,origV,needU,needV);
+                    }
+                }
+            }
+        }
+    }
+
+    public void testPartial( DenseMatrix64F A ,
+                             DenseMatrix64F U ,
+                             double sv[] ,
+                             DenseMatrix64F V ,
+                             boolean checkU , boolean checkV )
+    {
+        SingularValueDecomposition<DenseMatrix64F> alg = new SvdImplicitQrDecompose_D64(compact,checkU,checkV,false);
+
+        assertTrue(alg.decompose(A.copy()));
+
+        UtilTestMatrix.checkSameElements(1e-10,sv.length,sv,alg.getSingularValues());
+
+        if( checkU ) {
+            assertTrue(MatrixFeatures.isIdentical(U,alg.getU(null,false),1e-10));
+        }
+        if( checkV )
+            assertTrue(MatrixFeatures.isIdentical(V,alg.getV(null,false),1e-10));
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/decomposition/svd/implicitqr/TestSvdImplicitQrAlgorithm.java b/main/dense64/test/org/ejml/alg/dense/decomposition/svd/implicitqr/TestSvdImplicitQrAlgorithm.java
new file mode 100644
index 0000000..79339d2
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/decomposition/svd/implicitqr/TestSvdImplicitQrAlgorithm.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.svd.implicitqr;
+
+import org.ejml.UtilEjml;
+import org.ejml.alg.dense.decomposition.bidiagonal.BidiagonalDecompositionRow_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.simple.SimpleMatrix;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestSvdImplicitQrAlgorithm {
+
+    private Random rand = new Random(234234);
+
+    /**
+     * Computes the singular values of a bidiagonal matrix that is all ones.
+     * From exercise 5.9.45 in Fundamentals of Matrix Computations.
+     *
+     */
+    @Test
+    public void oneBidiagonalMatrix() {
+        SvdImplicitQrAlgorithm svd = new SvdImplicitQrAlgorithm(true);
+        for( int N = 5; N < 10; N++ ) {
+            double diag[] = new double[N];
+            double off[] = new double[N-1];
+            diag[0]=1;
+            for( int i = 0; i < N-1; i++ ) {
+                diag[i+1]=1;
+                off[i] = 1;
+            }
+
+            svd.setMatrix(N,N,diag,off);
+
+            assertTrue(svd.process());
+
+            for( int i = 0; i < N; i++ ) {
+                double val = 2.0*Math.cos( (i+1)*Math.PI/(2.0*N+1.0));
+
+                assertEquals(1,countNumFound(svd,val,1e-8));
+            }
+        }
+    }
+
+    /**
+     * A trivial case where all the elements are diagonal.  It should do nothing here.
+     */
+    @Test
+    public void knownDiagonal() {
+        double diag[] = new double[]{1,2,3,4,5};
+        double off[] = new double[diag.length-1];
+
+        SvdImplicitQrAlgorithm svd = new SvdImplicitQrAlgorithm(true);
+        svd.setMatrix(diag.length,diag.length,diag,off);
+
+        assertTrue(svd.process());
+
+        assertEquals(1,countNumFound(svd,5,1e-8));
+        assertEquals(1,countNumFound(svd,4,1e-8));
+        assertEquals(1,countNumFound(svd,3,1e-8));
+        assertEquals(1,countNumFound(svd,2,1e-8));
+        assertEquals(1,countNumFound(svd,1,1e-8));
+    }
+
+    /**
+     * Sees if it handles the case where there is a zero on the diagonal
+     */
+    @Test
+    public void zeroOnDiagonal() {
+        double diag[] = new double[]{1,2,3,4,5,6};
+        double off[] = new double[]{2,2,2,2,2};
+
+        diag[2] = 0;
+
+//        A.print();
+
+        SvdImplicitQrAlgorithm svd = new SvdImplicitQrAlgorithm(false);
+        svd.setMatrix(6,6,diag,off);
+
+        assertTrue(svd.process());
+
+        assertEquals(1,countNumFound(svd,6.82550,1e-4));
+        assertEquals(1,countNumFound(svd,5.31496,1e-4));
+        assertEquals(1,countNumFound(svd,3.76347,1e-4));
+        assertEquals(1,countNumFound(svd,3.28207,1e-4));
+        assertEquals(1,countNumFound(svd,1.49265,1e-4));
+        assertEquals(1,countNumFound(svd,0.00000,1e-4));
+    }
+
+    @Test
+    public void knownCaseSquare() {
+        DenseMatrix64F A = UtilEjml.parseMatrix("-3   1   3  -3   0\n" +
+                "   2  -4   0  -2   0\n" +
+                "   1  -4   4   1  -3\n" +
+                "  -1  -3   2   2  -4\n" +
+                "  -5   3   1   3   1",5);
+
+//        A.print();
+
+        SvdImplicitQrAlgorithm svd = createHelper(A);
+
+        assertTrue(svd.process());
+
+        assertEquals(1,countNumFound(svd,9.3431,1e-3));
+        assertEquals(1,countNumFound(svd,7.4856,1e-3));
+        assertEquals(1,countNumFound(svd,4.9653,1e-3));
+        assertEquals(1,countNumFound(svd,1.8178,1e-3));
+        assertEquals(1,countNumFound(svd,1.6475,1e-3));
+    }
+
+    /**
+     * This makes sure the U and V matrices are being correctly by the push code.
+     */
+    @Test
+    public void zeroOnDiagonalFull() {
+        for( int where = 0; where < 6; where++ ) {
+            double diag[] = new double[]{1,2,3,4,5,6};
+            double off[] = new double[]{2,2,2,2,2};
+
+            diag[where] = 0;
+
+            checkFullDecomposition(6, diag, off );
+        }
+    }
+
+    /**
+     * Decomposes a random matrix and see if the decomposition can reconstruct the original
+     *
+     */
+    @Test
+    public void randomMatricesFullDecompose() {
+
+        for( int N = 2; N <= 20; N++ ) {
+            double diag[] = new double[N];
+            double off[] = new double[N];
+
+            diag[0] = rand.nextDouble();
+            for( int i = 1; i < N; i++ ) {
+                diag[i]=rand.nextDouble();
+                off[i-1] = rand.nextDouble();
+            }
+
+            checkFullDecomposition(N, diag,off);
+        }
+    }
+
+    /**
+     * Checks the full decomposing my multiplying the components together and seeing if it
+     * gets the original matrix again.
+     */
+    private void checkFullDecomposition(int n, double diag[] , double off[] ) {
+//        a.print();
+
+        SvdImplicitQrAlgorithm svd = createHelper(n,n,diag.clone(),off.clone());
+        svd.setFastValues(true);
+        assertTrue(svd.process());
+
+//        System.out.println("Value total steps = "+svd.totalSteps);
+
+        svd.setFastValues(false);
+        double values[] = svd.diag.clone();
+        svd.setMatrix(n,n,diag.clone(),off.clone());
+        svd.setUt(CommonOps.identity(n));
+        svd.setVt(CommonOps.identity(n));
+        assertTrue(svd.process(values));
+
+//        System.out.println("Vector total steps = "+svd.totalSteps);
+
+        SimpleMatrix Ut = SimpleMatrix.wrap(svd.getUt());
+        SimpleMatrix Vt = SimpleMatrix.wrap(svd.getVt());
+        SimpleMatrix W = SimpleMatrix.diag(svd.diag);
+//
+//            Ut.mult(W).mult(V).print();
+        SimpleMatrix A_found = Ut.transpose().mult(W).mult(Vt);
+//            A_found.print();
+
+        assertEquals(diag[0],A_found.get(0,0),1e-8);
+        for( int i = 0; i < n-1; i++ ) {
+            assertEquals(diag[i+1],A_found.get(i+1,i+1),1e-8);
+            assertEquals(off[i],A_found.get(i,i+1),1e-8);
+        }
+    }
+
+    public static SvdImplicitQrAlgorithm createHelper( DenseMatrix64F a ) {
+        BidiagonalDecompositionRow_D64 bidiag = new BidiagonalDecompositionRow_D64();
+        assertTrue(bidiag.decompose(a.copy()));
+        double diag[] = new double[a.numRows];
+        double off[] = new double[ diag.length-1 ];
+        bidiag.getDiagonal(diag,off);
+
+        return createHelper(a.numRows,a.numCols,diag,off);
+    }
+
+    public static SvdImplicitQrAlgorithm createHelper( int numRows , int numCols ,
+                                                       double diag[] , double off[] ) {
+
+        SvdImplicitQrAlgorithm helper = new SvdImplicitQrAlgorithm();
+
+        helper.setMatrix(numRows,numCols,diag,off);
+        return helper;
+    }
+
+    /**
+     * Counts the number of times the specified eigenvalue appears.
+     */
+    public int countNumFound( SvdImplicitQrAlgorithm alg , double val , double tol ) {
+        int total = 0;
+
+        for( int i = 0; i < alg.getNumberOfSingularValues(); i++ ) {
+            double a = Math.abs(alg.getSingularValue(i));
+
+            if( Math.abs(a-val) <= tol ) {
+                total++;
+            }
+        }
+
+        return total;
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/linsol/GenericLinearSolverChecks.java b/main/dense64/test/org/ejml/alg/dense/linsol/GenericLinearSolverChecks.java
new file mode 100644
index 0000000..504a553
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/linsol/GenericLinearSolverChecks.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.EjmlUnitTests;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * Contains a series of tests where it solves equations from a known set problems.
+ *
+ * @author Peter Abeles
+ */
+public abstract class GenericLinearSolverChecks {
+
+    protected Random rand = new Random(0xff);
+
+    // by default have everything run
+    protected boolean shouldFailSingular = true;
+    protected boolean shouldWorkRectangle = true;
+
+    protected double tol = 1e-8;
+
+    @Test
+    public void solve_dimensionCheck() {
+        DenseMatrix64F A = RandomMatrices.createRandom(10,4,rand);
+
+        LinearSolver<DenseMatrix64F> solver = createSafeSolver(A);
+        assertTrue(solver.setA(A));
+
+        try {
+            DenseMatrix64F x = RandomMatrices.createRandom(4,2,rand);
+            DenseMatrix64F b = RandomMatrices.createRandom(9,2,rand);
+            solver.solve(b,x);
+            fail("Should have thrown an exception");
+        } catch( RuntimeException ignore ) {}
+
+        try {
+            DenseMatrix64F x = RandomMatrices.createRandom(4,3,rand);
+            DenseMatrix64F b = RandomMatrices.createRandom(10,2,rand);
+            solver.solve(b,x);
+            fail("Should have thrown an exception");
+        } catch( RuntimeException ignore ) {}
+
+        try {
+            DenseMatrix64F x = RandomMatrices.createRandom(5,2,rand);
+            DenseMatrix64F b = RandomMatrices.createRandom(10,2,rand);
+            solver.solve(b,x);
+            fail("Should have thrown an exception");
+        } catch( RuntimeException ignore ) {}
+
+
+        try {
+            DenseMatrix64F x = RandomMatrices.createRandom(4,2,rand);
+            DenseMatrix64F b = RandomMatrices.createRandom(10,1,rand);
+            solver.solve(b,x);
+            fail("Should have thrown an exception");
+        } catch( RuntimeException ignore ) {}
+    }
+
+    /**
+     * Checks to see if the modifyA() flag is set correctly
+     */
+    @Test
+    public void modifiesA() {
+        DenseMatrix64F A_orig = RandomMatrices.createRandom(4,4,rand);
+        DenseMatrix64F A = A_orig.copy();
+
+        LinearSolver<DenseMatrix64F> solver = createSafeSolver(A);
+
+        assertTrue(solver.setA(A));
+
+        boolean modified = !MatrixFeatures.isEquals(A_orig,A);
+
+        assertTrue(modified == solver.modifiesA());
+    }
+
+    /**
+     * Checks to see if the modifyB() flag is set correctly
+     */
+    @Test
+    public void modifiesB() {
+        DenseMatrix64F A = RandomMatrices.createRandom(4, 4, rand);
+
+        LinearSolver<DenseMatrix64F> solver = createSafeSolver(A);
+
+        assertTrue(solver.setA(A));
+
+        DenseMatrix64F B = RandomMatrices.createRandom(4,2,rand);
+        DenseMatrix64F B_orig = B.copy();
+        DenseMatrix64F X = new DenseMatrix64F(A.numRows,B.numCols);
+
+        solver.solve(B, X);
+
+        boolean modified = !MatrixFeatures.isEquals(B_orig,B);
+
+        assertTrue(modified == solver.modifiesB());
+    }
+
+    /**
+     * See if a matrix that is more singular has a lower quality.
+     */
+    @Test
+    public void checkQuality() {
+        DenseMatrix64F A_good = CommonOps.diag(4,3,2,1);
+        DenseMatrix64F A_bad = CommonOps.diag(4, 3, 2, 0.1);
+
+        LinearSolver<DenseMatrix64F> solver = createSafeSolver(A_good);
+
+        assertTrue(solver.setA(A_good));
+        double q_good;
+        try {
+            q_good = solver.quality();
+        } catch( IllegalArgumentException e ) {
+            // quality is not supported
+            return;
+        }
+
+        assertTrue(solver.setA(A_bad));
+        double q_bad = solver.quality();
+
+        assertTrue(q_bad < q_good);
+
+        assertEquals(q_bad*10.0,q_good,1e-8);
+    }
+
+    /**
+     * See if quality is scale invariant
+     */
+    @Test
+    public void checkQuality_scale() {
+        DenseMatrix64F A = CommonOps.diag(4,3,2,1);
+        DenseMatrix64F Asmall = A.copy();
+        CommonOps.scale(0.01,Asmall);
+
+        LinearSolver<DenseMatrix64F> solver = createSafeSolver(A);
+
+        assertTrue(solver.setA(A));
+        double q;
+        try {
+            q = solver.quality();
+        } catch( IllegalArgumentException e ) {
+            // quality is not supported
+            return;
+        }
+
+        assertTrue(solver.setA(Asmall));
+        double q_small = solver.quality();
+
+        assertEquals(q_small, q, 1e-8);
+    }
+
+    /**
+     * A very easy matrix to decompose
+     */
+    @Test
+    public void square_trivial() {
+        DenseMatrix64F A = new DenseMatrix64F(3,3, true, 5, 2, 3, 1.5, -2, 8, -3, 4.7, -0.5);
+        DenseMatrix64F b = new DenseMatrix64F(3,1, true, 18, 21.5, 4.9000);
+        DenseMatrix64F x = RandomMatrices.createRandom(3,1,rand);
+
+        LinearSolver<DenseMatrix64F> solver = createSafeSolver(A);
+        assertTrue(solver.setA(A));
+        solver.solve(b,x);
+
+
+        DenseMatrix64F x_expected = new DenseMatrix64F(3,1, true, 1, 2, 3);
+
+        EjmlUnitTests.assertEquals(x_expected,x,1e-8);
+    }
+
+    /**
+     * This test checks to see if it can solve a system that will require some algorithms to
+     * perform a pivot.  Pivots can change the data structure and can cause solve to fail if not
+     * handled correctly.
+     */
+    @Test
+    public void square_pivot() {
+        DenseMatrix64F A = new DenseMatrix64F(3,3, true, 0, 1, 2, -2, 4, 9, 0.5, 0, 5);
+        DenseMatrix64F b = new DenseMatrix64F(3,1, true, 8, 33, 15.5);
+        DenseMatrix64F x = RandomMatrices.createRandom(3,1,rand);
+
+        LinearSolver<DenseMatrix64F> solver = createSafeSolver(A);
+        assertTrue(solver.setA(A));
+        solver.solve(b,x);
+
+
+        DenseMatrix64F x_expected = new DenseMatrix64F(3,1, true, 1, 2, 3);
+
+        EjmlUnitTests.assertEquals(x_expected, x, 1e-8);
+    }
+
+    @Test
+    public void square_singular() {
+        DenseMatrix64F A = new DenseMatrix64F(3,3);
+
+        LinearSolver<DenseMatrix64F> solver = createSafeSolver(A);
+        assertTrue(shouldFailSingular == !solver.setA(A));
+    }
+
+    /**
+     * Have it solve for the coefficients in a polynomial
+     */
+    @Test
+    public void rectangular() {
+        if( !shouldWorkRectangle ) {
+            // skip this test
+            return;
+        }
+
+        double t[] = new double[]{-1,-0.75,-0.5,0,0.25,0.5,0.75};
+        double vals[] = new double[7];
+        double a=1,b=1.5,c=1.7;
+        for( int i = 0; i < t.length; i++ ) {
+            vals[i] = a + b*t[i] + c*t[i]*t[i];
+        }
+
+        DenseMatrix64F B = new DenseMatrix64F(7,1, true, vals);
+        DenseMatrix64F A = createPolyA(t,3);
+        DenseMatrix64F x = RandomMatrices.createRandom(3,1,rand);
+
+        LinearSolver<DenseMatrix64F> solver = createSafeSolver(A);
+        assertTrue(solver.setA(A));
+
+        solver.solve(B,x);
+
+        assertEquals(a,x.get(0,0),tol);
+        assertEquals(b,x.get(1,0),tol);
+        assertEquals(c,x.get(2,0),tol);
+    }
+
+    private DenseMatrix64F createPolyA( double t[] , int dof ) {
+        DenseMatrix64F A = new DenseMatrix64F(t.length,3);
+
+        for( int j = 0; j < t.length; j++ ) {
+            double val = t[j];
+
+            for( int i = 0; i < dof; i++ ) {
+                A.set(j,i,Math.pow(val,i));
+            }
+        }
+
+        return A;
+    }
+
+    @Test
+    public void inverse() {
+        DenseMatrix64F A = new DenseMatrix64F(3,3, true, 0, 1, 2, -2, 4, 9, 0.5, 0, 5);
+        DenseMatrix64F A_inv = RandomMatrices.createRandom(3, 3, rand);
+
+        LinearSolver<DenseMatrix64F> solver = createSafeSolver(A);
+
+        assertTrue(solver.setA(A));
+        solver.invert(A_inv);
+
+        DenseMatrix64F I = RandomMatrices.createRandom(3,3,rand);
+
+        CommonOps.mult(A,A_inv,I);
+
+        for( int i = 0; i < I.numRows; i++ ) {
+            for( int j = 0; j < I.numCols; j++ ) {
+                if( i == j )
+                    assertEquals(1,I.get(i,j),tol);
+                else
+                    assertEquals(0,I.get(i,j),tol);
+            }
+        }
+    }
+
+    protected LinearSolver<DenseMatrix64F>  createSafeSolver( DenseMatrix64F A ) {
+        return new LinearSolverSafe<DenseMatrix64F>( createSolver(A));
+    }
+
+    protected abstract LinearSolver<DenseMatrix64F> createSolver( DenseMatrix64F A );
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/linsol/GenericSolvePseudoInverseChecks.java b/main/dense64/test/org/ejml/alg/dense/linsol/GenericSolvePseudoInverseChecks.java
new file mode 100644
index 0000000..1574d65
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/linsol/GenericSolvePseudoInverseChecks.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.MatrixFeatures;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests the ability of a solver to handle different type of rank deficient matrices
+ *
+ * @author Peter Abeles
+ */
+public class GenericSolvePseudoInverseChecks {
+
+    Random rand = new Random(234);
+
+    LinearSolver<DenseMatrix64F> solver;
+
+    public GenericSolvePseudoInverseChecks(LinearSolver<DenseMatrix64F> solver) {
+        this.solver = new LinearSolverSafe<DenseMatrix64F>( solver );
+    }
+
+    public void all() {
+        zeroMatrix();
+        underDetermined_wide_solve();
+        underDetermined_wide_inv();
+        underDetermined_tall_solve();
+        singular_solve();
+        singular_inv();
+    }
+
+    /**
+     * Shouldn't blow if it the input matrix is zero.  But there is no solution...
+     */
+    public void zeroMatrix() {
+        DenseMatrix64F A = new DenseMatrix64F(3,3);
+        DenseMatrix64F y = new DenseMatrix64F(3,1,true,4,7,8);
+
+        assertTrue(solver.setA(A));
+
+        DenseMatrix64F x = new DenseMatrix64F(3,1);
+        solver.solve(y, x);
+
+        assertFalse(MatrixFeatures.hasUncountable(x));
+    }
+
+    /**
+     * Compute a solution for a system with more variables than equations
+     */
+    public void underDetermined_wide_solve() {
+        // create a matrix where two rows are linearly dependent
+        DenseMatrix64F A = new DenseMatrix64F(2,3,true,1,2,3,2,3,4);
+
+        DenseMatrix64F y = new DenseMatrix64F(2,1,true,4,7);
+        assertTrue(solver.setA(A));
+
+        DenseMatrix64F x = new DenseMatrix64F(3,1);
+        solver.solve(y,x);
+
+        DenseMatrix64F found = new DenseMatrix64F(2,1);
+        CommonOps.mult(A, x, found);
+
+        // there are multiple 'x' which will generate the same solution, see if this is one of them
+        assertTrue(MatrixFeatures.isEquals(y, found, 1e-8));
+    }
+
+    /**
+     * Compute the pseudo inverse a system with more variables than equations
+     */
+    public void underDetermined_wide_inv() {
+        // create a matrix where two rows are linearly dependent
+        DenseMatrix64F A = new DenseMatrix64F(2,3,true,1,2,3,2,3,4);
+
+        DenseMatrix64F y = new DenseMatrix64F(2,1,true,4,7);
+        assertTrue(solver.setA(A));
+
+        DenseMatrix64F x = new DenseMatrix64F(3,1);
+        solver.solve(y,x);
+
+        // now test the pseudo inverse
+        DenseMatrix64F A_pinv = new DenseMatrix64F(3,2);
+        DenseMatrix64F found = new DenseMatrix64F(3,1);
+        solver.invert(A_pinv);
+
+        CommonOps.mult(A_pinv,y,found);
+
+        assertTrue(MatrixFeatures.isEquals(x, found,1e-8));
+    }
+
+    /**
+     * Compute a solution for a system with more variables than equations
+     */
+    public void underDetermined_tall_solve() {
+        // create a matrix where two rows are linearly dependent
+        DenseMatrix64F A = new DenseMatrix64F(3,2,true,1,2,1,2,2,4);
+
+        DenseMatrix64F y = new DenseMatrix64F(3,1,true,4,4,8);
+        assertTrue(solver.setA(A));
+
+        DenseMatrix64F x = new DenseMatrix64F(2,1);
+        solver.solve(y,x);
+
+        DenseMatrix64F found = new DenseMatrix64F(3,1);
+        CommonOps.mult(A, x, found);
+
+        // there are multiple 'x' which will generate the same solution, see if this is one of them
+        assertTrue(MatrixFeatures.isEquals(y, found, 1e-8));
+    }
+
+    /**
+     * Compute a solution for a system with more variables than equations
+     */
+    public void singular_solve() {
+        // create a matrix where two rows are linearly dependent
+        DenseMatrix64F A = new DenseMatrix64F(3,3,true,1,2,3,2,3,4,2,3,4);
+
+        DenseMatrix64F y = new DenseMatrix64F(3,1,true,4,7,7);
+        assertTrue(solver.setA(A));
+
+        DenseMatrix64F x = new DenseMatrix64F(3,1);
+        solver.solve(y,x);
+
+        DenseMatrix64F found = new DenseMatrix64F(3,1);
+        CommonOps.mult(A, x, found);
+
+        // there are multiple 'x' which will generate the same solution, see if this is one of them
+        assertTrue(MatrixFeatures.isEquals(y, found, 1e-8));
+    }
+
+    /**
+     * Compute the pseudo inverse a system with more variables than equations
+     */
+    public void singular_inv() {
+        // create a matrix where two rows are linearly dependent
+        DenseMatrix64F A = new DenseMatrix64F(3,3,true,1,2,3,2,3,4,2,3,4);
+
+        DenseMatrix64F y = new DenseMatrix64F(3,1,true,4,7,7);
+        assertTrue(solver.setA(A));
+
+        DenseMatrix64F x = new DenseMatrix64F(3,1);
+        solver.solve(y,x);
+
+        // now test the pseudo inverse
+        DenseMatrix64F A_pinv = new DenseMatrix64F(3,3);
+        DenseMatrix64F found = new DenseMatrix64F(3,1);
+        solver.invert(A_pinv);
+
+        CommonOps.mult(A_pinv,y,found);
+
+        assertTrue(MatrixFeatures.isEquals(x, found,1e-8));
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/linsol/TestInvertUsingSolve.java b/main/dense64/test/org/ejml/alg/dense/linsol/TestInvertUsingSolve.java
new file mode 100644
index 0000000..7c416f1
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/linsol/TestInvertUsingSolve.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol;
+
+import org.ejml.alg.dense.decomposition.lu.LUDecompositionAlt_D64;
+import org.ejml.alg.dense.linsol.lu.LinearSolverLu_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestInvertUsingSolve {
+
+    Random rand = new Random(0xff);
+    double tol = 1e-8;
+
+    /**
+     * See if it can invert a matrix that is known to be invertable.
+     */
+    @Test
+    public void invert() {
+        DenseMatrix64F A = new DenseMatrix64F(3,3, true, 0, 1, 2, -2, 4, 9, 0.5, 0, 5);
+        DenseMatrix64F A_inv = RandomMatrices.createRandom(3,3,rand);
+
+        LUDecompositionAlt_D64 decomp = new LUDecompositionAlt_D64();
+        LinearSolver solver = new LinearSolverLu_D64(decomp);
+
+        solver.setA(A);
+        InvertUsingSolve.invert(solver,A,A_inv);
+
+        DenseMatrix64F I = RandomMatrices.createRandom(3,3,rand);
+
+        CommonOps.mult(A,A_inv,I);
+
+        for( int i = 0; i < I.numRows; i++ ) {
+            for( int j = 0; j < I.numCols; j++ ) {
+                if( i == j )
+                    assertEquals(1,I.get(i,j),tol);
+                else
+                    assertEquals(0,I.get(i,j),tol);
+            }
+        }
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/linsol/TestLinearSolverAbstract.java b/main/dense64/test/org/ejml/alg/dense/linsol/TestLinearSolverAbstract.java
new file mode 100644
index 0000000..1402398
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/linsol/TestLinearSolverAbstract.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.DecompositionInterface;
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestLinearSolverAbstract {
+    @Test
+    public void setA_getA() {
+        DenseMatrix64F A = new DenseMatrix64F(1,1);
+
+        MySolver s = new MySolver();
+        s.setA(A);
+
+        assertTrue(A==s.getA());
+    }
+
+    /**
+     * Checks to see if solve is called by the default invert.
+     */
+    @Test
+    public void invert() {
+        MySolver solver = new MySolver();
+
+        DenseMatrix64F A = new DenseMatrix64F(1,1);
+
+        solver.setA(A);
+        solver.invert(A);
+
+        assertTrue(solver.solveCalled);
+    }
+
+    private static class MySolver extends LinearSolverAbstract_D64
+    {
+        boolean solveCalled = false;
+
+        @Override
+        public boolean setA(DenseMatrix64F A) {
+            _setA(A);
+
+            return true;
+        }
+
+        @Override
+        public double quality() {
+            throw new IllegalArgumentException("Not supported by this solver.");
+        }
+
+        @Override
+        public void solve(DenseMatrix64F B, DenseMatrix64F X) {
+              solveCalled = true;
+        }
+
+        @Override
+        public boolean modifiesA() {
+            return false;
+        }
+
+        @Override
+        public boolean modifiesB() {
+            return false;
+        }
+
+        @Override
+        public <D extends DecompositionInterface> D getDecomposition() {
+            return null;
+        }
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/linsol/TestLinearSolver_B64_to_D64.java b/main/dense64/test/org/ejml/alg/dense/linsol/TestLinearSolver_B64_to_D64.java
new file mode 100644
index 0000000..7210b3f
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/linsol/TestLinearSolver_B64_to_D64.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol;
+
+import org.ejml.alg.block.linsol.qr.BlockQrHouseHolderSolver;
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+
+/**
+ * Test the wrapper by running it through the usual linear solver checks
+ *
+ * @author Peter Abeles
+ */
+public class TestLinearSolver_B64_to_D64 extends GenericLinearSolverChecks {
+
+    @Override
+    protected LinearSolver<DenseMatrix64F> createSolver(DenseMatrix64F A) {
+
+        LinearSolver<BlockMatrix64F> solver = new BlockQrHouseHolderSolver();
+
+        return new LinearSolver_B64_to_D64(solver);
+    }
+
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/linsol/chol/BaseCholeskySolveTests_D64.java b/main/dense64/test/org/ejml/alg/dense/linsol/chol/BaseCholeskySolveTests_D64.java
new file mode 100644
index 0000000..99f9e20
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/linsol/chol/BaseCholeskySolveTests_D64.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.chol;
+
+import org.ejml.alg.dense.linsol.LinearSolverSafe;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.EjmlUnitTests;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * @author Peter Abeles
+ */
+public abstract class BaseCholeskySolveTests_D64 {
+
+    Random rand = new Random(0x45);
+
+    public void standardTests() {
+
+        solve_dimensionCheck();
+        testSolve();
+        testInvert();
+        testQuality();
+        testQuality_scale();
+    }
+
+    public abstract LinearSolver<DenseMatrix64F> createSolver();
+
+    public LinearSolver<DenseMatrix64F> createSafeSolver() {
+        LinearSolver<DenseMatrix64F> solver = createSolver();
+        return new LinearSolverSafe<DenseMatrix64F>(solver);
+    }
+
+    @Test
+    public void setA_dimensionCheck() {
+
+        LinearSolver<DenseMatrix64F> solver = createSafeSolver();
+
+        try {
+            DenseMatrix64F A = RandomMatrices.createRandom(4,5,rand);
+            assertTrue(solver.setA(A));
+            fail("Should have thrown an exception");
+        } catch( RuntimeException ignore ) {}
+    }
+
+    @Test
+    public void solve_dimensionCheck() {
+
+        LinearSolver<DenseMatrix64F> solver = createSafeSolver();
+
+        DenseMatrix64F A = RandomMatrices.createSymmPosDef(4, rand);
+        assertTrue(solver.setA(A));
+
+        try {
+            DenseMatrix64F x = RandomMatrices.createRandom(4,3,rand);
+            DenseMatrix64F b = RandomMatrices.createRandom(4,2,rand);
+            solver.solve(b,x);
+            fail("Should have thrown an exception");
+        } catch( RuntimeException ignore ) {}
+
+        try {
+            DenseMatrix64F x = RandomMatrices.createRandom(5,2,rand);
+            DenseMatrix64F b = RandomMatrices.createRandom(4,2,rand);
+            solver.solve(b,x);
+            fail("Should have thrown an exception");
+        } catch( RuntimeException ignore ) {}
+
+        try {
+            DenseMatrix64F x = RandomMatrices.createRandom(5,2,rand);
+            DenseMatrix64F b = RandomMatrices.createRandom(5,2,rand);
+            solver.solve(b,x);
+            fail("Should have thrown an exception");
+        } catch( RuntimeException ignore ) {}
+    }
+
+    @Test
+    public void testSolve() {
+
+        LinearSolver<DenseMatrix64F> solver = createSafeSolver();
+
+        DenseMatrix64F A = new DenseMatrix64F(3,3, true, 1, 2, 4, 2, 13, 23, 4, 23, 90);
+        DenseMatrix64F b = new DenseMatrix64F(3,1, true, 17, 97, 320);
+        DenseMatrix64F x = RandomMatrices.createRandom(3,1,rand);
+        DenseMatrix64F A_orig = A.copy();
+        DenseMatrix64F B_orig = b.copy();
+
+        assertTrue(solver.setA(A));
+        solver.solve(b,x);
+
+        // see if the input got modified
+        EjmlUnitTests.assertEquals(A,A_orig,1e-5);
+        EjmlUnitTests.assertEquals(b,B_orig,1e-5);
+
+        DenseMatrix64F x_expected = new DenseMatrix64F(3,1, true, 1, 2, 3);
+
+        EjmlUnitTests.assertEquals(x_expected,x,1e-6);
+    }
+
+    @Test
+    public void testInvert() {
+
+        LinearSolver<DenseMatrix64F> solver = createSafeSolver();
+
+        DenseMatrix64F A = new DenseMatrix64F(3,3, true, 1, 2, 4, 2, 13, 23, 4, 23, 90);
+        DenseMatrix64F found = new DenseMatrix64F(A.numRows,A.numCols);
+
+        assertTrue(solver.setA(A));
+        solver.invert(found);
+
+        DenseMatrix64F A_inv = new DenseMatrix64F(3,3, true, 1.453515, -0.199546, -0.013605, -0.199546, 0.167800, -0.034014, -0.013605, -0.034014, 0.020408);
+
+        EjmlUnitTests.assertEquals(A_inv,found,1e-5);
+    }
+
+    @Test
+    public void testQuality() {
+
+        LinearSolver<DenseMatrix64F> solver = createSafeSolver();
+
+        DenseMatrix64F A = CommonOps.diag(3,2,1);
+        DenseMatrix64F B = CommonOps.diag(3,2,0.001);
+
+        assertTrue(solver.setA(A));
+        double qualityA = solver.quality();
+
+        assertTrue(solver.setA(B));
+        double qualityB = solver.quality();
+
+        assertTrue(qualityB < qualityA);
+    }
+
+    @Test
+    public void testQuality_scale() {
+
+        LinearSolver<DenseMatrix64F> solver = createSafeSolver();
+
+        DenseMatrix64F A = CommonOps.diag(3,2,1);
+        DenseMatrix64F B = A.copy();
+        CommonOps.scale(0.001,B);
+
+        assertTrue(solver.setA(A));
+        double qualityA = solver.quality();
+
+        assertTrue(solver.setA(B));
+        double qualityB = solver.quality();
+
+        assertEquals(qualityB,qualityA,1e-8);
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/linsol/chol/TestLinearSolverCholLDL_D64.java b/main/dense64/test/org/ejml/alg/dense/linsol/chol/TestLinearSolverCholLDL_D64.java
new file mode 100644
index 0000000..cef4165
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/linsol/chol/TestLinearSolverCholLDL_D64.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.chol;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.EjmlUnitTests;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestLinearSolverCholLDL_D64 {
+
+    Random rand = new Random(3466);
+
+    @Test
+    public void testInverseAndSolve() {
+        DenseMatrix64F A = new DenseMatrix64F(3,3, true, 1, 2, 4, 2, 13, 23, 4, 23, 90);
+        DenseMatrix64F b = new DenseMatrix64F(3,1, true, 17, 97, 320);
+        DenseMatrix64F x = RandomMatrices.createRandom(3,1,rand);
+
+        LinearSolverCholLDL_D64 solver = new LinearSolverCholLDL_D64();
+        assertTrue(solver.setA(A));
+        solver.invert(A);
+        solver.solve(b,x);
+
+
+        DenseMatrix64F A_inv = new DenseMatrix64F(3,3, true, 1.453515, -0.199546, -0.013605, -0.199546, 0.167800, -0.034014, -0.013605, -0.034014, 0.020408);
+        DenseMatrix64F x_expected = new DenseMatrix64F(3,1, true, 1, 2, 3);
+
+        EjmlUnitTests.assertEquals(A_inv,A,1e-5);
+        EjmlUnitTests.assertEquals(x_expected,x,1e-6);
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/linsol/chol/TestLinearSolverChol_B64.java b/main/dense64/test/org/ejml/alg/dense/linsol/chol/TestLinearSolverChol_B64.java
new file mode 100644
index 0000000..cd84271
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/linsol/chol/TestLinearSolverChol_B64.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.chol;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestLinearSolverChol_B64 extends BaseCholeskySolveTests_D64 {
+
+    @Override
+    public LinearSolver<DenseMatrix64F> createSolver() {
+        return new LinearSolverChol_B64();
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/linsol/chol/TestLinearSolverChol_D64.java b/main/dense64/test/org/ejml/alg/dense/linsol/chol/TestLinearSolverChol_D64.java
new file mode 100644
index 0000000..353764d
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/linsol/chol/TestLinearSolverChol_D64.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.chol;
+
+import org.ejml.alg.dense.decomposition.chol.CholeskyDecompositionInner_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestLinearSolverChol_D64 extends BaseCholeskySolveTests_D64 {
+
+    @Override
+    public LinearSolver<DenseMatrix64F> createSolver() {
+        CholeskyDecompositionInner_D64 alg = new CholeskyDecompositionInner_D64(true);
+        return new LinearSolverChol_D64(alg);
+    }
+
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/linsol/lu/TestLinearSolverLuBase_D64.java b/main/dense64/test/org/ejml/alg/dense/linsol/lu/TestLinearSolverLuBase_D64.java
new file mode 100644
index 0000000..eab0924
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/linsol/lu/TestLinearSolverLuBase_D64.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.lu;
+
+import org.ejml.alg.dense.decomposition.lu.LUDecompositionAlt_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.EjmlUnitTests;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestLinearSolverLuBase_D64 {
+
+    Random rand = new Random(0x334);
+
+    /**
+     * Make sure that improve solution doesn't make things worse.  This test does
+     * not realy test to see if it makes things better.
+     */
+    @Test
+    public void testImproveSol_noharm() {
+        DenseMatrix64F A = new DenseMatrix64F(3,3, true, 0, 1, 2, -2, 4, 9, 0.5, 0, 5);
+        DenseMatrix64F b = new DenseMatrix64F(3,1, true, 8, 33, 15.5);
+        DenseMatrix64F x = RandomMatrices.createRandom(3,1,rand);
+        DenseMatrix64F x_improved = new DenseMatrix64F(3,1);
+
+        LUDecompositionAlt_D64 alg = new LUDecompositionAlt_D64();
+
+        x_improved.set(x);
+
+        LinearSolverLu_D64 solver = new LinearSolverLu_D64(alg);
+        assertTrue(solver.setA(A));
+        solver.solve(x,b);
+        solver.improveSol(x_improved,b);
+
+//        DenseMatrix64F x_truth = new DenseMatrix64F(3,1,new double[]{1,2,3});
+
+        EjmlUnitTests.assertEquals(x,x_improved,1e-8);
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/linsol/lu/TestLinearSolverLuKJI_D64.java b/main/dense64/test/org/ejml/alg/dense/linsol/lu/TestLinearSolverLuKJI_D64.java
new file mode 100644
index 0000000..93c21b0
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/linsol/lu/TestLinearSolverLuKJI_D64.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.lu;
+
+import org.ejml.alg.dense.decomposition.lu.LUDecompositionAlt_D64;
+import org.ejml.alg.dense.linsol.GenericLinearSolverChecks;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestLinearSolverLuKJI_D64 extends GenericLinearSolverChecks {
+
+    public TestLinearSolverLuKJI_D64() {
+        shouldWorkRectangle = false;
+        shouldFailSingular = false;
+    }
+
+    @Override
+    protected LinearSolver<DenseMatrix64F> createSolver( DenseMatrix64F A ) {
+        LUDecompositionAlt_D64 decomp = new LUDecompositionAlt_D64();
+
+        return new LinearSolverLuKJI_D64(decomp);
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/test/org/ejml/alg/dense/linsol/lu/TestLinearSolverLu_D64.java b/main/dense64/test/org/ejml/alg/dense/linsol/lu/TestLinearSolverLu_D64.java
new file mode 100644
index 0000000..2a24ed3
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/linsol/lu/TestLinearSolverLu_D64.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.lu;
+
+import org.ejml.alg.dense.decomposition.lu.LUDecompositionAlt_D64;
+import org.ejml.alg.dense.linsol.GenericLinearSolverChecks;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestLinearSolverLu_D64 extends GenericLinearSolverChecks {
+
+    public TestLinearSolverLu_D64() {
+        shouldWorkRectangle = false;
+        shouldFailSingular = false;
+    }
+
+    @Override
+    protected LinearSolver<DenseMatrix64F> createSolver( DenseMatrix64F A ) {
+        LUDecompositionAlt_D64 decomp = new LUDecompositionAlt_D64();
+
+        return new LinearSolverLu_D64(decomp);
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/linsol/qr/TestAdjLinearSolverQr_D64.java b/main/dense64/test/org/ejml/alg/dense/linsol/qr/TestAdjLinearSolverQr_D64.java
new file mode 100644
index 0000000..00cd21f
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/linsol/qr/TestAdjLinearSolverQr_D64.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.qr;
+
+import org.ejml.alg.dense.linsol.AdjustableLinearSolver;
+import org.ejml.alg.dense.linsol.GenericLinearSolverChecks;
+import org.ejml.alg.dense.mult.SubmatrixOps;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestAdjLinearSolverQr_D64 extends GenericLinearSolverChecks {
+
+
+    @Test
+    public void addRowToA() {
+        int insert = 2;
+        int m = 5;
+        int n = 3;
+
+        DenseMatrix64F A = RandomMatrices.createRandom(m,n,rand);
+        double row[] = new double[]{1,2,3};
+
+        // create the modified A
+        DenseMatrix64F A_e = RandomMatrices.createRandom(m+1,n,rand);
+        SubmatrixOps.setSubMatrix(A,A_e,0,0,0,0,insert,n);
+        System.arraycopy(row, 0, A_e.data, insert * n, n);
+        SubmatrixOps.setSubMatrix(A,A_e,insert,0,insert+1,0,m-insert,n);
+
+        // Compute the solution to the modified  system
+        DenseMatrix64F X = RandomMatrices.createRandom(n,2,rand);
+        DenseMatrix64F Y = new DenseMatrix64F(A_e.numRows,X.numCols);
+        CommonOps.mult(A_e,X,Y);
+
+        // create the solver from A then add a A.  The solver
+        // should be equivalent to one created from A_e
+        AdjustableLinearSolver adjSolver = new AdjLinearSolverQr_D64();
+
+        assertTrue(adjSolver.setA(A));
+        adjSolver.addRowToA(row,insert);
+
+        // solve the system and see if it gets the expected solution
+        DenseMatrix64F X_found = RandomMatrices.createRandom(X.numRows,X.numCols,rand);
+        adjSolver.solve(Y,X_found);
+
+        // see if they produce the same results
+        assertTrue(MatrixFeatures.isIdentical(X_found,X,1e-8));
+    }
+
+    @Test
+    public void removeRowFromA() {
+        int remove = 2;
+        int m = 5;
+        int n = 3;
+
+        DenseMatrix64F A = RandomMatrices.createRandom(m,n,rand);
+
+        // create the modified A
+        DenseMatrix64F A_e = RandomMatrices.createRandom(m-1,n,rand);
+        SubmatrixOps.setSubMatrix(A,A_e,0,0,0,0,remove,n);
+        SubmatrixOps.setSubMatrix(A,A_e,remove+1,0,remove,0,m-remove-1,n);
+
+        // Compute the solution to the modified system
+        DenseMatrix64F X = RandomMatrices.createRandom(n,2,rand);
+        DenseMatrix64F Y = new DenseMatrix64F(A_e.numRows,X.numCols);
+        CommonOps.mult(A_e,X,Y);
+
+        // create the solver from the original system then modify it
+        AdjustableLinearSolver adjSolver = new AdjLinearSolverQr_D64();
+
+        adjSolver.setA(A);
+        adjSolver.removeRowFromA(remove);
+
+        // see if it produces the epected results
+
+        // solve the system and see if it gets the expected solution
+        DenseMatrix64F X_found = RandomMatrices.createRandom(X.numRows,X.numCols,rand);
+        adjSolver.solve(Y,X_found);
+
+        // see if they produce the same results
+        assertTrue(MatrixFeatures.isIdentical(X_found,X,1e-8));
+    }
+
+    @Override
+    protected LinearSolver createSolver( DenseMatrix64F A ) {
+        return new AdjLinearSolverQr_D64();
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/linsol/qr/TestLinearSolverQrBlock64_D64.java b/main/dense64/test/org/ejml/alg/dense/linsol/qr/TestLinearSolverQrBlock64_D64.java
new file mode 100644
index 0000000..4166260
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/linsol/qr/TestLinearSolverQrBlock64_D64.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.qr;
+
+import org.ejml.alg.dense.linsol.GenericLinearSolverChecks;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestLinearSolverQrBlock64_D64 extends GenericLinearSolverChecks {
+    @Override
+    protected LinearSolver<DenseMatrix64F> createSolver( DenseMatrix64F A ) {
+        return new LinearSolverQrBlock64_D64();
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/linsol/qr/TestLinearSolverQrHouseCol_D64.java b/main/dense64/test/org/ejml/alg/dense/linsol/qr/TestLinearSolverQrHouseCol_D64.java
new file mode 100644
index 0000000..e036a12
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/linsol/qr/TestLinearSolverQrHouseCol_D64.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.qr;
+
+import org.ejml.alg.dense.linsol.GenericLinearSolverChecks;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestLinearSolverQrHouseCol_D64 extends GenericLinearSolverChecks {
+
+    public TestLinearSolverQrHouseCol_D64() {
+//         shouldFailSingular = false;
+    }
+
+    @Override
+    protected LinearSolver<DenseMatrix64F> createSolver( DenseMatrix64F A ) {
+        return new LinearSolverQrHouseCol_D64();
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/test/org/ejml/alg/dense/linsol/qr/TestLinearSolverQrHouseTran_D64.java b/main/dense64/test/org/ejml/alg/dense/linsol/qr/TestLinearSolverQrHouseTran_D64.java
new file mode 100644
index 0000000..a1803b9
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/linsol/qr/TestLinearSolverQrHouseTran_D64.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.qr;
+
+import org.ejml.alg.dense.linsol.GenericLinearSolverChecks;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestLinearSolverQrHouseTran_D64 extends GenericLinearSolverChecks {
+
+    public TestLinearSolverQrHouseTran_D64() {
+//         shouldFailSingular = false;
+    }
+
+    @Override
+    protected LinearSolver<DenseMatrix64F> createSolver( DenseMatrix64F A ) {
+        return new LinearSolverQrHouseTran_D64();
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/test/org/ejml/alg/dense/linsol/qr/TestLinearSolverQrHouse_D64.java b/main/dense64/test/org/ejml/alg/dense/linsol/qr/TestLinearSolverQrHouse_D64.java
new file mode 100644
index 0000000..fac7829
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/linsol/qr/TestLinearSolverQrHouse_D64.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.qr;
+
+import org.ejml.alg.dense.linsol.GenericLinearSolverChecks;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestLinearSolverQrHouse_D64 extends GenericLinearSolverChecks {
+
+    public TestLinearSolverQrHouse_D64() {
+//         shouldFailSingular = false;
+    }
+
+    @Override
+    protected LinearSolver<DenseMatrix64F> createSolver( DenseMatrix64F A ) {
+        return new LinearSolverQrHouse_D64();
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/linsol/qr/TestLinearSolverQr_D64.java b/main/dense64/test/org/ejml/alg/dense/linsol/qr/TestLinearSolverQr_D64.java
new file mode 100644
index 0000000..b530380
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/linsol/qr/TestLinearSolverQr_D64.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.qr;
+
+import org.ejml.alg.dense.decomposition.qr.QRDecompositionHouseholderColumn_D64;
+import org.ejml.alg.dense.linsol.GenericLinearSolverChecks;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestLinearSolverQr_D64 extends GenericLinearSolverChecks {
+
+    public TestLinearSolverQr_D64() {
+//         shouldFailSingular = false;
+    }
+
+    @Override
+    protected LinearSolver<DenseMatrix64F> createSolver( DenseMatrix64F A ) {
+        return new LinearSolverQr_D64(new QRDecompositionHouseholderColumn_D64());
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/test/org/ejml/alg/dense/linsol/qr/TestSolveLinearSolverQrpHouseCol_D64.java b/main/dense64/test/org/ejml/alg/dense/linsol/qr/TestSolveLinearSolverQrpHouseCol_D64.java
new file mode 100644
index 0000000..a5a5298
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/linsol/qr/TestSolveLinearSolverQrpHouseCol_D64.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.qr;
+
+import org.ejml.alg.dense.decomposition.qr.QRColPivDecompositionHouseholderColumn_D64;
+import org.ejml.alg.dense.linsol.GenericLinearSolverChecks;
+import org.ejml.alg.dense.linsol.GenericSolvePseudoInverseChecks;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.junit.Test;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestSolveLinearSolverQrpHouseCol_D64 extends GenericLinearSolverChecks  {
+    public TestSolveLinearSolverQrpHouseCol_D64() {
+         shouldFailSingular = false;
+    }
+
+    @Override
+    protected LinearSolver<DenseMatrix64F> createSolver( DenseMatrix64F A ) {
+        return new LinearSolverQrpHouseCol_D64(new QRColPivDecompositionHouseholderColumn_D64(),true);
+    }
+
+    @Test
+    public void checkSingularBasic() {
+        LinearSolver<DenseMatrix64F> solver =
+                new LinearSolverQrpHouseCol_D64(new QRColPivDecompositionHouseholderColumn_D64(),true);
+        GenericSolvePseudoInverseChecks checks = new GenericSolvePseudoInverseChecks(solver);
+
+        checks.all();
+    }
+
+    @Test
+    public void checkSingularFull() {
+        LinearSolver<DenseMatrix64F> solver =
+                new LinearSolverQrpHouseCol_D64(new QRColPivDecompositionHouseholderColumn_D64(),false);
+        GenericSolvePseudoInverseChecks checks = new GenericSolvePseudoInverseChecks(solver);
+
+        checks.all();
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/linsol/qr/TestSolvePseudoInverseQrp_D64.java b/main/dense64/test/org/ejml/alg/dense/linsol/qr/TestSolvePseudoInverseQrp_D64.java
new file mode 100644
index 0000000..0c0264c
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/linsol/qr/TestSolvePseudoInverseQrp_D64.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.qr;
+
+import org.ejml.alg.dense.decomposition.qr.QRColPivDecompositionHouseholderColumn_D64;
+import org.ejml.alg.dense.linsol.GenericLinearSolverChecks;
+import org.ejml.alg.dense.linsol.GenericSolvePseudoInverseChecks;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.junit.Test;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestSolvePseudoInverseQrp_D64 extends GenericLinearSolverChecks  {
+    public TestSolvePseudoInverseQrp_D64() {
+         shouldFailSingular = false;
+    }
+
+    @Override
+    protected LinearSolver<DenseMatrix64F> createSolver( DenseMatrix64F A ) {
+        return new SolvePseudoInverseQrp_D64(new QRColPivDecompositionHouseholderColumn_D64(),true);
+    }
+
+    @Test
+    public void checkSingularBasic() {
+        LinearSolver<DenseMatrix64F> solver =
+                new SolvePseudoInverseQrp_D64(new QRColPivDecompositionHouseholderColumn_D64(),true);
+        GenericSolvePseudoInverseChecks checks = new GenericSolvePseudoInverseChecks(solver);
+
+        checks.all();
+    }
+
+    @Test
+    public void checkSingularFull() {
+        LinearSolver<DenseMatrix64F> solver =
+                new SolvePseudoInverseQrp_D64(new QRColPivDecompositionHouseholderColumn_D64(),false);
+        GenericSolvePseudoInverseChecks checks = new GenericSolvePseudoInverseChecks(solver);
+
+        checks.all();
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/linsol/svd/TestSolvePseudoInverseSvd.java b/main/dense64/test/org/ejml/alg/dense/linsol/svd/TestSolvePseudoInverseSvd.java
new file mode 100644
index 0000000..9948639
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/linsol/svd/TestSolvePseudoInverseSvd.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.svd;
+
+import org.ejml.alg.dense.linsol.GenericLinearSolverChecks;
+import org.ejml.alg.dense.linsol.GenericSolvePseudoInverseChecks;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.junit.Test;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestSolvePseudoInverseSvd extends GenericLinearSolverChecks {
+
+    public TestSolvePseudoInverseSvd() {
+        this.shouldFailSingular = false;
+    }
+
+    @Override
+    protected LinearSolver<DenseMatrix64F> createSolver( DenseMatrix64F A ) {
+        return new SolvePseudoInverseSvd(A.numRows,A.numCols);
+    }
+
+    @Test
+    public void checkSingularBasic() {
+        LinearSolver<DenseMatrix64F> solver = new SolvePseudoInverseSvd(10,10);
+        GenericSolvePseudoInverseChecks checks = new GenericSolvePseudoInverseChecks(solver);
+
+        checks.all();
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/misc/GeneralReducedRowEchelonFormChecks.java b/main/dense64/test/org/ejml/alg/dense/misc/GeneralReducedRowEchelonFormChecks.java
new file mode 100644
index 0000000..732d1a7
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/misc/GeneralReducedRowEchelonFormChecks.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.misc;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.linsol.ReducedRowEchelonForm;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+/**
+ * Generalized checks for Gauss-Jordan implementations
+ *
+ * @author Peter Abeles
+ */
+public abstract class GeneralReducedRowEchelonFormChecks {
+
+    Random rand = new Random(234);
+
+    ReducedRowEchelonForm alg;
+
+    public GeneralReducedRowEchelonFormChecks(ReducedRowEchelonForm alg) {
+        this.alg = alg;
+    }
+
+    /**
+     * See if it is reducing systems into RREF
+     */
+    @Test
+    public void testFormat() {
+        for( int i = 1; i < 10; i++ ) {
+            // test square
+            checkFormatRandom(1+i,1+i);
+            // test wide
+            checkFormatRandom(3 + i, 4 + i * 2);
+            // test tall
+            checkFormatRandom(4 + i * 2,3 + i);
+        }
+    }
+
+    /**
+     * Solve several linear systems and check against solution
+     */
+    @Test
+    public void testSolution() {
+        checkSolutionRandom(3,4,3);
+        checkSolutionRandom(3,5,3);
+        // Tall won't work because with this test because the system is inconsistent
+//        checkSolutionRandom(10,4,3);
+    }
+
+    @Test
+    public void testSingular() {
+        DenseMatrix64F A = new DenseMatrix64F(3,4,true,1,2,3,4,3,5,6,7,2,4,6,8,-3,4,9,3);
+
+        DenseMatrix64F found = A.copy();
+        alg.reduce(found,3);
+
+        checkRref(found,3);
+
+        DenseMatrix64F A1 = CommonOps.extract(A,0,3,0,3);
+        DenseMatrix64F X = CommonOps.extract(found,0,3,3,4);
+        DenseMatrix64F B = new DenseMatrix64F(3,1);
+
+        CommonOps.mult(A1,X,B);
+
+        for( int i = 0; i < 3; i++ )
+            assertEquals(A.get(i,3),B.get(i,0),1e-8);
+    }
+
+    /**
+     * Feed it specific matrices and see if it dies a horrible death
+     */
+    @Test
+    public void spotTests() {
+        DenseMatrix64F A = new DenseMatrix64F(4,6,true,
+                0,0,1,-1,-1,4,
+                2,4,2,4,2,4,
+                2,4,3,3,3,4,
+                3,6,6,3,6,6);
+
+        alg.reduce(A,5);
+        checkRref(A,5);
+    }
+
+    private void checkFormatRandom(int numRows, int numCols) {
+        DenseMatrix64F A = RandomMatrices.createRandom(numRows,numCols,-1,1,rand);
+        DenseMatrix64F found = A.copy();
+
+        alg.reduce(found,numCols);
+
+        checkRref(found, numCols);
+    }
+
+    private void checkSolutionRandom(int numRows, int numCols , int solWidth ) {
+        DenseMatrix64F A = RandomMatrices.createRandom(numRows,numCols,-1,1,rand);
+        DenseMatrix64F found = A.copy();
+
+        alg.reduce(found,solWidth);
+
+        checkRref(found,solWidth);
+
+        DenseMatrix64F A1 = CommonOps.extract(A,0,numRows,0,solWidth);
+        DenseMatrix64F X = CommonOps.extract(found,0,solWidth,solWidth,numCols);
+        DenseMatrix64F B = new DenseMatrix64F(numRows,numCols-solWidth);
+
+        CommonOps.mult(A1,X,B);
+
+        for( int i = 0; i < numRows; i++ )
+            for( int j = 0; j < numCols-solWidth; j++ )
+                assertEquals(A.get(i,j+solWidth),B.get(i,j),1e-8);
+    }
+
+
+    /**
+     * Checks to see if the provided matrix is in reduced row echelon format
+     * @param A
+     */
+    private void checkRref( DenseMatrix64F A , int systemWidth ) {
+        int prevLeading = -1;
+
+        for( int row = 0; row < A.numRows; row++ ) {
+
+            // find the next leading
+            for( int col = 0; col < systemWidth; col++ ) {
+                double val = A.get(row,col);
+
+                if( val == 1 ) {
+                    if( prevLeading > col )
+                        fail("The next leading one should be at a later column than the previous");
+                    prevLeading = col;
+
+                    for( int i = 0; i < A.numRows; i++ ) {
+                        if( i == row ) continue;
+                        assertTrue("Column should be all zeros, except at the leading",0==A.get(i,col));
+                    }
+
+                    break;
+                } else {
+                    assertEquals("Should be all zeros before the leading 1", 0, val, 1e-8);
+                }
+            }
+        }
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/misc/TestDeterminantFromMinor.java b/main/dense64/test/org/ejml/alg/dense/misc/TestDeterminantFromMinor.java
new file mode 100644
index 0000000..d420d9a
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/misc/TestDeterminantFromMinor.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.misc;
+
+import org.ejml.alg.dense.decomposition.lu.LUDecompositionAlt_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestDeterminantFromMinor {
+
+    /**
+     * Compare it against the algorithm for 4 by 4 matrices.
+     */
+    @Test
+    public void compareTo4x4() {
+        double[] mat = new double[]{5 ,-2 ,-4 ,0.5, 0.1, 91, 8, 66, 1, -2, 10, -4, -0.2, 7, -4, 0.8};
+
+        double val = NaiveDeterminant.recursive(new DenseMatrix64F(4,4,true,mat));
+
+        DeterminantFromMinor minor = new DeterminantFromMinor(4,3);
+        double minorVal = minor.compute(new DenseMatrix64F(4,4, true, mat));
+
+        assertEquals(val,minorVal,1e-6);
+    }
+
+    /**
+     * Compare it against the results found using Octave.
+     */
+    @Test
+    public void compareTo5x5() {
+        double[] mat = new double[]{5 ,-2, -4, 0.5, -0.3, 0.1, 91, 8, 66, 13, 1, -2, 10, -4, -0.01, -0.2, 7, -4, 0.8, -22, 5, 19, -23, 0.001, 87};
+
+        DeterminantFromMinor minor = new DeterminantFromMinor(5);
+        double minorVal = minor.compute(new DenseMatrix64F(5,5, true, mat));
+
+        assertEquals(-4745296.629148000851274,minorVal,1e-8);
+    }
+
+    @Test
+    public void compareToNaive10x10() {
+        Random rand = new Random(0xfff);
+
+        int width = 10;
+
+        DenseMatrix64F A = RandomMatrices.createRandom(width,width,rand);
+
+        DeterminantFromMinor minor = new DeterminantFromMinor(width);
+        double minorVal = minor.compute(new DenseMatrix64F(width,width, true, A.data));
+
+        double recVal = NaiveDeterminant.recursive(new DenseMatrix64F(width,width, true, A.data));
+
+        assertEquals(recVal,minorVal,1e-6);
+    }
+
+    /**
+     * Compare it against the naive algorithm and see if it gets the same results.
+     */
+    @Test
+    public void computeMediumSized() {
+        Random rand = new Random(0xfff);
+
+        for( int width = 5; width < 12; width++ ) {
+            DenseMatrix64F A = RandomMatrices.createRandom(width,width,rand);
+
+            LUDecompositionAlt_D64 lu = new LUDecompositionAlt_D64();
+            lu.decompose(A);
+
+            double luVal = lu.computeDeterminant().real;
+
+            DeterminantFromMinor minor = new DeterminantFromMinor(width);
+            double minorVal = minor.compute(new DenseMatrix64F(width,width, true, A.data));
+
+            assertEquals(luVal,minorVal,1e-6);
+        }
+    }
+
+    /**
+     * Make sure it produces the same results when it is called twice
+     */
+    @Test
+    public void testMultipleCalls() {
+        Random rand = new Random(0xfff);
+
+        int width = 6;
+
+        DenseMatrix64F A = RandomMatrices.createRandom(width,width,rand);
+
+        DeterminantFromMinor minor = new DeterminantFromMinor(width);
+        double first = minor.compute(A);
+        double second = minor.compute(A);
+
+        assertEquals(first,second,1e-10);
+
+        // does it produce the same results for a different matrix?
+        DenseMatrix64F B = RandomMatrices.createRandom(width,width,rand);
+        double third = minor.compute(B);
+
+        assertFalse(first==third);
+
+        // make sure it has a valid result the third time
+        double recVal = NaiveDeterminant.recursive(B);
+        assertEquals(third,recVal,1e-6);
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/misc/TestImplCommonOps_DenseMatrix64F.java b/main/dense64/test/org/ejml/alg/dense/misc/TestImplCommonOps_DenseMatrix64F.java
new file mode 100644
index 0000000..57f8d2b
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/misc/TestImplCommonOps_DenseMatrix64F.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.misc;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestImplCommonOps_DenseMatrix64F {
+
+    Random rand = new Random(234324);
+
+    @Test
+    public void extract() {
+        DenseMatrix64F A = RandomMatrices.createRandom(5, 5, 0, 1, rand);
+
+        DenseMatrix64F B = new DenseMatrix64F(3,3);
+
+        ImplCommonOps_DenseMatrix64F.extract(A, 1, 2, B, 1, 0,2,3);
+
+        for( int i = 1; i < 3; i++ ) {
+            for( int j = 2; j < 5; j++ ) {
+                assertEquals(A.get(i,j),B.get(i,j-2),1e-8);
+            }
+        }
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/misc/TestImplCommonOps_Matrix64F.java b/main/dense64/test/org/ejml/alg/dense/misc/TestImplCommonOps_Matrix64F.java
new file mode 100644
index 0000000..b05421b
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/misc/TestImplCommonOps_Matrix64F.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.misc;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestImplCommonOps_Matrix64F {
+
+    Random rand = new Random(234324);
+
+    @Test
+    public void extract() {
+        DenseMatrix64F A = RandomMatrices.createRandom(5, 5, 0, 1, rand);
+
+        DenseMatrix64F B = new DenseMatrix64F(3,3);
+
+        ImplCommonOps_Matrix64F.extract(A, 1, 2, B, 1, 0,2,3);
+
+        for( int i = 1; i < 3; i++ ) {
+            for( int j = 2; j < 5; j++ ) {
+                assertEquals(A.get(i,j),B.get(i,j-2),1e-8);
+            }
+        }
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/misc/TestRrefGaussJordanRowPivot.java b/main/dense64/test/org/ejml/alg/dense/misc/TestRrefGaussJordanRowPivot.java
new file mode 100644
index 0000000..52a1415
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/misc/TestRrefGaussJordanRowPivot.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.misc;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestRrefGaussJordanRowPivot extends GeneralReducedRowEchelonFormChecks {
+    public TestRrefGaussJordanRowPivot() {
+        super(new RrefGaussJordanRowPivot());
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/misc/TestTransposeAlgs.java b/main/dense64/test/org/ejml/alg/dense/misc/TestTransposeAlgs.java
new file mode 100644
index 0000000..93fcbe5
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/misc/TestTransposeAlgs.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.misc;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.EjmlUnitTests;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestTransposeAlgs {
+
+    Random rand = new Random(234234);
+
+    @Test
+    public void square() {
+        DenseMatrix64F mat = RandomMatrices.createRandom(5,5,rand);
+        DenseMatrix64F matTran = mat.copy();
+
+        TransposeAlgs.square(matTran);
+
+        assertEquals(mat.getNumCols(),matTran.getNumRows());
+        assertEquals(mat.getNumRows(),matTran.getNumCols());
+
+        EjmlUnitTests.assertEqualsTrans(mat,matTran,0);
+    }
+
+    @Test
+    public void block() {
+        // check various shapes to make sure blocking is handled correctly
+        for( int numRows = 1; numRows < 15; numRows += 2 ) {
+            for( int numCols = 1; numCols < 15; numCols += 2) {
+                DenseMatrix64F mat = RandomMatrices.createRandom(numRows,numCols,rand);
+                DenseMatrix64F matTran = new DenseMatrix64F(numCols,numRows);
+
+                TransposeAlgs.block(mat,matTran,7);
+
+                assertEquals(numCols,matTran.getNumRows());
+                assertEquals(numRows,matTran.getNumCols());
+
+                EjmlUnitTests.assertEqualsTrans(mat,matTran,0);
+            }
+        }
+    }
+
+    @Test
+    public void standard() {
+        DenseMatrix64F mat = RandomMatrices.createRandom(5,7,rand);
+        DenseMatrix64F matTran = new DenseMatrix64F(7,5);
+
+        TransposeAlgs.standard(mat,matTran);
+
+        assertEquals(mat.getNumCols(),matTran.getNumRows());
+        assertEquals(mat.getNumRows(),matTran.getNumCols());
+
+        EjmlUnitTests.assertEqualsTrans(mat,matTran,0);
+    }
+    
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/misc/TestUnrolledDeterminantFromMinor.java b/main/dense64/test/org/ejml/alg/dense/misc/TestUnrolledDeterminantFromMinor.java
new file mode 100644
index 0000000..f68ec10
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/misc/TestUnrolledDeterminantFromMinor.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.misc;
+
+import org.ejml.alg.dense.decomposition.lu.LUDecompositionAlt_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestUnrolledDeterminantFromMinor {
+
+    Random rand = new Random(234234);
+
+    @Test
+    public void testAll() {
+        for( int N = 2; N <= UnrolledDeterminantFromMinor.MAX; N++ ) {
+            DenseMatrix64F A = RandomMatrices.createRandom(N,N,rand);
+
+            double unrolled = UnrolledDeterminantFromMinor.det(A);
+            LUDecompositionAlt_D64 alg = new LUDecompositionAlt_D64();
+            assertTrue( alg.decompose(A) );
+            double expected = alg.computeDeterminant().real;
+
+            assertEquals(expected,unrolled,1e-8);
+        }
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/test/org/ejml/alg/dense/misc/TestUnrolledInverseFromMinor.java b/main/dense64/test/org/ejml/alg/dense/misc/TestUnrolledInverseFromMinor.java
new file mode 100644
index 0000000..a179f87
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/misc/TestUnrolledInverseFromMinor.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.misc;
+
+import org.ejml.alg.dense.decomposition.lu.LUDecompositionAlt_D64;
+import org.ejml.alg.dense.linsol.lu.LinearSolverLu_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.EjmlUnitTests;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestUnrolledInverseFromMinor {
+
+    Random rand = new Random(234234);
+
+    /**
+     * Compare it against LU decomposition
+     */
+    @Test
+    public void compareToLU() {
+
+        for( int N = 2; N <= UnrolledInverseFromMinor.MAX; N++ ) {
+            DenseMatrix64F A = RandomMatrices.createRandom(N,N,rand);
+
+            DenseMatrix64F expected = new DenseMatrix64F(N,N);
+            DenseMatrix64F found = new DenseMatrix64F(N,N);
+
+            // first compute inverse by LU
+            LUDecompositionAlt_D64 alg = new LUDecompositionAlt_D64();
+            LinearSolverLu_D64 solver = new LinearSolverLu_D64(alg);
+
+            assertTrue( solver.setA(A));
+            solver.invert(expected);
+
+            // compute the result from the algorithm being tested
+            UnrolledInverseFromMinor.inv(A,found);
+
+            EjmlUnitTests.assertEquals(expected,found,1e-8);
+        }
+
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/mult/CheckMatrixMultShape.java b/main/dense64/test/org/ejml/alg/dense/mult/CheckMatrixMultShape.java
new file mode 100644
index 0000000..1880160
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/mult/CheckMatrixMultShape.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.mult;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.MatrixDimensionException;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+
+/**
+ * Checks to see if the input to a matrix mutiply is accepted or rejected correctly depending
+ * on the shape in the input matrices.  Java reflections is used to grab all functions
+ * with "mult" in its name and then it determins if any of matrices are transposed.
+ *
+ * @author Peter Abeles
+ */
+public class CheckMatrixMultShape {
+
+    Class theClass;
+
+    public CheckMatrixMultShape( Class theClass ) {
+        this.theClass = theClass;
+    }
+
+    /**
+     * Perform all shape input checks.
+     *
+     * @throws Throwable
+     */
+    public void checkAll()
+    {
+        int numChecked = 0;
+        Method methods[] = theClass.getMethods();
+
+        for( Method method : methods ) {
+            String name = method.getName();
+
+            // only look at function which perform matrix multiplcation
+            if( !name.contains("mult") || name.contains("Element") ||
+                    name.contains("Inner") || name.contains("Outer") )
+                continue;
+
+            boolean transA = false;
+            boolean transB = false;
+
+            if( name.contains("TransAB")) {
+                transA = true;
+                transB = true;
+            } else if( name.contains("TransA")) {
+                transA = true;
+            } else if( name.contains("TransB")) {
+                transB = true;
+            }
+
+            try {
+                checkPositive(method, transA, transB);
+                checkNegative(method,transA,transB);
+            } catch( Throwable e ) {
+                System.out.println("Failed on "+name);
+                e.printStackTrace();
+                fail("An exception was thrown");
+            }
+            numChecked++;
+        }
+
+        // make sure some functions were checked!
+        assertTrue(numChecked!=0);
+    }
+
+    /**
+     * Iterate through a variety of different sizes and shapes of matrices.
+     */
+    private void checkPositive( Method func, boolean transA, boolean transB )
+            throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
+        for( int i = 1; i <= 4; i++ ) {
+            for( int j = 1; j <= 4; j++ ) {
+                for( int k = 1; k <= 4; k++ ) {
+                    checkPositive(func,transA,transB,i,j,k);
+                }
+            }
+        }
+    }
+
+    /**
+     * See if the function can be called with matrices of the correct size
+     */
+    private void checkPositive(Method func, boolean transA, boolean transB ,
+                               int m , int n , int o ) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+        DenseMatrix64F A,B;
+        DenseMatrix64F C = new DenseMatrix64F(m,o);
+
+        if( transA ) {
+            A = new DenseMatrix64F(n,m);
+        } else {
+            A = new DenseMatrix64F(m,n);
+        }
+        if( transB ) {
+            B = new DenseMatrix64F(o,n);
+        } else {
+            B = new DenseMatrix64F(n,o);
+        }
+
+        TestMatrixMatrixMult.invoke(func, 2.0, A, B, C);
+    }
+
+    /**
+     * See if the function throws an exception when it is given bad inputs
+     */
+    private void checkNegative(Method func, boolean transA, boolean transB) throws NoSuchMethodException, IllegalAccessException {
+
+//        System.out.println("func = "+func);
+        // correct = 2,4,4,3,2,3
+        //           i,j,j,k,i,k
+
+        // mis matched i
+        checkNegative(func,2,4,4,3,6,3,transA,transB);
+        // missmatched j
+        checkNegative(func,2,4,5,3,2,3,transA,transB);
+        // miss matched k
+        checkNegative(func,2,4,4,7,2,3,transA,transB);
+    }
+
+    /**
+     * See if the function throws an exception when it is given bad inputs
+     */
+    private void checkNegative(Method func,
+                               int m_a , int n_a , int m_b , int n_b , int m_c , int n_c ,
+                               boolean transA, boolean transB) throws NoSuchMethodException, IllegalAccessException {
+        DenseMatrix64F A,B;
+        DenseMatrix64F C = new DenseMatrix64F(m_c,n_c);
+
+        if( transA ) {
+            A = new DenseMatrix64F(n_a,m_a);
+        } else {
+            A = new DenseMatrix64F(m_a,n_a);
+        }
+        if( transB ) {
+            B = new DenseMatrix64F(n_b,m_b);
+        } else {
+            B = new DenseMatrix64F(m_b,n_b);
+        }
+
+        try {
+            TestMatrixMatrixMult.invoke(func, 2.0, A, B, C);
+            fail("An exception should have been thrown.");
+        } catch( InvocationTargetException e ) {
+            assertTrue(e.getCause().getClass() == MatrixDimensionException.class );
+        }
+    }
+}
\ No newline at end of file
diff --git a/main/dense64/test/org/ejml/alg/dense/mult/CheckMatrixVectorMultShape.java b/main/dense64/test/org/ejml/alg/dense/mult/CheckMatrixVectorMultShape.java
new file mode 100644
index 0000000..2dcf0cf
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/mult/CheckMatrixVectorMultShape.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.mult;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.MatrixDimensionException;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+
+/**
+ * Checks to see if the input to a matrix vector mutiply is accepted or rejected correctly depending
+ * on the shape in the input matrices.  Java reflections is used to grab all functions
+ * with "mult" in its name and then it determins if any of matrices are transposed.
+ *
+ * @author Peter Abeles
+ */
+public class CheckMatrixVectorMultShape {
+
+    Class theClass;
+
+    public CheckMatrixVectorMultShape( Class theClass ) {
+        this.theClass = theClass;
+    }
+
+    /**
+     * Perform all shape input checks.
+     *
+     * @throws Throwable
+     */
+    public void checkAll()
+    {
+        int numChecked = 0;
+        Method methods[] = theClass.getMethods();
+
+        for( Method method : methods ) {
+            String name = method.getName();
+
+            // only look at function which perform matrix multiplcation
+            if( !name.contains("mult"))
+                continue;
+
+            boolean transA = false;
+
+            if( name.contains("TransA")) {
+                transA = true;
+            }
+
+            try {
+                checkPositive(method, transA);
+                checkNegative(method,transA);
+            } catch( Throwable e ) {
+                System.out.println("Failed on function: "+name);
+                e.printStackTrace();
+                fail("An exception was thrown");
+            }
+            numChecked++;
+        }
+
+        // make sure some functions were checked!
+        assertTrue(numChecked!=0);
+    }
+
+    /**
+     * See if the function can be called with matrices of the correct size
+     */
+    private void checkPositive(Method func, boolean transA) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+        DenseMatrix64F A,B;
+        DenseMatrix64F C = new DenseMatrix64F(2,1);
+
+        if( transA ) {
+            A = new DenseMatrix64F(4,2);
+        } else {
+            A = new DenseMatrix64F(2,4);
+        }
+
+        // should work for B as a column or row vector
+        B = new DenseMatrix64F(4,1);
+        func.invoke(null,A,B,C);
+        B = new DenseMatrix64F(1,4);
+        func.invoke(null,A,B,C);
+    }
+
+    /**
+     * See if the function throws an exception when it is given bad inputs
+     */
+    private void checkNegative(Method func, boolean transA) throws NoSuchMethodException, IllegalAccessException {
+        DenseMatrix64F A,B;
+        DenseMatrix64F C = new DenseMatrix64F(2,1);
+
+        if( transA ) {
+            A = new DenseMatrix64F(2,4);
+        } else {
+            A = new DenseMatrix64F(4,2);
+        }
+
+        // see if it catched B not being a vector
+        B = new DenseMatrix64F(4,2);
+        invokeExpectFail(func, A, B, C);
+        // B is not compatible with A
+        B = new DenseMatrix64F(3,1);
+        invokeExpectFail(func, A, B, C);
+        // C is a row vector
+        B = new DenseMatrix64F(4,1);
+        C = new DenseMatrix64F(1,2);
+        invokeExpectFail(func, A, B, C);
+        // C is not a vector
+        C = new DenseMatrix64F(2,2);
+        invokeExpectFail(func, A, B, C);
+        // C is not compatible with A
+        C = new DenseMatrix64F(3,1);
+        invokeExpectFail(func, A, B, C);
+
+    }
+
+    private void invokeExpectFail(Method func, DenseMatrix64F a, DenseMatrix64F b, DenseMatrix64F c) throws IllegalAccessException {
+        try {
+            func.invoke(null, b, a, c);
+        } catch( InvocationTargetException e ) {
+            assertTrue(e.getCause().getClass() == MatrixDimensionException.class );
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/main/dense64/test/org/ejml/alg/dense/mult/TestMatrixMatrixMult.java b/main/dense64/test/org/ejml/alg/dense/mult/TestMatrixMatrixMult.java
new file mode 100644
index 0000000..bb461cc
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/mult/TestMatrixMatrixMult.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.mult;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.EjmlUnitTests;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestMatrixMatrixMult {
+    Random rand = new Random(121342);
+
+    /**
+     * Checks to see that it only accepts input matrices that have compatible shapes
+     */
+    @Test
+    public void checkShapesOfInput() {
+        CheckMatrixMultShape check = new CheckMatrixMultShape(MatrixMatrixMult.class);
+        check.checkAll();
+    }
+
+    @Test
+    public void checkZeroRowsColumns() throws InvocationTargetException, IllegalAccessException {
+        checkZeros(5,0,0,6);
+        checkZeros(0,5,5,0);
+    }
+
+    /**
+     * Checks to see if the input 'c' matrix is not 'a' or 'b'
+     */
+    @Test
+    public void checkInputInstance() throws IllegalAccessException {
+        Method methods[] = MatrixMatrixMult.class.getMethods();
+        for( Method method : methods ) {
+            String name = method.getName();
+
+            if( !name.contains("mult") )
+                continue;
+
+
+            // make sure it checks that the c matrix is not a or b
+            try {
+                DenseMatrix64F a = new DenseMatrix64F(2,2);
+                DenseMatrix64F b = new DenseMatrix64F(2,2);
+                invoke(method,2.0,a,b,a);
+                fail("An exception should have been thrown");
+            } catch( InvocationTargetException e ) {
+                assertTrue(e.getTargetException() instanceof IllegalArgumentException );
+            }
+
+            try {
+                DenseMatrix64F a = new DenseMatrix64F(2,2);
+                DenseMatrix64F b = new DenseMatrix64F(2,2);
+                invoke(method,2.0,a,b,b);
+                fail("An exception should have been thrown");
+            } catch( InvocationTargetException e ) {
+                assertTrue(e.getTargetException() instanceof IllegalArgumentException );
+            }
+        }
+    }
+
+    /**
+     * Use java reflections to get a list of all the functions.  From the name extract what
+     * the function is supposed to do.  then compute the expected results.
+     *
+     * Correctness is tested against a known case.
+     */
+    @Test
+    public void checkAllAgainstKnown() throws InvocationTargetException, IllegalAccessException {
+        double d[] = new double[]{0,1,2,3,4,5,6,7,8,9,10,11,12};
+        DenseMatrix64F a_orig = new DenseMatrix64F(2,3, true, d);
+        DenseMatrix64F b_orig = new DenseMatrix64F(3,4, true, d);
+        DenseMatrix64F c_orig = RandomMatrices.createRandom(2,4,rand);
+
+        DenseMatrix64F r_orig = new DenseMatrix64F(2,4, true, 20, 23, 26, 29, 56, 68, 80, 92);
+
+        checkResults(a_orig,b_orig,c_orig,r_orig);
+    }
+
+    /**
+     * Creates a bunch of random matrices and computes the expected results using mult().
+     *
+     * The known case is needed since this test case tests against other algorithms in
+     * this library, which could in theory be wrong.
+     *
+     * @throws InvocationTargetException
+     * @throws IllegalAccessException
+     */
+    @Test
+    public void checkAgainstRandomDiffShapes() throws InvocationTargetException, IllegalAccessException {
+
+        for( int i = 1; i <= 4; i++ ) {
+            for( int j = 1; j <= 4; j++ ) {
+                for( int k = 1; k <= 4; k++ ) {
+                    DenseMatrix64F a_orig = RandomMatrices.createRandom(i,j, rand);
+                    DenseMatrix64F b_orig = RandomMatrices.createRandom(j,k, rand);
+                    DenseMatrix64F c_orig = RandomMatrices.createRandom(i,k, rand);
+
+                    DenseMatrix64F r_orig = RandomMatrices.createRandom(i,k,rand);
+
+                    MatrixMatrixMult.mult_small(a_orig,b_orig,r_orig);
+
+                    checkResults(a_orig,b_orig,c_orig,r_orig);
+                }
+            }
+        }
+    }
+
+    /**
+     * Sees if all the matrix multiplications produce the expected results against the provided
+     * known solution.
+     */
+    private void checkResults( DenseMatrix64F a_orig ,
+                               DenseMatrix64F b_orig ,
+                               DenseMatrix64F c_orig ,
+                               DenseMatrix64F r_orig )
+            throws InvocationTargetException, IllegalAccessException
+    {
+        double alpha = 2.5;
+
+        int numChecked = 0;
+        Method methods[] = MatrixMatrixMult.class.getMethods();
+
+        for( Method method : methods ) {
+            String name = method.getName();
+//            System.out.println(name);
+
+            // only look at function which perform matrix multiplications
+            if( !name.contains("mult") )
+                continue;
+
+            DenseMatrix64F a = a_orig.copy();
+            DenseMatrix64F b = b_orig.copy();
+            DenseMatrix64F c = c_orig.copy();
+
+            boolean add = name.contains("multAdd");
+            boolean hasAlpha = method.getGenericParameterTypes()[0] == double.class;
+
+            if( name.contains("TransAB")) {
+                transpose(a);
+                transpose(b);
+            } else if( name.contains("TransA")) {
+                transpose(a);
+            } else if( name.contains("TransB")) {
+                transpose(b);
+            }
+
+            DenseMatrix64F expected = r_orig.copy();
+            double []expectedData = expected.data;
+
+            if( hasAlpha ) {
+                for( int i = 0; i < expectedData.length; i++ ) {
+                    expectedData[i] *= alpha;
+                }
+            }
+            if( add ) {
+                for( int i = 0; i < expectedData.length; i++ ) {
+                    expectedData[i] += c_orig.get(i);
+                }
+            }
+
+            invoke(method,alpha,a,b,c);
+
+            EjmlUnitTests.assertEquals(expected,c,1e-12);
+            numChecked++;
+        }
+
+        assertEquals(numChecked,32);
+    }
+
+    /**
+     * Sees if all the matrix multiplications produce the expected results against the provided
+     * known solution.
+     */
+    private void checkZeros( int rowsA , int colsA , int rowsB , int colsB )
+            throws InvocationTargetException, IllegalAccessException
+    {
+
+        double alpha = 2.5;
+
+        int numChecked = 0;
+        Method methods[] = MatrixMatrixMult.class.getMethods();
+
+        for( Method method : methods ) {
+            String name = method.getName();
+
+            // only look at function which perform matrix multiplications
+            if( !name.contains("mult") )
+                continue;
+
+//            System.out.println(name);
+
+            DenseMatrix64F a = new DenseMatrix64F(rowsA,colsA);
+            DenseMatrix64F b = new DenseMatrix64F(rowsB,colsB);
+            DenseMatrix64F c = RandomMatrices.createRandom(rowsA,colsB,rand);
+
+            boolean add = name.contains("multAdd");
+
+            if( name.contains("TransAB")) {
+                transpose(a);
+                transpose(b);
+            } else if( name.contains("TransA")) {
+                transpose(a);
+            } else if( name.contains("TransB")) {
+                transpose(b);
+            }
+
+            DenseMatrix64F original = c.copy();
+            invoke(method,alpha,a,b,c);
+
+            if( add ) {
+                assertTrue(MatrixFeatures.isEquals(original, c));
+            } else {
+                assertTrue(MatrixFeatures.isZeros(c, 1e-8));
+            }
+            numChecked++;
+        }
+
+        assertEquals(numChecked,32);
+    }
+
+    private void transpose( DenseMatrix64F a ) {
+        DenseMatrix64F b = new DenseMatrix64F(a.numCols,a.numRows);
+        CommonOps.transpose(a,b);
+        a.set(b);
+    }
+
+    public static void invoke(Method func,
+                              double alpha,
+                              DenseMatrix64F a, DenseMatrix64F b, DenseMatrix64F c)
+            throws IllegalAccessException, InvocationTargetException {
+        if( func.getParameterTypes().length == 3 ) {
+            func.invoke(null, a, b, c);
+        } else {
+            if( func.getParameterTypes()[0] == double.class ) {
+                if( func.getParameterTypes().length == 4 )
+                    func.invoke(null,alpha, a, b, c);
+                else
+                    func.invoke(null,alpha, a, b, c,null);
+            } else {
+                func.invoke(null, a, b, c,null);
+            }
+        }
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/mult/TestMatrixMultProduct.java b/main/dense64/test/org/ejml/alg/dense/mult/TestMatrixMultProduct.java
new file mode 100644
index 0000000..3dc9dd9
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/mult/TestMatrixMultProduct.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.mult;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestMatrixMultProduct {
+
+    Random rand = new Random(2345);
+
+    @Test
+    public void outer() {
+        DenseMatrix64F A = RandomMatrices.createRandom(20, 10, rand);
+        DenseMatrix64F found = new DenseMatrix64F(20,20);
+        DenseMatrix64F expected = new DenseMatrix64F(20,20);
+
+        MatrixMatrixMult.multTransB(A, A, expected);
+        MatrixMultProduct.outer(A, found);
+
+        assertTrue(MatrixFeatures.isIdentical(expected, found, 1e-8));
+    }
+
+    @Test
+    public void inner_small() {
+        DenseMatrix64F A = RandomMatrices.createRandom(20, 10, rand);
+        DenseMatrix64F found = new DenseMatrix64F(10,10);
+        DenseMatrix64F expected = new DenseMatrix64F(10,10);
+
+        MatrixMatrixMult.multTransA_reorder(A,A,expected);
+        MatrixMultProduct.inner_small(A, found);
+
+        assertTrue(MatrixFeatures.isIdentical(expected, found, 1e-8));
+    }
+
+    @Test
+    public void inner_reorder() {
+        DenseMatrix64F A = RandomMatrices.createRandom(20,10,rand);
+        DenseMatrix64F found = new DenseMatrix64F(10,10);
+        DenseMatrix64F expected = new DenseMatrix64F(10,10);
+
+        MatrixMatrixMult.multTransA_reorder(A,A,expected);
+        MatrixMultProduct.inner_reorder(A, found);
+
+        assertTrue(MatrixFeatures.isIdentical(expected, found, 1e-8));
+    }
+
+    @Test
+    public void inner_reorder_upper() {
+        DenseMatrix64F A = RandomMatrices.createRandom(20,10,rand);
+        DenseMatrix64F found = new DenseMatrix64F(10,10);
+        DenseMatrix64F expected = new DenseMatrix64F(10,10);
+
+        MatrixMatrixMult.multTransA_reorder(A,A,expected);
+        MatrixMultProduct.inner_reorder_upper(A, found);
+
+        // only check the upper triangle
+        for( int i = 0; i < found.numRows; i++ ) {
+            for( int j = i; j < found.numCols; j++ ) {
+                assertEquals(expected.get(i,j),found.get(i,j),1e-8);
+            }
+        }
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/mult/TestMatrixVectorMult.java b/main/dense64/test/org/ejml/alg/dense/mult/TestMatrixVectorMult.java
new file mode 100644
index 0000000..b15ec18
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/mult/TestMatrixVectorMult.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.mult;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.data.UtilTestMatrix;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestMatrixVectorMult {
+
+    Random rand = new Random(0x7354);
+
+    @Test
+    public void checkShapesOfInput() {
+        CheckMatrixVectorMultShape check = new CheckMatrixVectorMultShape(MatrixVectorMult.class);
+        check.checkAll();
+    }
+
+    @Test
+    public void mult() {
+        double d[] = new double[]{0,1,2,3,4,5};
+        DenseMatrix64F a = new DenseMatrix64F(2,3, true, d);
+        DenseMatrix64F b = new DenseMatrix64F(3,1, true, d);
+        DenseMatrix64F c = RandomMatrices.createRandom(2,1,rand);
+
+        MatrixVectorMult.mult(a,b,c);
+
+        UtilTestMatrix.checkMat(c,5,14);
+    }
+
+    @Test
+    public void mult_zero() {
+        double d[] = new double[]{0,1,2,3,4,5};
+        DenseMatrix64F a = new DenseMatrix64F(2,3, true, d);
+        DenseMatrix64F b = new DenseMatrix64F(3,1, true, d);
+        DenseMatrix64F c = RandomMatrices.createRandom(2,1,rand);
+
+        MatrixVectorMult.mult(a,b,c);
+
+        UtilTestMatrix.checkMat(c,5,14);
+    }
+
+    @Test
+    public void multAdd() {
+        double d[] = new double[]{0,1,2,3,4,5};
+        DenseMatrix64F a = new DenseMatrix64F(2,3, true, d);
+        DenseMatrix64F b = new DenseMatrix64F(3,1, true, d);
+        DenseMatrix64F c = new DenseMatrix64F(2,1, true, 2, 6);
+
+        MatrixVectorMult.multAdd(a,b,c);
+
+        UtilTestMatrix.checkMat(c,7,20);
+    }
+
+    @Test
+    public void multTransA_small() {
+        double d[] = new double[]{0,1,2,3,4,5};
+        DenseMatrix64F a = new DenseMatrix64F(3,2, true, d);
+        DenseMatrix64F b = new DenseMatrix64F(3,1, true, d);
+        DenseMatrix64F c = RandomMatrices.createRandom(2,1,rand);
+
+        MatrixVectorMult.multTransA_small(a,b,c);
+
+        UtilTestMatrix.checkMat(c,10,13);
+    }
+
+    @Test
+    public void multTransA_reorder() {
+        double d[] = new double[]{0,1,2,3,4,5};
+        DenseMatrix64F a = new DenseMatrix64F(3,2, true, d);
+        DenseMatrix64F b = new DenseMatrix64F(3,1, true, d);
+        DenseMatrix64F c = RandomMatrices.createRandom(2,1,rand);
+
+        MatrixVectorMult.multTransA_reorder(a,b,c);
+
+        UtilTestMatrix.checkMat(c,10,13);
+    }
+
+    @Test
+    public void multAddTransA_small() {
+        double d[] = new double[]{0,1,2,3,4,5};
+        DenseMatrix64F a = new DenseMatrix64F(3,2, true, d);
+        DenseMatrix64F b = new DenseMatrix64F(3,1, true, d);
+        DenseMatrix64F c = new DenseMatrix64F(2,1, true, 2, 6);
+
+        MatrixVectorMult.multAddTransA_small(a,b,c);
+
+        UtilTestMatrix.checkMat(c,12,19);
+    }
+
+    @Test
+    public void multAddTransA_reorder() {
+        double d[] = new double[]{0,1,2,3,4,5};
+        DenseMatrix64F a = new DenseMatrix64F(3,2, true, d);
+        DenseMatrix64F b = new DenseMatrix64F(3,1, true, d);
+        DenseMatrix64F c = new DenseMatrix64F(2,1, true, 2, 6);
+
+        MatrixVectorMult.multAddTransA_reorder(a,b,c);
+
+        UtilTestMatrix.checkMat(c,12,19);
+    }
+
+    @Test
+    public void checkZeroRowsColumns() throws InvocationTargetException, IllegalAccessException {
+        checkZeros(5,0);
+        checkZeros(0,5);
+    }
+
+    /**
+     * Sees if all the matrix multiplications produce the expected results against the provided
+     * known solution.
+     */
+    private void checkZeros( int rowsA , int colsA  )
+            throws InvocationTargetException, IllegalAccessException
+    {
+
+        int numChecked = 0;
+        Method methods[] = MatrixVectorMult.class.getMethods();
+
+        for( Method method : methods ) {
+            String name = method.getName();
+
+            // only look at function which perform matrix multiplications
+            if( !name.contains("mult") )
+                continue;
+
+//            System.out.println(name);
+
+            DenseMatrix64F a = new DenseMatrix64F(rowsA,colsA);
+            DenseMatrix64F b = new DenseMatrix64F(colsA,1);
+            DenseMatrix64F c = RandomMatrices.createRandom(rowsA,1,rand);
+
+            boolean add = name.contains("multAdd");
+
+            if( name.contains("TransAB")) {
+                CommonOps.transpose(a);
+                CommonOps.transpose(b);
+            } else if( name.contains("TransA")) {
+                CommonOps.transpose(a);
+            } else if( name.contains("TransB")) {
+                CommonOps.transpose(b);
+            }
+
+            DenseMatrix64F original = c.copy();
+            invoke(method,a,b,c);
+
+            if( add ) {
+                assertTrue(MatrixFeatures.isEquals(original, c));
+            } else {
+                assertTrue(MatrixFeatures.isZeros(c, 1e-8));
+            }
+            numChecked++;
+        }
+
+        assertEquals(numChecked,6);
+    }
+
+    public static void invoke(Method func,
+                              DenseMatrix64F a, DenseMatrix64F b, DenseMatrix64F c)
+            throws IllegalAccessException, InvocationTargetException {
+        if( func.getParameterTypes().length == 3 ) {
+            func.invoke(null, a, b, c);
+        } else {
+            throw new RuntimeException("WTF?");
+        }
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/mult/TestSubmatrixOps.java b/main/dense64/test/org/ejml/alg/dense/mult/TestSubmatrixOps.java
new file mode 100644
index 0000000..67240f2
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/mult/TestSubmatrixOps.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.mult;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.EjmlUnitTests;
+import org.junit.Test;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestSubmatrixOps {
+
+    @Test
+    public void setSubMatrix() {
+        DenseMatrix64F A = new DenseMatrix64F(5,5);
+        DenseMatrix64F B = new DenseMatrix64F(6,6);
+
+        for( int i = 0; i < A.data.length; i++ ) {
+            A.data[i] = 1;
+        }
+
+        SubmatrixOps.setSubMatrix(A,B,1,1,2,3,2,3);
+
+        // create a matrix that should be identical to B
+        DenseMatrix64F C = new DenseMatrix64F(6,6);
+        for( int i = 2; i < 4; i++ ) {
+            for( int j = 3; j < 6; j++ ) {
+                C.set(i,j,1);
+            }
+        }
+
+        // see if they are the same
+        EjmlUnitTests.assertEquals(B,C,0);
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/dense/mult/TestVectorVectorMult.java b/main/dense64/test/org/ejml/alg/dense/mult/TestVectorVectorMult.java
new file mode 100644
index 0000000..0a90431
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/dense/mult/TestVectorVectorMult.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.mult;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.EjmlUnitTests;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.ejml.simple.SimpleMatrix;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestVectorVectorMult {
+
+    Random rand = new Random(45837);
+
+    @Test
+    public void innerProduct() {
+        DenseMatrix64F A = new DenseMatrix64F(4,1, true, 1, 2, 3, 4);
+        DenseMatrix64F B = new DenseMatrix64F(4,1, true, -1, -2, -3, -4);
+
+        double val = VectorVectorMult.innerProd(A,B);
+
+        assertEquals(-30,val,1e-8);
+    }
+
+    @Test
+    public void innerProdA() {
+        DenseMatrix64F A = RandomMatrices.createRandom(3,4,rand);
+        DenseMatrix64F x = RandomMatrices.createRandom(3,1,rand);
+        DenseMatrix64F y = RandomMatrices.createRandom(4,1,rand);
+
+        DenseMatrix64F temp = new DenseMatrix64F(1,4);
+
+        // compute the expected result first
+        CommonOps.multTransA(x,A,temp);
+        double expected = VectorVectorMult.innerProd(temp,y);
+
+        double found = VectorVectorMult.innerProdA(x,A,y);
+
+        assertEquals(expected,found,1e-8);
+    }
+
+    @Test
+    public void innerProdTranA() {
+        DenseMatrix64F A = RandomMatrices.createRandom(3,3,rand);
+        DenseMatrix64F x = RandomMatrices.createRandom(3,1,rand);
+        DenseMatrix64F y = RandomMatrices.createRandom(3,1,rand);
+
+        DenseMatrix64F Atran = new DenseMatrix64F(3,3);
+        CommonOps.transpose(A,Atran);
+
+        DenseMatrix64F temp = new DenseMatrix64F(1,3);
+
+        // compute the expected result first
+        CommonOps.multTransA(x,Atran,temp);
+        double expected = VectorVectorMult.innerProd(temp,y);
+
+        double found = VectorVectorMult.innerProdTranA(x,A,y);
+
+        assertEquals(expected,found,1e-8);
+    }
+
+    @Test
+    public void outerProd() {
+        DenseMatrix64F A = new DenseMatrix64F(4,1, true, 1, 2, 3, 4);
+        DenseMatrix64F B = new DenseMatrix64F(4,1, true, -1, -2, -3, -4);
+
+        DenseMatrix64F C = RandomMatrices.createRandom(4,4,rand);
+        VectorVectorMult.outerProd(A,B,C);
+
+        // compare it against the equivalent matrix matrix multiply
+        DenseMatrix64F D =  RandomMatrices.createRandom(4,4,rand);
+        MatrixMatrixMult.multTransB(A,B,D);
+
+        EjmlUnitTests.assertEquals(D,C,0);
+    }
+
+    @Test
+    public void addOuterProd() {
+        DenseMatrix64F A = new DenseMatrix64F(4,1, true, 1, 2, 3, 4);
+        DenseMatrix64F B = new DenseMatrix64F(4,1, true, -1, -2, -3, -4);
+
+        DenseMatrix64F C = RandomMatrices.createRandom(4,4,rand);
+        DenseMatrix64F D =  C.copy();
+
+        VectorVectorMult.addOuterProd(1.0,A,B,C);
+
+        // compare it against the equivalent matrix matrix multiply
+        DenseMatrix64F E = RandomMatrices.createRandom(4,4,rand);
+        MatrixMatrixMult.multTransB(A,B,E);
+        CommonOps.add(D,E,D);
+
+        assertTrue(MatrixFeatures.isEquals(D,C));
+
+        // now try it with another gamma
+        C = RandomMatrices.createRandom(4,4,rand);
+        D = C.copy();
+
+        VectorVectorMult.addOuterProd(2.5,A,B,C);
+
+        MatrixMatrixMult.multTransB(2.5,A,B,E);
+        CommonOps.add(D,E,D);
+
+        EjmlUnitTests.assertEquals(D,C,0);
+    }
+
+    @Test
+    public void householder() {
+        DenseMatrix64F u = RandomMatrices.createRandom(4,1,rand);
+        DenseMatrix64F x = RandomMatrices.createRandom(4,1,rand);
+        DenseMatrix64F y = RandomMatrices.createRandom(4,1,rand);
+
+
+        double gamma = 4.5;
+
+        VectorVectorMult.householder(gamma,u,x,y);
+
+        DenseMatrix64F L = CommonOps.identity(4,4);
+        DenseMatrix64F y_exp = RandomMatrices.createRandom(4,1,rand);
+
+        VectorVectorMult.addOuterProd(gamma,u,u,L);
+        CommonOps.mult(L,x,y_exp);
+
+        EjmlUnitTests.assertEquals(y,y_exp,1e-8);
+    }
+
+    @Test
+    public void rank1Update_two_square() {
+        DenseMatrix64F A = RandomMatrices.createRandom(6,6,rand);
+        DenseMatrix64F u = RandomMatrices.createRandom(6,1,rand);
+        DenseMatrix64F w = RandomMatrices.createRandom(6,1,rand);
+        double gamma = -45;
+
+        SimpleMatrix _A = SimpleMatrix.wrap(A);
+        SimpleMatrix _u = SimpleMatrix.wrap(u);
+        SimpleMatrix _w = SimpleMatrix.wrap(w);
+        
+        SimpleMatrix expected = _A.plus(_u.mult(_w.transpose()).scale(gamma));
+        DenseMatrix64F found = new DenseMatrix64F(6,6);
+
+        VectorVectorMult.rank1Update(gamma,A,u,w,found);
+
+        EjmlUnitTests.assertEquals(expected.getMatrix(),found,1e-8);
+    }
+
+    @Test
+    public void rank1Update_one_square() {
+        DenseMatrix64F A = RandomMatrices.createRandom(6,6,rand);
+        DenseMatrix64F u = RandomMatrices.createRandom(6,1,rand);
+        DenseMatrix64F w = RandomMatrices.createRandom(6,1,rand);
+        double gamma = -45;
+
+        SimpleMatrix _A = SimpleMatrix.wrap(A);
+        SimpleMatrix _u = SimpleMatrix.wrap(u);
+        SimpleMatrix _w = SimpleMatrix.wrap(w);
+
+        SimpleMatrix expected = _A.plus(_u.mult(_w.transpose()).scale(gamma));
+        DenseMatrix64F found = A.copy();
+
+        VectorVectorMult.rank1Update(gamma,found,u,w);
+
+        EjmlUnitTests.assertEquals(expected.getMatrix(),found,1e-8);
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/fixed/CompareFixedToCommonOps.java b/main/dense64/test/org/ejml/alg/fixed/CompareFixedToCommonOps.java
new file mode 100644
index 0000000..3db875f
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/fixed/CompareFixedToCommonOps.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.fixed;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.data.FixedMatrix64F;
+import org.ejml.data.RealMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.ConvertMatrixType;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * @author Peter Abeles
+ */
+public abstract class CompareFixedToCommonOps {
+
+    Random rand = new Random(234);
+
+    Class classFixed;
+    int N;
+
+    public CompareFixedToCommonOps(Class classFixed) {
+        this.classFixed = classFixed;
+
+        N = Integer.parseInt(classFixed.getSimpleName().charAt(8)+"");
+    }
+
+    /**
+     * Compares equivalent functions in FixedOps to CommonOps.  Inputs are randomly generated
+     */
+    @Test
+    public void compareToCommonOps() {
+        Method[] methods = classFixed.getMethods();
+
+        int numNotMatched = 0;
+        int numPassed = 0;
+        int numFailed = 0;
+
+        for( Method fixedM : methods ) {
+            if( !isValid(fixedM))
+                continue;
+
+            Method commonM = null;
+            for( Method m : CommonOps.class.getMethods()) {
+                if( isMatch(fixedM,m)) {
+                    commonM = m;
+                    break;
+                }
+            }
+
+            if( commonM == null ) {
+//                System.out.println("not matched: "+fixedM.getName());
+                numNotMatched++;
+                continue;
+            }
+
+            if( compareToCommon(fixedM,commonM) ) {
+                numPassed++;
+            } else {
+                numFailed++;
+                System.out.println("Failed comparison to common: "+fixedM);
+            }
+        }
+
+        int numExpected = 55;
+        if( N > GenerateFixedOps.maxInverseSize ) {
+            numExpected -= 2;
+        }
+
+        assertEquals(0,numFailed);
+        assertEquals(1,numNotMatched);
+        assertEquals(numExpected,numPassed);
+    }
+
+    /**
+     * Checks to see if it is a valid Method which can be checked
+     */
+    private boolean isValid( Method m ) {
+        Class[] types = m.getParameterTypes();
+
+        for( Class c : types ) {
+            if(FixedMatrix64F.class.isAssignableFrom(c))
+                return true;
+        }
+        return false;
+    }
+
+    private boolean isMatch( Method fixed , Method common ) {
+        if( fixed.getName().compareTo(common.getName()) != 0 )
+            return false;
+
+        Class[] typesFixed = fixed.getParameterTypes();
+        Class[] typesCommon = common.getParameterTypes();
+
+        if( typesFixed.length != typesCommon.length )
+            return false;
+
+        for (int i = 0; i < typesFixed.length; i++) {
+            if( RealMatrix64F.class.isAssignableFrom(typesFixed[i]) ) {
+                if( !RealMatrix64F.class.isAssignableFrom(typesCommon[i]) ) {
+                    return false;
+                }
+            }
+        }
+
+        Class returnFixed = fixed.getReturnType();
+        Class returnCommon = common.getReturnType();
+
+        if( returnFixed == returnCommon )
+            return true;
+
+        if( RealMatrix64F.class.isAssignableFrom(returnFixed) &&
+                RealMatrix64F.class.isAssignableFrom(returnCommon) )
+            return true;
+
+        return false;
+    }
+
+    private boolean compareToCommon( Method fixed , Method common ) {
+        Class[] typesFixed = fixed.getParameterTypes();
+        Object[] inputsFixed = new Object[ typesFixed.length ];
+        Object[] inputsCommon = new Object[ typesFixed.length ];
+
+        if( !handleSpecialCase(fixed.getName(),typesFixed,inputsFixed,inputsCommon) )
+            declareParamStandard(typesFixed, inputsFixed, inputsCommon);
+
+        try {
+            Object retFixed = fixed.invoke(null,inputsFixed);
+            Object retCommon = common.invoke(null,inputsCommon);
+
+            checkEquivalent(retFixed,retCommon);
+
+            for( int i = 0; i < inputsFixed.length; i++ ) {
+                if( !checkEquivalent(inputsFixed[i],inputsCommon[i]) )
+                    return false;
+            }
+
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
+            fail("IllegalAccessException");
+        } catch (InvocationTargetException e) {
+            e.printStackTrace();
+            fail("InvocationTargetException");
+        }
+
+        return true;
+    }
+
+    private void declareParamStandard(Class[] typesFixed, Object[] inputsFixed, Object[] inputsCommon) {
+        for( int i = 0; i < typesFixed.length; i++ ) {
+            if(FixedMatrix64F.class.isAssignableFrom(typesFixed[i])) {
+                FixedMatrix64F f = null;
+                try {
+                    f = (FixedMatrix64F)typesFixed[i].newInstance();
+                } catch (InstantiationException e) {
+                    throw new RuntimeException(e);
+                } catch (IllegalAccessException e) {
+                    throw new RuntimeException(e);
+                }
+                DenseMatrix64F m = RandomMatrices.createRandom(f.getNumRows(), f.getNumCols(), rand);
+
+                ConvertMatrixType.convert(m, f);
+                inputsFixed[i] = f;
+                inputsCommon[i] = m;
+            } else if( double.class == typesFixed[i] ) {
+                inputsFixed[i] = 2.5;
+                inputsCommon[i] = 2.5;
+            } else if( int.class == typesFixed[i] ) {
+                inputsFixed[i] = 1;  // handle tailored towards extractRow and extractCol
+                inputsCommon[i] = 1;
+            }
+        }
+    }
+
+    private boolean handleSpecialCase( String name , Class[] typesFixed , Object[] inputsFixed, Object[] inputsCommon ) {
+        if( "mult".compareTo(name) == 0 ) {
+            try {
+                FixedMatrix64F f = (FixedMatrix64F)typesFixed[0].newInstance();
+
+                // see if it's a vector
+                if( f.getNumCols() == 1 || f.getNumRows() == 1  ) {
+                    // swap the type of vector
+
+                    declareParamStandard(typesFixed,inputsFixed,inputsCommon);
+                    DenseMatrix64F a = (DenseMatrix64F)inputsCommon[0];
+                    DenseMatrix64F b = (DenseMatrix64F)inputsCommon[2];
+
+                    a.numRows=f.getNumCols(); a.numCols=f.getNumRows();
+                    b.numRows=f.getNumCols(); b.numCols=f.getNumRows();
+                    return true;
+                }
+            } catch (InstantiationException e) {
+                throw new RuntimeException(e);
+            } catch (IllegalAccessException e) {
+                throw new RuntimeException(e);
+            }
+
+
+        }
+
+        return false;
+    }
+
+    private boolean checkEquivalent( Object a , Object b ) {
+        if( a == null ) {
+            return b == null;
+        } else if( Double.class == a.getClass() ) {
+            double valA = ((Double)a).doubleValue();
+            double valB = ((Double)b).doubleValue();
+
+            return Math.abs(valA-valB) < 1e-8;
+        } else if(FixedMatrix64F.class.isAssignableFrom(a.getClass()) ) {
+            DenseMatrix64F bb = (DenseMatrix64F)b;
+
+            FixedMatrix64F f = (FixedMatrix64F)a;
+            DenseMatrix64F m = new DenseMatrix64F(f.getNumRows(),f.getNumCols());
+            ConvertMatrixType.convert(f,m);
+            m.numRows = bb.numRows;
+            m.numCols = bb.numCols;
+
+            return MatrixFeatures.isIdentical(m, bb, 1e-8);
+
+        } else if( Boolean.class == a.getClass() ) {
+            return true;
+        } else if( Integer.class == a.getClass() ) {
+            return true;
+        } else {
+            fail("Not sure what this is");
+        }
+        return true;
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/fixed/TestFixedOps2.java b/main/dense64/test/org/ejml/alg/fixed/TestFixedOps2.java
new file mode 100644
index 0000000..0467f6b
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/fixed/TestFixedOps2.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.fixed;
+
+import org.ejml.data.FixedMatrix2_64F;
+import org.ejml.data.FixedMatrix2x2_64F;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestFixedOps2 extends CompareFixedToCommonOps {
+
+    public TestFixedOps2() {
+        super(FixedOps2.class);
+    }
+
+    @Test
+    public void diag() {
+        FixedMatrix2x2_64F m = new FixedMatrix2x2_64F(1,2,3,4);
+        FixedMatrix2_64F found = new FixedMatrix2_64F();
+
+        FixedOps2.diag(m,found);
+
+        assertEquals(1,found.a1,1e-8);
+        assertEquals(4,found.a2,1e-8);
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/fixed/TestFixedOps3.java b/main/dense64/test/org/ejml/alg/fixed/TestFixedOps3.java
new file mode 100644
index 0000000..d1442df
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/fixed/TestFixedOps3.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.fixed;
+
+import org.ejml.data.FixedMatrix3_64F;
+import org.ejml.data.FixedMatrix3x3_64F;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestFixedOps3 extends CompareFixedToCommonOps {
+
+    Random rand = new Random(234);
+
+    public TestFixedOps3() {
+        super(FixedOps3.class);
+    }
+
+    @Test
+    public void diag() {
+        FixedMatrix3x3_64F m = new FixedMatrix3x3_64F(1,2,3,4,5,6,7,8,9);
+        FixedMatrix3_64F found = new FixedMatrix3_64F();
+
+        FixedOps3.diag(m,found);
+
+        assertEquals(1,found.a1,1e-8);
+        assertEquals(5,found.a2,1e-8);
+        assertEquals(9,found.a3,1e-8);
+    }
+
+
+
+}
diff --git a/main/dense64/test/org/ejml/alg/fixed/TestFixedOps4.java b/main/dense64/test/org/ejml/alg/fixed/TestFixedOps4.java
new file mode 100644
index 0000000..7d6e267
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/fixed/TestFixedOps4.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.fixed;
+
+import org.ejml.data.FixedMatrix4_64F;
+import org.ejml.data.FixedMatrix4x4_64F;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestFixedOps4 extends CompareFixedToCommonOps {
+
+    public TestFixedOps4() {
+        super(FixedOps4.class);
+    }
+
+    @Test
+    public void diag() {
+        FixedMatrix4x4_64F m = new FixedMatrix4x4_64F(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16);
+        FixedMatrix4_64F found = new FixedMatrix4_64F();
+
+        FixedOps4.diag(m,found);
+
+        assertEquals(1,found.a1,1e-8);
+        assertEquals(6,found.a2,1e-8);
+        assertEquals(11,found.a3,1e-8);
+        assertEquals(16,found.a4,1e-8);
+    }
+
+
+
+}
diff --git a/main/dense64/test/org/ejml/alg/fixed/TestFixedOps5.java b/main/dense64/test/org/ejml/alg/fixed/TestFixedOps5.java
new file mode 100644
index 0000000..443a544
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/fixed/TestFixedOps5.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.fixed;
+
+import org.ejml.data.FixedMatrix5_64F;
+import org.ejml.data.FixedMatrix5x5_64F;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestFixedOps5 extends CompareFixedToCommonOps {
+
+    public TestFixedOps5() {
+        super(FixedOps5.class);
+    }
+
+    @Test
+    public void diag() {
+        FixedMatrix5x5_64F m = new FixedMatrix5x5_64F(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25);
+        FixedMatrix5_64F found = new FixedMatrix5_64F();
+
+        FixedOps5.diag(m,found);
+
+        assertEquals(1,found.a1,1e-8);
+        assertEquals(7,found.a2,1e-8);
+        assertEquals(13,found.a3,1e-8);
+        assertEquals(19,found.a4,1e-8);
+        assertEquals(25,found.a5,1e-8);
+    }
+}
diff --git a/main/dense64/test/org/ejml/alg/fixed/TestFixedOps6.java b/main/dense64/test/org/ejml/alg/fixed/TestFixedOps6.java
new file mode 100644
index 0000000..e1b5f1f
--- /dev/null
+++ b/main/dense64/test/org/ejml/alg/fixed/TestFixedOps6.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.fixed;
+
+import org.ejml.data.FixedMatrix6_64F;
+import org.ejml.data.FixedMatrix6x6_64F;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestFixedOps6 extends CompareFixedToCommonOps {
+
+    public TestFixedOps6() {
+        super(FixedOps6.class);
+    }
+
+    @Test
+    public void diag() {
+        FixedMatrix6x6_64F m = new FixedMatrix6x6_64F();
+        for( int i = 0; i < 36; i++ )
+            m.set(i/6,i%6,i+1);
+        FixedMatrix6_64F found = new FixedMatrix6_64F();
+
+        FixedOps6.diag(m,found);
+
+        assertEquals(1,found.a1,1e-8);
+        assertEquals(8,found.a2,1e-8);
+        assertEquals(15,found.a3,1e-8);
+        assertEquals(22,found.a4,1e-8);
+        assertEquals(29,found.a5,1e-8);
+        assertEquals(36,found.a6,1e-8);
+    }
+}
diff --git a/main/dense64/test/org/ejml/factory/TestDecompositionFactory.java b/main/dense64/test/org/ejml/factory/TestDecompositionFactory.java
new file mode 100644
index 0000000..01b9f9f
--- /dev/null
+++ b/main/dense64/test/org/ejml/factory/TestDecompositionFactory.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.factory;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.EigenDecomposition;
+import org.ejml.interfaces.decomposition.SingularValueDecomposition;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestDecompositionFactory {
+
+    Random rand = new Random(234234);
+
+    @Test
+    public void quality_eig() {
+        // I'm assuming it can process this matrix with no problems
+        DenseMatrix64F A = RandomMatrices.createSymmetric(5,-1,1,rand);
+
+        EigenDecomposition<DenseMatrix64F> eig = DecompositionFactory.eig(A.numRows,true);
+
+        assertTrue(eig.decompose(A));
+
+        double origQuality = DecompositionFactory.quality(A,eig);
+
+        // Mess up the EVD so that it will be of poor quality
+        eig.getEigenVector(2).set(2,0,5);
+
+        double modQuality = DecompositionFactory.quality(A,eig);
+
+        assertTrue(origQuality < modQuality);
+        assertTrue(origQuality < 1e-14);
+    }
+
+    @Test
+    public void quality_svd() {
+        // I'm assuming it can process this matrix with no problems
+        DenseMatrix64F A = RandomMatrices.createRandom(4,5,rand);
+
+        SingularValueDecomposition<DenseMatrix64F> svd = DecompositionFactory.svd(A.numRows,A.numCols,true,true,false);
+
+        assertTrue(svd.decompose(A));
+
+        double origQuality = DecompositionFactory.quality(A,svd);
+
+        // Mess up the SVD so that it will be of poor quality
+        svd.getSingularValues()[2] = 5;
+
+        double modQuality = DecompositionFactory.quality(A,svd);
+
+        assertTrue(origQuality < modQuality);
+        assertTrue(origQuality < 1e-14);
+    }
+}
diff --git a/main/dense64/test/org/ejml/factory/TestLinearSolverFactory.java b/main/dense64/test/org/ejml/factory/TestLinearSolverFactory.java
new file mode 100644
index 0000000..009cb8c
--- /dev/null
+++ b/main/dense64/test/org/ejml/factory/TestLinearSolverFactory.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.factory;
+
+import org.ejml.alg.dense.linsol.AdjustableLinearSolver;
+import org.ejml.alg.dense.linsol.LinearSolverSafe;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestLinearSolverFactory {
+
+    Random rand = new Random(234);
+
+    @Test
+    public void general() {
+        DenseMatrix64F A = RandomMatrices.createRandom(5,4,rand);
+        DenseMatrix64F x = RandomMatrices.createRandom(4,1,rand);
+        DenseMatrix64F y = new DenseMatrix64F(5,1);
+
+        LinearSolver<DenseMatrix64F> solver = LinearSolverFactory.general(A.numRows, A.numCols);
+
+        standardTest(A, x, y, solver);
+    }
+
+    @Test
+    public void linear() {
+        DenseMatrix64F A = RandomMatrices.createRandom(4,4,rand);
+        DenseMatrix64F x = RandomMatrices.createRandom(4,1,rand);
+        DenseMatrix64F y = new DenseMatrix64F(4,1);
+
+        LinearSolver<DenseMatrix64F> solver = LinearSolverFactory.linear(A.numRows);
+
+        standardTest(A, x, y, solver);
+    }
+
+    @Test
+    public void leastSquares() {
+        DenseMatrix64F A = RandomMatrices.createRandom(5,4,rand);
+        DenseMatrix64F x = RandomMatrices.createRandom(4,1,rand);
+        DenseMatrix64F y = new DenseMatrix64F(5,1);
+
+        LinearSolver<DenseMatrix64F> solver = LinearSolverFactory.leastSquares(A.numRows,A.numCols);
+
+        standardTest(A, x, y, solver);
+    }
+
+    @Test
+    public void symmetric() {
+        DenseMatrix64F A = RandomMatrices.createSymmPosDef(5,rand);
+        DenseMatrix64F x = RandomMatrices.createRandom(5,1,rand);
+        DenseMatrix64F y = new DenseMatrix64F(5,1);
+
+        LinearSolver<DenseMatrix64F> solver = LinearSolverFactory.symmPosDef(A.numCols);
+
+        standardTest(A, x, y, solver);
+    }
+
+    @Test
+    public void adjustable() {
+        DenseMatrix64F A = RandomMatrices.createRandom(5,4,rand);
+        DenseMatrix64F x = RandomMatrices.createRandom(4,1,rand);
+        DenseMatrix64F y = new DenseMatrix64F(5,1);
+
+        AdjustableLinearSolver solver = LinearSolverFactory.adjustable();
+
+        standardTest(A, x, y, solver);
+
+        // remove the last observation
+        solver.removeRowFromA(y.numRows-1);
+
+        // compute the adjusted solution
+        y.numRows--;
+        DenseMatrix64F x_adj = new DenseMatrix64F(4,1);
+        solver.solve(y,x_adj);
+
+        // The solution should still be the same
+        assertTrue(MatrixFeatures.isIdentical(x,x_adj,1e-8));
+    }
+
+    /**
+     * Given A and x it computes the value of y.  This is then compared against what the solver computes
+     * x should be.
+     */
+    private void standardTest(DenseMatrix64F a, DenseMatrix64F x, DenseMatrix64F y,
+                              LinearSolver<DenseMatrix64F> solver) {
+        solver = new LinearSolverSafe<DenseMatrix64F>(solver);
+
+        CommonOps.mult(a,x,y);
+
+        DenseMatrix64F x_found = new DenseMatrix64F(x.numRows,1);
+
+        assertTrue(solver.setA(a));
+        solver.solve(y,x_found);
+
+        assertTrue(MatrixFeatures.isIdentical(x,x_found,1e-8));
+    }
+}
diff --git a/main/dense64/test/org/ejml/ops/TestCommonOps.java b/main/dense64/test/org/ejml/ops/TestCommonOps.java
new file mode 100644
index 0000000..b3869e3
--- /dev/null
+++ b/main/dense64/test/org/ejml/ops/TestCommonOps.java
@@ -0,0 +1,1183 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.alg.dense.decomposition.lu.LUDecompositionAlt_D64;
+import org.ejml.alg.dense.linsol.lu.LinearSolverLu_D64;
+import org.ejml.alg.dense.mult.CheckMatrixMultShape;
+import org.ejml.alg.dense.mult.MatrixMatrixMult;
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.data.RowD1Matrix64F;
+import org.ejml.data.UtilTestMatrix;
+import org.junit.Test;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestCommonOps {
+
+    Random rand = new Random(0xFF);
+    double tol = 1e-8;
+
+    @Test
+    public void checkInputShape() {
+        CheckMatrixMultShape check = new CheckMatrixMultShape(CommonOps.class);
+        check.checkAll();
+    }
+
+    /**
+     * Make sure the multiplication methods here have the same behavior as the ones in MatrixMatrixMult.
+     */
+    @Test
+    public void checkAllMatrixMults() {
+        int numChecked = 0;
+        Method methods[] = CommonOps.class.getMethods();
+
+        boolean oneFailed = false;
+
+        for( Method method : methods ) {
+            String name = method.getName();
+
+            // only look at function which perform matrix multiplication
+            if( !name.contains("mult") || name.contains("Element") || 
+                    name.contains("Inner") || name.contains("Outer"))
+                continue;
+
+            boolean hasAlpha = method.getGenericParameterTypes().length==4;
+
+            Method checkMethod = findCheck(name,hasAlpha);
+
+            boolean tranA = false;
+            boolean tranB = false;
+            if( name.contains("TransAB")) {
+                tranA = true;
+                tranB = true;
+            } else if( name.contains("TransA")) {
+                tranA = true;
+            } else if( name.contains("TransB")) {
+                tranB = true;
+            }
+//            System.out.println("Function = "+name+"  alpha = "+hasAlpha);
+
+            try {
+                if( !checkMultMethod(method,checkMethod,hasAlpha,tranA,tranB) ) {
+                    System.out.println("Failed: Function = "+name+"  alpha = "+hasAlpha);
+                    oneFailed = true;
+                }
+            } catch (InvocationTargetException e) {
+                throw new RuntimeException(e);
+            } catch (IllegalAccessException e) {
+                throw new RuntimeException(e);
+            }
+            numChecked++;
+        }
+        assertEquals(16,numChecked);
+        assertTrue(!oneFailed);
+    }
+    /**
+     * See if zeros in rows and columns are handled correctly.
+     */
+    @Test
+    public void checkAllMatrixMult_Zeros() {
+        int numChecked = 0;
+        Method methods[] = CommonOps.class.getMethods();
+
+        boolean oneFailed = false;
+
+        for( Method method : methods ) {
+            String name = method.getName();
+
+            // only look at function which perform matrix multiplication
+            if( !name.contains("mult") || name.contains("Element") ||
+                    name.contains("Inner") || name.contains("Outer"))
+                continue;
+
+            try {
+
+                boolean failed = !checkMultMethod(method,6,0,0,5);
+                failed |= !checkMultMethod(method,0,5,5,0);
+                failed |= !checkMultMethod(method,1,0,0,5);
+                failed |= !checkMultMethod(method,6,0,0,1);
+                failed |= !checkMultMethod(method,0,1,1,5);
+                failed |= !checkMultMethod(method,5,1,1,0);
+                failed |= !checkMultMethod(method,0,0,0,0);
+
+                if( failed ) {
+                    System.out.println("Failed: Function = "+name);
+                    oneFailed = true;
+                }
+            } catch (InvocationTargetException e) {
+                throw new RuntimeException(e);
+            } catch (IllegalAccessException e) {
+                throw new RuntimeException(e);
+            }
+            numChecked++;
+        }
+        assertEquals(16,numChecked);
+        assertTrue(!oneFailed);
+    }
+
+
+    private Method findCheck( String name , boolean hasAlpha ) {
+        Method checkMethod;
+        try {
+            if( hasAlpha )
+                checkMethod = MatrixMatrixMult.class.getMethod(
+                        name,double.class,
+                        RowD1Matrix64F.class, RowD1Matrix64F.class,RowD1Matrix64F.class);
+            else
+                checkMethod = MatrixMatrixMult.class.getMethod(
+                        name, RowD1Matrix64F.class, RowD1Matrix64F.class,RowD1Matrix64F.class);
+        } catch (NoSuchMethodException e) {
+            checkMethod = null;
+        }
+        if( checkMethod == null ) {
+            try {
+            if( hasAlpha )
+                checkMethod = MatrixMatrixMult.class.getMethod(
+                        name+"_reorder",double.class,
+                        RowD1Matrix64F.class, RowD1Matrix64F.class,RowD1Matrix64F.class);
+            else
+                checkMethod = MatrixMatrixMult.class.getMethod(
+                        name+"_reorder", RowD1Matrix64F.class, RowD1Matrix64F.class,RowD1Matrix64F.class);
+            } catch (NoSuchMethodException e) {
+                throw new RuntimeException(e);
+            }
+        }
+        return checkMethod;
+    }
+
+    private boolean checkMultMethod(Method method, Method checkMethod, boolean hasAlpha,
+                                    boolean tranA, boolean tranB ) throws InvocationTargetException, IllegalAccessException {
+
+
+        // check various sizes
+        for( int i = 1; i < 40; i++ ) {
+            DenseMatrix64F a;
+            if( tranA ) a = RandomMatrices.createRandom(i+1,i,rand);
+            else  a = RandomMatrices.createRandom(i,i+1,rand);
+
+            DenseMatrix64F b;
+            if( tranB ) b = RandomMatrices.createRandom(i,i+1,rand);
+            else  b = RandomMatrices.createRandom(i+1,i,rand);
+
+            DenseMatrix64F c = RandomMatrices.createRandom(i,i,rand);
+            DenseMatrix64F c_alt = c.copy();
+
+            if( hasAlpha ) {
+                method.invoke(null,2.0,a,b,c);
+                checkMethod.invoke(null,2.0,a,b,c_alt);
+            } else {
+                method.invoke(null,a,b,c);
+                checkMethod.invoke(null,a,b,c_alt);
+            }
+
+            if( !MatrixFeatures.isIdentical(c_alt,c,tol))
+                return false;
+        }
+
+        // check various sizes column vector
+        for( int i = 1; i < 4; i++ ) {
+            DenseMatrix64F a;
+            if( tranA ) a = RandomMatrices.createRandom(i,i+1,rand);
+            else  a = RandomMatrices.createRandom(i+1,i,rand);
+
+            DenseMatrix64F b;
+            if( tranB ) b = RandomMatrices.createRandom(1,i,rand);
+            else  b = RandomMatrices.createRandom(i,1,rand);
+
+            DenseMatrix64F c = RandomMatrices.createRandom(i+1,1,rand);
+            DenseMatrix64F c_alt = c.copy();
+
+            if( hasAlpha ) {
+                method.invoke(null,2.0,a,b,c);
+                checkMethod.invoke(null,2.0,a,b,c_alt);
+            } else {
+                method.invoke(null,a,b,c);
+                checkMethod.invoke(null,a,b,c_alt);
+            }
+
+            if( !MatrixFeatures.isIdentical(c_alt,c,tol))
+                return false;
+        }
+
+        return true;
+    }
+
+    private boolean checkMultMethod(Method method, int rowsA , int colsA , int rowsB , int colsB ) throws InvocationTargetException, IllegalAccessException {
+
+        String name = method.getName();
+
+        boolean tranA = false;
+        boolean tranB = false;
+        if( name.contains("TransAB")) {
+            tranA = true;
+            tranB = true;
+        } else if( name.contains("TransA")) {
+            tranA = true;
+        } else if( name.contains("TransB")) {
+            tranB = true;
+        }
+
+        boolean add = name.contains("Add");
+        boolean hasAlpha = method.getGenericParameterTypes().length==4;
+
+        // check length zero rows and columns
+        DenseMatrix64F a = tranA ? new DenseMatrix64F(colsA,rowsA) : new DenseMatrix64F(rowsA,colsA);
+        DenseMatrix64F b = tranB ? new DenseMatrix64F(colsB,rowsB) : new DenseMatrix64F(rowsB,colsB);
+
+        DenseMatrix64F c = RandomMatrices.createRandom(rowsA,colsB,rand);
+
+        if( hasAlpha ) {
+            method.invoke(null,2.0,a,b,c);
+        } else {
+            method.invoke(null,a,b,c);
+        }
+
+        if( add ) {
+            DenseMatrix64F corig = c.copy();
+            assertTrue(MatrixFeatures.isIdentical(corig, c, 1e-8));
+        } else {
+            assertTrue(MatrixFeatures.isZeros(c, 1e-8));
+        }
+
+        return true;
+    }
+
+    @Test
+    public void dot() {
+        DenseMatrix64F a = RandomMatrices.createRandom(10,1,rand);
+        DenseMatrix64F b = RandomMatrices.createRandom(1,10,rand);
+
+        double found = CommonOps.dot(a,b);
+
+        double expected = 0;
+        for (int i = 0; i < 10; i++) {
+            expected += a.data[i]*b.data[i];
+        }
+
+        assertEquals(expected,found,1e-8);
+    }
+
+    @Test
+    public void multInner() {
+        DenseMatrix64F a = RandomMatrices.createRandom(10,4,rand);
+        DenseMatrix64F found = RandomMatrices.createRandom(4,4,rand);
+        DenseMatrix64F expected = RandomMatrices.createRandom(4,4,rand);
+
+        CommonOps.multTransA(a, a, expected);
+        CommonOps.multInner(a,found);
+
+        assertTrue(MatrixFeatures.isIdentical(expected,found,tol));
+    }
+
+    @Test
+    public void multOuter() {
+        DenseMatrix64F a = RandomMatrices.createRandom(10,4,rand);
+        DenseMatrix64F found = RandomMatrices.createRandom(10,10,rand);
+        DenseMatrix64F expected = RandomMatrices.createRandom(10,10,rand);
+
+        CommonOps.multTransB(a, a, expected);
+        CommonOps.multOuter(a, found);
+
+        assertTrue(MatrixFeatures.isIdentical(expected,found,tol));
+    }
+    
+    @Test
+    public void elementMult_two() {
+        DenseMatrix64F a = RandomMatrices.createRandom(5,4,rand);
+        DenseMatrix64F b = RandomMatrices.createRandom(5,4,rand);
+        DenseMatrix64F a_orig = a.copy();
+
+        CommonOps.elementMult(a,b);
+
+        for( int i = 0; i < 20; i++ ) {
+            assertEquals(a.get(i),b.get(i)*a_orig.get(i),1e-6);
+        }
+    }
+
+    @Test
+    public void elementMult_three() {
+        DenseMatrix64F a = RandomMatrices.createRandom(5,4,rand);
+        DenseMatrix64F b = RandomMatrices.createRandom(5,4,rand);
+        DenseMatrix64F c = RandomMatrices.createRandom(5,4,rand);
+
+        CommonOps.elementMult(a,b,c);
+
+        for( int i = 0; i < 20; i++ ) {
+            assertEquals(c.get(i),b.get(i)*a.get(i),1e-6);
+        }
+    }
+
+    @Test
+    public void elementDiv_two() {
+        DenseMatrix64F a = RandomMatrices.createRandom(5,4,rand);
+        DenseMatrix64F b = RandomMatrices.createRandom(5,4,rand);
+        DenseMatrix64F a_orig = a.copy();
+
+        CommonOps.elementDiv(a,b);
+
+        for( int i = 0; i < 20; i++ ) {
+            assertEquals(a.get(i),a_orig.get(i)/b.get(i),1e-6);
+        }
+    }
+
+    @Test
+    public void elementDiv_three() {
+        DenseMatrix64F a = RandomMatrices.createRandom(5,4,rand);
+        DenseMatrix64F b = RandomMatrices.createRandom(5,4,rand);
+        DenseMatrix64F c = RandomMatrices.createRandom(5,4,rand);
+
+        CommonOps.elementDiv(a,b,c);
+
+        for( int i = 0; i < 20; i++ ) {
+            assertEquals(c.get(i),a.get(i)/b.get(i),1e-6);
+        }
+    }
+
+    @Test
+    public void solve() {
+        DenseMatrix64F a = new DenseMatrix64F(2,2, true, 1, 2, 7, -3);
+        DenseMatrix64F b = RandomMatrices.createRandom(2,5,rand);
+        DenseMatrix64F c = RandomMatrices.createRandom(2,5,rand);
+        DenseMatrix64F c_exp = RandomMatrices.createRandom(2,5,rand);
+
+        assertTrue(CommonOps.solve(a,b,c));
+        LUDecompositionAlt_D64 alg = new LUDecompositionAlt_D64();
+        LinearSolverLu_D64 solver = new LinearSolverLu_D64(alg);
+        assertTrue(solver.setA(a));
+
+        solver.solve(b,c_exp);
+
+        EjmlUnitTests.assertEquals(c_exp,c,1e-8);
+    }
+
+    @Test
+    public void transpose_inplace() {
+        DenseMatrix64F mat = new DenseMatrix64F(3,3, true, 0, 1, 2, 3, 4, 5, 6, 7, 8);
+        DenseMatrix64F matTran = new DenseMatrix64F(3,3);
+
+        CommonOps.transpose(mat,matTran);
+        CommonOps.transpose(mat);
+
+        EjmlUnitTests.assertEquals(mat,matTran,1e-8);
+    }
+
+    @Test
+    public void transpose() {
+        DenseMatrix64F mat = new DenseMatrix64F(3,2, true, 0, 1, 2, 3, 4, 5);
+        DenseMatrix64F matTran = new DenseMatrix64F(2,3);
+
+        CommonOps.transpose(mat,matTran);
+
+        assertEquals(mat.getNumCols(),matTran.getNumRows());
+        assertEquals(mat.getNumRows(),matTran.getNumCols());
+
+        for( int y = 0; y < mat.getNumRows(); y++ ){
+            for( int x = 0; x < mat.getNumCols(); x++ ) {
+                assertEquals(mat.get(y,x),matTran.get(x,y),1e-6);
+            }
+        }
+    }
+
+    @Test
+    public void trace() {
+        DenseMatrix64F mat = new DenseMatrix64F(3,3, true, 0, 1, 2, 3, 4, 5, 6, 7, 8);
+
+        assertEquals(12,CommonOps.trace(mat),1e-6);
+
+        // non square
+        DenseMatrix64F B = RandomMatrices.createRandom(4,3,rand);
+        CommonOps.insert(mat,B,0,0);
+        assertEquals(12,CommonOps.trace(B),1e-6);
+
+        B = RandomMatrices.createRandom(3,4,rand);
+        CommonOps.insert(mat,B,0,0);
+        assertEquals(12,CommonOps.trace(B),1e-6);
+
+    }
+
+    @Test
+    public void invert() {
+        for( int i = 1; i <= 10; i++ ) {
+            DenseMatrix64F a = RandomMatrices.createRandom(i,i,rand);
+
+            LUDecompositionAlt_D64 lu = new LUDecompositionAlt_D64();
+            LinearSolverLu_D64 solver = new LinearSolverLu_D64(lu);
+            assertTrue(solver.setA(a));
+
+            DenseMatrix64F a_inv = new DenseMatrix64F(i,i);
+            DenseMatrix64F a_lu = new DenseMatrix64F(i,i);
+            solver.invert(a_lu);
+
+            CommonOps.invert(a,a_inv);
+            CommonOps.invert(a);
+
+            EjmlUnitTests.assertEquals(a,a_inv,1e-8);
+            EjmlUnitTests.assertEquals(a_lu,a,1e-8);
+        }
+    }
+
+    /**
+     * Checked against by computing a solution to the linear system then
+     * seeing if the solution produces the expected output
+     */
+    @Test
+    public void pinv() {
+        // check wide matrix
+        DenseMatrix64F A = new DenseMatrix64F(2,4,true,1,2,3,4,5,6,7,8);
+        DenseMatrix64F A_inv = new DenseMatrix64F(4,2);
+        DenseMatrix64F b = new DenseMatrix64F(2,1,true,3,4);
+        DenseMatrix64F x = new DenseMatrix64F(4,1);
+        DenseMatrix64F found = new DenseMatrix64F(2,1);
+        
+        CommonOps.pinv(A,A_inv);
+
+        CommonOps.mult(A_inv,b,x);
+        CommonOps.mult(A,x,found);
+
+        assertTrue(MatrixFeatures.isIdentical(b,found,1e-4));
+
+        // check tall matrix
+        CommonOps.transpose(A);
+        CommonOps.transpose(A_inv);
+        b = new DenseMatrix64F(4,1,true,3,4,5,6);
+        x.reshape(2,1);
+        found.reshape(4,1);
+
+        CommonOps.mult(A_inv,b,x);
+        CommonOps.mult(A, x, found);
+
+        assertTrue(MatrixFeatures.isIdentical(b,found,1e-4));
+    }
+
+    @Test
+    public void columnsToVectors() {
+        DenseMatrix64F M = RandomMatrices.createRandom(4,5,rand);
+
+        DenseMatrix64F v[] = CommonOps.columnsToVector(M, null);
+
+        assertEquals(M.numCols,v.length);
+
+        for( int i = 0; i < v.length; i++ ) {
+            DenseMatrix64F a = v[i];
+
+            assertEquals(M.numRows,a.numRows);
+            assertEquals(1,a.numCols);
+
+            for( int j = 0; j < M.numRows; j++ ) {
+                assertEquals(a.get(j),M.get(j,i),1e-8);
+            }
+        }
+    }
+    
+    @Test
+    public void identity() {
+        DenseMatrix64F A = CommonOps.identity(4);
+
+        assertEquals(4,A.numRows);
+        assertEquals(4,A.numCols);
+
+        assertEquals(4,CommonOps.elementSum(A),1e-8);
+    }
+
+    @Test
+    public void identity_rect() {
+        DenseMatrix64F A = CommonOps.identity(4,6);
+
+        assertEquals(4,A.numRows);
+        assertEquals(6,A.numCols);
+
+        assertEquals(4,CommonOps.elementSum(A),1e-8);
+    }
+
+    @Test
+    public void setIdentity() {
+        DenseMatrix64F A = RandomMatrices.createRandom(4,4,rand);
+
+        CommonOps.setIdentity(A);
+
+        assertEquals(4,A.numRows);
+        assertEquals(4,A.numCols);
+
+        assertEquals(4,CommonOps.elementSum(A),1e-8);
+    }
+
+    @Test
+    public void diag() {
+        DenseMatrix64F A = CommonOps.diag(2.0,3.0,6.0,7.0);
+
+        assertEquals(4,A.numRows);
+        assertEquals(4,A.numCols);
+
+        assertEquals(2,A.get(0,0),1e-8);
+        assertEquals(3,A.get(1,1),1e-8);
+        assertEquals(6,A.get(2,2),1e-8);
+        assertEquals(7,A.get(3,3),1e-8);
+
+        assertEquals(18,CommonOps.elementSum(A),1e-8);
+    }
+
+    @Test
+    public void diag_rect() {
+        DenseMatrix64F A = CommonOps.diagR(4,6,2.0,3.0,6.0,7.0);
+
+        assertEquals(4,A.numRows);
+        assertEquals(6,A.numCols);
+
+        assertEquals(2,A.get(0,0),1e-8);
+        assertEquals(3,A.get(1,1),1e-8);
+        assertEquals(6,A.get(2,2),1e-8);
+        assertEquals(7,A.get(3,3),1e-8);
+
+        assertEquals(18,CommonOps.elementSum(A),1e-8);
+    }
+
+    @Test
+    public void kron() {
+        DenseMatrix64F A = new DenseMatrix64F(2,2, true, 1, 2, 3, 4);
+        DenseMatrix64F B = new DenseMatrix64F(1,2, true, 4, 5);
+
+        DenseMatrix64F C = new DenseMatrix64F(2,4);
+        DenseMatrix64F C_expected = new DenseMatrix64F(2,4, true, 4, 5, 8, 10, 12, 15, 16, 20);
+
+        CommonOps.kron(A,B,C);
+
+        assertTrue(MatrixFeatures.isIdentical(C,C_expected,1e-8));
+
+        // test various shapes for problems
+        for( int i = 1; i <= 3; i++ ) {
+            for( int j = 1; j <= 3; j++ ) {
+                for( int k = 1; k <= 3; k++ ) {
+                    for( int l = 1; l <= 3; l++ ) {
+                        A = RandomMatrices.createRandom(i,j,rand);
+                        B = RandomMatrices.createRandom(k,l,rand);
+                        C = new DenseMatrix64F(A.numRows*B.numRows,A.numCols*B.numCols);
+
+                        CommonOps.kron(A,B,C);
+
+                        assertEquals(i*k,C.numRows);
+                        assertEquals(j*l,C.numCols);
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    public void extract() {
+        DenseMatrix64F A = RandomMatrices.createRandom(5,5, 0, 1, rand);
+
+        DenseMatrix64F B = new DenseMatrix64F(2,3);
+
+        CommonOps.extract(A,1,3,2,5,B,0,0);
+
+        for( int i = 1; i < 3; i++ ) {
+            for( int j = 2; j < 5; j++ ) {
+                assertEquals(A.get(i,j),B.get(i-1,j-2),1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void extract_ret() {
+        DenseMatrix64F A = RandomMatrices.createRandom(5,5, 0, 1, rand);
+
+        DenseMatrix64F B = CommonOps.extract(A,1,3,2,5);
+
+        assertEquals(B.numRows,2);
+        assertEquals(B.numCols,3);
+
+        for( int i = 1; i < 3; i++ ) {
+            for( int j = 2; j < 5; j++ ) {
+                assertEquals(A.get(i,j),B.get(i-1,j-2),1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void extractDiag() {
+        DenseMatrix64F a = RandomMatrices.createRandom(3,4, 0, 1, rand);
+
+        for( int i = 0; i < 3; i++ ) {
+            a.set(i,i,i+1);
+        }
+
+        DenseMatrix64F v = new DenseMatrix64F(3,1);
+        CommonOps.extractDiag(a,v);
+
+        for( int i = 0; i < 3; i++ ) {
+            assertEquals( i+1 , v.get(i) , 1e-8 );
+        }
+    }
+
+    @Test
+    public void extractRow() {
+        DenseMatrix64F A = RandomMatrices.createRandom(5,6, 0, 1, rand);
+
+        DenseMatrix64F B = CommonOps.extractRow(A, 3, null);
+
+        assertEquals(B.numRows,1);
+        assertEquals(B.numCols,6);
+
+        for( int i = 0; i < 6; i++ ) {
+            assertEquals(A.get(3,i),B.get(0,i),1e-8);
+        }
+    }
+
+    @Test
+    public void extractColumn() {
+        DenseMatrix64F A = RandomMatrices.createRandom(5,6, 0, 1, rand);
+
+        DenseMatrix64F B = CommonOps.extractColumn(A, 3, null);
+
+        assertEquals(B.numRows,5);
+        assertEquals(B.numCols,1);
+
+        for( int i = 0; i < 5; i++ ) {
+            assertEquals(A.get(i,3),B.get(i,0),1e-8);
+        }
+    }
+
+    @Test
+    public void insert() {
+        DenseMatrix64F A = new DenseMatrix64F(5,5);
+        for( int i = 0; i < A.numRows; i++ ) {
+            for( int j = 0; j < A.numCols; j++ ) {
+                A.set(i,j,i*A.numRows+j);
+            }
+        }
+
+        DenseMatrix64F B = new DenseMatrix64F(8,8);
+
+        CommonOps.insert(A, B, 1,2);
+
+        for( int i = 1; i < 6; i++ ) {
+            for( int j = 2; j < 7; j++ ) {
+                assertEquals(A.get(i-1,j-2),B.get(i,j),1e-8);
+            }
+        }
+    }
+
+   @Test
+    public void addEquals() {
+        DenseMatrix64F a = new DenseMatrix64F(2,3, true, 0, 1, 2, 3, 4, 5);
+        DenseMatrix64F b = new DenseMatrix64F(2,3, true, 5, 4, 3, 2, 1, 0);
+
+        CommonOps.addEquals(a,b);
+
+        UtilTestMatrix.checkMat(a,5,5,5,5,5,5);
+        UtilTestMatrix.checkMat(b,5,4,3,2,1,0);
+    }
+
+    @Test
+    public void addEquals_beta() {
+        DenseMatrix64F a = new DenseMatrix64F(2,3, true, 0, 1, 2, 3, 4, 5);
+        DenseMatrix64F b = new DenseMatrix64F(2,3, true, 5, 4, 3, 2, 1, 0);
+
+        CommonOps.addEquals(a,2.0,b);
+
+        UtilTestMatrix.checkMat(a,10,9,8,7,6,5);
+        UtilTestMatrix.checkMat(b,5,4,3,2,1,0);
+    }
+
+    @Test
+    public void add() {
+        DenseMatrix64F a = new DenseMatrix64F(2,3, true, 0, 1, 2, 3, 4, 5);
+        DenseMatrix64F b = new DenseMatrix64F(2,3, true, 5, 4, 3, 2, 1, 0);
+        DenseMatrix64F c = RandomMatrices.createRandom(2,3,rand);
+
+        CommonOps.add(a,b,c);
+
+        UtilTestMatrix.checkMat(a,0,1,2,3,4,5);
+        UtilTestMatrix.checkMat(b,5,4,3,2,1,0);
+        UtilTestMatrix.checkMat(c,5,5,5,5,5,5);
+    }
+
+    @Test
+    public void add_beta() {
+        DenseMatrix64F a = new DenseMatrix64F(2,3, true, 0, 1, 2, 3, 4, 5);
+        DenseMatrix64F b = new DenseMatrix64F(2,3, true, 5, 4, 3, 2, 1, 0);
+        DenseMatrix64F c = RandomMatrices.createRandom(2,3,rand);
+
+        CommonOps.add(a,2.0,b,c);
+
+        UtilTestMatrix.checkMat(a,0,1,2,3,4,5);
+        UtilTestMatrix.checkMat(b,5,4,3,2,1,0);
+        UtilTestMatrix.checkMat(c,10,9,8,7,6,5);
+    }
+
+    @Test
+    public void add_alpha_beta() {
+        DenseMatrix64F a = new DenseMatrix64F(2,3, true, 0, 1, 2, 3, 4, 5);
+        DenseMatrix64F b = new DenseMatrix64F(2,3, true, 5, 4, 3, 2, 1, 0);
+        DenseMatrix64F c = RandomMatrices.createRandom(2,3,rand);
+
+        CommonOps.add(2.0,a,2.0,b,c);
+
+        UtilTestMatrix.checkMat(a,0,1,2,3,4,5);
+        UtilTestMatrix.checkMat(b,5,4,3,2,1,0);
+        UtilTestMatrix.checkMat(c,10,10,10,10,10,10);
+    }
+
+    @Test
+    public void add_alpha() {
+        DenseMatrix64F a = new DenseMatrix64F(2,3, true, 0, 1, 2, 3, 4, 5);
+        DenseMatrix64F b = new DenseMatrix64F(2,3, true, 5, 4, 3, 2, 1, 0);
+        DenseMatrix64F c = RandomMatrices.createRandom(2,3,rand);
+
+        CommonOps.add(2.0,a,b,c);
+
+        UtilTestMatrix.checkMat(a,0,1,2,3,4,5);
+        UtilTestMatrix.checkMat(b,5,4,3,2,1,0);
+        UtilTestMatrix.checkMat(c,5,6,7,8,9,10);
+    }
+
+    @Test
+    public void add_scalar_c() {
+        DenseMatrix64F a = new DenseMatrix64F(2,3, true, 0, 1, 2, 3, 4, 5);
+        DenseMatrix64F c = RandomMatrices.createRandom(2,3,rand);
+
+        CommonOps.add(a,2.0,c);
+
+        UtilTestMatrix.checkMat(a,0,1,2,3,4,5);
+        UtilTestMatrix.checkMat(c,2,3,4,5,6,7);
+    }
+
+    @Test
+    public void add_scalar() {
+        DenseMatrix64F a = new DenseMatrix64F(2,3, true, 0, 1, 2, 3, 4, 5);
+
+        CommonOps.add(a,2.0);
+
+        UtilTestMatrix.checkMat(a,2,3,4,5,6,7);
+    }
+
+    @Test
+    public void subEquals() {
+        DenseMatrix64F a = new DenseMatrix64F(2,3, true, 0, 1, 2, 3, 4, 5);
+        DenseMatrix64F b = new DenseMatrix64F(2,3, true, 5, 5, 5, 5, 5, 5);
+
+        CommonOps.subtractEquals(a, b);
+
+        UtilTestMatrix.checkMat(a,-5,-4,-3,-2,-1,0);
+        UtilTestMatrix.checkMat(b,5,5,5,5,5,5);
+    }
+
+    @Test
+    public void subtract_matrix_matrix() {
+        DenseMatrix64F a = new DenseMatrix64F(2,3, true, 0, 1, 2, 3, 4, 5);
+        DenseMatrix64F b = new DenseMatrix64F(2,3, true, 5, 5, 5, 5, 5, 5);
+        DenseMatrix64F c = RandomMatrices.createRandom(2,3,rand);
+
+        CommonOps.subtract(a, b, c);
+
+        UtilTestMatrix.checkMat(a,0,1,2,3,4,5);
+        UtilTestMatrix.checkMat(b,5,5,5,5,5,5);
+        UtilTestMatrix.checkMat(c,-5,-4,-3,-2,-1,0);
+    }
+
+    @Test
+    public void subtract_matrix_double() {
+        DenseMatrix64F a = new DenseMatrix64F(2,3, true, 0, 1, 2, 3, 4, 5);
+        DenseMatrix64F c = RandomMatrices.createRandom(2,3,rand);
+
+        CommonOps.subtract(a, 2, c);
+
+        UtilTestMatrix.checkMat(a,0,1,2,3,4,5);
+        UtilTestMatrix.checkMat(c,-2,-1,0,1,2,3);
+    }
+
+    @Test
+    public void subtract_double_matrix() {
+        DenseMatrix64F a = new DenseMatrix64F(2,3, true, 0, 1, 2, 3, 4, 5);
+        DenseMatrix64F c = RandomMatrices.createRandom(2,3,rand);
+
+        CommonOps.subtract(2, a, c);
+
+        UtilTestMatrix.checkMat(a,0,1,2,3,4,5);
+        UtilTestMatrix.checkMat(c,2,1,0,-1,-2,-3);
+    }
+
+    @Test
+    public void scale() {
+        double s = 2.5;
+        double d[] = new double[]{10,12.5,-2,5.5};
+        DenseMatrix64F mat = new DenseMatrix64F(2,2, true, d);
+
+        CommonOps.scale(s,mat);
+
+        assertEquals(d[0]*s,mat.get(0,0),1e-8);
+        assertEquals(d[1]*s,mat.get(0,1),1e-8);
+        assertEquals(d[2]*s,mat.get(1,0),1e-8);
+        assertEquals(d[3]*s,mat.get(1,1),1e-8);
+    }
+
+    @Test
+    public void scale_two_input() {
+        double s = 2.5;
+        double d[] = new double[]{10,12.5,-2,5.5};
+        DenseMatrix64F mat = new DenseMatrix64F(2,2, true, d);
+        DenseMatrix64F r = new DenseMatrix64F(2,2, true, d);
+
+        CommonOps.scale(s,mat,r);
+
+        assertEquals(d[0],mat.get(0,0),1e-8);
+        assertEquals(d[1],mat.get(0,1),1e-8);
+        assertEquals(d[2],mat.get(1,0),1e-8);
+        assertEquals(d[3],mat.get(1,1),1e-8);
+
+        assertEquals(d[0]*s,r.get(0,0),1e-8);
+        assertEquals(d[1]*s,r.get(0,1),1e-8);
+        assertEquals(d[2]*s,r.get(1,0),1e-8);
+        assertEquals(d[3]*s,r.get(1,1),1e-8);
+    }
+
+    @Test
+    public void div_scalar_mat() {
+        double s = 2.5;
+        double d[] = new double[]{10,12.5,-2,5.5};
+        DenseMatrix64F mat = new DenseMatrix64F(2,2, true, d);
+
+        CommonOps.divide(s,mat);
+
+        assertEquals(s/d[0],mat.get(0,0),1e-8);
+        assertEquals(s/d[1],mat.get(0,1),1e-8);
+        assertEquals(s/d[2],mat.get(1,0),1e-8);
+        assertEquals(s/d[3],mat.get(1,1),1e-8);
+    }
+
+    @Test
+    public void div_mat_scalar() {
+        double s = 2.5;
+        double d[] = new double[]{10,12.5,-2,5.5};
+        DenseMatrix64F mat = new DenseMatrix64F(2,2, true, d);
+
+        CommonOps.divide(mat,s);
+
+        assertEquals(mat.get(0,0),d[0]/s,1e-8);
+        assertEquals(mat.get(0,1),d[1]/s,1e-8);
+        assertEquals(mat.get(1,0),d[2]/s,1e-8);
+        assertEquals(mat.get(1,1),d[3]/s,1e-8);
+    }
+
+    @Test
+    public void div_mat_scalar_out() {
+        double s = 2.5;
+        double d[] = new double[]{10,12.5,-2,5.5};
+        DenseMatrix64F mat = new DenseMatrix64F(2,2, true, d);
+        DenseMatrix64F r = new DenseMatrix64F(2,2, true, d);
+
+        CommonOps.divide(mat,s,r);
+
+        assertEquals(d[0],mat.get(0,0),1e-8);
+        assertEquals(d[1],mat.get(0,1),1e-8);
+        assertEquals(d[2],mat.get(1,0),1e-8);
+        assertEquals(d[3],mat.get(1,1),1e-8);
+
+        assertEquals(d[0]/s,r.get(0,0),1e-8);
+        assertEquals(d[1]/s,r.get(0,1),1e-8);
+        assertEquals(d[2]/s,r.get(1,0),1e-8);
+        assertEquals(d[3]/s,r.get(1,1),1e-8);
+    }
+
+    @Test
+    public void div_scalar_mat_out() {
+        double s = 2.5;
+        double d[] = new double[]{10,12.5,-2,5.5};
+        DenseMatrix64F mat = new DenseMatrix64F(2,2, true, d);
+        DenseMatrix64F r = new DenseMatrix64F(2,2, true, d);
+
+        CommonOps.divide(s,mat,r);
+
+        assertEquals(d[0],mat.get(0,0),1e-8);
+        assertEquals(d[1],mat.get(0,1),1e-8);
+        assertEquals(d[2],mat.get(1,0),1e-8);
+        assertEquals(d[3],mat.get(1,1),1e-8);
+
+        assertEquals(s/d[0],r.get(0,0),1e-8);
+        assertEquals(s/d[1],r.get(0,1),1e-8);
+        assertEquals(s/d[2],r.get(1,0),1e-8);
+        assertEquals(s/d[3],r.get(1,1),1e-8);
+    }
+
+    @Test
+    public void changeSign_one() {
+        DenseMatrix64F A = RandomMatrices.createRandom(2,3,rand);
+        DenseMatrix64F A_orig = A.copy();
+
+        CommonOps.changeSign(A);
+
+        for (int i = 0; i < A.getNumElements(); i++) {
+            assertEquals(-A.get(i),A_orig.get(i),1e-8);
+        }
+    }
+
+    @Test
+    public void changeSign_two() {
+        DenseMatrix64F A = RandomMatrices.createRandom(2,3,rand);
+        DenseMatrix64F B = RandomMatrices.createRandom(2,3,rand);
+
+        CommonOps.changeSign(A,B);
+
+        for (int i = 0; i < A.getNumElements(); i++) {
+            assertEquals(A.get(i),-B.get(i),1e-8);
+        }
+    }
+
+    @Test
+    public void fill_dense() {
+        double d[] = new double[]{10,12.5,-2,5.5};
+        DenseMatrix64F mat = new DenseMatrix64F(2,2, true, d);
+
+        CommonOps.fill(mat, 1);
+
+        for( int i = 0; i < mat.getNumElements(); i++ ) {
+            assertEquals(1,mat.get(i),1e-8);
+        }
+    }
+
+    @Test
+    public void fill_block() {
+        // pick the size such that it doesn't nicely line up along blocks
+        BlockMatrix64F mat = new BlockMatrix64F(10,14,3);
+
+        CommonOps.fill(mat, 1.5);
+
+        for( int i = 0; i < mat.getNumElements(); i++ ) {
+            assertEquals(1.5,mat.get(i),1e-8);
+        }
+    }
+
+    @Test
+    public void zero() {
+        double d[] = new double[]{10,12.5,-2,5.5};
+        DenseMatrix64F mat = new DenseMatrix64F(2,2, true, d);
+
+        mat.zero();
+
+        for( int i = 0; i < mat.getNumElements(); i++ ) {
+            assertEquals(0,mat.get(i),1e-8);
+        }
+    }
+
+    @Test
+    public void elementMax() {
+        DenseMatrix64F mat = new DenseMatrix64F(3,3, true, 0, 1, -2, 3, 4, 5, 6, 7, -8);
+
+        double m = CommonOps.elementMax(mat);
+        assertEquals(7,m,1e-8);
+    }
+
+    @Test
+    public void elementMin() {
+        DenseMatrix64F mat = new DenseMatrix64F(3,3, true, 0, 1, 2, -3, 4, 5, 6, 7, 8);
+
+        double m = CommonOps.elementMin(mat);
+        assertEquals(-3,m,1e-8);
+    }
+
+    @Test
+    public void elementMinAbs() {
+        DenseMatrix64F mat = new DenseMatrix64F(3,3, true, 0, 1, -2, 3, 4, 5, 6, 7, -8);
+
+        double m = CommonOps.elementMinAbs(mat);
+        assertEquals(0,m,1e-8);
+    }
+
+    @Test
+    public void elementMaxAbs() {
+        DenseMatrix64F mat = new DenseMatrix64F(3,3, true, 0, 1, 2, 3, 4, 5, -6, 7, -8);
+
+        double m = CommonOps.elementMaxAbs(mat);
+        assertEquals(8,m,1e-8);
+    }
+
+    @Test
+    public void elementSum() {
+        DenseMatrix64F M = RandomMatrices.createRandom(5,5,rand);
+        // make it smaller than the original size to make sure it is bounding
+        // the summation correctly
+        M.reshape(4,3, false);
+
+        double sum = 0;
+        for( int i = 0; i < M.numRows; i++ ) {
+            for( int j = 0; j < M.numCols; j++ ) {
+                sum += M.get(i,j);
+            }
+        }
+
+        assertEquals(sum,CommonOps.elementSum(M),1e-8);
+    }
+
+    @Test
+    public void elementSumAbs() {
+        DenseMatrix64F M = RandomMatrices.createRandom(5,5,rand);
+        // make it smaller than the original size to make sure it is bounding
+        // the summation correctly
+        M.reshape(4,3, false);
+
+        double sum = 0;
+        for( int i = 0; i < M.numRows; i++ ) {
+            for( int j = 0; j < M.numCols; j++ ) {
+                sum += Math.abs(M.get(i,j));
+            }
+        }
+
+        assertEquals(sum,CommonOps.elementSum(M),1e-8);
+    }
+
+    @Test
+    public void elementPower_mm() {
+        DenseMatrix64F A = RandomMatrices.createRandom(4, 5, rand);
+        DenseMatrix64F B = RandomMatrices.createRandom(4, 5, rand);
+        DenseMatrix64F C = RandomMatrices.createRandom(4, 5, rand);
+
+        CommonOps.elementPower(A, B, C);
+
+        for (int i = 0; i < A.getNumElements(); i++) {
+            double expected = Math.pow( A.get(i) , B.get(i));
+            assertEquals(expected,C.get(i),1e-8);
+        }
+    }
+
+    @Test
+    public void elementPower_ms() {
+        double a = 1.3;
+        DenseMatrix64F B = RandomMatrices.createRandom(4, 5, rand);
+        DenseMatrix64F C = RandomMatrices.createRandom(4, 5, rand);
+
+        CommonOps.elementPower(a,B,C);
+
+        for (int i = 0; i < C.getNumElements(); i++) {
+            double expected = Math.pow( a , B.get(i));
+            assertEquals(expected,C.get(i),1e-8);
+        }
+    }
+
+    @Test
+    public void elementPower_sm() {
+        DenseMatrix64F A = RandomMatrices.createRandom(4, 5, rand);
+        double b = 1.1;
+        DenseMatrix64F C = RandomMatrices.createRandom(4, 5, rand);
+
+        CommonOps.elementPower(A,b,C);
+
+        for (int i = 0; i < A.getNumElements(); i++) {
+            double expected = Math.pow( A.get(i) , b);
+            assertEquals(expected,C.get(i),1e-8);
+        }
+    }
+
+    @Test
+    public void elementLog() {
+        DenseMatrix64F A = RandomMatrices.createRandom(4, 5, rand);
+        DenseMatrix64F C = RandomMatrices.createRandom(4, 5, rand);
+
+        CommonOps.elementLog(A, C);
+
+        for (int i = 0; i < A.getNumElements(); i++) {
+            double expected = Math.log(A.get(i));
+            assertEquals(expected,C.get(i),1e-8);
+        }
+    }
+
+    @Test
+    public void elementExp() {
+        DenseMatrix64F A = RandomMatrices.createRandom(4, 5, rand);
+        DenseMatrix64F C = RandomMatrices.createRandom(4, 5, rand);
+
+        CommonOps.elementExp(A, C);
+
+        for (int i = 0; i < A.getNumElements(); i++) {
+            double expected = Math.exp(A.get(i));
+            assertEquals(expected,C.get(i),1e-8);
+        }
+    }
+
+    @Test
+    public void sumRows() {
+        DenseMatrix64F input = RandomMatrices.createRandom(4,5,rand);
+        DenseMatrix64F output = new DenseMatrix64F(4,1);
+
+        assertTrue( output == CommonOps.sumRows(input,output));
+
+        for( int i = 0; i < input.numRows; i++ ) {
+            double total = 0;
+            for( int j = 0; j < input.numCols; j++ ) {
+                total += input.get(i,j);
+            }
+            assertEquals( total, output.get(i),1e-8);
+        }
+
+        // check with a null output
+        DenseMatrix64F output2 = CommonOps.sumRows(input,null);
+
+        EjmlUnitTests.assertEquals(output,output2,1e-8);
+    }
+
+    @Test
+    public void sumCols() {
+        DenseMatrix64F input = RandomMatrices.createRandom(4,5,rand);
+        DenseMatrix64F output = new DenseMatrix64F(1,5);
+
+        assertTrue( output == CommonOps.sumCols(input,output));
+
+        for( int i = 0; i < input.numCols; i++ ) {
+            double total = 0;
+            for( int j = 0; j < input.numRows; j++ ) {
+                total += input.get(j,i);
+            }
+            assertEquals( total, output.get(i),1e-8);
+        }
+
+        // check with a null output
+        DenseMatrix64F output2 = CommonOps.sumCols(input,null);
+
+        EjmlUnitTests.assertEquals(output,output2,1e-8);
+    }
+
+    @Test
+    public void rref() {
+        DenseMatrix64F A = new DenseMatrix64F(4,6,true,
+                0,0,1,-1,-1,4,
+                2,4,2,4,2,4,
+                2,4,3,3,3,4,
+                3,6,6,3,6,6);
+
+        DenseMatrix64F expected = new DenseMatrix64F(4,6,true,
+                1,2,0,3,0,2,
+                0,0,1,-1,0,2,
+                0,0,0,0,1,-2,
+                0,0,0,0,0,0);
+
+        DenseMatrix64F found = CommonOps.rref(A,5, null);
+
+
+        assertTrue(MatrixFeatures.isEquals(found,expected));
+    }
+}
diff --git a/main/dense64/test/org/ejml/ops/TestCovarianceOps.java b/main/dense64/test/org/ejml/ops/TestCovarianceOps.java
new file mode 100644
index 0000000..ab21803
--- /dev/null
+++ b/main/dense64/test/org/ejml/ops/TestCovarianceOps.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.data.DenseMatrix64F;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestCovarianceOps {
+    @Test
+    public void isValid() {
+        // nothing is wrong with it
+        DenseMatrix64F m = CommonOps.identity(3);
+        assertEquals(0, CovarianceOps.isValid(m));
+
+        // negative diagonal term
+        m.set(1,1,-3);
+        assertEquals(1, CovarianceOps.isValid(m));
+
+        // not symetric
+        m = CommonOps.identity(3);
+        m.set(1,0,30);
+        assertEquals(2, CovarianceOps.isValid(m));
+
+        // not positive definite
+        m = CommonOps.identity(3);
+        m.set(1,2,-400);
+        m.set(2,1,-400);
+        assertEquals(3, CovarianceOps.isValid(m));
+    }
+}
diff --git a/main/dense64/test/org/ejml/ops/TestCovarianceRandomDraw.java b/main/dense64/test/org/ejml/ops/TestCovarianceRandomDraw.java
new file mode 100644
index 0000000..fe3700f
--- /dev/null
+++ b/main/dense64/test/org/ejml/ops/TestCovarianceRandomDraw.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.data.DenseMatrix64F;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+public class TestCovarianceRandomDraw
+{
+    public static int N = 6000;
+
+    /**
+     * Do a lot of draws on the distribution and see if a similar distribution is computed
+     * in the end.
+     */
+    @Test
+    public void testStatistics() {
+        DenseMatrix64F orig_P = new DenseMatrix64F(new double[][]{{6,-2},{-2,10}});
+
+        CovarianceRandomDraw dist = new CovarianceRandomDraw(new Random(0xfeed),orig_P);
+
+        DenseMatrix64F draws[] = new DenseMatrix64F[N];
+
+        // sample the distribution
+        for( int i = 0; i < N; i++ ) {
+            DenseMatrix64F x = new DenseMatrix64F(2,1);
+            dist.next(x);
+            draws[i] = x;
+        }
+
+        // compute the statistics
+        double raw_comp_x[] = new double[2];
+
+        // find the mean
+        for( int i = 0; i < N; i++ ) {
+            raw_comp_x[0] += draws[i].get(0,0);
+            raw_comp_x[1] += draws[i].get(1,0);
+        }
+
+        raw_comp_x[0] /= N;
+        raw_comp_x[1] /= N;
+
+        assertEquals(0,raw_comp_x[0],0.1);
+        assertEquals(0.0,raw_comp_x[1],0.1);
+
+        // now the covariance
+        DenseMatrix64F comp_P = new DenseMatrix64F(2,2);
+        DenseMatrix64F temp = new DenseMatrix64F(2,1);
+
+        for( int i = 0; i < N; i++ ) {
+            temp.set(0,0,draws[i].get(0,0)-raw_comp_x[0]);
+            temp.set(1,0,draws[i].get(1,0)-raw_comp_x[1]);
+
+            CommonOps.multAddTransB(temp,temp,comp_P);
+        }
+
+        CommonOps.scale(1.0/N,comp_P);
+
+        MatrixFeatures.isIdentical(comp_P,orig_P,0.3);
+    }
+
+    /**
+     * Make sure the input is not modified.
+     */
+    @Test
+    public void modifyInput() {
+        DenseMatrix64F orig_P = new DenseMatrix64F(new double[][]{{6,-2},{-2,10}});
+        DenseMatrix64F input = orig_P.copy();
+
+        CovarianceRandomDraw dist = new CovarianceRandomDraw(new Random(0xfeed),input);
+
+        assertTrue(MatrixFeatures.isIdentical(input,orig_P,1e-8));
+    }
+
+}
\ No newline at end of file
diff --git a/main/dense64/test/org/ejml/ops/TestEigenOps.java b/main/dense64/test/org/ejml/ops/TestEigenOps.java
new file mode 100644
index 0000000..642ecc8
--- /dev/null
+++ b/main/dense64/test/org/ejml/ops/TestEigenOps.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.data.Complex64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.DecompositionFactory;
+import org.ejml.interfaces.decomposition.EigenDecomposition;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestEigenOps {
+
+    Random rand = new Random(12344);
+
+    /**
+     * Compute an eigen value and compare against a known solution from octave.
+     */
+    @Test
+    public void computeEigenValue() {
+        DenseMatrix64F A = new DenseMatrix64F(3,3,
+                true, 0.053610, 0.030405, 0.892620, 0.090954, 0.074065, 0.875797, 0.105369, 0.928981, 0.965506);
+
+        DenseMatrix64F u = new DenseMatrix64F(3,1,
+                true, -0.4502917, -0.4655377, -0.7619134);
+
+        double value = EigenOps.computeEigenValue(A,u);
+
+        assertEquals(1.59540,value,1e-4);
+    }
+
+    /**
+     * Give it a matrix that describes a Markov process and see if it produces 1
+     */
+    @Test
+    public void boundLargestEigenValue_markov() {
+        // create the matrix
+        DenseMatrix64F A = RandomMatrices.createRandom(3,3,rand);
+
+        for( int i = 0; i < 3; i++ ) {
+            double total = 0;
+            for( int j = 0; j < 3; j++ ) {
+                total += A.get(i,j);
+            }
+
+            for( int j = 0; j < 3; j++ ) {
+                A.set(i,j,A.get(i,j)/total);
+            }
+        }
+
+        double[] val = EigenOps.boundLargestEigenValue(A,null);
+
+        assertEquals(1.0,val[0],1e-8);
+        assertEquals(1.0,val[1],1e-8);
+    }
+
+    @Test
+    public void createMatrixV() {
+        DenseMatrix64F A = RandomMatrices.createSymmetric(3,-1,1,rand);
+
+        EigenDecomposition<DenseMatrix64F> decomp = DecompositionFactory.eig(A.numRows,true);
+        assertTrue(decomp.decompose(A));
+
+        DenseMatrix64F V = EigenOps.createMatrixV(decomp);
+
+        for( int i = 0; i < 3; i++ ) {
+            DenseMatrix64F v = decomp.getEigenVector(i);
+
+            for( int j = 0; j < 3; j++ ) {
+                assertEquals(V.get(j,i),v.get(j),1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void createMatrixD() {
+        DenseMatrix64F A = RandomMatrices.createSymmetric(3,-1,1,rand);
+
+        EigenDecomposition<DenseMatrix64F> decomp = DecompositionFactory.eig(A.numRows,true);
+        assertTrue(decomp.decompose(A));
+
+        DenseMatrix64F D = EigenOps.createMatrixD(decomp);
+
+        for( int i = 0; i < 3; i++ ) {
+            Complex64F e = decomp.getEigenvalue(i);
+
+            if( e.isReal() ) {
+                assertEquals(e.real,D.get(i,i),1e-10);
+            }
+        }
+    }
+}
diff --git a/main/dense64/test/org/ejml/ops/TestMatrixFeatures.java b/main/dense64/test/org/ejml/ops/TestMatrixFeatures.java
new file mode 100644
index 0000000..1c7ea19
--- /dev/null
+++ b/main/dense64/test/org/ejml/ops/TestMatrixFeatures.java
@@ -0,0 +1,464 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.UtilEjml;
+import org.ejml.data.DenseMatrix64F;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.ejml.UtilEjml.parseMatrix;
+import static org.junit.Assert.*;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestMatrixFeatures {
+
+    Random rand = new Random(0xff24);
+
+    @Test
+    public void hasUncountable() {
+        DenseMatrix64F a = new DenseMatrix64F(4,4);
+
+        // check a negative case first
+        assertFalse(MatrixFeatures.hasUncountable(a));
+
+        // check two positve cases with different types of uncountables
+        a.set(2,2,Double.NaN);
+        assertTrue(MatrixFeatures.hasUncountable(a));
+
+        a.set(2,2,Double.POSITIVE_INFINITY);
+        assertTrue(MatrixFeatures.hasUncountable(a));
+    }
+
+    @Test
+    public void isZeros() {
+        DenseMatrix64F a = new DenseMatrix64F(4,4);
+        a.set(0,0,1);
+
+        assertFalse(MatrixFeatures.isZeros(a,0.1));
+        assertTrue(MatrixFeatures.isZeros(a,2));
+
+    }
+
+    @Test
+    public void isVector() {
+        DenseMatrix64F a = new DenseMatrix64F(4,4);
+
+        assertFalse(MatrixFeatures.isVector(a));
+
+        a.reshape(3,1, false);
+        assertTrue(MatrixFeatures.isVector(a));
+
+        a.reshape(1,3, false);
+        assertTrue(MatrixFeatures.isVector(a));
+    }
+
+    /**
+     * Check some trial cases.
+     */
+    @Test
+    public void isPositiveDefinite() {
+        DenseMatrix64F a = UtilEjml.parseMatrix("2 0 0 2",2);
+        DenseMatrix64F b = UtilEjml.parseMatrix("0 1 1 0",2);
+        DenseMatrix64F c = UtilEjml.parseMatrix("0 0 0 0",2);
+
+        assertTrue(MatrixFeatures.isPositiveDefinite(a));
+        assertFalse(MatrixFeatures.isPositiveDefinite(b));
+        assertFalse(MatrixFeatures.isPositiveDefinite(c));
+
+        // make sure the input isn't modified
+        assertEquals(2,a.get(0,0),1e-8);
+        assertEquals(2,a.get(1,1),1e-8);
+    }
+
+    @Test
+    public void isPositiveSemidefinite() {
+        DenseMatrix64F a = UtilEjml.parseMatrix("2 0 0 2",2);
+        DenseMatrix64F b = UtilEjml.parseMatrix("0 1 1 0",2);
+        DenseMatrix64F c = UtilEjml.parseMatrix("0 0 0 0",2);
+
+        assertTrue(MatrixFeatures.isPositiveSemidefinite(a));
+        assertFalse(MatrixFeatures.isPositiveSemidefinite(b));
+        assertTrue(MatrixFeatures.isPositiveSemidefinite(c));
+
+        // make sure the input isn't modified
+        assertEquals(2,a.get(0,0),1e-8);
+        assertEquals(2,a.get(1,1),1e-8);
+    }
+
+    @Test
+    public void isSquare() {
+        DenseMatrix64F a = new DenseMatrix64F(5,4);
+
+        assertFalse(MatrixFeatures.isSquare(a));
+
+        a.reshape(4,4, false);
+        assertTrue(MatrixFeatures.isSquare(a));
+    }
+
+    @Test
+    public void isDiagonalPositive() {
+        DenseMatrix64F m = CommonOps.identity(3);
+        assertTrue(MatrixFeatures.isDiagonalPositive(m));
+
+        m.set(1,1,-1);
+        assertFalse(MatrixFeatures.isDiagonalPositive(m));
+
+        m.set(1,1,Double.NaN);
+        assertFalse(MatrixFeatures.isDiagonalPositive(m));
+    }
+
+    @Test
+    public void isSymmetric() {
+        DenseMatrix64F m = CommonOps.identity(3);
+        m.set(1,2,5);m.set(2,1,5);
+        assertTrue(MatrixFeatures.isSymmetric(m));
+
+        m.set(1,2,50);
+        assertTrue(!MatrixFeatures.isSymmetric(m));
+
+        m.set(1,2,Double.NaN);
+        assertTrue(!MatrixFeatures.isSymmetric(m));
+    }
+
+    @Test
+    public void isSkewSymmetric() {
+        DenseMatrix64F m = CommonOps.identity(3);
+        m.set(1,2,5);m.set(2,1,-5);
+        assertTrue(MatrixFeatures.isSkewSymmetric(m,1e-8));
+
+        m.set(1,2,-5);
+        assertTrue(!MatrixFeatures.isSkewSymmetric(m,1e-8));
+
+        m.set(1,2,Double.NaN);
+        assertTrue(!MatrixFeatures.isSkewSymmetric(m,1e-8));
+    }
+
+
+    @Test
+    public void isEquals() {
+        String a = "-0.779094   1.682750   0.039239\n" +
+                 "   1.304014  -1.880739   1.438741\n" +
+                 "  -0.746918   1.382356  -0.520416";
+
+        DenseMatrix64F m = parseMatrix(a,3);
+        DenseMatrix64F n = parseMatrix(a,3);
+
+        assertTrue(MatrixFeatures.isEquals(m,n));
+
+        n.set(2,1,-0.5);
+        assertFalse(MatrixFeatures.isEquals(m,n));
+
+        m.set(2,1,Double.NaN);
+        n.set(2,1,Double.NaN);
+        assertFalse(MatrixFeatures.isEquals(m,n));
+        m.set(2,1,Double.POSITIVE_INFINITY);
+        n.set(2,1,Double.POSITIVE_INFINITY);
+        assertTrue(MatrixFeatures.isEquals(m,n));
+    }
+
+    @Test
+    public void isEquals_tol() {
+        String a = "-0.779094   1.682750   0.039239\n" +
+                 "   1.304014  -1.880739   1.438741\n" +
+                 "  -0.746918   1.382356  -0.520416";
+
+        DenseMatrix64F m = parseMatrix(a,3);
+        DenseMatrix64F n = parseMatrix(a,3);
+
+        assertTrue(MatrixFeatures.isEquals(m,n,1e-6));
+
+        n.set(2,1,n.get(2,1)+1e-25);
+        assertTrue(MatrixFeatures.isEquals(m,n,1e-6));
+
+        n.set(2,1,n.get(2,1)+1e-2);
+        assertFalse(MatrixFeatures.isEquals(m,n,1e-6));
+
+        m.set(2,1,Double.NaN);
+        n.set(2,1,Double.NaN);
+        assertFalse(MatrixFeatures.isEquals(m,n,1e-6));
+        m.set(2,1,Double.POSITIVE_INFINITY);
+        n.set(2,1,Double.POSITIVE_INFINITY);
+        assertFalse(MatrixFeatures.isEquals(m,n,1e-6));
+    }
+
+    @Test
+    public void isEqualsTriangle() {
+
+        // see if it works with different sized matrices
+        for( int m = 2; m < 10; m+=3) {
+            for( int n = 2; n < 10; n += 3 ) {
+                DenseMatrix64F a = RandomMatrices.createRandom(m,n,rand);
+                DenseMatrix64F b = a.copy();
+
+                // make the bottom triangle not the same
+                b.set(m-1,0,0);
+
+                assertTrue("m = "+m+" n = "+n,MatrixFeatures.isEqualsTriangle(a,b, true, 1e-8));
+                assertFalse(MatrixFeatures.isEqualsTriangle(a,b, false, 1e-8));
+
+                // make the upper triangle not the same
+                b = a.copy();
+                b.set(0,n-1,0);
+
+                assertFalse(MatrixFeatures.isEqualsTriangle(a,b, true, 1e-8));
+                assertTrue(MatrixFeatures.isEqualsTriangle(a,b, false, 1e-8));
+            }
+        }
+    }
+
+    @Test
+    public void isIdentical() {
+
+        double values[] = new double[]{1.0,Double.NaN,Double.POSITIVE_INFINITY,Double.NEGATIVE_INFINITY};
+
+        for( int i = 0; i < values.length; i++ ) {
+            for( int j = 0; j < values.length; j++ ) {
+                checkIdentical(values[i],values[j],1e-8,i==j);
+            }
+        }
+
+        checkIdentical(1.0,1.5,1e-8,false);
+        checkIdentical(1.5,1.0,1e-8,false);
+        checkIdentical(1.0,1.0000000001,1e-8,true);
+        checkIdentical(1.0,Double.NaN,1e-8,false);
+        checkIdentical(Double.NaN,1.0,1e-8,false);
+    }
+
+    private void checkIdentical( double valA , double valB , double tol , boolean expected ) {
+        DenseMatrix64F A = new DenseMatrix64F(2,2);
+        CommonOps.fill(A, valA);
+        DenseMatrix64F B = new DenseMatrix64F(2,2);
+        CommonOps.fill(B, valB);
+
+        assertEquals(expected,MatrixFeatures.isIdentical(A,B,tol));
+    }
+
+    @Test
+    public void isInverse() {
+        DenseMatrix64F A = RandomMatrices.createRandom(3,3,-1,1,rand);
+        DenseMatrix64F A_inv = A.copy();
+
+        CommonOps.invert(A_inv);
+
+        assertTrue(MatrixFeatures.isInverse(A,A_inv,1e-10));
+
+        A_inv.set(1,2,3);
+        assertFalse(MatrixFeatures.isInverse(A,A_inv,1e-10));
+
+        A_inv.set(1,2,Double.NaN);
+        assertFalse(MatrixFeatures.isInverse(A,A_inv,1e-10));
+    }
+
+    /**
+     * Makes sure it isn't modifying the input
+     */
+    @Test
+    public void isInverse_nomodify() {
+        DenseMatrix64F A = RandomMatrices.createRandom(3,3,-1,1,rand);
+        DenseMatrix64F B = RandomMatrices.createRandom(3,3,-1,1,rand);
+        DenseMatrix64F A_copy = A.copy();
+        DenseMatrix64F B_copy = B.copy();
+
+        MatrixFeatures.isInverse(A,B,1e-10);
+
+        assertTrue(MatrixFeatures.isIdentical(A,A_copy,1e-8));
+        assertTrue(MatrixFeatures.isIdentical(B,B_copy,1e-8));
+    }
+
+    @Test
+    public void hasNaN() {
+        DenseMatrix64F m = new DenseMatrix64F(3,3);
+        assertFalse(MatrixFeatures.hasNaN(m));
+
+        m.set(1,2,-Double.NaN);
+        assertTrue(MatrixFeatures.hasNaN(m));
+    }
+
+    @Test
+    public void isOrthogonal() {
+        // rotation matrices are orthogonal
+        double c = Math.cos(0.1);
+        double s = Math.sin(0.1);
+
+        DenseMatrix64F A = new DenseMatrix64F(new double[][]{{c,s},{-s,c}});
+
+        assertTrue(MatrixFeatures.isOrthogonal(A,1e-6f));
+
+        // try a negative case now
+        A.set(0,1,495);
+
+        assertFalse(MatrixFeatures.isOrthogonal(A,1e-6f));
+
+        A.set(0,1,Double.NaN);
+
+        assertFalse(MatrixFeatures.isOrthogonal(A,1e-6f));
+    }
+
+    @Test
+    public void isRowsLinearIndependent() {
+        // test a positive case
+        DenseMatrix64F A = new DenseMatrix64F(2,3, true, 1, 2, 3, 2, 3, 4);
+        assertTrue(MatrixFeatures.isRowsLinearIndependent(A));
+
+        // make sure the input wasn't modified
+        DenseMatrix64F A_copy = new DenseMatrix64F(2,3, true, 1, 2, 3, 2, 3, 4);
+        assertTrue(MatrixFeatures.isIdentical(A,A_copy,1e-8));
+
+        // test negative case
+        A = new DenseMatrix64F(2,3, true, 1, 2, 3, 1, 2, 3);
+        assertFalse(MatrixFeatures.isRowsLinearIndependent(A));
+    }
+
+    @Test
+    public void isConstantVal() {
+        DenseMatrix64F a = new DenseMatrix64F(3,4);
+
+        CommonOps.fill(a, 2.4);
+
+        assertTrue(MatrixFeatures.isConstantVal(a,2.4,1e-8));
+        assertFalse(MatrixFeatures.isConstantVal(a,6,1e-8));
+
+        a.set(1,1,Double.NaN);
+        assertFalse(MatrixFeatures.isConstantVal(a,2.4,1e-8));
+    }
+
+    @Test
+    public void isIdentity() {
+        DenseMatrix64F I = CommonOps.identity(4);
+
+        assertTrue(MatrixFeatures.isIdentity(I,1e-8));
+
+        I.set(3,2,0.1);
+        assertFalse(MatrixFeatures.isIdentity(I,1e-8));
+
+        I.set(3,2,Double.NaN);
+        assertFalse(MatrixFeatures.isIdentity(I,1e-8));
+    }
+
+    @Test
+    public void isNegative() {
+        DenseMatrix64F a = RandomMatrices.createRandom(4,5,rand);
+        DenseMatrix64F b = a.copy();
+        CommonOps.scale(-1,b);
+
+        // test the positive case first
+        assertTrue(MatrixFeatures.isNegative(a,b,1e-8));
+
+        // now the negative case
+        b.set(2,2,10);
+        assertFalse(MatrixFeatures.isNegative(a,b,1e-8));
+
+        b.set(2,2,Double.NaN);
+        assertFalse(MatrixFeatures.isNegative(a,b,1e-8));
+    }
+
+    @Test
+    public void isUpperTriangle() {
+        // test matrices that are upper triangular to various degree hessenberg
+        for( int hessenberg = 0; hessenberg < 2; hessenberg++ ) {
+            DenseMatrix64F A = new DenseMatrix64F(6,6);
+            for( int i = 0; i < A.numRows; i++ ) {
+                int s = i <= hessenberg ? 0 : i-hessenberg;
+
+                for( int j = s; j < A.numCols; j++ ) {
+                   A.set(i,j,2);
+                }
+            }
+
+            // test positive
+            for( int i = hessenberg; i < A.numRows; i++ ) {
+                assertTrue(MatrixFeatures.isUpperTriangle(A,i,1e-8));
+            }
+
+            // test negative
+            for( int i = 0; i < hessenberg; i++ ) {
+                assertFalse(MatrixFeatures.isUpperTriangle(A,i,1e-8));
+            }
+
+            // see if it handles NaN well
+            A.set(4,0,Double.NaN);
+            assertFalse(MatrixFeatures.isUpperTriangle(A,0,1e-8));
+        }
+    }
+
+    @Test
+    public void rank() {
+        DenseMatrix64F a = UtilEjml.parseMatrix("2 0 0 2",2);
+        DenseMatrix64F a_copy = a.copy();
+
+        assertEquals(2,MatrixFeatures.rank(a));
+        // make sure the input wasn't modified
+        assertTrue(MatrixFeatures.isIdentical(a,a_copy,1e-8));
+
+        a = UtilEjml.parseMatrix("2 0 0 0",2);
+        assertEquals(1,MatrixFeatures.rank(a));
+    }
+
+    @Test
+    public void rank_threshold() {
+        DenseMatrix64F a = UtilEjml.parseMatrix("2 0 0 2",2);
+        DenseMatrix64F a_copy = a.copy();
+
+        assertEquals(2,MatrixFeatures.rank(a,1e-14));
+        // make sure the input wasn't modified
+        assertTrue(MatrixFeatures.isIdentical(a,a_copy,1e-8));
+
+        a = UtilEjml.parseMatrix("2 0 0 1e-20",2);
+        assertEquals(1,MatrixFeatures.rank(a,1e-14));
+
+        // make sure it's using the threshold parameter
+        a = UtilEjml.parseMatrix("2 0 0 1e-20",2);
+        assertEquals(2,MatrixFeatures.rank(a,1e-200));
+    }
+
+    @Test
+    public void nullity() {
+        DenseMatrix64F a = UtilEjml.parseMatrix("2 0 0 2",2);
+        DenseMatrix64F a_copy = a.copy();
+
+        assertEquals(0,MatrixFeatures.nullity(a));
+        // make sure the input wasn't modified
+        assertTrue(MatrixFeatures.isIdentical(a,a_copy,1e-8));
+
+        a = UtilEjml.parseMatrix("2 0 0 0",2);
+        assertEquals(1,MatrixFeatures.nullity(a));
+    }
+
+    @Test
+    public void nullity_threshold() {
+        DenseMatrix64F a = UtilEjml.parseMatrix("2 0 0 2",2);
+        DenseMatrix64F a_copy = a.copy();
+
+        assertEquals(0,MatrixFeatures.nullity(a, 1e-14));
+        // make sure the input wasn't modified
+        assertTrue(MatrixFeatures.isIdentical(a,a_copy,1e-8));
+
+        a = UtilEjml.parseMatrix("2 0 0 1e-20",2);
+        assertEquals(1,MatrixFeatures.nullity(a, 1e-14));
+
+        // make sure it's using the threshold parameter
+        a = UtilEjml.parseMatrix("2 0 0 1e-20",2);
+        assertEquals(0,MatrixFeatures.nullity(a, 1e-200));
+    }
+}
diff --git a/main/dense64/test/org/ejml/ops/TestNormOps.java b/main/dense64/test/org/ejml/ops/TestNormOps.java
new file mode 100644
index 0000000..d83c0aa
--- /dev/null
+++ b/main/dense64/test/org/ejml/ops/TestNormOps.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.simple.SimpleMatrix;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestNormOps {
+
+    Random rand = new Random(234);
+
+    DenseMatrix64F zeroMatrix = new DenseMatrix64F(3,4);
+    DenseMatrix64F unzeroMatrix = new DenseMatrix64F(3,2, true, 0.2, 1, -2, 3, 6, 5);
+    DenseMatrix64F unzeroVector = new DenseMatrix64F(5,1, true, 0.3, 1, -2, 3, 4);
+    DenseMatrix64F squareMatrix = new DenseMatrix64F(2,2, true, 0.2, 1, -2, 3);
+
+
+    /**
+     * Tests against the condition number from octave.
+     */
+    @Test
+    public void conditionP() {
+        double val = NormOps.conditionP(squareMatrix,1);
+
+        assertEquals(7.6923,val,1e-3);
+
+        // check the non-square case
+        val = NormOps.conditionP(unzeroMatrix,1);
+
+        assertEquals(3.4325,val,1e-3);
+
+        // see if the other pseudo-inverse works
+        DenseMatrix64F trans = unzeroMatrix.copy();
+        CommonOps.transpose(trans);
+        val = NormOps.conditionP(trans,1);
+
+        assertEquals(3.4887,val,1e-3);
+    }
+
+    /**
+     * Tests against the condition number from octave.
+     */
+    @Test
+    public void conditionP2() {
+         double val = NormOps.conditionP2(unzeroMatrix);
+
+        assertEquals(2.1655,val,1e-3);
+
+        checkUncountable(NormOps.conditionP2(zeroMatrix));
+    }
+
+    /**
+     * Tested using the following operation in octave:
+     *
+     * sum(abs(a(:)).^3.5)^(1/3.5)
+     */
+    @Test
+    public void elementP() {
+         double val = NormOps.elementP(unzeroMatrix,3.5);
+
+        assertEquals(6.9108,val,1e-3);
+
+        checkUncountable(NormOps.elementP(zeroMatrix,3.5));
+    }
+
+    @Test
+    public void fastElementP() {
+         double val = NormOps.fastElementP(unzeroMatrix,3.5);
+
+        assertEquals(6.9108,val,1e-3);
+
+        checkUncountable(NormOps.fastElementP(zeroMatrix,3.5));
+    }
+
+    @Test
+    public void normalizeF() {
+        DenseMatrix64F a = unzeroVector.copy();
+
+        NormOps.normalizeF(a);
+
+        assertEquals(1,NormOps.normF(a),1e-6);
+    }
+
+    @Test
+    public void fastNormF() {
+        double val = NormOps.fastNormF(unzeroMatrix);
+
+        assertEquals(8.6626,val,1e-3);
+
+        checkUncountable(NormOps.fastNormF(zeroMatrix));
+    }
+
+    @Test
+    public void normF() {
+        double val = NormOps.normF(unzeroMatrix);
+
+        assertEquals(8.6626,val,1e-3);
+
+        checkUncountable(NormOps.normF(zeroMatrix));
+    }
+
+    @Test
+    public void fastNormP2() {
+        // check induced matrix norm
+        double found = NormOps.fastNormP2(unzeroMatrix);
+        double expected = NormOps.inducedP2(unzeroMatrix);
+        assertEquals(expected,found,1e-3);
+
+        // check vector norm
+        found = NormOps.fastNormP2(unzeroVector);
+        expected = NormOps.normF(unzeroVector);
+        assertEquals(expected,found,1e-3);
+    }
+
+    @Test
+    public void normP() {
+        // check induced matrix norm
+        double found = NormOps.normP(unzeroMatrix,2);
+        double expected = NormOps.inducedP2(unzeroMatrix);
+        assertEquals(expected,found,1e-3);
+
+        // check vector norm
+        found = NormOps.normP(unzeroVector,2);
+        expected = NormOps.normF(unzeroVector);
+        assertEquals(expected,found,1e-3);
+    }
+
+    @Test
+    public void fastNormP() {
+        // check induced matrix norm
+        double found = NormOps.fastNormP(unzeroMatrix,2);
+        double expected = NormOps.inducedP2(unzeroMatrix);
+        assertEquals(expected,found,1e-3);
+
+        // check vector norm
+        found = NormOps.fastNormP(unzeroVector,2);
+        expected = NormOps.normF(unzeroVector);
+        assertEquals(expected,found,1e-3);
+    }
+
+    @Test
+    public void normP1() {
+        // check induced matrix norm
+        double found = NormOps.normP1(unzeroMatrix);
+        double expected = NormOps.inducedP1(unzeroMatrix);
+        assertEquals(expected,found,1e-3);
+
+        // check vector norm
+        found = NormOps.normP1(unzeroVector);
+        expected = CommonOps.elementSumAbs(unzeroVector);
+        assertEquals(expected,found,1e-3);
+    }
+
+    @Test
+    public void normP2() {
+        // check induced matrix norm
+        double found = NormOps.normP2(unzeroMatrix);
+        double expected = NormOps.inducedP2(unzeroMatrix);
+        assertEquals(expected,found,1e-3);
+
+        // check vector norm
+        found = NormOps.normP2(unzeroVector);
+        expected = NormOps.normF(unzeroVector);
+        assertEquals(expected,found,1e-3);
+    }
+
+    @Test
+    public void normPInf() {
+        // check induced matrix norm
+        double found = NormOps.normPInf(unzeroMatrix);
+        double expected = NormOps.inducedPInf(unzeroMatrix);
+        assertEquals(expected,found,1e-3);
+
+        // check vector norm
+        found = NormOps.normPInf(unzeroVector);
+        expected = CommonOps.elementMaxAbs(unzeroVector);
+        assertEquals(expected,found,1e-3);
+    }
+
+    @Test
+    public void inducedP1() {
+        double val = NormOps.inducedP1(unzeroMatrix);
+        assertEquals(9,val,1e-3);
+
+        checkUncountable(NormOps.inducedP1(zeroMatrix));
+    }
+
+    @Test
+    public void inducedP2() {
+        double val = NormOps.inducedP2(unzeroMatrix);
+        assertEquals(7.8645,val,1e-3);
+
+        checkUncountable(NormOps.inducedP2(zeroMatrix));
+
+        // make sure the largest singular value is being returned not just the first
+        for( int i = 0; i < 20; i++ ) {
+            SimpleMatrix A = SimpleMatrix.random(5,5,-10,10,rand);
+            double largest = A.svd().getW().get(0);
+
+            assertEquals(largest,NormOps.inducedP2(A.getMatrix()),1e-8);
+        }
+    }
+
+    @Test
+    public void inducedPInf() {
+        double val = NormOps.inducedPInf(unzeroMatrix);
+        assertEquals(11,val,1e-3);
+
+        checkUncountable(NormOps.inducedPInf(zeroMatrix));
+    }
+
+    private static void checkUncountable( double val ) {
+        assertFalse(Double.isInfinite(val));
+        assertFalse(Double.isNaN(val));
+    }
+}
diff --git a/main/dense64/test/org/ejml/ops/TestRandomMatrices.java b/main/dense64/test/org/ejml/ops/TestRandomMatrices.java
new file mode 100644
index 0000000..e458010
--- /dev/null
+++ b/main/dense64/test/org/ejml/ops/TestRandomMatrices.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.alg.dense.mult.VectorVectorMult;
+import org.ejml.data.Complex64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.data.UtilTestMatrix;
+import org.ejml.factory.DecompositionFactory;
+import org.ejml.interfaces.decomposition.EigenDecomposition;
+import org.ejml.interfaces.decomposition.SingularValueDecomposition;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestRandomMatrices {
+
+    Random rand = new Random(48757);
+
+    /**
+     * Checks to see if all the vectors are orthogonal and of unit length.
+     */
+    @Test
+    public void createSpan() {
+        // test with combinations of vectors and numbers
+        for( int dimension = 3; dimension <= 5; dimension++ ) {
+            for( int numVectors = 1; numVectors <= dimension; numVectors++ ) {
+                DenseMatrix64F span[] = RandomMatrices.createSpan(dimension,numVectors,rand);
+
+                assertEquals(numVectors,span.length);
+
+                for( int i = 0; i < span.length; i++ ) {
+                    DenseMatrix64F a = span[i];
+
+                    assertEquals(1,NormOps.fastNormF(a),1e-8);
+
+                    for( int j = i+1; j < span.length; j++ ) {
+                        double dot = VectorVectorMult.innerProd(a,span[j]);
+                        assertEquals(0,dot,1e-8);
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    public void createInSpan() {
+        DenseMatrix64F span[] = RandomMatrices.createSpan(5,5,rand);
+
+        DenseMatrix64F A = RandomMatrices.createInSpan(span,-1,1,rand);
+
+        // reconstructed matrix
+        DenseMatrix64F R = new DenseMatrix64F(A.numRows,A.numCols);
+
+        DenseMatrix64F tmp = new DenseMatrix64F(A.numRows,A.numCols);
+
+        // project the matrix into the span and recreate the original matrix
+        for( int i = 0; i < 5; i++ ) {
+            double val =  VectorVectorMult.innerProd(span[i],A);
+            assertTrue( Math.abs(val) > 1e-10 );
+
+            CommonOps.scale(val,span[i],tmp);
+            CommonOps.add(R,tmp,R);
+        }
+
+        double error = SpecializedOps.diffNormF(A,R);
+
+        assertEquals( error , 0 , 1e-8 );
+    }
+
+    @Test
+    public void createOrthogonal() {
+        for( int numRows = 3; numRows <= 5; numRows++ ) {
+            for( int numCols = 1; numCols <= numRows; numCols++ ) {
+                DenseMatrix64F Q = RandomMatrices.createOrthogonal(numRows,numCols,rand);
+
+                assertEquals(Q.numRows,numRows);
+                assertEquals(Q.numCols,numCols);
+
+                assertTrue(CommonOps.elementSum(Q) != 0);
+                assertTrue(MatrixFeatures.isOrthogonal(Q,1e-8));
+            }
+        }
+    }
+
+    @Test
+    public void createDiagonal_square() {
+        DenseMatrix64F A = RandomMatrices.createDiagonal(5,1,10,rand);
+
+        assertTrue(CommonOps.elementSum(A) > 5 );
+        for( int i = 0; i < A.numRows; i++ ) {
+            for( int j = 0; j < A.numCols; j++ ) {
+                double v = A.get(i,j);
+
+                if( i == j ) {
+                    assertTrue(v >= 1 || v <= 10);
+                } else {
+                    assertTrue(v == 0);
+                }
+            }
+        }
+    }
+
+    @Test
+    public void createDiagonal_general() {
+        testDiagonal(5,3);
+        testDiagonal(3,5);
+        testDiagonal(3,3);
+    }
+
+    public void testDiagonal( int numRows , int numCols ) {
+        DenseMatrix64F A = RandomMatrices.createDiagonal(numRows,numCols,1,10,rand);
+
+        assertEquals(A.getNumRows(),numRows);
+        assertEquals(A.getNumCols(),numCols);
+
+        assertTrue(CommonOps.elementSum(A) > 5 );
+        for( int i = 0; i < A.numRows; i++ ) {
+            for( int j = 0; j < A.numCols; j++ ) {
+                double v = A.get(i,j);
+
+                if( i == j ) {
+                    assertTrue(v >= 1 || v <= 10);
+                } else {
+                    assertTrue(v == 0);
+                }
+            }
+        }
+    }
+
+    @Test
+    public void createSingularValues() {
+        // check case when sv is more than or equal to the matrix dimension
+        double sv[] = new double[]{8.2,6.2,4.1,2};
+
+        for( int numRows = 1; numRows <= 4; numRows++ ) {
+            for( int numCols = 1; numCols <= 4; numCols++ ) {
+                DenseMatrix64F A = RandomMatrices.createSingularValues(numRows,numCols, rand, sv);
+
+                SingularValueDecomposition<DenseMatrix64F> svd =
+                        DecompositionFactory.svd(A.numRows,A.numCols,true,true,false);
+                assertTrue(svd.decompose(A));
+
+                int o = Math.min(numRows,numCols);
+
+                UtilTestMatrix.checkSameElements(1e-8,o,sv,svd.getSingularValues());
+            }
+        }
+
+        // see if it fills in zeros when it is smaller than the dimension
+        DenseMatrix64F A = RandomMatrices.createSingularValues(5,5, rand, sv);
+
+        SingularValueDecomposition<DenseMatrix64F> svd =
+                DecompositionFactory.svd(A.numRows,A.numCols,true,true,false);
+        assertTrue(svd.decompose(A));
+
+        UtilTestMatrix.checkSameElements(1e-8,sv.length,sv,svd.getSingularValues());
+        assertEquals(0,svd.getSingularValues()[4],1e-8);
+    }
+
+    @Test
+    public void createEigenvaluesSymm() {
+        DenseMatrix64F A = RandomMatrices.createEigenvaluesSymm(5,rand,1,2,3,4,5);
+
+        // this should be symmetric
+        assertTrue(MatrixFeatures.isSymmetric(A,1e-10));
+
+        // decompose the matrix and extract its eigenvalues
+        EigenDecomposition<DenseMatrix64F> eig = DecompositionFactory.eig(A.numRows,true);
+        assertTrue(eig.decompose(A));
+
+        double ev[] = new double[5];
+        for( int i = 0; i < 5; i++ ) {
+            Complex64F e = eig.getEigenvalue(i);
+            assertTrue(e.isReal());
+
+            ev[i] = e.real;
+        }
+
+        // need to sort the eigenvalues so that I know where they are in the array
+        Arrays.sort(ev);
+
+        // see if they are what I expected them to be
+        for( int i = 0; i < ev.length; i++ ) {
+            assertEquals(i+1.0,ev[i],1e-8);
+        }
+    }
+
+    @Test
+    public void addRandom() {
+        DenseMatrix64F A = new DenseMatrix64F(3,4);
+
+        CommonOps.fill(A, -2.0);
+
+        RandomMatrices.addRandom(A,1,2,rand);
+
+        for( int i = 0; i < A.getNumElements(); i++ ) {
+            assertTrue(A.get(i) >= -1 && A.get(i) <= 0 );
+        }
+    }
+
+    @Test
+    public void createRandom() {
+        DenseMatrix64F A = RandomMatrices.createRandom(5,4,rand);
+
+        checkRandom1(A);
+    }
+
+    @Test
+    public void createRandom_min_max() {
+        DenseMatrix64F A = RandomMatrices.createRandom(30,20,-1,1,rand);
+
+        checkRandomRange(A);
+    }
+
+    @Test
+    public void setRandom() {
+        DenseMatrix64F A = new DenseMatrix64F(5,4);
+
+        RandomMatrices.setRandom(A,rand);
+
+        checkRandom1(A);
+    }
+
+    private void checkRandom1(DenseMatrix64F a) {
+        assertEquals(5, a.numRows);
+        assertEquals(4, a.numCols);
+
+        double total = 0;
+        for( int i = 0; i < a.numRows; i++ ) {
+            for( int j = 0; j < a.numCols; j++ ) {
+                double val = a.get(i,j);
+
+                assertTrue( val >= 0);
+                assertTrue( val <= 1);
+                total += val;
+            }
+        }
+
+        assertTrue(total>0);
+    }
+
+    @Test
+    public void setRandom_min_max() {
+        DenseMatrix64F A = new DenseMatrix64F(30,20);
+        RandomMatrices.setRandom(A,-1,1,rand);
+
+        checkRandomRange(A);
+    }
+
+    private void checkRandomRange(DenseMatrix64F a) {
+        assertEquals(30, a.numRows);
+        assertEquals(20, a.numCols);
+
+        int numNeg = 0;
+        int numPos = 0;
+        for( int i = 0; i < a.numRows; i++ ) {
+            for( int j = 0; j < a.numCols; j++ ) {
+                double val = a.get(i,j);
+
+                if( val < 0 )
+                    numNeg++;
+                else
+                    numPos++;
+
+                if( Math.abs(val) > 1 )
+                    fail("Out of range");
+            }
+        }
+
+        assertTrue(numNeg>0);
+        assertTrue(numPos>0);
+    }
+
+    @Test
+    public void createGaussian() {
+        DenseMatrix64F A = RandomMatrices.createGaussian(30, 20, 2, 0.5, rand);
+
+        checkGaussian(A);
+    }
+
+    @Test
+    public void setGaussian() {
+        DenseMatrix64F A = new DenseMatrix64F(30,20);
+
+        RandomMatrices.setGaussian(A, 2, 0.5, rand);
+
+        checkGaussian(A);
+    }
+
+    private void checkGaussian(DenseMatrix64F a) {
+        assertEquals(30, a.numRows);
+        assertEquals(20, a.numCols);
+
+        int numNeg = 0;
+        int numPos = 0;
+        double mean = 0;
+        for( int i = 0; i < a.numRows; i++ ) {
+            for( int j = 0; j < a.numCols; j++ ) {
+                double val = a.get(i,j);
+
+                if( val-2 < 0 )
+                    numNeg++;
+                else
+                    numPos++;
+
+                mean += val;
+            }
+        }
+
+        mean /= a.numRows*a.numCols;
+
+        assertEquals(mean,2,0.01);
+
+        assertTrue(numNeg>0);
+        assertTrue(numPos>0);
+    }
+
+    @Test
+    public void createSymmPosDef() {
+        for( int i = 0; i < 10; i++ ) {
+            DenseMatrix64F A = RandomMatrices.createSymmPosDef(6+i,rand);
+
+            assertTrue(MatrixFeatures.isPositiveDefinite(A));
+        }
+    }
+
+    @Test
+    public void createSymmetric() {
+        DenseMatrix64F A = RandomMatrices.createSymmetric(10,-1,1,rand);
+
+        assertTrue(MatrixFeatures.isSymmetric(A,1e-8));
+
+        // see if it has the expected range of elements
+        double min = CommonOps.elementMin(A);
+        double max = CommonOps.elementMax(A);
+
+        assertTrue(min < 0 && min >= -1);
+        assertTrue(max > 0 && max <= 1);
+    }
+
+    @Test
+    public void createUpperTriangle() {
+        for( int hess = 0; hess < 3; hess++ ) {
+            DenseMatrix64F A = RandomMatrices.createUpperTriangle(10,hess,-1,1,rand);
+
+            assertTrue(MatrixFeatures.isUpperTriangle(A,hess,1e-8));
+
+            // quick sanity check to make sure it could be proper
+            assertTrue(A.get(hess,0) != 0 );
+
+            // see if it has the expected range of elements
+            double min = CommonOps.elementMin(A);
+            double max = CommonOps.elementMax(A);
+
+            assertTrue(min < 0 && min >= -1);
+            assertTrue(max > 0 && max <= 1);
+        }
+    }
+}
diff --git a/main/dense64/test/org/ejml/ops/TestSingularOps.java b/main/dense64/test/org/ejml/ops/TestSingularOps.java
new file mode 100644
index 0000000..000e4d6
--- /dev/null
+++ b/main/dense64/test/org/ejml/ops/TestSingularOps.java
@@ -0,0 +1,418 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.UtilEjml;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.DecompositionFactory;
+import org.ejml.interfaces.decomposition.SingularValueDecomposition;
+import org.ejml.simple.SimpleMatrix;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestSingularOps {
+
+    Random rand = new Random(234234);
+
+    @Test
+    public void descendingOrder() {
+        // test different shapes of input matrices
+        testDescendingOrder(3, 4, false,false);
+        testDescendingOrder(4, 3, false,false);
+        testDescendingOrder(3, 4, true,false);
+        testDescendingOrder(4, 3, true,false);
+
+        testDescendingInputTransposed(4,5,true,true,false);
+    }
+
+
+    @Test
+    public void descendingOrder_array() {
+        // test different shapes of input matrices
+        testDescendingOrder(3, 4, false, true);
+        testDescendingOrder(4, 3, false, true);
+        testDescendingOrder(3, 4, true, true);
+        testDescendingOrder(4, 3, true, true);
+
+        testDescendingInputTransposed(4,5,true,true,true);
+    }
+
+    /**
+     * Creates a random SVD that is highly unlikely to be in the correct order.  Adjust its order
+     * and see if it produces the same matrix.
+     */
+    private void testDescendingOrder(int numRows, int numCols, boolean compact, boolean testArray ) {
+        SimpleMatrix U,W,V;
+
+        int minLength = Math.min(numRows,numCols);
+        double singularValues[] = new double[minLength];
+
+        if( compact ) {
+            U = SimpleMatrix.wrap(RandomMatrices.createOrthogonal(numRows,minLength,rand));
+            W = SimpleMatrix.wrap(RandomMatrices.createDiagonal(minLength,minLength,0,1,rand));
+            V = SimpleMatrix.wrap(RandomMatrices.createOrthogonal(numCols,minLength,rand));
+        } else {
+            U = SimpleMatrix.wrap(RandomMatrices.createOrthogonal(numRows,numRows,rand));
+            W = SimpleMatrix.wrap(RandomMatrices.createDiagonal(numRows,numCols,0,1,rand));
+            V = SimpleMatrix.wrap(RandomMatrices.createOrthogonal(numCols,numCols,rand));
+        }
+
+        // Compute A
+        SimpleMatrix A=U.mult(W).mult(V.transpose());
+
+        // extract array of singular values
+        for( int i = 0; i < singularValues.length; i++ )
+            singularValues[i] = W.get(i,i);
+        
+        // put into descending order
+        if( testArray ) {
+            SingularOps.descendingOrder(U.getMatrix(),false,singularValues,minLength,V.getMatrix(),false);
+            // put back into W
+            for( int i = 0; i < singularValues.length; i++ )
+                W.set(i,i,singularValues[i]);
+        } else {
+            SingularOps.descendingOrder(U.getMatrix(),false,W.getMatrix(),V.getMatrix(),false);
+        }
+
+        // see if it changed the results
+        SimpleMatrix A_found = U.mult(W).mult(V.transpose());
+
+        assertTrue(A.isIdentical(A_found,1e-8));
+
+        // make sure singular values are descending
+        if( testArray ) {
+            for( int i = 1; i < minLength; i++ ) {
+                assertTrue(singularValues[i-1] >= singularValues[i]);
+            }
+        } else {
+            for( int i = 1; i < minLength; i++ ) {
+                assertTrue(W.get(i-1,i-1) >= W.get(i,i));
+            }
+        }
+    }
+
+    /**
+     * Use the transpose flags and see what happens
+     */
+    private void testDescendingInputTransposed(int numRows, int numCols,
+                                               boolean tranU , boolean tranV , boolean testArray ) {
+        SimpleMatrix U,S,V;
+
+        int minLength = Math.min(numRows,numCols);
+        double singularValues[] = new double[minLength];
+
+        U = SimpleMatrix.wrap(RandomMatrices.createOrthogonal(numRows,minLength,rand));
+        S = SimpleMatrix.wrap(RandomMatrices.createDiagonal(minLength,minLength,0,1,rand));
+        V = SimpleMatrix.wrap(RandomMatrices.createOrthogonal(numCols,minLength,rand));
+
+        // Compute A
+        SimpleMatrix A=U.mult(S).mult(V.transpose());
+
+        // extract array of singular values
+        for( int i = 0; i < singularValues.length; i++ )
+            singularValues[i] = S.get(i,i);
+
+        // put into ascending order
+        if( tranU ) U = U.transpose();
+        if( tranV ) V = V.transpose();
+
+        // put into descending order
+        if( testArray ) {
+            SingularOps.descendingOrder(U.getMatrix(),tranU,singularValues,minLength,V.getMatrix(),tranV);
+            // put back into S
+            for( int i = 0; i < singularValues.length; i++ )
+                S.set(i,i,singularValues[i]);
+        } else {
+            SingularOps.descendingOrder(U.getMatrix(),tranU,S.getMatrix(),V.getMatrix(),tranV);
+        }
+
+        // see if it changed the results
+        if( tranU ) U = U.transpose();
+        if( tranV ) V = V.transpose();
+        SimpleMatrix A_found = U.mult(S).mult(V.transpose());
+
+        assertTrue(A.isIdentical(A_found,1e-8));
+
+        // make sure singular values are descending
+        if( testArray ) {
+            for( int i = 1; i < minLength; i++ ) {
+                assertTrue(singularValues[i-1] >= singularValues[i]);
+            }
+        } else {
+            for( int i = 1; i < minLength; i++ ) {
+                assertTrue(S.get(i-1,i-1) >= S.get(i,i));
+            }
+        }
+    }
+
+    /**
+     * See if it blows up with uncountable numbers
+     */
+    @Test
+    public void descendingOrder_NaN() {
+        int numRows = 5;
+        int numCols = 7;
+        int minLength = Math.min(numRows,numCols);
+
+        SimpleMatrix U,S,V;
+
+        U = SimpleMatrix.wrap(RandomMatrices.createOrthogonal(numRows,minLength,rand));
+        S = SimpleMatrix.wrap(RandomMatrices.createDiagonal(minLength,minLength,0,1,rand));
+        V = SimpleMatrix.wrap(RandomMatrices.createOrthogonal(numCols,minLength,rand));
+
+        // put in a NaN
+        S.set(2,2,Double.NaN);
+
+        SingularOps.descendingOrder(U.getMatrix(),false,S.getMatrix(),V.getMatrix(),false);
+
+        assertTrue( Double.isNaN(S.get(minLength-1,minLength-1)));
+
+        // put in an Inf
+        S.set(2,2,Double.POSITIVE_INFINITY);
+
+        SingularOps.descendingOrder(U.getMatrix(),false,S.getMatrix(),V.getMatrix(),false);
+
+        assertTrue( Double.isInfinite(S.get(0,0)));
+    }
+
+
+    /**
+     * Gives it correct input matrices and makes sure no exceptions are thrown.  All permutations
+     * are tested.
+     */
+    @Test
+    public void checkSvdMatrixSize_positive() {
+        checkSvdMatrixSize_positive(4,5);
+        checkSvdMatrixSize_positive(5,4);
+    }
+
+    /**
+     * Checks a few of the many possible bad inputs
+     */
+    @Test
+    public void checkSvdMatrixSize_negative() {
+        checkSvdMatrixSize_negative(4,5);
+        checkSvdMatrixSize_negative(5,4);
+    }
+
+    private void checkSvdMatrixSize_positive( int numRows , int numCols )
+    {
+        int s = Math.min(numRows,numCols);
+
+        // create a none compact SVD
+        DenseMatrix64F U = new DenseMatrix64F(numRows,numRows);
+        DenseMatrix64F W = new DenseMatrix64F(numRows,numCols);
+        DenseMatrix64F V = new DenseMatrix64F(numCols,numCols);
+
+        SingularOps.checkSvdMatrixSize(U,false,W,V,false);
+        CommonOps.transpose(U);CommonOps.transpose(V);
+        SingularOps.checkSvdMatrixSize(U,true,W,V,true);
+
+        // compact SVD
+        U = new DenseMatrix64F(numRows,s);
+        W = new DenseMatrix64F(s,s);
+        V = new DenseMatrix64F(numCols,s);
+
+        SingularOps.checkSvdMatrixSize(U,false,W,V,false);
+        CommonOps.transpose(U);CommonOps.transpose(V);
+        SingularOps.checkSvdMatrixSize(U,true,W,V,true);
+
+        // see what happens if you throw in some null matrices
+        SingularOps.checkSvdMatrixSize(null,false,W,null,false);
+        SingularOps.checkSvdMatrixSize(null,true,W,V,true);
+        SingularOps.checkSvdMatrixSize(U,true,W,null,true);
+    }
+
+    private void checkSvdMatrixSize_negative( int numRows , int numCols )
+    {
+        int s = Math.min(numRows,numCols);
+
+        // create a none compact SVD
+        DenseMatrix64F U = new DenseMatrix64F(numRows,s);
+        DenseMatrix64F W = new DenseMatrix64F(numRows,numCols);
+        DenseMatrix64F V = new DenseMatrix64F(numCols,s);
+
+        try {
+            SingularOps.checkSvdMatrixSize(U,false,W,V,false);
+            fail("An exception should have been thrown");
+        } catch( RuntimeException e) {}
+
+
+        // compact SVD
+        U = new DenseMatrix64F(numRows,s);
+        W = new DenseMatrix64F(s,s);
+        V = new DenseMatrix64F(numCols,s);
+
+        try {
+            SingularOps.checkSvdMatrixSize(U,true,W,V,true);
+            fail("An exception should have been thrown");
+        } catch( RuntimeException e) {}
+        CommonOps.transpose(U);CommonOps.transpose(V);
+        try {
+            SingularOps.checkSvdMatrixSize(U,false,W,V,false);
+            fail("An exception should have been thrown");
+        } catch( RuntimeException e) {}
+    }
+
+    @Test
+    public void nullVector() {
+        for( int numRows = 2; numRows < 10; numRows++ ) {
+            for( int numCols = 2; numCols < 10; numCols++ ) {
+                // construct a matrix with a null space by decomposition a random matrix
+                // and setting one of its singular values to zero
+                SimpleMatrix A = SimpleMatrix.wrap(RandomMatrices.createRandom(numRows,numCols,rand));
+
+                SingularValueDecomposition<DenseMatrix64F> svd = DecompositionFactory.svd(A.numRows(), A.numCols(),true,true,false);
+                assertTrue(svd.decompose(A.getMatrix()));
+
+                SimpleMatrix U = SimpleMatrix.wrap(svd.getU(null,false));
+                SimpleMatrix S = SimpleMatrix.wrap(svd.getW(null));
+                SimpleMatrix Vt = SimpleMatrix.wrap(svd.getV(null,true));
+
+                // pick an element inconveniently in the middle to be the null space
+                S.set(1,1,0);
+                svd.getSingularValues()[1] = 0;
+
+                A=U.mult(S).mult(Vt);
+
+                // Find the right null space
+                SimpleMatrix v = SimpleMatrix.wrap(SingularOps.nullVector(svd, true , null));
+
+                // see if the returned vector really is the null space
+                SimpleMatrix ns = A.mult(v);
+
+                for( int i = 0; i < ns.numRows(); i++ ) {
+                    assertEquals(0,ns.get(i),1e-8);
+                }
+
+                // Find the left null space
+                v = SimpleMatrix.wrap(SingularOps.nullVector(svd, false , null));
+
+                // see if the returned vector really is the null space
+                ns = v.transpose().mult(A);
+
+                for( int i = 0; i < ns.numRows(); i++ ) {
+                    assertEquals(0,ns.get(i),1e-8);
+                }
+            }
+        }
+    }
+
+    @Test
+    public void nullSpace() {
+        for( int numRows = 2; numRows < 5; numRows++ ) {
+            for( int numCols = 2; numCols < 5; numCols++ ) {
+
+                // construct a matrix with a null space by decomposition a random matrix
+                // and setting one of its singular values to zero
+                SimpleMatrix A = SimpleMatrix.wrap(RandomMatrices.createRandom(numRows,numCols,rand));
+
+                SingularValueDecomposition<DenseMatrix64F> svd = DecompositionFactory.svd(A.numRows(), A.numCols(),true,true,false);
+                assertTrue(svd.decompose(A.getMatrix()));
+
+                SimpleMatrix U = SimpleMatrix.wrap(svd.getU(null,false));
+                SimpleMatrix S = SimpleMatrix.wrap(svd.getW(null));
+                SimpleMatrix Vt = SimpleMatrix.wrap(svd.getV(null,true));
+
+                // pick an element inconveniently in the middle to be the null space
+                S.set(1,1,0);
+                svd.getSingularValues()[1] = 0;
+
+                A=U.mult(S).mult(Vt);
+
+                // now find the null space
+                SimpleMatrix ns = SimpleMatrix.wrap(SingularOps.nullSpace(svd,null,1e-15));
+
+                // make sure the null space is not all zero
+                assertTrue( Math.abs(CommonOps.elementMaxAbs(ns.getMatrix())) > 0 );
+
+                // check the null space's size
+                assertEquals(ns.numRows(),A.numCols());
+                assertEquals(ns.numCols(),1+Math.max(numCols-numRows,0));
+
+                // see if the results are null
+                SimpleMatrix found = A.mult(ns);
+                assertTrue( Math.abs(CommonOps.elementMaxAbs(found.getMatrix())) <= 1e-15 );
+            }
+        }
+    }
+
+    /**
+     * Decompose a singular matrix and see if it produces the expected result
+     */
+    @Test
+    public void rank_and_nullity(){
+        DenseMatrix64F A = new DenseMatrix64F(3,3, true,
+                -0.988228951897092, -1.086594333683141, -1.433160736952583,
+                -3.190200029661606, 0.190459703263404, -6.475629910954768,
+                1.400596416735888, 7.158603907761226, -0.778109120408813);
+        rank_and_nullity(A,2,1);
+
+        //wide matrix
+        A = new DenseMatrix64F(1,3,true,1,0,0);
+        rank_and_nullity(A,1,2);
+
+        // tall matrix
+        A = new DenseMatrix64F(3,1,true,1,0,0);
+        rank_and_nullity(A,1,0);
+    }
+
+    public void rank_and_nullity( DenseMatrix64F A , int rank , int nullity ) {
+        SingularValueDecomposition<DenseMatrix64F> alg = DecompositionFactory.svd(A.numRows,A.numCols,true,true,false);
+        assertTrue(alg.decompose(A));
+
+        assertEquals(rank,SingularOps.rank(alg, UtilEjml.EPS));
+        assertEquals(nullity,SingularOps.nullity(alg, UtilEjml.EPS));
+    }
+
+    /**
+     * Decompose a singular matrix and see if it produces the expected result
+     */
+    @Test
+    public void rank_and_nullity_noArgument(){
+        DenseMatrix64F A = new DenseMatrix64F(3,3, true,
+                -0.988228951897092, -1.086594333683141, -1.433160736952583,
+                -3.190200029661606, 0.190459703263404, -6.475629910954768,
+                1.400596416735888, 7.158603907761226, -0.778109120408813);
+        rank_and_nullity_noArgument(A, 2, 1);
+
+        //wide matrix
+        A = new DenseMatrix64F(1,3,true,1,0,0);
+        rank_and_nullity_noArgument(A,1,2);
+
+        // tall matrix
+        A = new DenseMatrix64F(3,1,true,1,0,0);
+        rank_and_nullity_noArgument(A,1,0);
+    }
+
+    public void rank_and_nullity_noArgument( DenseMatrix64F A , int rank , int nullity ) {
+        SingularValueDecomposition<DenseMatrix64F> alg = DecompositionFactory.svd(A.numRows,A.numCols,true,true,false);
+        assertTrue(alg.decompose(A));
+
+        assertEquals(rank,SingularOps.rank(alg));
+        assertEquals(nullity,SingularOps.nullity(alg));
+    }
+}
diff --git a/main/dense64/test/org/ejml/ops/TestSpecializedOps.java b/main/dense64/test/org/ejml/ops/TestSpecializedOps.java
new file mode 100644
index 0000000..42631ec
--- /dev/null
+++ b/main/dense64/test/org/ejml/ops/TestSpecializedOps.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.alg.dense.mult.VectorVectorMult;
+import org.ejml.data.DenseMatrix64F;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestSpecializedOps {
+    Random rand = new Random(7476547);
+
+    @Test
+    public void createReflector() {
+        DenseMatrix64F u = RandomMatrices.createRandom(4,1,rand);
+
+        DenseMatrix64F Q = SpecializedOps.createReflector(u);
+
+        assertTrue(MatrixFeatures.isOrthogonal(Q,1e-8));
+
+        DenseMatrix64F w = new DenseMatrix64F(4,1);
+
+        CommonOps.mult(Q,u,w);
+
+        assertTrue(MatrixFeatures.isNegative(u,w,1e-8));
+    }
+
+    @Test
+    public void createReflector_gamma() {
+        DenseMatrix64F u = RandomMatrices.createRandom(4,1,rand);
+        double gamma = 1.5;
+        DenseMatrix64F Q = SpecializedOps.createReflector(u,gamma);
+
+        DenseMatrix64F w = RandomMatrices.createRandom(4,1,rand);
+
+        DenseMatrix64F v_found = new DenseMatrix64F(4,1);
+        CommonOps.mult(Q,w,v_found);
+
+        DenseMatrix64F v_exp = new DenseMatrix64F(4,1);
+
+        VectorVectorMult.householder(-gamma,u,w,v_exp);
+
+        assertTrue(MatrixFeatures.isIdentical(v_found,v_exp,1e-8));
+    }
+
+    @Test
+    public void copyChangeRow() {
+        DenseMatrix64F A = RandomMatrices.createRandom(3,2,rand);
+
+        DenseMatrix64F B = A.copy();
+
+        int []order = new int[]{2,0,1};
+
+        DenseMatrix64F C = SpecializedOps.copyChangeRow(order,A,null);
+
+        // make sure it didn't modify A
+        assertTrue(MatrixFeatures.isIdentical(A,B,0));
+
+        // see if the row change was correctly performed
+        for( int i = 0; i < order.length; i++ ) {
+            int o = order[i];
+
+            for( int j = 0; j < A.numCols; j++ ) {
+                assertEquals(A.get(o,j),C.get(i,j),1e-16);
+            }
+        }
+    }
+
+    @Test
+    public void copyTriangle() {
+
+        for( int m = 2; m <= 6; m += 2 ) {
+            for( int n = 2; n <= 6; n += 2 ) {
+                DenseMatrix64F A = RandomMatrices.createRandom(m,n,rand);
+
+                DenseMatrix64F B = SpecializedOps.copyTriangle(A,null,true);
+
+                assertTrue(MatrixFeatures.isEqualsTriangle(A,B, true, 1e-8));
+                assertFalse(MatrixFeatures.isEquals(A,B,1e-8));
+
+                CommonOps.fill(B, 0);
+                SpecializedOps.copyTriangle(A,B,false);
+                assertTrue(MatrixFeatures.isEqualsTriangle(A,B, false, 1e-8));
+                assertFalse(MatrixFeatures.isEquals(A,B,1e-8));
+            }
+        }
+    }
+
+    @Test
+    public void diffNormF() {
+        DenseMatrix64F a = RandomMatrices.createRandom(3,2,rand);
+        DenseMatrix64F b = RandomMatrices.createRandom(3,2,rand);
+        DenseMatrix64F c = RandomMatrices.createRandom(3,2,rand);
+
+        CommonOps.subtract(a, b, c);
+        double expectedNorm = NormOps.fastNormF(c);
+        double foundNorm = SpecializedOps.diffNormF(a,b);
+
+        assertEquals(expectedNorm,foundNorm,1e-8);
+    }
+
+    @Test
+    public void diffNormP1() {
+        DenseMatrix64F a = RandomMatrices.createRandom(3,2,rand);
+        DenseMatrix64F b = RandomMatrices.createRandom(3,2,rand);
+        DenseMatrix64F c = RandomMatrices.createRandom(3,2,rand);
+
+        CommonOps.subtract(a, b, c);
+        double expectedNorm = 0;
+        for( int i = 0; i < c.getNumElements(); i++ ) {
+            expectedNorm += Math.abs(c.get(i));
+        }
+        double foundNorm = SpecializedOps.diffNormP1(a,b);
+
+        assertEquals(expectedNorm,foundNorm,1e-8);
+    }
+
+    @Test
+    public void addIdentity() {
+        DenseMatrix64F M = RandomMatrices.createRandom(4,4,rand);
+        DenseMatrix64F A = M.copy();
+
+        SpecializedOps.addIdentity(A,A,2.0);
+        CommonOps.subtractEquals(A, M);
+
+        double total = CommonOps.elementSum(A);
+
+        assertEquals(4*2,total,1e-8);
+    }
+
+    @Test
+    public void subvector() {
+        DenseMatrix64F A = RandomMatrices.createRandom(4,5,rand);
+
+        DenseMatrix64F v = new DenseMatrix64F(7,1);
+
+        // first extract a row vector
+        SpecializedOps.subvector(A,2,1,2,true,1,v);
+
+        assertEquals(0,v.get(0),1e-8);
+
+        for( int i = 0; i < 2; i++ ) {
+            assertEquals(A.get(2,1+i),v.get(1+i),1e-8);
+        }
+
+        // now extract a column vector
+        SpecializedOps.subvector(A,2,1,2,false,1,v);
+
+        for( int i = 0; i < 2; i++ ) {
+            assertEquals(A.get(2+i,1),v.get(1+i),1e-8);
+        }
+    }
+
+    @Test
+    public void splitIntoVectors() {
+
+        DenseMatrix64F A = RandomMatrices.createRandom(3,5,rand);
+
+        // column vectors
+        DenseMatrix64F v[] = SpecializedOps.splitIntoVectors(A,true);
+
+        assertEquals(5,v.length);
+        for( int i = 0; i < v.length; i++ ) {
+            DenseMatrix64F a = v[i];
+
+            assertEquals(3,a.getNumRows());
+            assertEquals(1,a.getNumCols());
+
+            for( int j = 0; j < A.numRows; j++ ) {
+                assertEquals(A.get(j,i),a.get(j),1e-8);
+            }
+        }
+
+        // row vectors
+        v = SpecializedOps.splitIntoVectors(A,false);
+
+        assertEquals(3,v.length);
+        for( int i = 0; i < v.length; i++ ) {
+            DenseMatrix64F a = v[i];
+
+            assertEquals(1,a.getNumRows());
+            assertEquals(5,a.getNumCols());
+
+            for( int j = 0; j < A.numCols; j++ ) {
+                assertEquals(A.get(i,j),a.get(j),1e-8);
+            }
+        }
+
+    }
+
+    @Test
+    public void pivotMatrix() {
+        int pivots[] = new int[]{1,0,3,2};
+
+        DenseMatrix64F A = RandomMatrices.createRandom(4,4,rand);
+        DenseMatrix64F P = SpecializedOps.pivotMatrix(null,pivots,4,false);
+        DenseMatrix64F Pt = SpecializedOps.pivotMatrix(null,pivots,4,true);
+
+        DenseMatrix64F B = new DenseMatrix64F(4,4);
+
+        // see if it swapped the rows
+        CommonOps.mult(P,A,B);
+
+        for( int i = 0; i < 4; i++ ) {
+            int index = pivots[i];
+            for( int j = 0; j < 4; j++ ) {
+                double val = A.get(index,j);
+                assertEquals(val,B.get(i,j),1e-8);
+            }
+        }
+
+        // see if it transposed
+        CommonOps.transpose(P,B);
+
+        assertTrue(MatrixFeatures.isIdentical(B,Pt,1e-8));
+    }
+
+    @Test
+    public void diagProd() {
+        DenseMatrix64F A = new DenseMatrix64F(3,3);
+
+        A.set(0,0,1);
+        A.set(1,1,2);
+        A.set(2,2,3);
+
+        double found = SpecializedOps.diagProd(A);
+
+        assertEquals(6,found,1e-8);
+    }
+
+    @Test
+    public void elementDiagonalMaxAbs() {
+        DenseMatrix64F A = RandomMatrices.createRandom(4,5,rand);
+
+        double expected = 0;
+        for (int i = 0; i < 4; i++) {
+            double a = A.get(i,i);
+            if( Math.abs(a) > expected )
+                expected = Math.abs(a);
+        }
+
+        double found = SpecializedOps.elementDiagonalMaxAbs(A);
+        assertEquals(expected,found,1e-8);
+    }
+
+    @Test
+    public void qualityTriangular() {
+        DenseMatrix64F A = RandomMatrices.createRandom(4,4,rand);
+
+        double max = SpecializedOps.elementDiagonalMaxAbs(A);
+        double expected = 1.0;
+        for (int i = 0; i < 4; i++) {
+            expected *= A.get(i,i)/max;
+        }
+
+        double found = SpecializedOps.qualityTriangular(A);
+        assertEquals(expected,found,1e-8);
+    }
+    
+    @Test
+    public void elementSumSq() {
+        DenseMatrix64F A = new DenseMatrix64F(2,3,true,1,2,3,4,5,6);
+        
+        double expected = 1+4+9+16+25+36;
+        double found = SpecializedOps.elementSumSq(A);
+        
+        assertEquals(expected,found,1e-8);
+    }
+}
diff --git a/main/denseC64/build.gradle b/main/denseC64/build.gradle
new file mode 100644
index 0000000..4587fdc
--- /dev/null
+++ b/main/denseC64/build.gradle
@@ -0,0 +1,10 @@
+dependencies {
+    compile project(':main:core')
+    testCompile project(':main:dense64')
+}
+
+idea {
+    module {
+        name = "EJML Dense C64"
+    }
+}
\ No newline at end of file
diff --git a/main/denseC64/generate/org/ejml/alg/dense/mult/GenerateCMatrixMatrixMult.java b/main/denseC64/generate/org/ejml/alg/dense/mult/GenerateCMatrixMatrixMult.java
new file mode 100644
index 0000000..9f426a0
--- /dev/null
+++ b/main/denseC64/generate/org/ejml/alg/dense/mult/GenerateCMatrixMatrixMult.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.mult;
+
+import org.ejml.alg.generic.CodeGeneratorMisc;
+
+import java.io.FileNotFoundException;
+import java.io.PrintStream;
+
+/**
+ * @author Peter Abeles
+ */
+public class GenerateCMatrixMatrixMult {
+    PrintStream stream;
+
+    public GenerateCMatrixMatrixMult(String fileName) throws FileNotFoundException {
+        stream = new PrintStream(fileName);
+    }
+
+    public void createClass() {
+        String preamble = CodeGeneratorMisc.COPYRIGHT +
+                "package org.ejml.alg.dense.mult;\n" +
+                "\n" +
+                "import org.ejml.data.CDenseMatrix64F;\n" +
+                "import org.ejml.ops.CCommonOps;\n" +
+                "\n" +
+                "/**\n" +
+                " * <p>Matrix multiplication routines for complex dense matrices in a row-major format.</p>\n" +
+                " *\n" +
+                " * <p>\n" +
+                " * DO NOT MODIFY! Auto generated by {@link org.ejml.alg.dense.mult.GenerateCMatrixMatrixMult}.\n" +
+                " * </p>\n" +
+                " *\n" +
+                " * @author Peter Abeles\n" +
+                " */\n" +
+                "public class CMatrixMatrixMult {\n";
+
+        stream.print(preamble);
+
+        for( int i = 0; i < 2; i++ ) {
+            boolean alpha = i == 1;
+            for( int j = 0; j < 2; j++ ) {
+                boolean add = j == 1;
+                printMult_reroder(alpha,add);
+                stream.print("\n");
+                printMult_small(alpha,add);
+                stream.print("\n");
+            }
+        }
+        stream.print("}\n");
+    }
+
+    public void printMult_reroder( boolean alpha , boolean add ) {
+        String header,valLine;
+
+        header = makeHeader("mult","reorder",add,alpha, false, false,false);
+
+        String tempVars = "";
+
+        if( alpha ) {
+            tempVars = "        double realTmp,imgTmp;";
+            valLine = "            realTmp = a.data[indexA++];\n" +
+                      "            imgTmp = a.data[indexA++];\n" +
+                      "            realA = realAlpha*realTmp - imgAlpha*imgTmp;\n" +
+                      "            imgA = realAlpha*imgTmp + imgAlpha*realTmp;\n";
+        } else {
+            valLine = "            realA = a.data[indexA++];\n" +
+                      "            imgA = a.data[indexA++];\n";
+        }
+
+        String assignment = add ? "+=" : "=";
+
+        String foo = header + makeBoundsCheck(false,false, null)+handleZeros(add) +
+                "        double realA,imgA;\n" +
+                tempVars +
+                "\n" +
+                "        int indexCbase= 0;\n" +
+                "        int strideA = a.getRowStride();\n" +
+                "        int strideB = b.getRowStride();\n" +
+                "        int strideC = c.getRowStride();\n" +
+                "        int endOfKLoop = b.numRows*strideB;\n" +
+                "\n" +
+                "        for( int i = 0; i < a.numRows; i++ ) {\n" +
+                "            int indexA = i*strideA;\n" +
+                "\n" +
+                "            // need to assign c.data to a value initially\n" +
+                "            int indexB = 0;\n" +
+                "            int indexC = indexCbase;\n" +
+                "            int end = indexB + strideB;\n" +
+                "\n" +
+                valLine +
+                "\n" +
+                "            while( indexB < end ) {\n" +
+                "                double realB = b.data[indexB++];\n" +
+                "                double imgB = b.data[indexB++];\n" +
+                "\n" +
+                "                c.data[indexC++] "+assignment+" realA*realB - imgA*imgB;\n" +
+                "                c.data[indexC++] "+assignment+" realA*imgB + imgA*realB;\n" +
+                "            }\n" +
+                "\n" +
+                "            // now add to it\n" +
+                "            while( indexB != endOfKLoop ) { // k loop\n" +
+                "                indexC = indexCbase;\n" +
+                "                end = indexB + strideB;\n" +
+                "\n" +
+                valLine +
+                "\n" +
+                "                while( indexB < end ) { // j loop\n" +
+                "                    double realB = b.data[indexB++];\n" +
+                "                    double imgB = b.data[indexB++];\n" +
+                "\n" +
+                "                    c.data[indexC++] += realA*realB - imgA*imgB;\n" +
+                "                    c.data[indexC++] += realA*imgB + imgA*realB;\n" +
+                "                }\n" +
+                "            }\n" +
+                "            indexCbase += strideC;\n" +
+                "        }\n" +
+                "    }\n\n";
+
+        stream.print(foo);
+    }
+
+    public void printMult_small( boolean alpha , boolean add ) {
+        String header,valLine;
+
+        header = makeHeader("mult","small",add,alpha, false, false,false);
+
+        String assignment = add ? "+=" : "=";
+
+        if( alpha ) {
+            valLine = "                c.data[cIndex++] "+assignment+" realAlpha*realTotal - imgAlpha*imgTotal;\n" +
+                      "                c.data[cIndex++] "+assignment+" realAlpha*imgTotal + imgAlpha*realTotal;\n";
+        } else {
+            valLine = "                c.data[cIndex++] "+assignment+" realTotal;\n" +
+                      "                c.data[cIndex++] "+assignment+" imgTotal;\n";
+        }
+
+        String foo =
+                header + makeBoundsCheck(false,false, null)+
+                        "        int aIndexStart = 0;\n" +
+                        "        int cIndex = 0;\n" +
+                        "\n" +
+                        "        int strideA = a.getRowStride();\n" +
+                        "        int strideB = b.getRowStride();\n" +
+                        "\n" +
+                        "        for( int i = 0; i < a.numRows; i++ ) {\n" +
+                        "            for( int j = 0; j < b.numCols; j++ ) {\n" +
+                        "                double realTotal = 0;\n" +
+                        "                double imgTotal = 0;\n" +
+                        "\n" +
+                        "                int indexA = aIndexStart;\n" +
+                        "                int indexB = j*2;\n" +
+                        "                int end = indexA + strideA;\n" +
+                        "                while( indexA < end ) {\n" +
+                        "                    double realA = a.data[indexA++];\n" +
+                        "                    double imgA = a.data[indexA++];\n" +
+                        "\n" +
+                        "                    double realB = b.data[indexB];\n" +
+                        "                    double imgB = b.data[indexB+1];\n" +
+                        "\n" +
+                        "                    realTotal += realA*realB - imgA*imgB;\n" +
+                        "                    imgTotal += realA*imgB + imgA*realB;\n" +
+                        "\n" +
+                        "                    indexB += strideB;\n" +
+                        "                }\n" +
+                        "\n" +
+                        valLine +
+                        "            }\n" +
+                        "            aIndexStart += strideA;\n" +
+                        "        }\n" +
+                        "    }\n\n";
+
+        stream.print(foo);
+    }
+
+    private String makeBoundsCheck(boolean tranA, boolean tranB, String auxLength)
+    {
+        String a_numCols = tranA ? "a.numRows" : "a.numCols";
+        String a_numRows = tranA ? "a.numCols" : "a.numRows";
+        String b_numCols = tranB ? "b.numRows" : "b.numCols";
+        String b_numRows = tranB ? "b.numCols" : "b.numRows";
+
+        String ret =
+                "        if( a == c || b == c )\n" +
+                        "            throw new IllegalArgumentException(\"Neither 'a' or 'b' can be the same matrix as 'c'\");\n"+
+                        "        else if( "+a_numCols+" != "+b_numRows+" ) {\n" +
+                        "            throw new MatrixDimensionException(\"The 'a' and 'b' matrices do not have compatible dimensions\");\n" +
+                        "        } else if( "+a_numRows+" != c.numRows || "+b_numCols+" != c.numCols ) {\n" +
+                        "            throw new MatrixDimensionException(\"The results matrix does not have the desired dimensions\");\n" +
+                        "        }\n" +
+                        "\n";
+
+        if( auxLength != null ) {
+            ret += "        if( aux == null ) aux = new double[ "+auxLength+" ];\n\n";
+        }
+
+        return ret;
+    }
+
+    private String handleZeros( boolean add ) {
+
+        String fill = add ? "" : "            CCommonOps.fill(c,0,0);\n";
+
+        String ret =
+                "        if( a.numCols == 0 || a.numRows == 0 ) {\n" +
+                        fill +
+                        "            return;\n" +
+                        "        }\n";
+        return ret;
+    }
+
+    private String makeHeader(String nameOp, String variant,
+                              boolean add, boolean hasAlpha, boolean hasAux,
+                              boolean tranA, boolean tranB)
+    {
+        if( add ) nameOp += "Add";
+
+        // make the op name
+        if( tranA && tranB ) {
+            nameOp += "TransAB";
+        } else if( tranA ) {
+            nameOp += "TransA";
+        } else if( tranB ) {
+            nameOp += "TransB";
+        }
+
+        String ret = "    public static void "+nameOp;
+
+        if( variant != null ) ret += "_"+variant+"( ";
+        else ret += "( ";
+
+        if( hasAlpha ) ret += "double realAlpha , double imgAlpha , ";
+
+        if( hasAux ) {
+            ret += "CDenseMatrix64F a , CDenseMatrix64F b , CDenseMatrix64F c , double []aux )\n";
+        } else {
+            ret += "CDenseMatrix64F a , CDenseMatrix64F b , CDenseMatrix64F c )\n";
+        }
+
+        ret += "    {\n";
+
+        return ret;
+    }
+
+    public static void main(String[] args) throws FileNotFoundException {
+        GenerateCMatrixMatrixMult gen = new GenerateCMatrixMatrixMult("CMatrixMatrixMult.java");
+
+        gen.createClass();
+    }
+}
diff --git a/main/denseC64/src/org/ejml/alg/dense/decompose/CTriangularSolver.java b/main/denseC64/src/org/ejml/alg/dense/decompose/CTriangularSolver.java
new file mode 100644
index 0000000..3e7b49f
--- /dev/null
+++ b/main/denseC64/src/org/ejml/alg/dense/decompose/CTriangularSolver.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decompose;
+
+/**
+ * <p>
+ * This contains algorithms for solving systems of equations where T is a
+ * non-singular triangular complex matrix:<br>
+ * <br>
+ * T*x = b<br>
+ * <br>
+ * where x and b are vectors, and T is an n by n matrix. T can either be a lower or upper triangular matrix.<br>
+ * </p>
+ * <p>
+ * These functions are designed for use inside of other algorithms.  To use them directly
+ * is dangerous since no sanity checks are performed.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class CTriangularSolver {
+    /**
+     * <p>
+     * This is a forward substitution solver for non-singular upper triangular matrices.
+     * <br>
+     * b = U<sup>-1</sup>b<br>
+     * <br>
+     * where b is a vector, U is an n by n matrix.<br>
+     * </p>
+     *
+     * @param U An n by n non-singular upper triangular matrix. Not modified.
+     * @param b A vector of length n. Modified.
+     * @param n The size of the matrices.
+     */
+    public static void solveU( double U[] , double []b , int n )
+    {
+//        for( int i =n-1; i>=0; i-- ) {
+//            double sum = b[i];
+//            for( int j = i+1; j <n; j++ ) {
+//                sum -= U[i*n+j]* b[j];
+//            }
+//            b[i] = sum/U[i*n+i];
+//        }
+        int stride = n*2;
+        for( int i =n-1; i>=0; i-- ) {
+            double sumReal = b[i*2];
+            double sumImg = b[i*2+1];
+            int indexU = i*stride+i*2+2;
+            for( int j = i+1; j <n; j++ ) {
+                double realB = b[j*2];
+                double imgB = b[j*2+1];
+
+                double realU = U[indexU++];
+                double imgU = U[indexU++];
+
+                sumReal -= realB*realU - imgB*imgU;
+                sumImg -= realB*imgU + imgB*realU;
+            }
+
+            // b = sum/U
+            double realU = U[i*stride+i*2];
+            double imgU = U[i*stride+i*2+1];
+
+            double normU = realU*realU + imgU*imgU;
+            b[i*2] = (sumReal*realU + sumImg*imgU)/normU;
+            b[i*2+1] = (sumImg*realU - sumReal*imgU)/normU;
+        }
+    }
+
+    /**
+     * <p>
+     * Solves for non-singular lower triangular matrices with real valued diagonal elements
+     * using forward substitution.
+     * <br>
+     * b = L<sup>-1</sup>b<br>
+     * <br>
+     * where b is a vector, L is an n by n matrix.<br>
+     * </p>
+     *
+     * @param L An n by n non-singular lower triangular matrix. Not modified.
+     * @param b A vector of length n. Modified.
+     * @param n The size of the matrices.
+     */
+    public static void solveL_diagReal(double L[], double[] b, int n)
+    {
+//        for( int i = 0; i < n; i++ ) {
+//            double sum = b[i];
+//            for( int k=0; k<i; k++ ) {
+//                sum -= L[i*n+k]* b[k];
+//            }
+//            b[i] = sum / L[i*n+i];
+//        }
+        int stride = n*2;
+
+        for( int i = 0; i < n; i++ ) {
+            double realSum = b[i*2];
+            double imagSum = b[i*2+1];
+
+            int indexL = i*stride;
+            int indexB = 0;
+            for( int k=0; k<i; k++ ) {
+                double realL = L[indexL++];
+                double imagL = L[indexL++];
+
+                double realB = b[indexB++];
+                double imagB = b[indexB++];
+
+                realSum -= realL*realB - imagL*imagB;
+                imagSum -= realL*imagB + imagL*realB;
+            }
+
+            double realL = L[indexL];
+
+            b[i*2    ] = realSum/realL;
+            b[i*2 + 1] = imagSum/realL;
+        }
+    }
+
+    /**
+     * <p>
+     * This is a forward substitution solver for non-singular lower triangular matrices with
+     * real valued diagonal elements.
+     * <br>
+     * b = (L<sup>CT</sup>)<sup>-1</sup>b<br>
+     * <br>
+     * where b is a vector, L is an n by n matrix.<br>
+     * </p>
+     * <p>
+     * L is a lower triangular matrix, but it comes up with a solution as if it was
+     * an upper triangular matrix that was computed by conjugate transposing L.
+     * </p>
+     *
+     * @param L An n by n non-singular lower triangular matrix. Not modified.
+     * @param b A vector of length n. Modified.
+     * @param n The size of the matrices.
+     */
+    public static void solveConjTranL_diagReal(double L[], double[] b, int n)
+    {
+//        for( int i =n-1; i>=0; i-- ) {
+//            double sum = b[i];
+//            for( int k = i+1; k <n; k++ ) {
+//                sum -= L[k*n+i]* b[k];
+//            }
+//            b[i] = sum/L[i*n+i];
+//        }
+
+        for( int i = n-1; i>=0; i-- ) {
+            double realSum = b[i*2];
+            double imagSum = b[i*2+1];
+
+            int indexB = (i+1)*2;
+            for( int k = i+1; k <n; k++ ) {
+                int indexL = (k*n+i)*2;
+
+                double realL = L[indexL];
+                double imagL = L[indexL+1];
+
+                double realB = b[indexB++];
+                double imagB = b[indexB++];
+
+                realSum -= realL*realB + imagL*imagB;
+                imagSum -= realL*imagB - imagL*realB;
+            }
+
+            double realL =  L[(i*n+i)*2];
+
+            b[i*2    ] = realSum/realL;
+            b[i*2 + 1] = imagSum/realL;
+        }
+    }
+}
diff --git a/main/denseC64/src/org/ejml/alg/dense/decompose/chol/CholeskyDecompositionCommon_CD64.java b/main/denseC64/src/org/ejml/alg/dense/decompose/chol/CholeskyDecompositionCommon_CD64.java
new file mode 100644
index 0000000..d7a4c4b
--- /dev/null
+++ b/main/denseC64/src/org/ejml/alg/dense/decompose/chol/CholeskyDecompositionCommon_CD64.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decompose.chol;
+
+
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.data.Complex64F;
+import org.ejml.interfaces.decomposition.CholeskyDecomposition;
+import org.ejml.ops.CCommonOps;
+
+
+/**
+ *
+ * <p>
+ * This is an abstract class for a Cholesky decomposition.  It provides the solvers, but the actual
+ * decomposition is provided in other classes.
+ * </p>
+ *
+ * @see org.ejml.interfaces.decomposition.CholeskyDecomposition
+ * @author Peter Abeles
+ */
+public abstract class CholeskyDecompositionCommon_CD64
+        implements CholeskyDecomposition<CDenseMatrix64F> {
+
+    // width and height of the matrix
+    protected int n;
+
+    // the decomposed matrix
+    protected CDenseMatrix64F T;
+    protected double[] t;
+
+
+    // is it a lower triangular matrix or an upper triangular matrix
+    protected boolean lower;
+
+    // storage for the determinant
+    protected Complex64F det = new Complex64F();
+
+    /**
+     * Specifies if a lower or upper variant should be constructed.
+     *
+     * @param lower should a lower or upper triangular matrix be used.
+     */
+    public CholeskyDecompositionCommon_CD64(boolean lower) {
+        this.lower = lower;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isLower() {
+        return lower;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean decompose( CDenseMatrix64F mat ) {
+        if( mat.numRows != mat.numCols ) {
+            throw new IllegalArgumentException("Must be a square matrix.");
+        }
+
+        n = mat.numRows;
+
+        T = mat;
+        t = T.data;
+
+        if(lower) {
+            return decomposeLower();
+        } else {
+            return decomposeUpper();
+        }
+    }
+
+    @Override
+    public boolean inputModified() {
+        return true;
+    }
+
+    /**
+     * Performs an lower triangular decomposition.
+     *
+     * @return true if the matrix was decomposed.
+     */
+    protected abstract boolean decomposeLower();
+
+    /**
+     * Performs an upper triangular decomposition.
+     *
+     * @return true if the matrix was decomposed.
+     */
+    protected abstract boolean decomposeUpper();
+
+    @Override
+    public CDenseMatrix64F getT( CDenseMatrix64F T ) {
+        // see if it needs to declare a new matrix or not
+        if( T == null ) {
+            T = new CDenseMatrix64F(n,n);
+        } else {
+            if( T.numRows != n || T.numCols != n )
+                throw new IllegalArgumentException("Unexpected matrix dimension for T.");
+
+            CCommonOps.fill(T, 0,0);
+        }
+
+        // write the values to T
+        if( lower ) {
+            for( int i = 0; i < n; i++ ) {
+                int index = i*n*2;
+                for( int j = 0; j <= i; j++ ) {
+                    T.data[index] = this.T.data[index];
+                    index++;
+                    T.data[index] = this.T.data[index];
+                    index++;
+                }
+            }
+        } else {
+            for( int i = 0; i < n; i++ ) {
+                int index = (i*n + i)*2;
+                for( int j = i; j < n; j++ ) {
+                    T.data[index] = this.T.data[index];
+                    index++;
+                    T.data[index] = this.T.data[index];
+                    index++;
+                }
+            }
+        }
+
+        return T;
+    }
+
+    /**
+     * Returns the raw decomposed matrix.
+     *
+     * @return A lower or upper triangular matrix.
+     */
+    public CDenseMatrix64F _getT() {
+        return T;
+    }
+
+    @Override
+    public Complex64F computeDeterminant() {
+        double prod = 1;
+
+        // take advantage of the diagonal elements all being real
+        int total = n*n*2;
+        for( int i = 0; i < total; i += 2*(n + 1) ) {
+            prod *= t[i];
+        }
+
+        det.real = prod*prod;
+        det.imaginary = 0;
+
+        return det;
+    }
+}
\ No newline at end of file
diff --git a/main/denseC64/src/org/ejml/alg/dense/decompose/chol/CholeskyDecompositionInner_CD64.java b/main/denseC64/src/org/ejml/alg/dense/decompose/chol/CholeskyDecompositionInner_CD64.java
new file mode 100644
index 0000000..ec745e2
--- /dev/null
+++ b/main/denseC64/src/org/ejml/alg/dense/decompose/chol/CholeskyDecompositionInner_CD64.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decompose.chol;
+
+import org.ejml.UtilEjml;
+
+/**
+ * <p>
+ * This implementation of a Cholesky decomposition using the inner-product form.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class CholeskyDecompositionInner_CD64 extends CholeskyDecompositionCommon_CD64 {
+
+    // tolerance for testing to see if diagonal elements are real
+    double tolerance = UtilEjml.EPS;
+
+    public CholeskyDecompositionInner_CD64() {
+        super(true);
+    }
+
+    public CholeskyDecompositionInner_CD64(boolean lower) {
+        super(lower);
+    }
+
+    public void setTolerance(double tolerance) {
+        this.tolerance = tolerance;
+    }
+
+    @Override
+    protected boolean decomposeLower() {
+        if( n == 0 )
+            throw new IllegalArgumentException("Cholesky is undefined for 0 by 0 matrix");
+
+        double real_el_ii=0;
+
+        int stride = n*2;
+        for( int i = 0; i < n; i++ ) {
+            for( int j = i; j < n; j++ ) {
+                double realSum = t[i*stride+j*2  ];
+                double imagSum = t[i*stride+j*2+1];
+
+                if( i == j ) {
+                    // its easy to prove that for the cholesky decomposition to be valid the original
+                    // diagonal elements must be real
+                    if( Math.abs(imagSum) > tolerance*Math.abs(realSum) )
+                        return false;
+
+                    // This takes advantage of the fact that when you multiply a complex number by
+                    // its conjigate the result is a real number
+                    int end = i*stride+i*2;
+                    for( int index = i*stride; index < end; ) {
+                        double real = t[index++];
+                        double imag = t[index++];
+
+                        realSum -= real*real + imag*imag;
+                    }
+
+                    if( realSum <= 0 ) {
+                        return false;
+                    }
+
+                    real_el_ii = Math.sqrt(realSum);
+                    t[i*stride+i*2]   = real_el_ii;
+                    t[i*stride+i*2+1] = 0;
+                } else {
+                    int iEl = i*stride; // row i is inside the lower triangle
+                    int jEl = j*stride; // row j conjugate transposed upper triangle
+                    int end = iEl+i*2;
+                    // k = 0:i-1
+                    for( ; iEl<end; ) {
+//                    sum -= el[i*n+k]*el[j*n+k];
+                        double realI = t[iEl++];
+                        double imagI = t[iEl++];
+
+                        double realJ = t[jEl++];
+                        double imagJ = t[jEl++];
+
+                        // multiply by the complex conjugate of I since the triangle being stored
+                        // is the conjugate of L
+                        realSum -= realI*realJ + imagI*imagJ;
+                        imagSum -= realI*imagJ - realJ*imagI;
+                    }
+
+                    // divide the sum by the diagonal element, which is always real
+                    // Note that it is storing the conjugate of L
+                    t[j*stride+i*2]   = realSum/real_el_ii;
+                    t[j*stride+i*2+1] = imagSum/real_el_ii;
+                }
+            }
+        }
+        // Make it L instead of the conjugate of L
+        for (int i = 1; i < n; i++) {
+            for (int j = 0; j < i; j++) {
+                t[i*stride+j*2+1] = -t[i*stride+j*2+1];
+            }
+        }
+
+        return true;
+    }
+
+
+    @Override
+    protected boolean decomposeUpper()
+    {
+        // See code comments in lower for more details on the algorithm
+
+        if( n == 0 )
+            throw new IllegalArgumentException("Cholesky is undefined for 0 by 0 matrix");
+
+        double real_el_ii=0;
+
+        int stride = n*2;
+
+        for( int i = 0; i < n; i++ ) {
+            for( int j = i; j < n; j++ ) {
+                double realSum = t[i*stride+j*2  ];
+                double imagSum = t[i*stride+j*2+1];
+
+                if( i == j ) {
+                    if( Math.abs(imagSum) > tolerance*Math.abs(realSum) )
+                        return false;
+
+                    for (int k = 0; k < i; k++) {
+                        double real = t[k*stride+i*2 ];
+                        double imag = t[k*stride+i*2+1];
+
+                        realSum -= real*real + imag*imag;
+                    }
+
+                    if( realSum <= 0 ) {
+                        return false;
+                    }
+
+                    real_el_ii = Math.sqrt(realSum);
+                    t[i*stride+i*2]   = real_el_ii;
+                    t[i*stride+i*2+1] = 0;
+                } else {
+                    for( int k = 0; k < i; k++ ) {
+                        double realI = t[k*stride+i*2  ];
+                        double imagI = t[k*stride+i*2+1];
+
+                        double realJ = t[k*stride+j*2  ];
+                        double imagJ = t[k*stride+j*2+1];
+
+                        realSum -= realI*realJ + imagI*imagJ;
+                        imagSum -= realI*imagJ - realJ*imagI;
+                    }
+
+                    t[i*stride+j*2]   = realSum/real_el_ii;
+                    t[i*stride+j*2+1] = imagSum/real_el_ii;
+                }
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/main/denseC64/src/org/ejml/alg/dense/decompose/lu/LUDecompositionAlt_CD64.java b/main/denseC64/src/org/ejml/alg/dense/decompose/lu/LUDecompositionAlt_CD64.java
new file mode 100644
index 0000000..c9218aa
--- /dev/null
+++ b/main/denseC64/src/org/ejml/alg/dense/decompose/lu/LUDecompositionAlt_CD64.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decompose.lu;
+
+import org.ejml.data.CDenseMatrix64F;
+
+
+/**
+ * <p>
+ * An LU decomposition algorithm that originally came from Jama.  In general this is faster than
+ * what is in NR since it creates a cache of a column, which makes a big difference in larger
+ * matrices.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class LUDecompositionAlt_CD64 extends LUDecompositionBase_CD64 {
+
+    /**
+     * This is a modified version of what was found in the JAMA package.  The order that it
+     * performs its permutations in is the primary difference from NR
+     *
+     * @param a The matrix that is to be decomposed.  Not modified.
+     * @return true If the matrix can be decomposed and false if it can not.
+     */
+    public boolean decompose( CDenseMatrix64F a )
+    {
+        decomposeCommonInit(a);
+
+        double LUcolj[] = vv;
+
+        for( int j = 0; j < n; j++ ) {
+
+            // make a copy of the column to avoid cache jumping issues
+            for( int i = 0; i < m; i++) {
+                LUcolj[i*2] = dataLU[i*stride + j*2];
+                LUcolj[i*2+1] = dataLU[i*stride + j*2+1];
+            }
+
+            // Apply previous transformations.
+            for( int i = 0; i < m; i++ ) {
+                int rowIndex = i*stride;
+
+                // Most of the time is spent in the following dot product.
+                int kmax = i < j ? i : j;
+                double realS = 0.0;
+                double imgS = 0.0;
+
+                for (int k = 0; k < kmax; k++) {
+                    double realD = dataLU[rowIndex+k*2];
+                    double imgD  = dataLU[rowIndex+k*2+1];
+
+                    double realCol = LUcolj[k*2];
+                    double imgCol  = LUcolj[k*2+1];
+
+                    realS += realD*realCol - imgD*imgCol;
+                    imgS += realD*imgCol + imgD*realCol;
+                }
+
+                dataLU[rowIndex+j*2]   = LUcolj[i*2]   -= realS;
+                dataLU[rowIndex+j*2+1] = LUcolj[i*2+1] -= imgS;
+            }
+
+            // Find pivot and exchange if necessary.
+            int p = j;
+            double max = mag(LUcolj, p * 2);
+            for (int i = j+1; i < m; i++) {
+                double v = mag(LUcolj, i * 2);
+                if ( v > max) {
+                    p = i;
+                    max = v;
+                }
+            }
+
+            if (p != j) {
+                // swap the rows
+//                for (int k = 0; k < n; k++) {
+//                    double t = dataLU[p*n + k];
+//                    dataLU[p*n + k] = dataLU[j*n + k];
+//                    dataLU[j*n + k] = t;
+//                }
+                int rowP = p*stride;
+                int rowJ = j*stride;
+                int endP = rowP+stride;
+                for (;rowP < endP; rowP++,rowJ++) {
+                    double t = dataLU[rowP];
+                    dataLU[rowP]   = dataLU[rowJ];
+                    dataLU[rowJ]   = t;
+                }
+                int k = pivot[p]; pivot[p] = pivot[j]; pivot[j] = k;
+                pivsign = -pivsign;
+            }
+            indx[j] = p;
+
+            // Compute multipliers.
+            if (j < m ) {
+                double realLujj = dataLU[j*stride+j*2];
+                double imgLujj  = dataLU[j*stride+j*2+1];
+
+                double magLujj = realLujj*realLujj + imgLujj*imgLujj;
+
+                if( realLujj != 0 || imgLujj != 0) {
+                    for (int i = j+1; i < m; i++) {
+
+                        double realLU = dataLU[i*stride+j*2];
+                        double imagLU = dataLU[i*stride+j*2+1];
+
+                        dataLU[i*stride+j*2]   = (realLU*realLujj + imagLU*imgLujj)/magLujj;
+                        dataLU[i*stride+j*2+1] = (imagLU*realLujj - realLU*imgLujj)/magLujj;
+                    }
+                }
+            }
+        }
+
+        return true;
+    }
+
+    private static double mag( double d[] , int index ) {
+        double r = d[index];
+        double i = d[index+1];
+        return r*r + i*i;
+    }
+}
diff --git a/main/denseC64/src/org/ejml/alg/dense/decompose/lu/LUDecompositionBase_CD64.java b/main/denseC64/src/org/ejml/alg/dense/decompose/lu/LUDecompositionBase_CD64.java
new file mode 100644
index 0000000..146599f
--- /dev/null
+++ b/main/denseC64/src/org/ejml/alg/dense/decompose/lu/LUDecompositionBase_CD64.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decompose.lu;
+
+import org.ejml.UtilEjml;
+import org.ejml.alg.dense.decompose.CTriangularSolver;
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.data.Complex64F;
+import org.ejml.interfaces.decomposition.LUDecomposition;
+import org.ejml.ops.CCommonOps;
+import org.ejml.ops.CSpecializedOps;
+
+
+/**
+ * <p>
+ * Contains common data structures and operations for LU decomposition algorithms.
+ * </p>
+ * @author Peter Abeles
+ */
+public abstract class LUDecompositionBase_CD64
+        implements LUDecomposition<CDenseMatrix64F> {
+    // the decomposed matrix
+    protected CDenseMatrix64F LU;
+
+    // it can decompose a matrix up to this size
+    protected int maxWidth=-1;
+
+    // the shape of the matrix
+    protected int m,n,stride;
+    // data in the matrix
+    protected double dataLU[];
+
+    // used in set, solve, invert
+    protected double vv[];
+    // used in set
+    protected int indx[];
+    protected int pivot[];
+
+    // used by determinant
+    protected double pivsign;
+    protected Complex64F det = new Complex64F();
+
+    public void setExpectedMaxSize( int numRows , int numCols )
+    {
+        LU = new CDenseMatrix64F(numRows,numCols);
+
+        this.dataLU = LU.data;
+        maxWidth = Math.max(numRows,numCols);
+
+        vv = new double[ maxWidth*2 ];
+        indx = new int[ maxWidth ];
+        pivot = new int[ maxWidth ];
+    }
+
+    public CDenseMatrix64F getLU() {
+        return LU;
+    }
+
+    public int[] getIndx() {
+        return indx;
+    }
+
+    public int[] getPivot() {
+        return pivot;
+    }
+
+    @Override
+    public boolean inputModified() {
+        return false;
+    }
+
+    /**
+     * Writes the lower triangular matrix into the specified matrix.
+     *
+     * @param lower Where the lower triangular matrix is written to.
+     */
+    @Override
+    public CDenseMatrix64F getLower( CDenseMatrix64F lower )
+    {
+        int numRows = LU.numRows;
+        int numCols = LU.numRows < LU.numCols ? LU.numRows : LU.numCols;
+
+        if( lower == null ) {
+            lower = new CDenseMatrix64F(numRows,numCols);
+        } else {
+            if( lower.numCols != numCols || lower.numRows != numRows )
+                throw new IllegalArgumentException("Unexpected matrix dimension");
+            CCommonOps.fill(lower,0, 0);
+        }
+
+        for( int i = 0; i < numCols; i++ ) {
+            lower.set(i,i,1.0,0.0);
+
+            for( int j = 0; j < i; j++ ) {
+                int indexLU = LU.getIndex(i,j);
+                int indexL = lower.getIndex(i,j);
+
+                double real = LU.data[indexLU];
+                double imaginary = LU.data[indexLU+1];
+
+                lower.data[indexL] = real;
+                lower.data[indexL+1] = imaginary;
+            }
+        }
+
+        if( numRows > numCols ) {
+            for( int i = numCols; i < numRows; i++ ) {
+                for( int j = 0; j < numCols; j++ ) {
+                    int indexLU = LU.getIndex(i,j);
+                    int indexL = lower.getIndex(i,j);
+
+                    double real = LU.data[indexLU];
+                    double imaginary = LU.data[indexLU+1];
+
+                    lower.data[indexL] = real;
+                    lower.data[indexL+1] = imaginary;
+                }
+            }
+        }
+        return lower;
+    }
+
+    /**
+     * Writes the upper triangular matrix into the specified matrix.
+     *
+     * @param upper Where the upper triangular matrix is writen to.
+     */
+    @Override
+    public CDenseMatrix64F getUpper( CDenseMatrix64F upper )
+    {
+        int numRows = LU.numRows < LU.numCols ? LU.numRows : LU.numCols;
+        int numCols = LU.numCols;
+
+        if( upper == null ) {
+            upper = new CDenseMatrix64F(numRows, numCols);
+        } else {
+            if( upper.numCols != numCols || upper.numRows != numRows )
+                throw new IllegalArgumentException("Unexpected matrix dimension");
+            CCommonOps.fill(upper, 0,0);
+        }
+
+        for( int i = 0; i < numRows; i++ ) {
+            for( int j = i; j < numCols; j++ ) {
+                int indexLU = LU.getIndex(i,j);
+                int indexU = upper.getIndex(i,j);
+
+                double real = LU.data[indexLU];
+                double imaginary = LU.data[indexLU+1];
+
+                upper.data[indexU] = real;
+                upper.data[indexU+1] = imaginary;
+            }
+        }
+
+        return upper;
+    }
+
+    public CDenseMatrix64F getPivot( CDenseMatrix64F pivot ) {
+        return CSpecializedOps.pivotMatrix(pivot, this.pivot, LU.numRows, false);
+    }
+
+    protected void decomposeCommonInit(CDenseMatrix64F a) {
+        if( a.numRows > maxWidth || a.numCols > maxWidth ) {
+            setExpectedMaxSize(a.numRows,a.numCols);
+        }
+
+        m = a.numRows;
+        n = a.numCols;
+        stride = n*2;
+
+        LU.set(a);
+        for (int i = 0; i < m; i++) {
+            pivot[i] = i;
+        }
+        pivsign = 1;
+    }
+
+    /**
+     * Determines if the decomposed matrix is singular.  This function can return
+     * false and the matrix be almost singular, which is still bad.
+     *
+     * @return true if singular false otherwise.
+     */
+    @Override
+    public boolean isSingular() {
+
+        for( int i = 0; i < m; i++ ) {
+            double real = dataLU[i*stride+i*2];
+            double imaginary = dataLU[i*stride+i*2+1];
+
+            double mag2 = real*real + imaginary*imaginary;
+
+            if( mag2 < UtilEjml.EPS*UtilEjml.EPS )
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Computes the determinant from the LU decomposition.
+     *
+     * @return The matrix's determinant.
+     */
+    @Override
+    public Complex64F computeDeterminant() {
+        if( m != n )
+            throw new IllegalArgumentException("Must be a square matrix.");
+
+        double realRet = pivsign;
+        double realImg = 0;
+
+        int total = m*stride;
+        for( int i = 0; i < total; i += stride + 2 ) {
+            double real = dataLU[i];
+            double imaginary = dataLU[i+1];
+
+            double r = realRet*real - realImg*imaginary;
+            double t = realRet*imaginary + realImg*real;
+
+            realRet = r;
+            realImg = t;
+        }
+
+        det.set(realRet,realImg);
+        return det;
+    }
+
+    public double quality() {
+        return CSpecializedOps.qualityTriangular(LU);
+    }
+
+    /**
+     * a specialized version of solve that avoid additional checks that are not needed.
+     */
+    public void _solveVectorInternal( double []vv )
+    {
+        // Solve L*Y = B
+        solveL(vv);
+
+        // Solve U*X = Y;
+        CTriangularSolver.solveU(dataLU, vv, n);
+    }
+
+    /**
+     * Solve the using the lower triangular matrix in LU.  Diagonal elements are assumed
+     * to be 1
+     */
+    protected void solveL(double[] vv) {
+
+        int ii = 0;
+
+        for( int i = 0; i < n; i++ ) {
+            int ip = indx[i];
+            double sumReal = vv[ip*2];
+            double sumImg = vv[ip*2+1];
+
+            vv[ip*2] = vv[i*2];
+            vv[ip*2+1] = vv[i*2+1];
+
+            if( ii != 0 ) {
+//                for( int j = ii-1; j < i; j++ )
+//                    sum -= dataLU[i* n +j]*vv[j];
+                int index = i*stride + (ii-1)*2;
+                for( int j = ii-1; j < i; j++ ){
+                    double luReal = dataLU[index++];
+                    double luImg  = dataLU[index++];
+
+                    double vvReal = vv[j*2];
+                    double vvImg  = vv[j*2+1];
+
+                    sumReal -= luReal*vvReal - luImg*vvImg;
+                    sumImg  -= luReal*vvImg  + luImg*vvReal;
+                }
+            } else if( sumReal*sumReal + sumImg*sumImg != 0.0 ) {
+                ii=i+1;
+            }
+            vv[i*2] = sumReal;
+            vv[i*2+1] = sumImg;
+        }
+    }
+
+    public double[] _getVV() {
+        return vv;
+    }
+}
\ No newline at end of file
diff --git a/main/denseC64/src/org/ejml/alg/dense/decompose/qr/QRDecompositionHouseholderColumn_CD64.java b/main/denseC64/src/org/ejml/alg/dense/decompose/qr/QRDecompositionHouseholderColumn_CD64.java
new file mode 100644
index 0000000..b6d6ed8
--- /dev/null
+++ b/main/denseC64/src/org/ejml/alg/dense/decompose/qr/QRDecompositionHouseholderColumn_CD64.java
@@ -0,0 +1,331 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decompose.qr;
+
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.data.Complex64F;
+import org.ejml.interfaces.decomposition.QRDecomposition;
+import org.ejml.ops.CCommonOps;
+
+
+/**
+ * <p>
+ * Householder QR decomposition is rich in operations along the columns of the matrix.  This can be
+ * taken advantage of by solving for the Q matrix in a column major format to reduce the number
+ * of CPU cache misses and the number of copies that are performed.
+ * </p>
+ *
+ * @see org.ejml.alg.dense.decomposition.qr.QRDecompositionHouseholder_D64
+ *
+ * @author Peter Abeles
+ */
+public class QRDecompositionHouseholderColumn_CD64 implements QRDecomposition<CDenseMatrix64F> {
+
+    /**
+     * Where the Q and R matrices are stored.  R is stored in the
+     * upper triangular portion and Q on the lower bit.  Lower columns
+     * are where u is stored.  Q_k = (I - gamma_k*u_k*u_k^T).
+     */
+    protected double dataQR[][]; // [ column][ row ]
+
+    // used internally to store temporary data
+    protected double v[];
+
+    // dimension of the decomposed matrices
+    protected int numCols; // this is 'n'
+    protected int numRows; // this is 'm'
+    protected int minLength;
+
+    // the computed gamma for Q_k matrix
+    protected double gammas[];
+    // local variables
+    protected double gamma;
+    protected Complex64F tau = new Complex64F();
+
+    // did it encounter an error?
+    protected boolean error;
+
+    public void setExpectedMaxSize( int numRows , int numCols ) {
+        this.numCols = numCols;
+        this.numRows = numRows;
+        minLength = Math.min(numCols,numRows);
+        int maxLength = Math.max(numCols,numRows);
+
+        if( dataQR == null || dataQR.length < numCols || dataQR[0].length < numRows*2 ) {
+            dataQR = new double[ numCols ][  numRows*2 ];
+            v = new double[ maxLength*2 ];
+            gammas = new double[ minLength ];
+        }
+
+        if( v.length < maxLength*2 ) {
+            v = new double[ maxLength*2 ];
+        }
+        if( gammas.length < minLength ) {
+            gammas = new double[ minLength ];
+        }
+    }
+
+    /**
+     * Returns the combined QR matrix in a 2D array format that is column major.
+     *
+     * @return The QR matrix in a 2D matrix column major format. [ column ][ row ]
+     */
+    public double[][] getQR() {
+        return dataQR;
+    }
+
+    /**
+     * Computes the Q matrix from the imformation stored in the QR matrix.  This
+     * operation requires about 4(m<sup>2</sup>n-mn<sup>2</sup>+n<sup>3</sup>/3) flops.
+     *
+     * @param Q The orthogonal Q matrix.
+     */
+    @Override
+    public CDenseMatrix64F getQ( CDenseMatrix64F Q , boolean compact ) {
+        if( compact ) {
+            if( Q == null ) {
+                Q = CCommonOps.identity(numRows, minLength);
+            } else {
+                if( Q.numRows != numRows || Q.numCols != minLength ) {
+                    throw new IllegalArgumentException("Unexpected matrix dimension.");
+                } else {
+                    CCommonOps.setIdentity(Q);
+                }
+            }
+        } else {
+            if( Q == null ) {
+                Q = CCommonOps.identity(numRows);
+            } else {
+                if( Q.numRows != numRows || Q.numCols != numRows ) {
+                    throw new IllegalArgumentException("Unexpected matrix dimension.");
+                } else {
+                    CCommonOps.setIdentity(Q);
+                }
+            }
+        }
+
+        for( int j = minLength-1; j >= 0; j-- ) {
+            double u[] = dataQR[j];
+
+            double vvReal = u[j*2];
+            double vvImag = u[j*2+1];
+
+            u[j*2]   = 1;
+            u[j*2+1] = 0;
+            double gammaReal = gammas[j];
+
+            QrHelperFunctions_CD64.rank1UpdateMultR(Q, u,0, gammaReal,j, j, numRows, v);
+
+            u[j*2]   = vvReal;
+            u[j*2+1] = vvImag;
+        }
+
+        return Q;
+    }
+
+    /**
+     * Returns an upper triangular matrix which is the R in the QR decomposition.  If compact then the input
+     * expected to be size = [min(rows,cols) , numCols] otherwise size = [numRows,numCols].
+     *
+     * @param R Storage for upper triangular matrix.
+     * @param compact If true then a compact matrix is expected.
+     */
+    @Override
+    public CDenseMatrix64F getR(CDenseMatrix64F R, boolean compact) {
+        if( R == null ) {
+            if( compact ) {
+                R = new CDenseMatrix64F(minLength,numCols);
+            } else
+                R = new CDenseMatrix64F(numRows,numCols);
+        } else {
+            if( compact ) {
+                if( R.numCols != numCols || R.numRows != minLength )
+                    throw new IllegalArgumentException(
+                            "Unexpected dimensions: found( "+R.numRows+" "+R.numCols+" ) expected( "+minLength+" "+numCols+" )");
+            } else {
+                if( R.numCols != numCols || R.numRows != numRows )
+                    throw new IllegalArgumentException("Unexpected dimensions");
+            }
+
+            for( int i = 0; i < R.numRows; i++ ) {
+                int min = Math.min(i,R.numCols);
+                for( int j = 0; j < min; j++ ) {
+                    R.set(i,j,0,0);
+                }
+            }
+        }
+
+        for( int j = 0; j < numCols; j++ ) {
+            double colR[] = dataQR[j];
+            int l = Math.min(j,numRows-1);
+            for( int i = 0; i <= l; i++ ) {
+                R.set(i,j,colR[i*2],colR[i*2+1]);
+            }
+        }
+
+        return R;
+    }
+
+    /**
+     * <p>
+     * To decompose the matrix 'A' it must have full rank.  'A' is a 'm' by 'n' matrix.
+     * It requires about 2n*m<sup>2</sup>-2m<sup>2</sup>/3 flops.
+     * </p>
+     *
+     * <p>
+     * The matrix provided here can be of different
+     * dimension than the one specified in the constructor.  It just has to be smaller than or equal
+     * to it.
+     * </p>
+     */
+    @Override
+    public boolean decompose( CDenseMatrix64F A ) {
+        setExpectedMaxSize(A.numRows, A.numCols);
+
+        convertToColumnMajor(A);
+
+        error = false;
+
+        for( int j = 0; j < minLength; j++ ) {
+            householder(j);
+            updateA(j);
+        }
+
+        return !error;
+    }
+
+    @Override
+    public boolean inputModified() {
+        return false;
+    }
+
+    /**
+     * Converts the standard row-major matrix into a column-major vector
+     * that is advantageous for this problem.
+     *
+     * @param A original matrix that is to be decomposed.
+     */
+    protected void convertToColumnMajor(CDenseMatrix64F A) {
+        for( int x = 0; x < numCols; x++ ) {
+            double colQ[] = dataQR[x];
+            int indexCol = 0;
+            for( int y = 0; y < numRows; y++ ) {
+                int index = (y*numCols+x)*2;
+                colQ[indexCol++] = A.data[index];
+                colQ[indexCol++] = A.data[index+1];
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Computes the householder vector "u" for the first column of submatrix j.  Note this is
+     * a specialized householder for this problem.  There is some protection against
+     * overfloaw and underflow.
+     * </p>
+     * <p>
+     * Q = I - γuu<sup>T</sup>
+     * </p>
+     * <p>
+     * This function finds the values of 'u' and 'γ'.
+     * </p>
+     *
+     * @param j Which submatrix to work off of.
+     */
+    protected void householder( int j )
+    {
+        final double u[] = dataQR[j];
+
+        // find the largest value in this column
+        // this is used to normalize the column and mitigate overflow/underflow
+        final double max = QrHelperFunctions_CD64.findMax(u, j, numRows - j);
+
+        if( max == 0.0 ) {
+            gamma = 0;
+            error = true;
+        } else {
+            // computes tau and gamma, and normalizes u by max
+            gamma = QrHelperFunctions_CD64.computeTauGammaAndDivide(j, numRows, u, max, tau);
+
+            // divide u by u_0
+//            double u_0 = u[j] + tau;
+            double real_u_0 = u[j*2] + tau.real;
+            double imag_u_0 = u[j*2+1] + tau.imaginary;
+            QrHelperFunctions_CD64.divideElements(j + 1, numRows, u, 0, real_u_0,imag_u_0 );
+
+            tau.real *= max;
+            tau.imaginary *= max;
+
+            u[j*2]   = -tau.real;
+            u[j*2+1] = -tau.imaginary;
+        }
+
+        gammas[j] = gamma;
+    }
+
+    /**
+     * <p>
+     * Takes the results from the householder computation and updates the 'A' matrix.<br>
+     * <br>
+     * A = (I - γ*u*u<sup>H</sup>)A
+     * </p>
+     *
+     * @param w The submatrix.
+     */
+    protected void updateA( int w )
+    {
+        final double u[] = dataQR[w];
+
+        for( int j = w+1; j < numCols; j++ ) {
+
+            final double colQ[] = dataQR[j];
+            // first element in u is assumed to be 1.0 + 0*i
+            double realSum = colQ[w*2];
+            double imagSum = colQ[w*2+1];
+
+            for( int k = w+1; k < numRows; k++ ) {
+                double realU = u[k*2];
+                double imagU = -u[k*2+1];
+
+                double realQ = colQ[k*2];
+                double imagQ = colQ[k*2+1];
+
+                realSum += realU*realQ - imagU*imagQ;
+                imagSum += imagU*realQ + realU*imagQ;
+            }
+            realSum *= gamma;
+            imagSum *= gamma;
+
+            colQ[w*2  ] -= realSum;
+            colQ[w*2+1] -= imagSum;
+
+            for( int i = w+1; i < numRows; i++ ) {
+                double realU = u[i*2];
+                double imagU = u[i*2+1];
+
+                colQ[i*2]  -= realU*realSum - imagU*imagSum;
+                colQ[i*2+1]-= imagU*realSum + realU*imagSum;
+            }
+        }
+    }
+
+    public double[] getGammas() {
+        return gammas;
+    }
+}
\ No newline at end of file
diff --git a/main/denseC64/src/org/ejml/alg/dense/decompose/qr/QRDecompositionHouseholderTran_CD64.java b/main/denseC64/src/org/ejml/alg/dense/decompose/qr/QRDecompositionHouseholderTran_CD64.java
new file mode 100644
index 0000000..43ea338
--- /dev/null
+++ b/main/denseC64/src/org/ejml/alg/dense/decompose/qr/QRDecompositionHouseholderTran_CD64.java
@@ -0,0 +1,384 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decompose.qr;
+
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.data.Complex64F;
+import org.ejml.interfaces.decomposition.QRDecomposition;
+import org.ejml.ops.CCommonOps;
+
+
+/**
+ * <p>
+ * Householder QR decomposition is rich in operations along the columns of the matrix.  This can be
+ * taken advantage of by solving for the Q matrix in a column major format to reduce the number
+ * of CPU cache misses and the number of copies that are performed.
+ * </p>
+ *
+ * @see org.ejml.alg.dense.decomposition.qr.QRDecompositionHouseholder_D64
+ *
+ * @author Peter Abeles
+ */
+// TODO figure out why this is significantly slower than col
+public class QRDecompositionHouseholderTran_CD64 implements QRDecomposition<CDenseMatrix64F> {
+
+    /**
+     * Where the Q and R matrices are stored.  For speed reasons
+     * this is transposed
+     */
+    protected CDenseMatrix64F QR;
+
+    // used internally to store temporary data
+    protected double v[];
+
+    // dimension of the decomposed matrices
+    protected int numCols; // this is 'n'
+    protected int numRows; // this is 'm'
+    protected int minLength;
+
+    // the computed gamma for Q_k matrix
+    protected double gammas[];
+    // local variables
+    protected double gamma;
+    protected Complex64F tau = new Complex64F();
+
+    // did it encounter an error?
+    protected boolean error;
+
+    public void setExpectedMaxSize( int numRows , int numCols ) {
+        this.numCols = numCols;
+        this.numRows = numRows;
+        minLength = Math.min(numCols,numRows);
+        int maxLength = Math.max(numCols,numRows);
+
+        if( QR == null ) {
+            QR = new CDenseMatrix64F(numCols,numRows);
+            v = new double[ maxLength*2 ];
+            gammas = new double[ minLength ];
+        } else {
+            QR.reshape(numCols,numRows);
+        }
+
+        if( v.length < maxLength*2 ) {
+            v = new double[ maxLength*2 ];
+        }
+        if( gammas.length < minLength ) {
+            gammas = new double[ minLength ];
+        }
+    }
+
+    /**
+     * Inner matrix that stores the decomposition
+     */
+    public CDenseMatrix64F getQR() {
+        return QR;
+    }
+
+    /**
+     * Computes the Q matrix from the information stored in the QR matrix.  This
+     * operation requires about 4(m<sup2</sup>n-mn<sup>2</sup>+n<sup>3</sup>/3) flops.
+     *
+     * @param Q The orthogonal Q matrix.
+     */
+    @Override
+    public CDenseMatrix64F getQ( CDenseMatrix64F Q , boolean compact ) {
+        if( compact ) {
+            if( Q == null ) {
+                Q = CCommonOps.identity(numRows, minLength);
+            } else {
+                if( Q.numRows != numRows || Q.numCols != minLength ) {
+                    throw new IllegalArgumentException("Unexpected matrix dimension.");
+                } else {
+                    CCommonOps.setIdentity(Q);
+                }
+            }
+        } else {
+            if( Q == null ) {
+                Q = CCommonOps.identity(numRows);
+            } else {
+                if( Q.numRows != numRows || Q.numCols != numRows ) {
+                    throw new IllegalArgumentException("Unexpected matrix dimension.");
+                } else {
+                    CCommonOps.setIdentity(Q);
+                }
+            }
+        }
+
+        // Unlike applyQ() this takes advantage of zeros in the identity matrix
+        // by not multiplying across all rows.
+        for( int j = minLength-1; j >= 0; j-- ) {
+            int diagIndex = (j*numRows+j)*2;
+            double realBefore = QR.data[diagIndex];
+            double imagBefore = QR.data[diagIndex+1];
+
+            QR.data[diagIndex] = 1;
+            QR.data[diagIndex+1] = 0;
+
+            QrHelperFunctions_CD64.rank1UpdateMultR(Q, QR.data, j * numRows, gammas[j], j, j, numRows, v);
+
+            QR.data[diagIndex] = realBefore;
+            QR.data[diagIndex+1] = imagBefore;
+        }
+
+        return Q;
+    }
+
+    /**
+     * A = Q*A
+     *
+     * @param A Matrix that is being multiplied by Q.  Is modified.
+     */
+    public void applyQ( CDenseMatrix64F A ) {
+        if( A.numRows != numRows )
+            throw new IllegalArgumentException("A must have at least "+numRows+" rows.");
+
+        for( int j = minLength-1; j >= 0; j-- ) {
+            int diagIndex = (j*numRows+j)*2;
+            double realBefore = QR.data[diagIndex];
+            double imagBefore = QR.data[diagIndex+1];
+
+            QR.data[diagIndex] = 1;
+            QR.data[diagIndex+1] = 0;
+
+            QrHelperFunctions_CD64.rank1UpdateMultR(A, QR.data, j * numRows, gammas[j], 0, j, numRows, v);
+
+            QR.data[diagIndex] = realBefore;
+            QR.data[diagIndex+1] = imagBefore;
+        }
+    }
+
+    /**
+     * A = Q<sup>H</sup>*A
+     *
+     * @param A Matrix that is being multiplied by Q<sup>T</sup>.  Is modified.
+     */
+    public void applyTranQ( CDenseMatrix64F A ) {
+        for( int j = 0; j < minLength; j++ ) {
+            int diagIndex = (j*numRows+j)*2;
+            double realBefore = QR.data[diagIndex];
+            double imagBefore = QR.data[diagIndex+1];
+
+            QR.data[diagIndex] = 1;
+            QR.data[diagIndex+1] = 0;
+
+            QrHelperFunctions_CD64.rank1UpdateMultR(A, QR.data, j * numRows, gammas[j], 0, j, numRows, v);
+
+            QR.data[diagIndex] = realBefore;
+            QR.data[diagIndex+1] = imagBefore;
+        }
+    }
+
+    /**
+     * Returns an upper triangular matrix which is the R in the QR decomposition.
+     *
+     * @param R An upper triangular matrix.
+     * @param compact
+     */
+    @Override
+    public CDenseMatrix64F getR(CDenseMatrix64F R, boolean compact) {
+        if( R == null ) {
+            if( compact ) {
+                R = new CDenseMatrix64F(minLength,numCols);
+            } else
+                R = new CDenseMatrix64F(numRows,numCols);
+        } else {
+            if( compact ) {
+                if( R.numCols != numCols || R.numRows != minLength )
+                    throw new IllegalArgumentException("Unexpected dimensions");
+            } else {
+                if( R.numCols != numCols || R.numRows != numRows )
+                    throw new IllegalArgumentException("Unexpected dimensions");
+            }
+
+            for( int i = 0; i < R.numRows; i++ ) {
+                int min = Math.min(i,R.numCols);
+                for( int j = 0; j < min; j++ ) {
+                    R.set(i, j, 0, 0);
+                }
+            }
+        }
+
+        for( int i = 0; i < R.numRows; i++ ) {
+            for( int j = i; j < R.numCols; j++ ) {
+                int index = QR.getIndex(j,i);
+                R.set(i,j,QR.data[index],QR.data[index+1]);
+            }
+        }
+
+        return R;
+    }
+
+    /**
+     * <p>
+     * To decompose the matrix 'A' it must have full rank.  'A' is a 'm' by 'n' matrix.
+     * It requires about 2n*m<sup>2</sup>-2m<sup>2</sup>/3 flops.
+     * </p>
+     *
+     * <p>
+     * The matrix provided here can be of different
+     * dimension than the one specified in the constructor.  It just has to be smaller than or equal
+     * to it.
+     * </p>
+     */
+    @Override
+    public boolean decompose( CDenseMatrix64F A ) {
+        setExpectedMaxSize(A.numRows, A.numCols);
+
+        CCommonOps.transpose(A, QR);
+
+        error = false;
+
+        for( int j = 0; j < minLength; j++ ) {
+            householder(j);
+            updateA(j);
+        }
+
+        return !error;
+    }
+
+    @Override
+    public boolean inputModified() {
+        return false;
+    }
+
+    /**
+     * <p>
+     * Computes the householder vector "u" for the first column of submatrix j.  Note this is
+     * a specialized householder for this problem.  There is some protection against
+     * overflow and underflow.
+     * </p>
+     * <p>
+     * Q = I - γuu<sup>H</sup>
+     * </p>
+     * <p>
+     * This function finds the values of 'u' and 'γ'.
+     * </p>
+     *
+     * @param j Which submatrix to work off of.
+     */
+    protected void householder( final int j )
+    {
+        int startQR = j*numRows;
+        int endQR = startQR+numRows;
+        startQR += j;
+
+        final double max = QrHelperFunctions_CD64.findMax(QR.data, startQR, numRows - j);
+
+        if( max == 0.0 ) {
+            gamma = 0;
+            error = true;
+        } else {
+            // computes tau and normalizes u by max
+            gamma = QrHelperFunctions_CD64.computeTauGammaAndDivide(startQR, endQR, QR.data, max,tau);
+
+            // divide u by u_0
+            double realU0 = QR.data[startQR*2] + tau.real;
+            double imagU0 = QR.data[startQR*2+1] + tau.imaginary;
+
+            QrHelperFunctions_CD64.divideElements(startQR + 1, endQR, QR.data,0,realU0, imagU0);
+
+            tau.real *= max;
+            tau.imaginary *= max;
+
+            QR.data[startQR*2] = -tau.real;
+            QR.data[startQR*2+1] = -tau.imaginary;
+        }
+
+        gammas[j] = gamma;
+    }
+
+    /**
+     * <p>
+     * Takes the results from the householder computation and updates the 'A' matrix.<br>
+     * <br>
+     * A = (I - γ*u*u<sup>H</sup>)A
+     * </p>
+     *
+     * @param w The submatrix.
+     */
+    protected void updateA( final int w )
+    {
+//        int rowW = w*numRows;
+//        int rowJ = rowW + numRows;
+//
+//        for( int j = w+1; j < numCols; j++ , rowJ += numRows) {
+//            double val = QR.data[rowJ + w];
+//
+//            // val = gamma*u^T * A
+//            for( int k = w+1; k < numRows; k++ ) {
+//                val += QR.data[rowW + k]*QR.data[rowJ + k];
+//            }
+//            val *= gamma;
+//
+//            // A - val*u
+//            QR.data[rowJ + w] -= val;
+//            for( int i = w+1; i < numRows; i++ ) {
+//                QR.data[rowJ + i] -= QR.data[rowW + i]*val;
+//            }
+//        }
+
+        final double data[] = QR.data;
+        int rowW = w*numRows + w + 1;
+        int rowJ = rowW + numRows;
+        final int rowJEnd = 2*(rowJ + (numCols-w-1)*numRows);
+        final int indexWEnd = 2*(rowW + numRows - w - 1);
+
+        rowJ = 2*rowJ;
+        rowW = 2*rowW;
+
+        for( ; rowJEnd != rowJ; rowJ += numRows*2) {
+            // assume the first element in u is 1
+            double realVal = data[rowJ - 2];
+            double imagVal = data[rowJ - 1];
+
+            int indexW = rowW;
+            int indexJ = rowJ;
+
+            while( indexW != indexWEnd ) {
+                double realW = data[indexW++];
+                double imagW = -data[indexW++];
+
+                double realJ = data[indexJ++];
+                double imagJ = data[indexJ++];
+
+                realVal += realW*realJ - imagW*imagJ;
+                imagVal += realW*imagJ + imagW*realJ;
+            }
+            realVal *= gamma;
+            imagVal *= gamma;
+
+            data[rowJ - 2] -= realVal;
+            data[rowJ - 1] -= imagVal;
+
+            indexW = rowW;
+            indexJ = rowJ;
+            while( indexW != indexWEnd ) {
+                double realW = data[indexW++];
+                double imagW = data[indexW++];
+
+                data[indexJ++] -= realW*realVal - imagW*imagVal;
+                data[indexJ++] -= realW*imagVal + imagW*realVal;
+            }
+        }
+    }
+
+    public double[] getGammas() {
+        return gammas;
+    }
+}
\ No newline at end of file
diff --git a/main/denseC64/src/org/ejml/alg/dense/decompose/qr/QRDecompositionHouseholder_CD64.java b/main/denseC64/src/org/ejml/alg/dense/decompose/qr/QRDecompositionHouseholder_CD64.java
new file mode 100644
index 0000000..ef66eca
--- /dev/null
+++ b/main/denseC64/src/org/ejml/alg/dense/decompose/qr/QRDecompositionHouseholder_CD64.java
@@ -0,0 +1,444 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decompose.qr;
+
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.interfaces.decomposition.QRDecomposition;
+import org.ejml.ops.CCommonOps;
+
+
+/**
+ * <p>
+ * This variation of complex QR decomposition uses reflections to compute the Q matrix.
+ * Each reflection uses a householder operations, hence its name.  To provide a meaningful solution
+ * the original matrix must have full rank.  This is intended for processing of small to medium matrices.
+ * </p>
+ * <p>
+ * Both Q and R are stored in the same m by n matrix.  Q is not stored directly, instead the u from
+ * Q<sub>k</sub>=(I-γ*u*u<sup>H</sup>) is stored.  Decomposition requires about 2n*m<sup>2</sup>-2m<sup>2</sup>/3 flops.
+ * </p>
+ *
+ * <p>
+ * See the QR reflections algorithm described in:<br>
+ * David S. Watkins, "Fundamentals of Matrix Computations" 2nd Edition, 2002
+ * </p>
+ *
+ * <p>
+ * For the most part this is a straight forward implementation.  To improve performance on large matrices a column is
+ * written to an array and the order
+ * of some of the loops has been changed.  This will degrade performance noticeably on small matrices.  Since
+ * it is unlikely that the QR decomposition would be a bottle neck when small matrices are involved only
+ * one implementation is provided.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class QRDecompositionHouseholder_CD64 implements QRDecomposition<CDenseMatrix64F> {
+
+    /**
+     * Where the Q and R matrices are stored.  R is stored in the
+     * upper triangular portion and Q on the lower bit.  Lower columns
+     * are where u is stored.  Q_k = (I - gamma_k*u_k*u_k^H).
+     */
+    protected CDenseMatrix64F QR;
+
+    // used internally to store temporary data
+    protected double u[],v[];
+
+    // dimension of the decomposed matrices
+    protected int numCols; // this is 'n'
+    protected int numRows; // this is 'm'
+    protected int minLength;
+
+    protected double dataQR[];
+
+    // the computed gamma for Q_k matrix
+    protected double gammas[];
+    // local variables
+    protected double realGamma; // gamma is always real
+    protected double realTau,imagTau;
+
+
+    // did it encounter an error?
+    protected boolean error;
+
+    public void setExpectedMaxSize( int numRows , int numCols ) {
+        error = false;
+
+        this.numCols = numCols;
+        this.numRows = numRows;
+        minLength = Math.min(numRows,numCols);
+        int maxLength = Math.max(numRows,numCols);
+
+        if( QR == null ) {
+            QR = new CDenseMatrix64F(numRows,numCols);
+            u = new double[ maxLength*2 ];
+            v = new double[ maxLength*2 ];
+            gammas = new double[ minLength ];
+        } else {
+            QR.reshape(numRows,numCols);
+        }
+
+        dataQR = QR.data;
+
+        if( u.length < maxLength*2 ) {
+            u = new double[ maxLength*2 ];
+            v = new double[ maxLength*2 ];
+        }
+
+        if( gammas.length < minLength ) {
+            gammas = new double[ minLength ];
+        }
+    }
+
+    /**
+     * Returns a single matrix which contains the combined values of Q and R.  This
+     * is possible since Q is symmetric and R is upper triangular.
+     *
+     * @return The combined Q R matrix.
+     */
+    public CDenseMatrix64F getQR() {
+        return QR;
+    }
+
+    /**
+     * Computes the Q matrix from the information stored in the QR matrix.  This
+     * operation requires about 4(m<sup>2</sup>n-mn<sup>2</sup>+n<sup>3</sup>/3) flops.
+     *
+     * @param Q The orthogonal Q matrix.
+     */
+    @Override
+    public CDenseMatrix64F getQ( CDenseMatrix64F Q , boolean compact ) {
+        if( compact ) {
+            if( Q == null ) {
+                Q = CCommonOps.identity(numRows,minLength);
+            } else {
+                if( Q.numRows != numRows || Q.numCols != minLength ) {
+                    throw new IllegalArgumentException("Unexpected matrix dimension.");
+                } else {
+                    CCommonOps.setIdentity(Q);
+                }
+            }
+        } else {
+            if( Q == null ) {
+                Q = CCommonOps.identity(numRows);
+            } else {
+                if( Q.numRows != numRows || Q.numCols != numRows ) {
+                    throw new IllegalArgumentException("Unexpected matrix dimension.");
+                } else {
+                    CCommonOps.setIdentity(Q);
+                }
+            }
+        }
+
+        for( int j = minLength-1; j >= 0; j-- ) {
+            u[2*j]   = 1;
+            u[2*j+1] = 0;
+
+            for( int i = j+1; i < numRows; i++ ) {
+                int indexQR = QR.getIndex(i,j);
+                u[i*2] = QR.data[indexQR];
+                u[i*2+1] = QR.data[indexQR+1];
+            }
+            QrHelperFunctions_CD64.rank1UpdateMultR(Q,u,0,gammas[j],j,j,numRows,v);
+        }
+
+        return Q;
+    }
+
+    /**
+     * Returns an upper triangular matrix which is the R in the QR decomposition.
+     *
+     * @param R An upper triangular matrix.
+     * @param compact
+     */
+    @Override
+    public CDenseMatrix64F getR(CDenseMatrix64F R, boolean compact) {
+        if( R == null ) {
+            if( compact ) {
+                R = new CDenseMatrix64F(minLength,numCols);
+            } else
+                R = new CDenseMatrix64F(numRows,numCols);
+        } else {
+            if( compact ) {
+                if( R.numCols != numCols || R.numRows != minLength )
+                    throw new IllegalArgumentException("Unexpected dimensions");
+            } else {
+                if( R.numCols != numCols || R.numRows != numRows )
+                    throw new IllegalArgumentException("Unexpected dimensions");
+            }
+
+            for( int i = 0; i < R.numRows; i++ ) {
+                int min = Math.min(i,R.numCols);
+                for( int j = 0; j < min; j++ ) {
+                    R.set(i,j,0,0);
+                }
+            }
+        }
+
+        for( int i = 0; i < minLength; i++ ) {
+            for( int j = i; j < numCols; j++ ) {
+                int indexQR = QR.getIndex(i,j);
+                double realQR = QR.data[indexQR];
+                double imagQR = QR.data[indexQR+1];
+
+                R.set(i,j,realQR,imagQR);
+            }
+        }
+
+        return R;
+    }
+
+    /**
+     * <p>
+     * In order to decompose the matrix 'A' it must have full rank.  'A' is a 'm' by 'n' matrix.
+     * It requires about 2n*m<sup>2</sup>-2m<sup>2</sup>/3 flops.
+     * </p>
+     *
+     * <p>
+     * The matrix provided here can be of different
+     * dimension than the one specified in the constructor.  It just has to be smaller than or equal
+     * to it.
+     * </p>
+     */
+    @Override
+    public boolean decompose( CDenseMatrix64F A ) {
+        commonSetup(A);
+
+        for( int j = 0; j < minLength; j++ ) {
+            householder(j);
+            updateA(j);
+        }
+
+        return !error;
+    }
+
+    @Override
+    public boolean inputModified() {
+        return false;
+    }
+
+    /**
+     * <p>
+     * Computes the householder vector "u" for the first column of submatrix j.  Note this is
+     * a specialized householder for this problem.  There is some protection against
+     * overflow and underflow.
+     * </p>
+     * <p>
+     * Q = I - γuu<sup>H</sup>
+     * </p>
+     * <p>
+     * This function finds the values of 'u' and 'γ'.
+     * </p>
+     *
+     * @param j Which submatrix to work off of.
+     */
+    protected void householder( int j )
+    {
+        // find the element with the largest absolute value in the column and make a copy
+        int indexQR = 2*(j+j*numCols);
+        int indexU = 2*j;
+        double max = 0;
+        for( int i = j; i < numRows; i++ ) {
+
+            double realD = u[indexU++] = dataQR[indexQR];
+            double imagD = u[indexU++] = dataQR[indexQR+1];
+
+            // absolute value of d
+            double magD = realD*realD + imagD*imagD;
+            if( max < magD ) {
+                max = magD;
+            }
+            indexQR += numCols*2;
+        }
+        max = Math.sqrt(max);
+
+        if( max == 0.0 ) {
+            realGamma = 0;
+            error = true;
+        } else {
+            // compute the norm2 of the vector, with each element
+            // normalized by the max value to avoid overflow problems
+            double nx = 0;
+            indexU = 2*j;
+
+            for( int i = j; i < numRows; i++ ) {
+                double realD = u[indexU++] /= max;
+                double imagD = u[indexU++] /= max;
+
+                nx += realD*realD + imagD*imagD;
+            }
+            nx = Math.sqrt(nx);
+
+            double real_x0 = u[2*j];
+            double imag_x0 = u[2*j+1];
+            double mag_x0 = Math.sqrt(real_x0*real_x0 + imag_x0*imag_x0);
+
+            // TODO Could stability be improved by computing theta so that this
+            // special case doesn't need to be handled?
+            if( mag_x0 == 0 ) {
+                realTau = nx;
+                imagTau = 0;
+            } else {
+                realTau = real_x0 / mag_x0 * nx;
+                imagTau = imag_x0 / mag_x0 * nx;
+            }
+
+            double top,bottom;
+
+            // if there is a chance they can cancel swap the sign
+            if ( real_x0*realTau<0) {
+                realTau = -realTau;
+                imagTau = -imagTau;
+                top = nx * nx - nx *mag_x0;
+                bottom = mag_x0*mag_x0 - 2.0* nx *mag_x0 + nx * nx;
+            } else {
+                top = nx * nx + nx *mag_x0;
+                bottom = mag_x0*mag_x0 + 2.0* nx *mag_x0 + nx * nx;
+            }
+
+            realGamma = bottom/top;
+
+            double real_u_0 = real_x0 + realTau;
+            double imag_u_0 = imag_x0 + imagTau;
+            double norm_u_0 = real_u_0*real_u_0 + imag_u_0*imag_u_0;
+
+            indexU = (j+1)*2;
+            for( int i = j+1; i < numRows; i++ ) {
+                double realU = u[indexU];
+                double imagU = u[indexU+1];
+
+                u[indexU++] = (realU*real_u_0 + imagU*imag_u_0)/norm_u_0;
+                u[indexU++] = (imagU*real_u_0 - realU*imag_u_0)/norm_u_0;
+            }
+            u[2*j  ] = 1;
+            u[2*j+1] = 0;
+
+            realTau *= max;
+            imagTau *= max;
+        }
+
+        gammas[j] = realGamma;
+    }
+
+    /**
+     * <p>
+     * Takes the results from the householder computation and updates the 'A' matrix.<br>
+     * <br>
+     * A = (I - γ*u*u<sup>H</sup>)A
+     * </p>
+     *
+     * @param w The submatrix.
+     */
+    protected void updateA( int w )
+    {
+        // much of the code below is equivalent to the rank1Update function
+        // however, since τ has already been computed there is no need to
+        // recompute it, saving a few multiplication operations
+//        for( int i = w+1; i < numCols; i++ ) {
+//            double val = 0;
+//
+//            for( int k = w; k < numRows; k++ ) {
+//                val += u[k]*dataQR[k*numCols +i];
+//            }
+//            v[i] = gamma*val;
+//        }
+
+        // This is functionally the same as the above code but the order has been changed
+        // to avoid jumping the cpu cache
+
+        int stride = numCols*2;
+        double realU = u[w*2];
+        double imagU = -u[w*2+1];
+
+        int indexQR = w*stride+(w+1)*2;
+        for( int i = w+1; i < numCols; i++ ) {
+
+            double realQR = dataQR[indexQR++];
+            double imagQR = dataQR[indexQR++];
+
+            v[i*2]   = realU*realQR - imagU*imagQR;
+            v[i*2+1] = realU*imagQR + imagU*realQR;
+        }
+
+        for( int k = w+1; k < numRows; k++ ) {
+            realU = u[k*2];
+            imagU = -u[k*2+1];
+
+            indexQR = k*stride+(w+1)*2;
+            for( int i = w+1; i < numCols; i++ ) {
+                double realQR = dataQR[indexQR++];
+                double imagQR = dataQR[indexQR++];
+
+//                v[i] += u[k]*dataQR[k*numCols +i];
+                v[i*2]   += realU*realQR - imagU*imagQR;
+                v[i*2+1] += realU*imagQR + imagU*realQR;
+            }
+        }
+
+        for( int i = w+1; i < numCols; i++ ) {
+            v[i*2] *= realGamma;
+            v[i*2+1] *= realGamma;
+        }
+
+        // end of reordered code
+
+        for( int i = w; i < numRows; i++ ) {
+            double realI = u[i*2];
+            double imagI = u[i*2+1];
+
+            indexQR = i*stride+(w+1)*2;
+            for( int j = w+1; j < numCols; j++ ) {
+                double realJ = v[j*2];
+                double imagJ = v[j*2+1];
+
+//                dataQR[i*numCols+j] -= valU*v[j];
+                dataQR[indexQR++] -= realI*realJ - imagI*imagJ;
+                dataQR[indexQR++] -= realI*imagJ + imagI*realJ;
+            }
+        }
+
+        if( w < numCols ) {
+            dataQR[2*w+w*stride] = -realTau;
+            dataQR[2*w+w*stride+1] = -imagTau;
+        }
+
+        // save the Q matrix in the lower portion of QR
+        for( int i = w+1; i < numRows; i++ ) {
+            dataQR[2*w+i*stride]     = u[i*2];
+            dataQR[2*w+i*stride + 1] = u[i*2 + 1];
+        }
+    }
+
+    /**
+     * This function performs sanity check on the input for decompose and sets up the QR matrix.
+     *
+     * @param A
+     */
+    protected void commonSetup(CDenseMatrix64F A) {
+        setExpectedMaxSize(A.numRows,A.numCols);
+
+        QR.set(A);
+    }
+
+    public double[] getGammas() {
+        return gammas;
+    }
+
+}
\ No newline at end of file
diff --git a/main/denseC64/src/org/ejml/alg/dense/decompose/qr/QrHelperFunctions_CD64.java b/main/denseC64/src/org/ejml/alg/dense/decompose/qr/QrHelperFunctions_CD64.java
new file mode 100644
index 0000000..97f7ba1
--- /dev/null
+++ b/main/denseC64/src/org/ejml/alg/dense/decompose/qr/QrHelperFunctions_CD64.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decompose.qr;
+
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.data.Complex64F;
+
+
+/**
+ * <p>
+ * Contains different functions that are useful for computing the QR decomposition of a matrix.
+ * </p>
+ *
+ * <p>
+ * Two different families of functions are provided for help in computing reflectors.  Internally
+ * both of these functions switch between normalization by division or multiplication.  Multiplication
+ * is most often significantly faster than division (2 or 3 times) but produces less accurate results
+ * on very small numbers.  It checks to see if round off error is significant and decides which
+ * one it should do.
+ * </p>
+ *
+ * <p>
+ * Tests were done using the stability benchmark in jmatbench and there doesn't seem to be
+ * any advantage to always dividing by the max instead of checking and deciding.  The most
+ * noticeable difference between the two methods is with very small numbers.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class QrHelperFunctions_CD64 {
+
+    /**
+     * Returns the maximum magnitude of the complex numbers
+     * @param u Array of complex numbers
+     * @param startU first index to consider in u
+     * @param length Number of complex numebrs to consider
+     * @return magnitude
+     */
+    public static double findMax( double[] u, int startU , int length ) {
+        double max = -1;
+
+        int index = startU*2;
+        int stopIndex = (startU + length)*2;
+        for( ; index < stopIndex;) {
+            double real = u[index++];
+            double img = u[index++];
+
+            double val = real*real + img*img;
+
+            if( val > max ) {
+                max = val;
+            }
+        }
+
+        return Math.sqrt(max);
+    }
+
+    /**
+     * Performs the following operation:<br>
+     * u[(startU+j):(startU+numRows)] /= A<br>
+     * were u and A are a complex
+     */
+    public static void divideElements(final int j, final int numRows , final double[] u,
+                                      final int startU ,
+                                      final double realA, final double imagA ) {
+
+        double mag2 = realA*realA + imagA*imagA;
+
+        int index = (j+startU)*2;
+
+        for( int i = j; i < numRows; i++ ) {
+            double realU = u[index];
+            double imagU = u[index+1];
+
+            // u[i+startU] /= u_0;
+            u[index++] = (realU*realA + imagU*imagA)/mag2;
+            u[index++] = (imagU*realA - realU*imagA)/mag2;
+        }
+    }
+
+    /**
+     * Performs the following operations:
+     * <pre>
+     * x = x / max
+     * tau = x0*|x|/|xo|   adjust sign to avoid cancelation
+     * u = x; u0 = x0 + tau; u = u/u0  (x is not divided by x0)
+     * gamma = 2/|u|^2
+     * </pre>
+     * Note that u is not explicitly computed here.
+     *
+     * @param start Element in 'u' that it starts at.
+     * @param stop Element in 'u' that it stops at.
+     * @param x Array
+     * @param max Max value in 'u' that is used to normalize it.
+     * @param tau Storage for tau
+     * @return Returns gamma
+     */
+    public static double computeTauGammaAndDivide(final int start, final int stop,
+                                                  final double[] x, final double max,
+                                                  Complex64F tau) {
+
+        int index = start*2;
+        double nx = 0;
+        for (int i = start; i < stop; i++) {
+            double realX = x[index++] /= max;
+            double imagX = x[index++] /= max;
+
+            nx += realX * realX + imagX * imagX;
+        }
+
+        nx = Math.sqrt(nx);
+
+        double real_x0 = x[2*start];
+        double imag_x0 = x[2*start+1];
+        double mag_x0 = Math.sqrt(real_x0*real_x0 + imag_x0*imag_x0);
+
+        // TODO Could stability be improved by computing theta so that this
+        // special case doesn't need to be handled?
+        if( mag_x0 == 0 ) {
+            tau.real = nx;
+            tau.imaginary = 0;
+        } else {
+            tau.real = real_x0 / mag_x0 * nx;
+            tau.imaginary = imag_x0 / mag_x0 * nx;
+        }
+
+        double top,bottom;
+
+        // if there is a chance they can cancel swap the sign
+        if ( real_x0*tau.real<0) {
+            tau.real = -tau.real;
+            tau.imaginary = -tau.imaginary;
+            top = nx * nx - nx *mag_x0;
+            bottom = mag_x0*mag_x0 - 2.0* nx *mag_x0 + nx * nx;
+        } else {
+            top = nx * nx + nx *mag_x0;
+            bottom = mag_x0*mag_x0 + 2.0* nx *mag_x0 + nx * nx;
+        }
+
+        return bottom/top; // gamma
+    }
+
+    /**
+     * <p>
+     * Performs a rank-1 update operation on the submatrix specified by w with the multiply on the right.<br>
+     * <br>
+     * A = (I - γ*u*u<sup>H</sup>)*A<br>
+     * </p>
+     *
+     * @param A matrix
+     * @param u vector
+     * @param offsetU offset added to w0 when indexing u.  Multiplied by 2 since complex.
+     * @param gammaR real component of gamma
+     * @param colA0 first column in A sub-matrix.
+     * @param w0 first index in sub-array in u and row sub-matrix in A
+     * @param w1 last index + 1 in sub-array in u and row sub-matrix in A
+     * @param _temp temporary storage.  Same size as u.
+     */
+    public static void rank1UpdateMultR(CDenseMatrix64F A,
+                                        double u[], int offsetU,
+                                        double gammaR ,
+                                        int colA0,
+                                        int w0, int w1,
+                                        double _temp[])
+    {
+//        for( int i = colA0; i < A.numCols; i++ ) {
+//            double val = 0;
+//
+//            for( int k = w0; k < w1; k++ ) {
+//                val += u[k+offsetU]*A.data[k*A.numCols +i];
+//            }
+//            _temp[i] = gamma*val;
+//        }
+
+        // reordered to reduce cpu cache issues
+        int indexU = (w0+offsetU)*2;
+        double realU = u[indexU];
+        double imagU = -u[indexU+1];
+
+        int indexA = w0*A.numCols*2 + colA0*2;
+        int indexTmp = colA0*2;
+
+        for( int i = colA0; i < A.numCols; i++ ) {
+            double realA = A.data[indexA++];
+            double imagA = A.data[indexA++];
+
+            _temp[indexTmp++] = realU*realA - imagU*imagA;
+            _temp[indexTmp++] = realU*imagA + imagU*realA;
+        }
+
+        for( int k = w0+1; k < w1; k++ ) {
+            indexA = k*A.numCols*2 + colA0*2;
+            indexU = (k+offsetU)*2;
+            indexTmp = colA0*2;
+
+            realU = u[indexU];
+            imagU = -u[indexU+1];
+
+            for( int i = colA0; i < A.numCols; i++ ) {
+                double realA = A.data[indexA++];
+                double imagA = A.data[indexA++];
+
+                _temp[indexTmp++] += realU*realA - imagU*imagA;
+                _temp[indexTmp++] += realU*imagA + imagU*realA;
+            }
+        }
+
+        indexTmp = colA0*2;
+        for( int i = colA0; i < A.numCols; i++ ) {
+            double realTmp = _temp[indexTmp];
+            double imagTmp = _temp[indexTmp+1];
+
+            _temp[indexTmp++] = gammaR*realTmp;
+            _temp[indexTmp++] = gammaR*imagTmp;
+        }
+
+        // end of reorder
+
+        for( int i = w0; i < w1; i++ ) {
+            indexA = i*A.numCols*2 + colA0*2;
+            indexU = (i+offsetU)*2;
+            indexTmp = colA0*2;
+
+            realU = u[indexU];
+            imagU = u[indexU+1];
+
+            for( int j = colA0; j < A.numCols; j++ ) {
+                double realTmp = _temp[indexTmp++];
+                double imagTmp = _temp[indexTmp++];
+
+                A.data[indexA++] -= realU*realTmp - imagU*imagTmp;
+                A.data[indexA++] -= realU*imagTmp + imagU*realTmp;
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs a rank-1 update operation on the submatrix specified by w with the multiply on the left.<br>
+     * <br>
+     * A = A(I - γ*u*u<sup>H</sup>)<br>
+     * </p>
+     * <p>
+     * The order that matrix multiplies are performed has been carefully selected
+     * to minimize the number of operations.
+     * </p>
+     *
+     * <p>
+     * Before this can become a truly generic operation the submatrix specification needs
+     * to be made more generic.
+     * </p>
+     */
+    public static void rank1UpdateMultL( CDenseMatrix64F A , double u[] ,
+                                         double gammaR , double gammaI ,
+                                         int colA0,
+                                         int w0 , int w1 )
+    {
+        for( int i = colA0; i < A.numRows; i++ ) {
+            int startIndex = i*A.numCols*2+w0*2;
+            double realSum = 0,imagSum=0;
+            int rowIndex = startIndex;
+            int indexU = w0*2;
+            for( int j = w0; j < w1; j++ ) {
+                double realA = A.data[rowIndex++];
+                double imajA = A.data[rowIndex++];
+
+                double realU = u[indexU++];
+                double imajU = u[indexU++];
+
+                realSum += realA*realU - imajA*imajU;
+                imagSum += realA*imajU + imajA*realU;
+            }
+            double realTmp = -(gammaR*realSum - gammaI*imagSum);
+            double imagTmp = -(gammaR*imagSum + gammaI*realSum);
+
+            rowIndex = startIndex;
+            indexU = w0*2;
+            for( int j = w0; j < w1; j++ ) {
+                double realU = u[indexU++];
+                double imagU = -u[indexU++];
+
+                A.data[rowIndex++] += realTmp*realU - imagTmp*imagU;
+                A.data[rowIndex++] += realTmp*imagU + imagTmp*realU;
+            }
+        }
+    }
+}
diff --git a/main/denseC64/src/org/ejml/alg/dense/linsol/CInvertUsingSolve.java b/main/denseC64/src/org/ejml/alg/dense/linsol/CInvertUsingSolve.java
new file mode 100644
index 0000000..301bcb4
--- /dev/null
+++ b/main/denseC64/src/org/ejml/alg/dense/linsol/CInvertUsingSolve.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol;
+
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.ejml.ops.CCommonOps;
+
+
+/**
+ * A matrix can be easily inverted by solving a system with an identify matrix.  The only
+ * disadvantage of this approach is that additional computations are required compared to
+ * a specialized solution.
+ *
+ * @author Peter Abeles
+ */
+public class CInvertUsingSolve {
+
+    public static void invert( LinearSolver<CDenseMatrix64F> solver , CDenseMatrix64F A , CDenseMatrix64F A_inv , CDenseMatrix64F storage) {
+
+        if( A.numRows != A_inv.numRows || A.numCols != A_inv.numCols) {
+            throw new IllegalArgumentException("A and A_inv must have the same dimensions");
+        }
+
+        CCommonOps.setIdentity(storage);
+
+        solver.solve(storage,A_inv);
+    }
+
+    public static void invert( LinearSolver<CDenseMatrix64F> solver , CDenseMatrix64F A , CDenseMatrix64F A_inv ) {
+
+        if( A.numRows != A_inv.numRows || A.numCols != A_inv.numCols) {
+            throw new IllegalArgumentException("A and A_inv must have the same dimensions");
+        }
+
+        CCommonOps.setIdentity(A_inv);
+
+        solver.solve(A_inv,A_inv);
+    }
+}
diff --git a/main/denseC64/src/org/ejml/alg/dense/linsol/LinearSolverAbstract_CD64.java b/main/denseC64/src/org/ejml/alg/dense/linsol/LinearSolverAbstract_CD64.java
new file mode 100644
index 0000000..72d3ee4
--- /dev/null
+++ b/main/denseC64/src/org/ejml/alg/dense/linsol/LinearSolverAbstract_CD64.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol;
+
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+
+
+/**
+ * <p>
+ * An abstract class that provides some common functionality and a default implementation
+ * of invert that uses the solve function of the child class.
+ * </p>
+ *
+ * <p>
+ * The extending class must explicity call {@link #_setA(org.ejml.data.CDenseMatrix64F)}
+ * inside of its {@link #setA} function.
+ * </p>
+ * 
+ * @author Peter Abeles
+ */
+public abstract class LinearSolverAbstract_CD64 implements LinearSolver<CDenseMatrix64F> {
+
+    protected CDenseMatrix64F A;
+    protected int numRows;
+    protected int numCols;
+    protected int stride;
+
+    public CDenseMatrix64F getA() {
+        return A;
+    }
+
+    protected void _setA(CDenseMatrix64F A) {
+        this.A = A;
+        this.numRows = A.numRows;
+        this.numCols = A.numCols;
+        this.stride = numCols*2;
+    }
+
+    @Override
+    public void invert(CDenseMatrix64F A_inv) {
+        CInvertUsingSolve.invert(this,A,A_inv);
+    }
+}
diff --git a/main/denseC64/src/org/ejml/alg/dense/linsol/chol/LinearSolverChol_CD64.java b/main/denseC64/src/org/ejml/alg/dense/linsol/chol/LinearSolverChol_CD64.java
new file mode 100644
index 0000000..5f22056
--- /dev/null
+++ b/main/denseC64/src/org/ejml/alg/dense/linsol/chol/LinearSolverChol_CD64.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.chol;
+
+import org.ejml.alg.dense.decompose.CTriangularSolver;
+import org.ejml.alg.dense.decompose.chol.CholeskyDecompositionCommon_CD64;
+import org.ejml.alg.dense.linsol.LinearSolverAbstract_CD64;
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.interfaces.decomposition.CholeskyDecomposition;
+import org.ejml.ops.CSpecializedOps;
+
+import java.util.Arrays;
+
+
+/**
+* @author Peter Abeles
+*/
+public class LinearSolverChol_CD64 extends LinearSolverAbstract_CD64 {
+
+    CholeskyDecompositionCommon_CD64 decomposer;
+    int n;
+    double vv[] = new double[0];
+    double t[];
+
+    public LinearSolverChol_CD64(CholeskyDecompositionCommon_CD64 decomposer) {
+        this.decomposer = decomposer;
+    }
+
+    @Override
+    public boolean setA(CDenseMatrix64F A) {
+        if( A.numRows != A.numCols )
+            throw new IllegalArgumentException("Matrix must be square");
+
+        _setA(A);
+
+        if( decomposer.decompose(A) ){
+            n = A.numCols;
+            if( vv.length < n*2 )
+                vv = new double[n*2];
+            t = decomposer._getT().data;
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public double quality() {
+        return CSpecializedOps.qualityTriangular(decomposer._getT());
+    }
+
+    /**
+     * <p>
+     * Using the decomposition, finds the value of 'X' in the linear equation below:<br>
+     *
+     * A*x = b<br>
+     *
+     * where A has dimension of n by n, x and b are n by m dimension.
+     * </p>
+     * <p>
+     * *Note* that 'b' and 'x' can be the same matrix instance.
+     * </p>
+     *
+     * @param B A matrix that is n by m.  Not modified.
+     * @param X An n by m matrix where the solution is writen to.  Modified.
+     */
+    @Override
+    public void solve( CDenseMatrix64F B , CDenseMatrix64F X ) {
+        if( B.numCols != X.numCols || B.numRows != n || X.numRows != n) {
+            throw new IllegalArgumentException("Unexpected matrix size");
+        }
+
+        int numCols = B.numCols;
+
+        double dataB[] = B.data;
+        double dataX[] = X.data;
+
+        if(decomposer.isLower()) {
+            for( int j = 0; j < numCols; j++ ) {
+                for( int i = 0; i < n; i++ ) {
+                    vv[i*2]   = dataB[(i*numCols+j)*2];
+                    vv[i*2+1] = dataB[(i*numCols+j)*2+1];
+                }
+                solveInternalL();
+                for( int i = 0; i < n; i++ ) {
+                    dataX[(i*numCols+j)*2  ] = vv[i*2];
+                    dataX[(i*numCols+j)*2+1] = vv[i*2+1];
+                }
+            }
+        } else {
+            throw new RuntimeException("Implement");
+        }
+    }
+
+    /**
+     * Used internally to find the solution to a single column vector.
+     */
+    private void solveInternalL() {
+        // This takes advantage of the diagonal elements always being real numbers
+
+        // solve L*y=b storing y in x
+        CTriangularSolver.solveL_diagReal(t, vv, n);
+
+        // solve L^T*x=y
+        CTriangularSolver.solveConjTranL_diagReal(t, vv, n);
+    }
+
+    /**
+     * Sets the matrix 'inv' equal to the inverse of the matrix that was decomposed.
+     *
+     * @param inv Where the value of the inverse will be stored.  Modified.
+     */
+    @Override
+    public void invert( CDenseMatrix64F inv ) {
+        if( inv.numRows != n || inv.numCols != n ) {
+            throw new RuntimeException("Unexpected matrix dimension");
+        }
+        if( inv.data == t ) {
+            throw new IllegalArgumentException("Passing in the same matrix that was decomposed.");
+        }
+
+        if(decomposer.isLower()) {
+            setToInverseL(inv.data);
+        } else {
+            throw new RuntimeException("Implement");
+        }
+    }
+
+    /**
+     * Sets the matrix to the inverse using a lower triangular matrix.
+     */
+    public void setToInverseL( double a[] ) {
+
+        // the more direct method which takes full advantage of the sparsity of the data structures proved to
+        // be difficult to get right due to the conjugates and reordering.
+        // See comparable real number code for an example.
+        for (int col = 0; col < n; col++) {
+            Arrays.fill(vv,0);
+            vv[col*2] = 1;
+            CTriangularSolver.solveL_diagReal(t, vv, n);
+            CTriangularSolver.solveConjTranL_diagReal(t, vv, n);
+            for( int i = 0; i < n; i++ ) {
+                a[(i*numCols+col)*2  ] = vv[i*2];
+                a[(i*numCols+col)*2+1] = vv[i*2+1];
+            }
+        }
+        // NOTE: If you want to make inverse faster take advantage of the sparsity
+    }
+
+    @Override
+    public boolean modifiesA() {
+        return decomposer.inputModified();
+    }
+
+    @Override
+    public boolean modifiesB() {
+        return false;
+    }
+
+    @Override
+    public CholeskyDecomposition<CDenseMatrix64F> getDecomposition() {
+        return decomposer;
+    }
+}
diff --git a/main/denseC64/src/org/ejml/alg/dense/linsol/lu/LinearSolverLuBase_CD64.java b/main/denseC64/src/org/ejml/alg/dense/linsol/lu/LinearSolverLuBase_CD64.java
new file mode 100644
index 0000000..3d0bcb7
--- /dev/null
+++ b/main/denseC64/src/org/ejml/alg/dense/linsol/lu/LinearSolverLuBase_CD64.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.lu;
+
+import org.ejml.alg.dense.decompose.lu.LUDecompositionBase_CD64;
+import org.ejml.alg.dense.linsol.LinearSolverAbstract_CD64;
+import org.ejml.data.CDenseMatrix64F;
+
+import java.util.Arrays;
+
+
+/**
+ * @author Peter Abeles
+ */
+public abstract class LinearSolverLuBase_CD64 extends LinearSolverAbstract_CD64 {
+
+    protected LUDecompositionBase_CD64 decomp;
+
+    public LinearSolverLuBase_CD64(LUDecompositionBase_CD64 decomp) {
+        this.decomp = decomp;
+
+    }
+
+    @Override
+    public boolean setA(CDenseMatrix64F A) {
+        _setA(A);
+
+        return decomp.decompose(A);
+    }
+
+    @Override
+    public double quality() {
+        return decomp.quality();
+    }
+
+    @Override
+    public void invert(CDenseMatrix64F A_inv) {
+        double []vv = decomp._getVV();
+        CDenseMatrix64F LU = decomp.getLU();
+
+        if( A_inv.numCols != LU.numCols || A_inv.numRows != LU.numRows )
+            throw new IllegalArgumentException("Unexpected matrix dimension");
+
+        int n = A.numCols;
+
+        double dataInv[] = A_inv.data;
+        int strideAinv = A_inv.getRowStride();
+
+        for( int j = 0; j < n; j++ ) {
+            // don't need to change inv into an identity matrix before hand
+            Arrays.fill(vv,0,n*2,0);
+            vv[j*2] = 1;
+            vv[j*2+1] = 0;
+
+            decomp._solveVectorInternal(vv);
+//            for( int i = 0; i < n; i++ ) dataInv[i* n +j] = vv[i];
+            int index = j*2;
+            for( int i = 0; i < n; i++ , index += strideAinv) {
+                dataInv[index] = vv[i*2];
+                dataInv[index+1] = vv[i*2+1];
+            }
+        }
+    }
+
+    @Override
+    public boolean modifiesA() {
+        return false;
+    }
+
+    @Override
+    public boolean modifiesB() {
+        return false;
+    }
+
+    @Override
+    public LUDecompositionBase_CD64 getDecomposition() {
+        return decomp;
+    }
+}
\ No newline at end of file
diff --git a/main/denseC64/src/org/ejml/alg/dense/linsol/lu/LinearSolverLu_CD64.java b/main/denseC64/src/org/ejml/alg/dense/linsol/lu/LinearSolverLu_CD64.java
new file mode 100644
index 0000000..d6b1910
--- /dev/null
+++ b/main/denseC64/src/org/ejml/alg/dense/linsol/lu/LinearSolverLu_CD64.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.lu;
+
+import org.ejml.alg.dense.decompose.lu.LUDecompositionBase_CD64;
+import org.ejml.data.CDenseMatrix64F;
+
+
+/**
+ * For each column in the B matrix it makes a copy, which is then solved for and
+ * writen into X.  By making a copy of the column cpu cache issues are reduced.
+ *
+ * @author Peter Abeles
+ */
+public class LinearSolverLu_CD64 extends LinearSolverLuBase_CD64 {
+
+    public LinearSolverLu_CD64(LUDecompositionBase_CD64 decomp) {
+        super(decomp);
+    }
+
+
+
+    @Override
+    public void solve(CDenseMatrix64F b, CDenseMatrix64F x) {
+        if( b.numCols != x.numCols || b.numRows != numRows || x.numRows != numCols) {
+            throw new IllegalArgumentException("Unexpected matrix size");
+        }
+
+        int bnumCols = b.numCols;
+        int bstride = b.getRowStride();
+
+        double dataB[] = b.data;
+        double dataX[] = x.data;
+
+        double []vv = decomp._getVV();
+
+//        for( int j = 0; j < numCols; j++ ) {
+//            for( int i = 0; i < this.numCols; i++ ) vv[i] = dataB[i*numCols+j];
+//            decomp._solveVectorInternal(vv);
+//            for( int i = 0; i < this.numCols; i++ ) dataX[i*numCols+j] = vv[i];
+//        }
+        for( int j = 0; j < bnumCols; j++ ) {
+            int index = j*2;
+            for( int i = 0; i < numRows; i++ , index += bstride ) {
+                vv[i*2]   = dataB[index];
+                vv[i*2+1] = dataB[index+1];
+            }
+            decomp._solveVectorInternal(vv);
+            index = j*2;
+            for( int i = 0; i < numRows; i++ , index += bstride ) {
+                dataX[index]   = vv[i*2];
+                dataX[index+1] = vv[i*2+1];
+            }
+        }
+
+    }
+}
diff --git a/main/denseC64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQrHouseCol_CD64.java b/main/denseC64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQrHouseCol_CD64.java
new file mode 100644
index 0000000..88b869d
--- /dev/null
+++ b/main/denseC64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQrHouseCol_CD64.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.qr;
+
+import org.ejml.alg.dense.decompose.CTriangularSolver;
+import org.ejml.alg.dense.decompose.qr.QRDecompositionHouseholderColumn_CD64;
+import org.ejml.alg.dense.decompose.qr.QrHelperFunctions_CD64;
+import org.ejml.alg.dense.linsol.LinearSolverAbstract_CD64;
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.ops.CSpecializedOps;
+
+
+/**
+ * <p>
+ * QR decomposition can be used to solve for systems.  However, this is not as computationally efficient
+ * as LU decomposition and costs about 3n<sup>2</sup> flops.
+ * </p>
+ * <p>
+ * It solve for x by first multiplying b by the transpose of Q then solving for the result.
+ * <br>
+ * QRx=b<br>
+ * Rx=Q^H b<br>
+ * </p>
+ *
+ * <p>
+ * A column major decomposition is used in this solver.
+ * <p>
+ *
+ * @author Peter Abeles
+ */
+public class LinearSolverQrHouseCol_CD64 extends LinearSolverAbstract_CD64 {
+
+    private QRDecompositionHouseholderColumn_CD64 decomposer;
+
+    private CDenseMatrix64F a = new CDenseMatrix64F(1,1);
+    private CDenseMatrix64F temp = new CDenseMatrix64F(1,1);
+
+    protected int maxRows = -1;
+    protected int maxCols = -1;
+
+    private double[][] QR; // a column major QR matrix
+    private CDenseMatrix64F R = new CDenseMatrix64F(1,1);
+    private double gammas[];
+
+    /**
+     * Creates a linear solver that uses QR decomposition.
+     */
+    public LinearSolverQrHouseCol_CD64() {
+        decomposer = new QRDecompositionHouseholderColumn_CD64();
+    }
+
+    public void setMaxSize( int maxRows , int maxCols )
+    {
+        this.maxRows = maxRows; this.maxCols = maxCols;
+    }
+
+    /**
+     * Performs QR decomposition on A
+     *
+     * @param A not modified.
+     */
+    @Override
+    public boolean setA(CDenseMatrix64F A) {
+        if( A.numRows < A.numCols )
+            throw new IllegalArgumentException("Can't solve for wide systems.  More variables than equations.");
+        if( A.numRows > maxRows || A.numCols > maxCols )
+            setMaxSize(A.numRows,A.numCols);
+
+        R.reshape(A.numCols,A.numCols);
+        a.reshape(A.numRows,1);
+        temp.reshape(A.numRows,1);
+
+        _setA(A);
+        if( !decomposer.decompose(A) )
+            return false;
+
+        gammas = decomposer.getGammas();
+        QR = decomposer.getQR();
+        decomposer.getR(R,true);
+        return true;
+    }
+
+    @Override
+    public double quality() {
+        return CSpecializedOps.qualityTriangular(R);
+    }
+
+    /**
+     * Solves for X using the QR decomposition.
+     *
+     * @param B A matrix that is n by m.  Not modified.
+     * @param X An n by m matrix where the solution is written to.  Modified.
+     */
+    @Override
+    public void solve(CDenseMatrix64F B, CDenseMatrix64F X) {
+        if( X.numRows != numCols )
+            throw new IllegalArgumentException("Unexpected dimensions for X: X rows = "+X.numRows+" expected = "+numCols);
+        else if( B.numRows != numRows || B.numCols != X.numCols )
+            throw new IllegalArgumentException("Unexpected dimensions for B");
+
+        int BnumCols = B.numCols;
+        
+        // solve each column one by one
+        for( int colB = 0; colB < BnumCols; colB++ ) {
+
+            // make a copy of this column in the vector
+            for( int i = 0; i < numRows; i++ ) {
+                int indexB = (i*BnumCols + colB)*2;
+                a.data[i*2]   = B.data[indexB];
+                a.data[i*2+1] = B.data[indexB+1];
+            }
+
+            // Solve Qa=b
+            // a = Q'b
+            // a = Q_{n-1}...Q_2*Q_1*b
+            //
+            // Q_n*b = (I-gamma*u*u^T)*b = b - u*(gamma*U^T*b)
+            for( int n = 0; n < numCols; n++ ) {
+                double []u = QR[n];
+
+                double realVV = u[n*2];
+                double imagVV = u[n*2+1];
+
+                u[n*2]   = 1;
+                u[n*2+1] = 0;
+
+                QrHelperFunctions_CD64.rank1UpdateMultR(a, u,0, gammas[n], 0, n, numRows, temp.data);
+
+                u[n*2]   = realVV;
+                u[n*2+1] = imagVV;
+            }
+
+            // solve for Rx = b using the standard upper triangular solver
+            CTriangularSolver.solveU(R.data, a.data, numCols);
+
+            // save the results
+            for( int i = 0; i < numCols; i++ ) {
+                int indexB = (i*BnumCols + colB)*2;
+                X.data[indexB]   = a.data[i*2];
+                X.data[indexB+1] = a.data[i*2+1];
+            }
+        }
+    }
+
+    @Override
+    public boolean modifiesA() {
+        return false;
+    }
+
+    @Override
+    public boolean modifiesB() {
+        return false;
+    }
+
+    @Override
+    public QRDecompositionHouseholderColumn_CD64 getDecomposition() {
+        return decomposer;
+    }
+}
\ No newline at end of file
diff --git a/main/denseC64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQrHouseTran_CD64.java b/main/denseC64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQrHouseTran_CD64.java
new file mode 100644
index 0000000..5082d95
--- /dev/null
+++ b/main/denseC64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQrHouseTran_CD64.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.qr;
+
+import org.ejml.alg.dense.decompose.CTriangularSolver;
+import org.ejml.alg.dense.decompose.qr.QRDecompositionHouseholderTran_CD64;
+import org.ejml.alg.dense.linsol.LinearSolverAbstract_CD64;
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.interfaces.decomposition.QRDecomposition;
+import org.ejml.ops.CSpecializedOps;
+
+
+/**
+ * <p>
+ * QR decomposition can be used to solve for systems.  However, this is not as computationally efficient
+ * as LU decomposition and costs about 3n<sup>2</sup> flops.
+ * </p>
+ * <p>
+ * It solve for x by first multiplying b by the transpose of Q then solving for the result.
+ * <br>
+ * QRx=b<br>
+ * Rx=Q^H b<br>
+ * </p>
+ *
+ * <p>
+ * A column major decomposition is used in this solver.
+ * <p>
+ *
+ * @author Peter Abeles
+ */
+public class LinearSolverQrHouseTran_CD64 extends LinearSolverAbstract_CD64 {
+
+    private QRDecompositionHouseholderTran_CD64 decomposer;
+
+    private double []a;
+
+    protected int maxRows = -1;
+    protected int maxCols = -1;
+
+    private CDenseMatrix64F QR; // a column major QR matrix
+    private CDenseMatrix64F U;
+
+    /**
+     * Creates a linear solver that uses QR decomposition.
+     */
+    public LinearSolverQrHouseTran_CD64() {
+        decomposer = new QRDecompositionHouseholderTran_CD64();
+    }
+
+    public void setMaxSize( int maxRows , int maxCols )
+    {
+        this.maxRows = maxRows; this.maxCols = maxCols;
+
+        a = new double[ maxRows*2 ];
+    }
+
+    /**
+     * Performs QR decomposition on A
+     *
+     * @param A not modified.
+     */
+    @Override
+    public boolean setA(CDenseMatrix64F A) {
+        if( A.numRows > maxRows || A.numCols > maxCols )
+            setMaxSize(A.numRows,A.numCols);
+
+        _setA(A);
+        if( !decomposer.decompose(A) )
+            return false;
+
+        QR = decomposer.getQR();
+        return true;
+    }
+
+    @Override
+    public double quality() {
+        // even those it is transposed the diagonal elements are at the same
+        // elements
+        return CSpecializedOps.qualityTriangular(QR);
+    }
+
+    /**
+     * Solves for X using the QR decomposition.
+     *
+     * @param B A matrix that is n by m.  Not modified.
+     * @param X An n by m matrix where the solution is written to.  Modified.
+     */
+    @Override
+    public void solve(CDenseMatrix64F B, CDenseMatrix64F X) {
+        if( X.numRows != numCols )
+            throw new IllegalArgumentException("Unexpected dimensions for X: X rows = "+X.numRows+" expected = "+numCols);
+        else if( B.numRows != numRows || B.numCols != X.numCols )
+            throw new IllegalArgumentException("Unexpected dimensions for B");
+
+        U = decomposer.getR(U,true);
+        final double gammas[] = decomposer.getGammas();
+        final double dataQR[] = QR.data;
+
+        final int BnumCols = B.numCols;
+
+        // solve each column one by one
+        for( int colB = 0; colB < BnumCols; colB++ ) {
+
+            // make a copy of this column in the vector
+            for( int i = 0; i < numRows; i++ ) {
+                int indexB = (i*BnumCols + colB)*2;
+                a[i*2]   = B.data[indexB];
+                a[i*2+1] = B.data[indexB+1];
+            }
+
+            // Solve Qa=b
+            // a = Q'b
+            // a = Q_{n-1}...Q_2*Q_1*b
+            //
+            // Q_n*b = (I-gamma*u*u^H)*b = b - u*(gamma*U^H*b)
+            for( int n = 0; n < numCols; n++ ) {
+                int indexU = (n*numRows + n + 1)*2;
+
+                double realUb = a[n*2];
+                double imagUb = a[n*2+1];
+
+                // U^H*b
+                for( int i = n+1; i < numRows; i++ ) {
+                    double realU = dataQR[indexU++];
+                    double imagU = -dataQR[indexU++];
+
+                    double realB = a[i*2];
+                    double imagB = a[i*2+1];
+
+                    realUb += realU*realB - imagU*imagB;
+                    imagUb += realU*imagB + imagU*realB;
+                }
+
+                // gamma*U^T*b
+                realUb *= gammas[n];
+                imagUb *= gammas[n];
+
+                a[n*2]   -= realUb;
+                a[n*2+1] -= imagUb;
+
+                indexU = (n*numRows + n + 1)*2;
+                for( int i = n+1; i < numRows; i++) {
+                    double realU = dataQR[indexU++];
+                    double imagU = dataQR[indexU++];
+
+                    a[i*2]   -= realU*realUb - imagU*imagUb;
+                    a[i*2+1] -= realU*imagUb + imagU*realUb;
+                }
+            }
+
+            // solve for Rx = b using the standard upper triangular solver
+            CTriangularSolver.solveU(U.data, a, numCols);
+
+            // save the results
+
+            for( int i = 0; i < numCols; i++ ) {
+                int indexX = (i*X.numCols+colB)*2;
+
+                X.data[indexX]   = a[i*2];
+                X.data[indexX+1] = a[i*2+1];
+            }
+        }
+    }
+
+    @Override
+    public boolean modifiesA() {
+        return decomposer.inputModified();
+    }
+
+    @Override
+    public boolean modifiesB() {
+        return false;
+    }
+
+    @Override
+    public QRDecomposition<CDenseMatrix64F> getDecomposition() {
+        return decomposer;
+    }
+}
\ No newline at end of file
diff --git a/main/denseC64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQrHouse_CD64.java b/main/denseC64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQrHouse_CD64.java
new file mode 100644
index 0000000..abe5c39
--- /dev/null
+++ b/main/denseC64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQrHouse_CD64.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.qr;
+
+import org.ejml.alg.dense.decompose.CTriangularSolver;
+import org.ejml.alg.dense.decompose.qr.QRDecompositionHouseholder_CD64;
+import org.ejml.alg.dense.linsol.LinearSolverAbstract_CD64;
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.interfaces.decomposition.QRDecomposition;
+import org.ejml.ops.CSpecializedOps;
+
+
+/**
+ * <p>
+ * QR decomposition can be used to solve for systems.  However, this is not as computationally efficient
+ * as LU decomposition and costs about 3n<sup>2</sup> flops.
+ * </p>
+ * <p>
+ * It solve for x by first multiplying b by the transpose of Q then solving for the result.
+ * <br>
+ * QRx=b<br>
+ * Rx=Q^H b<br>
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class LinearSolverQrHouse_CD64 extends LinearSolverAbstract_CD64 {
+
+    private QRDecompositionHouseholder_CD64 decomposer;
+
+    private double []a,u;
+
+    private int maxRows = -1;
+
+    private CDenseMatrix64F QR;
+    private double gammas[];
+
+    /**
+     * Creates a linear solver that uses QR decomposition.
+     */
+    public LinearSolverQrHouse_CD64() {
+        decomposer = new QRDecompositionHouseholder_CD64();
+
+
+    }
+
+    public void setMaxSize( int maxRows ) {
+        this.maxRows = maxRows;
+
+        a = new double[ maxRows*2 ];
+        u = new double[ maxRows*2 ];
+    }
+
+    /**
+     * Performs QR decomposition on A
+     *
+     * @param A not modified.
+     */
+    @Override
+    public boolean setA(CDenseMatrix64F A) {
+        if( A.numRows > maxRows ) {
+            setMaxSize(A.numRows);
+        }
+
+        _setA(A);
+        if( !decomposer.decompose(A) )
+            return false;
+        
+        gammas = decomposer.getGammas();
+        QR = decomposer.getQR();
+
+        return true;
+    }
+
+    @Override
+    public double quality() {
+        return CSpecializedOps.qualityTriangular(QR);
+    }
+
+    /**
+     * Solves for X using the QR decomposition.
+     *
+     * @param B A matrix that is n by m.  Not modified.
+     * @param X An n by m matrix where the solution is writen to.  Modified.
+     */
+    @Override
+    public void solve(CDenseMatrix64F B, CDenseMatrix64F X) {
+        if( X.numRows != numCols )
+            throw new IllegalArgumentException("Unexpected dimensions for X");
+        else if( B.numRows != numRows || B.numCols != X.numCols )
+            throw new IllegalArgumentException("Unexpected dimensions for B");
+
+        int BnumCols = B.numCols;
+
+        // solve each column one by one
+        for( int colB = 0; colB < BnumCols; colB++ ) {
+
+            // make a copy of this column in the vector
+            for( int i = 0; i < numRows; i++ ) {
+                int indexB = (i*BnumCols + colB)*2;
+                a[i*2]   = B.data[indexB];
+                a[i*2+1] = B.data[indexB+1];
+            }
+
+            // Solve Qa=b
+            // a = Q'b
+            // a = Q_{n-1}...Q_2*Q_1*b
+            //
+            // Q_n*b = (I-gamma*u*u^H)*b = b - u*(gamma*U^H*b)
+            for( int n = 0; n < numCols; n++ ) {
+                u[n*2] = 1;
+                u[n*2+1] = 0;
+
+                double realUb = a[2*n];
+                double imagUb = a[2*n+1];
+                // U^H*b
+                for( int i = n+1; i < numRows; i++ ) {
+                    int indexQR = (i*QR.numCols+n)*2;
+                    double realU = u[i*2]   = QR.data[indexQR];
+                    double imagU = u[i*2+1] = QR.data[indexQR+1];
+
+                    double realB = a[i*2];
+                    double imagB = a[i*2+1];
+
+                    realUb += realU*realB + imagU*imagB;
+                    imagUb += realU*imagB - imagU*realB;
+                }
+
+                // gamma*U^H*b
+                realUb *= gammas[n];
+                imagUb *= gammas[n];
+
+                for( int i = n; i < numRows; i++ ) {
+                    double realU = u[i*2];
+                    double imagU = u[i*2+1];
+
+                    a[i*2  ] -= realU*realUb - imagU*imagUb;
+                    a[i*2+1] -= realU*imagUb + imagU*realUb;
+                }
+            }
+
+            // solve for Rx = b using the standard upper triangular solver
+            CTriangularSolver.solveU(QR.data, a, numCols);
+
+            // save the results
+            for( int i = 0; i < numCols; i++ ) {
+                int indexX = (i*X.numCols+colB)*2;
+
+                X.data[indexX]   = a[i*2];
+                X.data[indexX+1] = a[i*2+1];
+            }
+        }
+    }
+
+    @Override
+    public boolean modifiesA() {
+        return false;
+    }
+
+    @Override
+    public boolean modifiesB() {
+        return false;
+    }
+
+    @Override
+    public QRDecomposition<CDenseMatrix64F> getDecomposition() {
+        return decomposer;
+    }
+}
diff --git a/main/denseC64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQr_CD64.java b/main/denseC64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQr_CD64.java
new file mode 100644
index 0000000..827cd71
--- /dev/null
+++ b/main/denseC64/src/org/ejml/alg/dense/linsol/qr/LinearSolverQr_CD64.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.qr;
+
+import org.ejml.alg.dense.decompose.CTriangularSolver;
+import org.ejml.alg.dense.linsol.LinearSolverAbstract_CD64;
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.interfaces.decomposition.QRDecomposition;
+import org.ejml.ops.CCommonOps;
+import org.ejml.ops.CSpecializedOps;
+
+
+/**
+ * <p>
+ * A solver for a generic QR decomposition algorithm.  This will in general be a bit slower than the
+ * specialized once since the full Q and R matrices need to be extracted.
+ * </p>
+ * <p>
+ * It solve for x by first multiplying b by the transpose of Q then solving for the result.
+ * <br>
+ * QRx=b<br>
+ * Rx=Q^H b<br>
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class LinearSolverQr_CD64 extends LinearSolverAbstract_CD64 {
+
+    private QRDecomposition<CDenseMatrix64F> decomposer;
+
+    protected int maxRows = -1;
+    protected int maxCols = -1;
+
+    protected CDenseMatrix64F Q;
+    protected CDenseMatrix64F Qt;
+    protected CDenseMatrix64F R;
+
+    private CDenseMatrix64F Y,Z;
+
+    /**
+     * Creates a linear solver that uses QR decomposition.
+     *
+     */
+    public LinearSolverQr_CD64(QRDecomposition<CDenseMatrix64F> decomposer) {
+        this.decomposer = decomposer;
+    }
+
+    /**
+     * Changes the size of the matrix it can solve for
+     *
+     * @param maxRows Maximum number of rows in the matrix it will decompose.
+     * @param maxCols Maximum number of columns in the matrix it will decompose.
+     */
+    public void setMaxSize( int maxRows , int maxCols )
+    {
+        this.maxRows = maxRows; this.maxCols = maxCols;
+
+        Q = new CDenseMatrix64F(maxRows,maxRows);
+        Qt = new CDenseMatrix64F(maxRows,maxRows);
+        R = new CDenseMatrix64F(maxRows,maxCols);
+
+        Y = new CDenseMatrix64F(maxRows,1);
+        Z = new CDenseMatrix64F(maxRows,1);
+    }
+
+    /**
+     * Performs QR decomposition on A
+     *
+     * @param A not modified.
+     */
+    @Override
+    public boolean setA(CDenseMatrix64F A) {
+        if( A.numRows > maxRows || A.numCols > maxCols ) {
+            setMaxSize(A.numRows,A.numCols);
+        }
+
+        _setA(A);
+        if( !decomposer.decompose(A) )
+            return false;
+
+        Q.reshape(numRows,numRows);
+        R.reshape(numRows,numCols);
+        decomposer.getQ(Q,false);
+        decomposer.getR(R,false);
+        CCommonOps.transposeConjugate(Q,Qt);
+
+        return true;
+    }
+
+    @Override
+    public double quality() {
+        return CSpecializedOps.qualityTriangular(R);
+    }
+
+    /**
+     * Solves for X using the QR decomposition.
+     *
+     * @param B A matrix that is n by m.  Not modified.
+     * @param X An n by m matrix where the solution is written to.  Modified.
+     */
+    @Override
+    public void solve(CDenseMatrix64F B, CDenseMatrix64F X) {
+        if( X.numRows != numCols )
+            throw new IllegalArgumentException("Unexpected dimensions for X");
+        else if( B.numRows != numRows || B.numCols != X.numCols )
+            throw new IllegalArgumentException("Unexpected dimensions for B");
+
+        int BnumCols = B.numCols;
+
+        Y.reshape(numRows,1);
+        Z.reshape(numRows,1);
+
+        // solve each column one by one
+        for( int colB = 0; colB < BnumCols; colB++ ) {
+
+            // make a copy of this column in the vector
+            for( int i = 0; i < numRows; i++ ) {
+                int indexB = B.getIndex(i,colB);
+                Y.data[i*2]   = B.data[indexB];
+                Y.data[i*2+1] = B.data[indexB+1];
+            }
+
+            // Solve Qa=b
+            // a = Q'b
+            CCommonOps.mult(Qt, Y, Z);
+
+            // solve for Rx = b using the standard upper triangular solver
+            CTriangularSolver.solveU(R.data, Z.data, numCols);
+
+            // save the results
+            for( int i = 0; i < numCols; i++ ) {
+                X.set(i,colB,Z.data[i*2],Z.data[i*2+1]);
+            }
+        }
+    }
+
+    @Override
+    public boolean modifiesA() {
+        return decomposer.inputModified();
+    }
+
+    @Override
+    public boolean modifiesB() {
+        return false;
+    }
+
+    @Override
+    public QRDecomposition<CDenseMatrix64F> getDecomposition() {
+        return decomposer;
+    }
+
+    public QRDecomposition<CDenseMatrix64F> getDecomposer() {
+        return decomposer;
+    }
+
+    public CDenseMatrix64F getQ() {
+        return Q;
+    }
+
+    public CDenseMatrix64F getR() {
+        return R;
+    }
+}
\ No newline at end of file
diff --git a/main/denseC64/src/org/ejml/alg/dense/misc/CTransposeAlgs.java b/main/denseC64/src/org/ejml/alg/dense/misc/CTransposeAlgs.java
new file mode 100644
index 0000000..7b00be5
--- /dev/null
+++ b/main/denseC64/src/org/ejml/alg/dense/misc/CTransposeAlgs.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.misc;
+
+import org.ejml.data.CDenseMatrix64F;
+
+/**
+ * Algorithms for transposing dense complex matrices
+ *
+ * @author Peter Abeles
+ */
+public class CTransposeAlgs {
+    /**
+     * In-place transpose for a square matrix.  On most architectures it is faster than the standard transpose
+     * algorithm, but on most modern computers it's slower than block transpose.
+     *
+     * @param mat The matrix that is transposed in-place.  Modified.
+     */
+    public static void square( CDenseMatrix64F mat )
+    {
+        int index = 2;
+        int rowStride = mat.getRowStride();
+        int indexEnd = rowStride;
+        for( int i = 0; i < mat.numRows;
+             i++ , index += (i+1)*2 , indexEnd += rowStride ) {
+
+            int indexOther = (i+1)*rowStride + i*2;
+            for( ; index < indexEnd; index += 2, indexOther += rowStride) {
+                double real = mat.data[ index ];
+                double img = mat.data[ index+1 ];
+
+                mat.data[ index ] = mat.data[ indexOther ];
+                mat.data[ index+1 ] = mat.data[ indexOther+1 ];
+                mat.data[indexOther] = real;
+                mat.data[indexOther+1] = img;
+            }
+        }
+    }
+
+    public static void squareConjugate( CDenseMatrix64F mat )
+    {
+        int index = 2;
+        int rowStride = mat.getRowStride();
+        int indexEnd = rowStride;
+        for( int i = 0; i < mat.numRows;
+             i++ , index += (i+1)*2 , indexEnd += rowStride ) {
+
+            mat.data[ index-1 ] = -mat.data[ index-1 ];
+
+            int indexOther = (i+1)*rowStride + i*2;
+            for( ; index < indexEnd; index += 2, indexOther += rowStride) {
+                double real = mat.data[ index ];
+                double img = mat.data[ index+1 ];
+
+                mat.data[ index ] = mat.data[ indexOther ];
+                mat.data[ index+1 ] = -mat.data[ indexOther+1 ];
+                mat.data[indexOther] = real;
+                mat.data[indexOther+1] = -img;
+            }
+        }
+    }
+
+    /**
+     * A straight forward transpose.  Good for small non-square matrices.
+     *
+     * @param A Original matrix.  Not modified.
+     * @param A_tran Transposed matrix.  Modified.
+     */
+    public static void standard( CDenseMatrix64F A, CDenseMatrix64F A_tran)
+    {
+        int index = 0;
+        int rowStrideTran = A_tran.getRowStride();
+        int rowStride = A.getRowStride();
+        for( int i = 0; i < A_tran.numRows; i++ ) {
+            int index2 = i*2;
+
+            int end = index + rowStrideTran;
+            while( index < end ) {
+                A_tran.data[index++] = A.data[ index2 ];
+                A_tran.data[index++] = A.data[ index2+1 ];
+                index2 += rowStride;
+            }
+        }
+    }
+
+    /**
+     * A straight forward conjugate transpose.  Good for small non-square matrices.
+     *
+     * @param A Original matrix.  Not modified.
+     * @param A_tran Transposed matrix.  Modified.
+     */
+    public static void standardConjugate( CDenseMatrix64F A, CDenseMatrix64F A_tran)
+    {
+        int index = 0;
+        int rowStrideTran = A_tran.getRowStride();
+        int rowStride = A.getRowStride();
+        for( int i = 0; i < A_tran.numRows; i++ ) {
+            int index2 = i*2;
+
+            int end = index + rowStrideTran;
+            while( index < end ) {
+                A_tran.data[index++] = A.data[ index2 ];
+                A_tran.data[index++] = -A.data[ index2+1 ];
+                index2 += rowStride;
+            }
+        }
+    }
+}
diff --git a/main/denseC64/src/org/ejml/alg/dense/mult/CMatrixMatrixMult.java b/main/denseC64/src/org/ejml/alg/dense/mult/CMatrixMatrixMult.java
new file mode 100644
index 0000000..39c026e
--- /dev/null
+++ b/main/denseC64/src/org/ejml/alg/dense/mult/CMatrixMatrixMult.java
@@ -0,0 +1,469 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.mult;
+
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.ops.CCommonOps;
+import org.ejml.ops.MatrixDimensionException;
+
+/**
+ * <p>Matrix multiplication routines for complex dense matrices in a row-major format.</p>
+ *
+ * <p>
+ * DO NOT MODIFY! Auto generated by {@link org.ejml.alg.dense.mult.GenerateCMatrixMatrixMult}.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class CMatrixMatrixMult {
+    public static void mult_reorder( CDenseMatrix64F a , CDenseMatrix64F b , CDenseMatrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numCols != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numRows != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        if( a.numCols == 0 || a.numRows == 0 ) {
+            CCommonOps.fill(c,0,0);
+            return;
+        }
+        double realA,imgA;
+
+        int indexCbase= 0;
+        int strideA = a.getRowStride();
+        int strideB = b.getRowStride();
+        int strideC = c.getRowStride();
+        int endOfKLoop = b.numRows*strideB;
+
+        for( int i = 0; i < a.numRows; i++ ) {
+            int indexA = i*strideA;
+
+            // need to assign c.data to a value initially
+            int indexB = 0;
+            int indexC = indexCbase;
+            int end = indexB + strideB;
+
+            realA = a.data[indexA++];
+            imgA = a.data[indexA++];
+
+            while( indexB < end ) {
+                double realB = b.data[indexB++];
+                double imgB = b.data[indexB++];
+
+                c.data[indexC++] = realA*realB - imgA*imgB;
+                c.data[indexC++] = realA*imgB + imgA*realB;
+            }
+
+            // now add to it
+            while( indexB != endOfKLoop ) { // k loop
+                indexC = indexCbase;
+                end = indexB + strideB;
+
+            realA = a.data[indexA++];
+            imgA = a.data[indexA++];
+
+                while( indexB < end ) { // j loop
+                    double realB = b.data[indexB++];
+                    double imgB = b.data[indexB++];
+
+                    c.data[indexC++] += realA*realB - imgA*imgB;
+                    c.data[indexC++] += realA*imgB + imgA*realB;
+                }
+            }
+            indexCbase += strideC;
+        }
+    }
+
+
+    public static void mult_small( CDenseMatrix64F a , CDenseMatrix64F b , CDenseMatrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numCols != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numRows != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        int aIndexStart = 0;
+        int cIndex = 0;
+
+        int strideA = a.getRowStride();
+        int strideB = b.getRowStride();
+
+        for( int i = 0; i < a.numRows; i++ ) {
+            for( int j = 0; j < b.numCols; j++ ) {
+                double realTotal = 0;
+                double imgTotal = 0;
+
+                int indexA = aIndexStart;
+                int indexB = j*2;
+                int end = indexA + strideA;
+                while( indexA < end ) {
+                    double realA = a.data[indexA++];
+                    double imgA = a.data[indexA++];
+
+                    double realB = b.data[indexB];
+                    double imgB = b.data[indexB+1];
+
+                    realTotal += realA*realB - imgA*imgB;
+                    imgTotal += realA*imgB + imgA*realB;
+
+                    indexB += strideB;
+                }
+
+                c.data[cIndex++] = realTotal;
+                c.data[cIndex++] = imgTotal;
+            }
+            aIndexStart += strideA;
+        }
+    }
+
+
+    public static void multAdd_reorder( CDenseMatrix64F a , CDenseMatrix64F b , CDenseMatrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numCols != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numRows != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        if( a.numCols == 0 || a.numRows == 0 ) {
+            return;
+        }
+        double realA,imgA;
+
+        int indexCbase= 0;
+        int strideA = a.getRowStride();
+        int strideB = b.getRowStride();
+        int strideC = c.getRowStride();
+        int endOfKLoop = b.numRows*strideB;
+
+        for( int i = 0; i < a.numRows; i++ ) {
+            int indexA = i*strideA;
+
+            // need to assign c.data to a value initially
+            int indexB = 0;
+            int indexC = indexCbase;
+            int end = indexB + strideB;
+
+            realA = a.data[indexA++];
+            imgA = a.data[indexA++];
+
+            while( indexB < end ) {
+                double realB = b.data[indexB++];
+                double imgB = b.data[indexB++];
+
+                c.data[indexC++] += realA*realB - imgA*imgB;
+                c.data[indexC++] += realA*imgB + imgA*realB;
+            }
+
+            // now add to it
+            while( indexB != endOfKLoop ) { // k loop
+                indexC = indexCbase;
+                end = indexB + strideB;
+
+            realA = a.data[indexA++];
+            imgA = a.data[indexA++];
+
+                while( indexB < end ) { // j loop
+                    double realB = b.data[indexB++];
+                    double imgB = b.data[indexB++];
+
+                    c.data[indexC++] += realA*realB - imgA*imgB;
+                    c.data[indexC++] += realA*imgB + imgA*realB;
+                }
+            }
+            indexCbase += strideC;
+        }
+    }
+
+
+    public static void multAdd_small( CDenseMatrix64F a , CDenseMatrix64F b , CDenseMatrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numCols != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numRows != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        int aIndexStart = 0;
+        int cIndex = 0;
+
+        int strideA = a.getRowStride();
+        int strideB = b.getRowStride();
+
+        for( int i = 0; i < a.numRows; i++ ) {
+            for( int j = 0; j < b.numCols; j++ ) {
+                double realTotal = 0;
+                double imgTotal = 0;
+
+                int indexA = aIndexStart;
+                int indexB = j*2;
+                int end = indexA + strideA;
+                while( indexA < end ) {
+                    double realA = a.data[indexA++];
+                    double imgA = a.data[indexA++];
+
+                    double realB = b.data[indexB];
+                    double imgB = b.data[indexB+1];
+
+                    realTotal += realA*realB - imgA*imgB;
+                    imgTotal += realA*imgB + imgA*realB;
+
+                    indexB += strideB;
+                }
+
+                c.data[cIndex++] += realTotal;
+                c.data[cIndex++] += imgTotal;
+            }
+            aIndexStart += strideA;
+        }
+    }
+
+
+    public static void mult_reorder( double realAlpha , double imgAlpha , CDenseMatrix64F a , CDenseMatrix64F b , CDenseMatrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numCols != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numRows != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        if( a.numCols == 0 || a.numRows == 0 ) {
+            CCommonOps.fill(c,0,0);
+            return;
+        }
+        double realA,imgA;
+        double realTmp,imgTmp;
+        int indexCbase= 0;
+        int strideA = a.getRowStride();
+        int strideB = b.getRowStride();
+        int strideC = c.getRowStride();
+        int endOfKLoop = b.numRows*strideB;
+
+        for( int i = 0; i < a.numRows; i++ ) {
+            int indexA = i*strideA;
+
+            // need to assign c.data to a value initially
+            int indexB = 0;
+            int indexC = indexCbase;
+            int end = indexB + strideB;
+
+            realTmp = a.data[indexA++];
+            imgTmp = a.data[indexA++];
+            realA = realAlpha*realTmp - imgAlpha*imgTmp;
+            imgA = realAlpha*imgTmp + imgAlpha*realTmp;
+
+            while( indexB < end ) {
+                double realB = b.data[indexB++];
+                double imgB = b.data[indexB++];
+
+                c.data[indexC++] = realA*realB - imgA*imgB;
+                c.data[indexC++] = realA*imgB + imgA*realB;
+            }
+
+            // now add to it
+            while( indexB != endOfKLoop ) { // k loop
+                indexC = indexCbase;
+                end = indexB + strideB;
+
+            realTmp = a.data[indexA++];
+            imgTmp = a.data[indexA++];
+            realA = realAlpha*realTmp - imgAlpha*imgTmp;
+            imgA = realAlpha*imgTmp + imgAlpha*realTmp;
+
+                while( indexB < end ) { // j loop
+                    double realB = b.data[indexB++];
+                    double imgB = b.data[indexB++];
+
+                    c.data[indexC++] += realA*realB - imgA*imgB;
+                    c.data[indexC++] += realA*imgB + imgA*realB;
+                }
+            }
+            indexCbase += strideC;
+        }
+    }
+
+
+    public static void mult_small( double realAlpha , double imgAlpha , CDenseMatrix64F a , CDenseMatrix64F b , CDenseMatrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numCols != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numRows != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        int aIndexStart = 0;
+        int cIndex = 0;
+
+        int strideA = a.getRowStride();
+        int strideB = b.getRowStride();
+
+        for( int i = 0; i < a.numRows; i++ ) {
+            for( int j = 0; j < b.numCols; j++ ) {
+                double realTotal = 0;
+                double imgTotal = 0;
+
+                int indexA = aIndexStart;
+                int indexB = j*2;
+                int end = indexA + strideA;
+                while( indexA < end ) {
+                    double realA = a.data[indexA++];
+                    double imgA = a.data[indexA++];
+
+                    double realB = b.data[indexB];
+                    double imgB = b.data[indexB+1];
+
+                    realTotal += realA*realB - imgA*imgB;
+                    imgTotal += realA*imgB + imgA*realB;
+
+                    indexB += strideB;
+                }
+
+                c.data[cIndex++] = realAlpha*realTotal - imgAlpha*imgTotal;
+                c.data[cIndex++] = realAlpha*imgTotal + imgAlpha*realTotal;
+            }
+            aIndexStart += strideA;
+        }
+    }
+
+
+    public static void multAdd_reorder( double realAlpha , double imgAlpha , CDenseMatrix64F a , CDenseMatrix64F b , CDenseMatrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numCols != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numRows != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        if( a.numCols == 0 || a.numRows == 0 ) {
+            return;
+        }
+        double realA,imgA;
+        double realTmp,imgTmp;
+        int indexCbase= 0;
+        int strideA = a.getRowStride();
+        int strideB = b.getRowStride();
+        int strideC = c.getRowStride();
+        int endOfKLoop = b.numRows*strideB;
+
+        for( int i = 0; i < a.numRows; i++ ) {
+            int indexA = i*strideA;
+
+            // need to assign c.data to a value initially
+            int indexB = 0;
+            int indexC = indexCbase;
+            int end = indexB + strideB;
+
+            realTmp = a.data[indexA++];
+            imgTmp = a.data[indexA++];
+            realA = realAlpha*realTmp - imgAlpha*imgTmp;
+            imgA = realAlpha*imgTmp + imgAlpha*realTmp;
+
+            while( indexB < end ) {
+                double realB = b.data[indexB++];
+                double imgB = b.data[indexB++];
+
+                c.data[indexC++] += realA*realB - imgA*imgB;
+                c.data[indexC++] += realA*imgB + imgA*realB;
+            }
+
+            // now add to it
+            while( indexB != endOfKLoop ) { // k loop
+                indexC = indexCbase;
+                end = indexB + strideB;
+
+            realTmp = a.data[indexA++];
+            imgTmp = a.data[indexA++];
+            realA = realAlpha*realTmp - imgAlpha*imgTmp;
+            imgA = realAlpha*imgTmp + imgAlpha*realTmp;
+
+                while( indexB < end ) { // j loop
+                    double realB = b.data[indexB++];
+                    double imgB = b.data[indexB++];
+
+                    c.data[indexC++] += realA*realB - imgA*imgB;
+                    c.data[indexC++] += realA*imgB + imgA*realB;
+                }
+            }
+            indexCbase += strideC;
+        }
+    }
+
+
+    public static void multAdd_small( double realAlpha , double imgAlpha , CDenseMatrix64F a , CDenseMatrix64F b , CDenseMatrix64F c )
+    {
+        if( a == c || b == c )
+            throw new IllegalArgumentException("Neither 'a' or 'b' can be the same matrix as 'c'");
+        else if( a.numCols != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numRows != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        int aIndexStart = 0;
+        int cIndex = 0;
+
+        int strideA = a.getRowStride();
+        int strideB = b.getRowStride();
+
+        for( int i = 0; i < a.numRows; i++ ) {
+            for( int j = 0; j < b.numCols; j++ ) {
+                double realTotal = 0;
+                double imgTotal = 0;
+
+                int indexA = aIndexStart;
+                int indexB = j*2;
+                int end = indexA + strideA;
+                while( indexA < end ) {
+                    double realA = a.data[indexA++];
+                    double imgA = a.data[indexA++];
+
+                    double realB = b.data[indexB];
+                    double imgB = b.data[indexB+1];
+
+                    realTotal += realA*realB - imgA*imgB;
+                    imgTotal += realA*imgB + imgA*realB;
+
+                    indexB += strideB;
+                }
+
+                c.data[cIndex++] += realAlpha*realTotal - imgAlpha*imgTotal;
+                c.data[cIndex++] += realAlpha*imgTotal + imgAlpha*realTotal;
+            }
+            aIndexStart += strideA;
+        }
+    }
+
+
+}
diff --git a/main/denseC64/src/org/ejml/alg/dense/mult/CVectorVectorMult.java b/main/denseC64/src/org/ejml/alg/dense/mult/CVectorVectorMult.java
new file mode 100644
index 0000000..a1be324
--- /dev/null
+++ b/main/denseC64/src/org/ejml/alg/dense/mult/CVectorVectorMult.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.mult;
+
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.data.Complex64F;
+
+/**
+ * Operations that involve multiplication of two vectors.
+ *
+ * @author Peter Abeles
+ */
+public class CVectorVectorMult {
+    /**
+     * <p>
+     * Computes the inner product of the two vectors.  In geometry this is known as the dot product.<br>
+     * <br>
+     * ∑<sub>k=1:n</sub> x<sub>k</sub> * y<sub>k</sub><br>
+     * where x and y are vectors with n elements.
+     * </p>
+     *
+     * <p>
+     * These functions are often used inside of highly optimized code and therefor sanity checks are
+     * kept to a minimum.  It is not recommended that any of these functions be used directly.
+     * </p>
+     *
+     * @param x A vector with n elements. Not modified.
+     * @param y A vector with n elements. Not modified.
+     * @return The inner product of the two vectors.
+     */
+    public static Complex64F innerProd( CDenseMatrix64F x, CDenseMatrix64F y , Complex64F output )
+    {
+        if( output == null )
+            output = new Complex64F();
+        else {
+            output.real = output.imaginary = 0;
+        }
+
+        int m = x.getDataLength();
+
+        for( int i = 0; i < m; i += 2 ) {
+            double realX = x.data[i];
+            double imagX = x.data[i+1];
+
+            double realY = y.data[i];
+            double imagY = y.data[i+1];
+
+            output.real += realX*realY - imagX*imagY;
+            output.imaginary += realX*imagY + imagX*realY;
+        }
+
+        return output;
+    }
+
+    /**
+     * <p>
+     * Computes the inner product between a vector and the conjugate of another one.
+     * <br>
+     * <br>
+     * ∑<sub>k=1:n</sub> x<sub>k</sub> * conj(y<sub>k</sub>)<br>
+     * where x and y are vectors with n elements.
+     * </p>
+     *
+     * <p>
+     * These functions are often used inside of highly optimized code and therefor sanity checks are
+     * kept to a minimum.  It is not recommended that any of these functions be used directly.
+     * </p>
+     *
+     * @param x A vector with n elements. Not modified.
+     * @param y A vector with n elements. Not modified.
+     * @return The inner product of the two vectors.
+     */
+    public static Complex64F innerProdH( CDenseMatrix64F x, CDenseMatrix64F y , Complex64F output )
+    {
+        if( output == null )
+            output = new Complex64F();
+        else {
+            output.real = output.imaginary = 0;
+        }
+
+        int m = x.getDataLength();
+
+        for( int i = 0; i < m; i += 2 ) {
+            double realX = x.data[i];
+            double imagX = x.data[i+1];
+
+            double realY = y.data[i];
+            double imagY = -y.data[i+1];
+
+            output.real += realX*realY - imagX*imagY;
+            output.imaginary += realX*imagY + imagX*realY;
+        }
+
+        return output;
+    }
+
+    /**
+     * <p>
+     * Sets A ∈ ℜ <sup>m × n</sup> equal to an outer product multiplication of the two
+     * vectors.  This is also known as a rank-1 operation.<br>
+     * <br>
+     * A = x * y<sup>T</sup>
+     * where x ∈ ℜ <sup>m</sup> and y ∈ ℜ <sup>n</sup> are vectors.
+     * </p>
+     * <p>
+     * Which is equivalent to: A<sub>ij</sub> = x<sub>i</sub>*y<sub>j</sub>
+     * </p>
+     *
+     * @param x A vector with m elements. Not modified.
+     * @param y A vector with n elements. Not modified.
+     * @param A A Matrix with m by n elements. Modified.
+     */
+    public static void outerProd( CDenseMatrix64F x, CDenseMatrix64F y, CDenseMatrix64F A ) {
+        int m = A.numRows;
+        int n = A.numCols;
+
+        int index = 0;
+        for( int i = 0; i < m; i++ ) {
+            double realX = x.data[i*2];
+            double imagX = x.data[i*2+1];
+
+            int indexY = 0;
+            for( int j = 0; j < n; j++ ) {
+                double realY = y.data[indexY++];
+                double imagY = y.data[indexY++];
+
+                A.data[index++] = realX*realY - imagX*imagY;
+                A.data[index++] = realX*imagY + imagX*realY;
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Sets A ∈ ℜ <sup>m × n</sup> equal to an outer product multiplication of the two
+     * vectors.  This is also known as a rank-1 operation.<br>
+     * <br>
+     * A = x * y<sup>H</sup>
+     * where x ∈ ℜ <sup>m</sup> and y ∈ ℜ <sup>n</sup> are vectors.
+     * </p>
+     * <p>
+     * Which is equivalent to: A<sub>ij</sub> = x<sub>i</sub>*y<sub>j</sub>
+     * </p>
+     *
+     * @param x A vector with m elements. Not modified.
+     * @param y A vector with n elements. Not modified.
+     * @param A A Matrix with m by n elements. Modified.
+     */
+    public static void outerProdH( CDenseMatrix64F x, CDenseMatrix64F y, CDenseMatrix64F A ) {
+        int m = A.numRows;
+        int n = A.numCols;
+
+        int index = 0;
+        for( int i = 0; i < m; i++ ) {
+            double realX = x.data[i*2];
+            double imagX = x.data[i*2+1];
+
+            int indexY = 0;
+            for( int j = 0; j < n; j++ ) {
+                double realY = y.data[indexY++];
+                double imagY = -y.data[indexY++];
+
+                A.data[index++] = realX*realY - imagX*imagY;
+                A.data[index++] = realX*imagY + imagX*realY;
+            }
+        }
+    }
+}
diff --git a/main/denseC64/src/org/ejml/factory/CDecompositionFactory.java b/main/denseC64/src/org/ejml/factory/CDecompositionFactory.java
new file mode 100644
index 0000000..374853c
--- /dev/null
+++ b/main/denseC64/src/org/ejml/factory/CDecompositionFactory.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.factory;
+
+import org.ejml.alg.dense.decompose.chol.CholeskyDecompositionInner_CD64;
+import org.ejml.alg.dense.decompose.lu.LUDecompositionAlt_CD64;
+import org.ejml.alg.dense.decompose.qr.QRDecompositionHouseholderColumn_CD64;
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.interfaces.decomposition.CholeskyDecomposition;
+import org.ejml.interfaces.decomposition.DecompositionInterface;
+import org.ejml.interfaces.decomposition.LUDecomposition;
+import org.ejml.interfaces.decomposition.QRDecomposition;
+
+/**
+ * <p>
+ * Contains operations related to creating and evaluating the quality of common matrix decompositions.  Except
+ * in specialized situations, matrix decompositions should be instantiated from this factory instead of being
+ * directly constructed.  Low level implementations are more prone to changes and new algorithms will be
+ * automatically placed here.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class CDecompositionFactory {
+    /**
+     * <p>
+     * Returns a {@link org.ejml.interfaces.decomposition.LUDecomposition} that has been optimized for the specified matrix size.
+     * </p>
+     *
+     * @param numRows Number of rows the returned decomposition is optimized for.
+     * @param numCols Number of columns that the returned decomposition is optimized for.
+     * @return LUDecomposition
+     */
+    public static LUDecomposition<CDenseMatrix64F> lu( int numRows , int numCols ) {
+        return new LUDecompositionAlt_CD64();
+    }
+
+    /**
+     * <p>
+     * Returns a {@link org.ejml.interfaces.decomposition.QRDecomposition} that has been optimized for the specified matrix size.
+     * </p>
+     *
+     * @param numRows Number of rows the returned decomposition is optimized for.
+     * @param numCols Number of columns that the returned decomposition is optimized for.
+     * @return QRDecomposition
+     */
+    public static QRDecomposition<CDenseMatrix64F> qr( int numRows , int numCols ) {
+        return new QRDecompositionHouseholderColumn_CD64();
+    }
+
+    /**
+     * <p>
+     * Returns a {@link org.ejml.interfaces.decomposition.CholeskyDecomposition} that has been optimized for the specified matrix size.
+     * </p>
+     *
+     * @param size Number of rows and columns it should be optimized for
+     * @param lower if true then it will be a lower cholesky.  false for upper.  Try lower.
+     * @return QRDecomposition
+     */
+    public static CholeskyDecomposition<CDenseMatrix64F> chol( int size , boolean lower ) {
+        return new CholeskyDecompositionInner_CD64(lower);
+    }
+
+    /**
+     * Decomposes the input matrix 'a' and makes sure it isn't modified.
+     */
+    public static boolean decomposeSafe(DecompositionInterface<CDenseMatrix64F> decomposition, CDenseMatrix64F a) {
+
+        if( decomposition.inputModified() ) {
+            a = a.copy();
+        }
+        return decomposition.decompose(a);
+    }
+}
diff --git a/main/denseC64/src/org/ejml/factory/CLinearSolverFactory.java b/main/denseC64/src/org/ejml/factory/CLinearSolverFactory.java
new file mode 100644
index 0000000..a1dd22e
--- /dev/null
+++ b/main/denseC64/src/org/ejml/factory/CLinearSolverFactory.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.factory;
+
+import org.ejml.alg.dense.decompose.chol.CholeskyDecompositionInner_CD64;
+import org.ejml.alg.dense.decompose.lu.LUDecompositionAlt_CD64;
+import org.ejml.alg.dense.linsol.chol.LinearSolverChol_CD64;
+import org.ejml.alg.dense.linsol.lu.LinearSolverLu_CD64;
+import org.ejml.alg.dense.linsol.qr.LinearSolverQrHouseCol_CD64;
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+
+/**
+ * Factory for creating linear solvers of complex matrices
+ *
+ * @author Peter Abeles
+ */
+public class CLinearSolverFactory {
+
+    /**
+     * Creates a linear solver which uses LU decomposition internally
+     *
+     * @param matrixSize Approximate of rows and columns
+     * @return Linear solver
+     */
+    public static LinearSolver<CDenseMatrix64F> lu( int matrixSize ) {
+        return new LinearSolverLu_CD64(new LUDecompositionAlt_CD64());
+    }
+
+    /**
+     * Creates a linear solver which uses Cholesky decomposition internally
+     *
+     * @param matrixSize Approximate of rows and columns
+     * @return Linear solver
+     */
+    public static LinearSolver<CDenseMatrix64F> chol( int matrixSize ) {
+        return new LinearSolverChol_CD64(new CholeskyDecompositionInner_CD64());
+    }
+
+    /**
+     * Creates a linear solver which uses QR decomposition internally
+     *
+     * @param numRows Approximate of rows
+     * @param numCols Approximate of columns
+     * @return Linear solver
+     */
+    public static LinearSolver<CDenseMatrix64F> qr(  int numRows , int numCols ) {
+        return new LinearSolverQrHouseCol_CD64();
+    }
+}
diff --git a/main/denseC64/src/org/ejml/ops/CCommonOps.java b/main/denseC64/src/org/ejml/ops/CCommonOps.java
new file mode 100644
index 0000000..1106d42
--- /dev/null
+++ b/main/denseC64/src/org/ejml/ops/CCommonOps.java
@@ -0,0 +1,978 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.EjmlParameters;
+import org.ejml.alg.dense.decompose.lu.LUDecompositionAlt_CD64;
+import org.ejml.alg.dense.linsol.LinearSolverSafe;
+import org.ejml.alg.dense.misc.CTransposeAlgs;
+import org.ejml.alg.dense.mult.CMatrixMatrixMult;
+import org.ejml.data.*;
+import org.ejml.factory.CLinearSolverFactory;
+import org.ejml.interfaces.linsol.LinearSolver;
+
+import java.util.Arrays;
+
+/**
+ * Common operations on complex numbers
+ *
+ * @author Peter Abeles
+ */
+public class CCommonOps {
+
+    /**
+     * <p>
+     * Creates an identity matrix of the specified size.<br>
+     * <br>
+     * a<sub>ij</sub> = 0+0i   if i ≠ j<br>
+     * a<sub>ij</sub> = 1+0i   if i = j<br>
+     * </p>
+     *
+     * @param width The width and height of the identity matrix.
+     * @return A new instance of an identity matrix.
+     */
+    public static CDenseMatrix64F identity( int width ) {
+        CDenseMatrix64F A = new CDenseMatrix64F(width,width);
+
+        for (int i = 0; i < width; i++) {
+            A.set(i,i,1,0);
+        }
+
+        return A;
+    }
+
+    /**
+     * <p>
+     * Creates a matrix with diagonal elements set to 1 and the rest 0.<br>
+     * <br>
+     * a<sub>ij</sub> = 0+0i   if i ≠ j<br>
+     * a<sub>ij</sub> = 1+0i   if i = j<br>
+     * </p>
+     *
+     * @param width The width of the identity matrix.
+     * @param height The height of the identity matrix.
+     * @return A new instance of an identity matrix.
+     */
+    public static CDenseMatrix64F identity( int width , int height) {
+        CDenseMatrix64F A = new CDenseMatrix64F(width,height);
+
+        int m = Math.min(width,height);
+        for (int i = 0; i < m; i++) {
+            A.set(i,i,1,0);
+        }
+
+        return A;
+    }
+
+    /**
+     * <p>
+     * Creates a new square matrix whose diagonal elements are specified by data and all
+     * the other elements are zero.<br>
+     * <br>
+     * a<sub>ij</sub> = 0         if i ≤ j<br>
+     * a<sub>ij</sub> = diag[i]   if i = j<br>
+     * </p>
+     *
+     * @param data Contains the values of the diagonal elements of the resulting matrix.
+     * @return A new complex matrix.
+     */
+    public static CDenseMatrix64F diag( double... data ) {
+        if( data.length%2 == 1 )
+            throw new IllegalArgumentException("must be an even number of arguments");
+
+        int N = data.length/2;
+
+        CDenseMatrix64F m = new CDenseMatrix64F(N,N);
+
+        int index = 0;
+        for (int i = 0; i < N; i++) {
+            m.set(i,i,data[index++],data[index++]);
+        }
+
+        return m;
+    }
+
+    /**
+     * Converts the real matrix into a complex matrix.
+     *
+     * @param input Real matrix. Not modified.
+     * @param output Complex matrix. Modified.
+     */
+    public static void convert( D1Matrix64F input , CD1Matrix64F output ) {
+        if( input.numCols != output.numCols || input.numRows != output.numRows ) {
+            throw new IllegalArgumentException("The matrices are not all the same dimension.");
+        }
+
+        Arrays.fill(output.data, 0, output.getDataLength(), 0);
+
+        final int length = output.getDataLength();
+
+        for( int i = 0; i < length; i += 2 ) {
+            output.data[i] = input.data[i/2];
+        }
+    }
+
+    /**
+     * Places the real component of the input matrix into the output matrix.
+     *
+     * @param input Complex matrix. Not modified.
+     * @param output real matrix. Modified.
+     */
+    public static DenseMatrix64F stripReal( CD1Matrix64F input , DenseMatrix64F output ) {
+        if( output == null ) {
+            output = new DenseMatrix64F(input.numRows,input.numCols);
+        } else if( input.numCols != output.numCols || input.numRows != output.numRows ) {
+            throw new IllegalArgumentException("The matrices are not all the same dimension.");
+        }
+
+        final int length = input.getDataLength();
+
+        for( int i = 0; i < length; i += 2 ) {
+            output.data[i/2] = input.data[i];
+        }
+        return output;
+    }
+
+    /**
+     * Places the imaginary component of the input matrix into the output matrix.
+     *
+     * @param input Complex matrix. Not modified.
+     * @param output real matrix. Modified.
+     */
+    public static DenseMatrix64F stripImaginary( CD1Matrix64F input , DenseMatrix64F output ) {
+        if( output == null ) {
+            output = new DenseMatrix64F(input.numRows,input.numCols);
+        } else if( input.numCols != output.numCols || input.numRows != output.numRows ) {
+            throw new IllegalArgumentException("The matrices are not all the same dimension.");
+        }
+
+        final int length = input.getDataLength();
+
+        for( int i = 1; i < length; i += 2 ) {
+            output.data[i/2] = input.data[i];
+        }
+        return output;
+    }
+
+    /**
+     * <p>
+     * Computes the magnitude of the complex number in the input matrix and stores the results in the output
+     * matrix.
+     * </p>
+     *
+     * magnitude = sqrt(real^2 + imaginary^2)
+     *
+     * @param input Complex matrix. Not modified.
+     * @param output real matrix. Modified.
+     */
+    public static void magnitude( CD1Matrix64F input , D1Matrix64F output ) {
+        if( input.numCols != output.numCols || input.numRows != output.numRows ) {
+            throw new IllegalArgumentException("The matrices are not all the same dimension.");
+        }
+
+        final int length = input.getDataLength();
+
+        for( int i = 0; i < length; i += 2 ) {
+            double real = input.data[i];
+            double imaginary = input.data[i+1];
+
+            output.data[i/2] = Math.sqrt(real*real + imaginary*imaginary);
+        }
+    }
+
+    /**
+     * <p>
+     * Computes the complex conjugate of the input matrix.<br>
+     * <br>
+     * real<sub>i,j</sub> = real<sub>i,j</sub><br>
+     * imaginary<sub>i,j</sub> = -1*imaginary<sub>i,j</sub><br>
+     * </p>
+     *
+     * @param input Input matrix.  Not modified.
+     * @param output The complex conjugate of the input matrix.  Modified.
+     */
+    public static void conjugate( CD1Matrix64F input , CD1Matrix64F output ) {
+        if( input.numCols != output.numCols || input.numRows != output.numRows ) {
+            throw new IllegalArgumentException("The matrices are not all the same dimension.");
+        }
+
+        final int length = input.getDataLength();
+
+        for( int i = 0; i < length; i += 2 ) {
+            output.data[i] = input.data[i];
+            output.data[i+1] = -input.data[i+1];
+        }
+    }
+
+    /**
+     * <p>
+     * Sets every element in the matrix to the specified value.<br>
+     * <br>
+     * a<sub>ij</sub> = value
+     * <p>
+     *
+     * @param a A matrix whose elements are about to be set. Modified.
+     * @param real The real component
+     * @param imaginary The imaginary component
+     */
+    public static void fill(CD1Matrix64F a, double real, double imaginary)
+    {
+        int N = a.getDataLength();
+        for (int i = 0; i < N; i += 2) {
+            a.data[i] = real;
+            a.data[i+1] = imaginary;
+        }
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a + b <br>
+     * c<sub>ij</sub> = a<sub>ij</sub> + b<sub>ij</sub> <br>
+     * </p>
+     *
+     * <p>
+     * Matrix C can be the same instance as Matrix A and/or B.
+     * </p>
+     *
+     * @param a A Matrix. Not modified.
+     * @param b A Matrix. Not modified.
+     * @param c A Matrix where the results are stored. Modified.
+     */
+    public static void add( CD1Matrix64F a , CD1Matrix64F b , CD1Matrix64F c )
+    {
+        if( a.numCols != b.numCols || a.numRows != b.numRows
+                || a.numCols != c.numCols || a.numRows != c.numRows ) {
+            throw new IllegalArgumentException("The matrices are not all the same dimension.");
+        }
+
+        final int length = a.getDataLength();
+
+        for( int i = 0; i < length; i++ ) {
+            c.data[i] = a.data[i]+b.data[i];
+        }
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a - b <br>
+     * c<sub>ij</sub> = a<sub>ij</sub> - b<sub>ij</sub> <br>
+     * </p>
+     *
+     * <p>
+     * Matrix C can be the same instance as Matrix A and/or B.
+     * </p>
+     *
+     * @param a A Matrix. Not modified.
+     * @param b A Matrix. Not modified.
+     * @param c A Matrix where the results are stored. Modified.
+     */
+    public static void subtract( CD1Matrix64F a , CD1Matrix64F b , CD1Matrix64F c )
+    {
+        if( a.numCols != b.numCols || a.numRows != b.numRows
+                || a.numCols != c.numCols || a.numRows != c.numRows ) {
+            throw new IllegalArgumentException("The matrices are not all the same dimension.");
+        }
+
+        final int length = a.getDataLength();
+
+        for( int i = 0; i < length; i++ ) {
+            c.data[i] = a.data[i]-b.data[i];
+        }
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = a * b <br>
+     * <br>
+     * c<sub>ij</sub> = ∑<sub>k=1:n</sub> { * a<sub>ik</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void mult(CDenseMatrix64F a, CDenseMatrix64F b, CDenseMatrix64F c)
+    {
+        if( b.numCols >= EjmlParameters.CMULT_COLUMN_SWITCH) {
+            CMatrixMatrixMult.mult_reorder(a, b, c);
+        } else {
+            CMatrixMatrixMult.mult_small(a, b, c);
+        }
+    }
+
+    /**
+     * <p>Performs the following operation:<br>
+     * <br>
+     * c = α * a * b <br>
+     * <br>
+     * c<sub>ij</sub> = α ∑<sub>k=1:n</sub> { * a<sub>ik</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param realAlpha real component of scaling factor.
+     * @param imgAlpha imaginary component of scaling factor.
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void mult( double realAlpha , double imgAlpha , CDenseMatrix64F a , CDenseMatrix64F b , CDenseMatrix64F c )
+    {
+        if( b.numCols >= EjmlParameters.CMULT_COLUMN_SWITCH ) {
+            CMatrixMatrixMult.mult_reorder(realAlpha,imgAlpha,a,b,c);
+        } else {
+            CMatrixMatrixMult.mult_small(realAlpha,imgAlpha,a,b,c);
+        }
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c = c + a * b<br>
+     * c<sub>ij</sub> = c<sub>ij</sub> + ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAdd( CDenseMatrix64F a , CDenseMatrix64F b , CDenseMatrix64F c )
+    {
+        if( b.numCols >= EjmlParameters.MULT_COLUMN_SWITCH ) {
+            CMatrixMatrixMult.multAdd_reorder(a, b, c);
+        } else {
+            CMatrixMatrixMult.multAdd_small(a,b,c);
+        }
+    }
+
+    /**
+     * <p>
+     * Performs the following operation:<br>
+     * <br>
+     * c = c + α * a * b<br>
+     * c<sub>ij</sub> = c<sub>ij</sub> +  α * ∑<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>kj</sub>}
+     * </p>
+     *
+     * @param realAlpha real component of scaling factor.
+     * @param imgAlpha imaginary component of scaling factor.
+     * @param a The left matrix in the multiplication operation. Not modified.
+     * @param b The right matrix in the multiplication operation. Not modified.
+     * @param c Where the results of the operation are stored. Modified.
+     */
+    public static void multAdd( double realAlpha , double imgAlpha , CDenseMatrix64F a , CDenseMatrix64F b , CDenseMatrix64F c )
+    {
+        if( b.numCols >= EjmlParameters.CMULT_COLUMN_SWITCH ) {
+            CMatrixMatrixMult.multAdd_reorder(realAlpha,imgAlpha,a,b,c);
+        } else {
+            CMatrixMatrixMult.multAdd_small(realAlpha,imgAlpha,a,b,c);
+        }
+    }
+
+
+
+    /**
+     * <p>Performs an "in-place" transpose.</p>
+     *
+     * <p>
+     * For square matrices the transpose is truly in-place and does not require
+     * additional memory.  For non-square matrices, internally a temporary matrix is declared and
+     * {@link #transpose(org.ejml.data.CDenseMatrix64F, org.ejml.data.CDenseMatrix64F)} is invoked.
+     * </p>
+     *
+     * @param mat The matrix that is to be transposed. Modified.
+     */
+    public static void transpose( CDenseMatrix64F mat ) {
+        if( mat.numCols == mat.numRows ){
+            CTransposeAlgs.square(mat);
+        } else {
+            CDenseMatrix64F b = new CDenseMatrix64F(mat.numCols,mat.numRows);
+            transpose(mat, b);
+            mat.reshape(b.numRows, b.numCols);
+            mat.set(b);
+        }
+    }
+
+    /**
+     * <p>Performs an "in-place" conjugate transpose.</p>
+     *
+     * @see #transpose(org.ejml.data.CDenseMatrix64F)
+     *
+     * @param mat The matrix that is to be transposed. Modified.
+     */
+    public static void transposeConjugate( CDenseMatrix64F mat ) {
+        if( mat.numCols == mat.numRows ){
+            CTransposeAlgs.squareConjugate(mat);
+        } else {
+            CDenseMatrix64F b = new CDenseMatrix64F(mat.numCols,mat.numRows);
+            transposeConjugate(mat, b);
+            mat.reshape(b.numRows, b.numCols);
+            mat.set(b);
+        }
+    }
+
+    /**
+     * <p>
+     * Transposes input matrix 'a' and stores the results in output matrix 'b':<br>
+     * <br>
+     * b<sub>ij</sub> = a<sub>ji</sub><br>
+     * where 'b' is the transpose of 'a'.
+     * </p>
+     *
+     * @param input The original matrix.  Not modified.
+     * @param output Where the transpose is stored. If null a new matrix is created. Modified.
+     * @return The transposed matrix.
+     */
+    public static CDenseMatrix64F transpose( CDenseMatrix64F input , CDenseMatrix64F output )
+    {
+        if( output == null ) {
+            output = new CDenseMatrix64F(input.numCols,input.numRows);
+        } else if( input.numCols != output.numRows || input.numRows != output.numCols ) {
+            throw new IllegalArgumentException("Input and output shapes are not compatible");
+        }
+
+        CTransposeAlgs.standard(input,output);
+
+        return output;
+    }
+
+    /**
+     * <p>
+     * Conjugate transposes input matrix 'a' and stores the results in output matrix 'b':<br>
+     * <br>
+     * b-real<sub>i,j</sub> = a-real<sub>j,i</sub><br>
+     * b-imaginary<sub>i,j</sub> = -1*a-imaginary<sub>j,i</sub><br>
+     * where 'b' is the transpose of 'a'.
+     * </p>
+     *
+     * @param input The original matrix.  Not modified.
+     * @param output Where the transpose is stored. If null a new matrix is created. Modified.
+     * @return The transposed matrix.
+     */
+    public static CDenseMatrix64F transposeConjugate( CDenseMatrix64F input , CDenseMatrix64F output )
+    {
+        if( output == null ) {
+            output = new CDenseMatrix64F(input.numCols,input.numRows);
+        } else if( input.numCols != output.numRows || input.numRows != output.numCols ) {
+            throw new IllegalArgumentException("Input and output shapes are not compatible");
+        }
+
+        CTransposeAlgs.standardConjugate(input, output);
+
+        return output;
+    }
+
+    /**
+     * <p>
+     * Performs a matrix inversion operation on the specified matrix and stores the results
+     * in the same matrix.<br>
+     * <br>
+     * a = a<sup>-1<sup>
+     * </p>
+     *
+     * <p>
+     * If the algorithm could not invert the matrix then false is returned.  If it returns true
+     * that just means the algorithm finished.  The results could still be bad
+     * because the matrix is singular or nearly singular.
+     * </p>
+     *
+     * @param A The matrix that is to be inverted.  Results are stored here.  Modified.
+     * @return true if it could invert the matrix false if it could not.
+     */
+    public static boolean invert( CDenseMatrix64F A )
+    {
+        LinearSolver<CDenseMatrix64F> solver = CLinearSolverFactory.lu(A.numRows);
+
+        if( solver.setA(A) ) {
+            solver.invert(A);
+        } else {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * <p>
+     * Performs a matrix inversion operation that does not modify the original
+     * and stores the results in another matrix.  The two matrices must have the
+     * same dimension.<br>
+     * <br>
+     * b = a<sup>-1<sup>
+     * </p>
+     *
+     * <p>
+     * If the algorithm could not invert the matrix then false is returned.  If it returns true
+     * that just means the algorithm finished.  The results could still be bad
+     * because the matrix is singular or nearly singular.
+     * </p>
+     *
+     * <p>
+     * For medium to large matrices there might be a slight performance boost to using
+     * {@link CLinearSolverFactory} instead.
+     * </p>
+     *
+     * @param input The matrix that is to be inverted. Not modified.
+     * @param output Where the inverse matrix is stored.  Modified.
+     * @return true if it could invert the matrix false if it could not.
+     */
+    public static boolean invert( CDenseMatrix64F input , CDenseMatrix64F output )
+    {
+        LinearSolver<CDenseMatrix64F> solver = CLinearSolverFactory.lu(input.numRows);
+
+        if( solver.modifiesA() )
+            input = input.copy();
+
+        if( !solver.setA(input))
+            return false;
+        solver.invert(output);
+        return true;
+    }
+
+    /**
+     * <p>
+     * Solves for x in the following equation:<br>
+     * <br>
+     * A*x = b
+     * </p>
+     *
+     * <p>
+     * If the system could not be solved then false is returned.  If it returns true
+     * that just means the algorithm finished operating, but the results could still be bad
+     * because 'A' is singular or nearly singular.
+     * </p>
+     *
+     * <p>
+     * If repeat calls to solve are being made then one should consider using {@link CLinearSolverFactory}
+     * instead.
+     * </p>
+     *
+     * <p>
+     * It is ok for 'b' and 'x' to be the same matrix.
+     * </p>
+     *
+     * @param a A matrix that is m by n. Not modified.
+     * @param b A matrix that is n by k. Not modified.
+     * @param x A matrix that is m by k. Modified.
+     *
+     * @return true if it could invert the matrix false if it could not.
+     */
+    public static boolean solve( CDenseMatrix64F a , CDenseMatrix64F b , CDenseMatrix64F x )
+    {
+        LinearSolver<CDenseMatrix64F> solver;
+        if( a.numCols == a.numRows ) {
+            solver = CLinearSolverFactory.lu(a.numRows);
+        } else {
+            solver = CLinearSolverFactory.qr(a.numRows, a.numCols);
+        }
+
+        // make sure the inputs 'a' and 'b' are not modified
+        solver = new LinearSolverSafe<CDenseMatrix64F>(solver);
+
+        if( !solver.setA(a) )
+            return false;
+
+        solver.solve(b,x);
+        return true;
+    }
+
+    /**
+     * Returns the determinant of the matrix.  If the inverse of the matrix is also
+     * needed, then using {@link LUDecompositionAlt_CD64} directly (or any
+     * similar algorithm) can be more efficient.
+     *
+     * @param mat The matrix whose determinant is to be computed.  Not modified.
+     * @return The determinant.
+     */
+    public static Complex64F det( CDenseMatrix64F mat  )
+    {
+        LUDecompositionAlt_CD64 alg = new LUDecompositionAlt_CD64();
+
+        if( alg.inputModified() ) {
+            mat = mat.copy();
+        }
+
+        if( !alg.decompose(mat) )
+            return new Complex64F();
+        return alg.computeDeterminant();
+    }
+
+    /**
+     * <p>Performs  element by element multiplication operation with a complex numbert<br>
+     * <br>
+     * output<sub>ij</sub> = input<sub>ij</sub> * (real + imaginary*i) <br>
+     * </p>
+     * @param input The left matrix in the multiplication operation. Not modified.
+     * @param real Real component of the number it is multiplied by
+     * @param imaginary Imaginary component of the number it is multiplied by
+     * @param output Where the results of the operation are stored. Modified.
+     */
+    public static void elementMultiply( CD1Matrix64F input , double real , double imaginary, CD1Matrix64F output )
+    {
+        if( input.numCols != output.numCols || input.numRows != output.numRows ) {
+            throw new IllegalArgumentException("The 'input' and 'output' matrices do not have compatible dimensions");
+        }
+
+        int N = input.getDataLength();
+        for (int i = 0; i < N; i += 2 ) {
+            double inReal = input.data[i];
+            double intImag = input.data[i+1];
+
+            output.data[i] = inReal*real - intImag*imaginary;
+            output.data[i+1] = inReal*imaginary + intImag*real;
+        }
+    }
+
+    /**
+     * <p>Performs  element by element division operation with a complex number on the right<br>
+     * <br>
+     * output<sub>ij</sub> = input<sub>ij</sub> / (real + imaginary*i) <br>
+     * </p>
+     * @param input The left matrix in the multiplication operation. Not modified.
+     * @param real Real component of the number it is multiplied by
+     * @param imaginary Imaginary component of the number it is multiplied by
+     * @param output Where the results of the operation are stored. Modified.
+     */
+    public static void elementDivide( CD1Matrix64F input , double real , double imaginary, CD1Matrix64F output )
+    {
+        if( input.numCols != output.numCols || input.numRows != output.numRows ) {
+            throw new IllegalArgumentException("The 'input' and 'output' matrices do not have compatible dimensions");
+        }
+
+        double norm = real*real + imaginary*imaginary;
+
+        int N = input.getDataLength();
+        for (int i = 0; i < N; i += 2 ) {
+            double inReal = input.data[i];
+            double inImag = input.data[i+1];
+
+            output.data[i]   = (inReal*real + inImag*imaginary)/norm;
+            output.data[i+1] = (inImag*real - inReal*imaginary)/norm;
+        }
+    }
+
+    /**
+     * <p>Performs  element by element division operation with a complex number on the right<br>
+     * <br>
+     * output<sub>ij</sub> = (real + imaginary*i) / input<sub>ij</sub> <br>
+     * </p>
+     * @param real Real component of the number it is multiplied by
+     * @param imaginary Imaginary component of the number it is multiplied by
+     * @param input The right matrix in the multiplication operation. Not modified.
+     * @param output Where the results of the operation are stored. Modified.
+     */
+    public static void elementDivide( double real , double imaginary, CD1Matrix64F input , CD1Matrix64F output )
+    {
+        if( input.numCols != output.numCols || input.numRows != output.numRows ) {
+            throw new IllegalArgumentException("The 'input' and 'output' matrices do not have compatible dimensions");
+        }
+
+        int N = input.getDataLength();
+        for (int i = 0; i < N; i += 2 ) {
+            double inReal = input.data[i];
+            double inImag = input.data[i+1];
+
+            double norm = inReal*inReal + inImag*inImag;
+
+            output.data[i]   = (real*inReal + imaginary*inImag)/norm;
+            output.data[i+1] = (imaginary*inReal - real*inImag)/norm;
+        }
+    }
+
+    /**
+     * <p>
+     * Returns the value of the real element in the matrix that has the minimum value.<br>
+     * <br>
+     * Min{ a<sub>ij</sub> } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The the minimum value out of all the real values.
+     */
+    public static double elementMinReal( CD1Matrix64F a ) {
+        final int size = a.getDataLength();
+
+        double min = a.data[0];
+        for( int i = 2; i < size; i += 2 ) {
+            double val = a.data[i];
+            if( val < min ) {
+                min = val;
+            }
+        }
+
+        return min;
+    }
+
+    /**
+     * <p>
+     * Returns the value of the imaginary element in the matrix that has the minimum value.<br>
+     * <br>
+     * Min{ a<sub>ij</sub> } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The the minimum value out of all the real values.
+     */
+    public static double elementMinImaginary( CD1Matrix64F a ) {
+        final int size = a.getDataLength();
+
+        double min = a.data[1];
+        for( int i = 3; i < size; i += 2 ) {
+            double val = a.data[i];
+            if( val < min ) {
+                min = val;
+            }
+        }
+
+        return min;
+    }
+
+    /**
+     * <p>
+     * Returns the value of the real element in the matrix that has the minimum value.<br>
+     * <br>
+     * Min{ a<sub>ij</sub> } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The the minimum value out of all the real values.
+     */
+    public static double elementMaxReal( CD1Matrix64F a ) {
+        final int size = a.getDataLength();
+
+        double max = a.data[0];
+        for( int i = 2; i < size; i += 2 ) {
+            double val = a.data[i];
+            if( val > max ) {
+                max = val;
+            }
+        }
+
+        return max;
+    }
+
+    /**
+     * <p>
+     * Returns the value of the imaginary element in the matrix that has the minimum value.<br>
+     * <br>
+     * Min{ a<sub>ij</sub> } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The the minimum value out of all the real values.
+     */
+    public static double elementMaxImaginary( CD1Matrix64F a ) {
+        final int size = a.getDataLength();
+
+        double max = a.data[1];
+        for( int i = 3; i < size; i += 2 ) {
+            double val = a.data[i];
+            if( val > max ) {
+                max = val;
+            }
+        }
+
+        return max;
+    }
+
+    /**
+     * <p>
+     * Returns the magnitude squared of the complex element with the largest magnitude<br>
+     * <br>
+     * Max{ |a<sub>ij</sub>|^2 } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max magnitude squared
+     */
+    public static double elementMaxMagnitude2( CD1Matrix64F a ) {
+        final int size = a.getDataLength();
+
+        double max = 0;
+        for( int i = 0; i < size; ) {
+            double real = a.data[i++];
+            double imaginary = a.data[i++];
+
+            double m = real*real + imaginary*imaginary;
+
+            if( m > max ) {
+                max = m;
+            }
+        }
+
+        return max;
+    }
+
+    /**
+     * Sets all the diagonal elements equal to one and everything else equal to zero.
+     * If this is a square matrix then it will be an identity matrix.
+     *
+     * @param mat A square matrix.
+     */
+    public static void setIdentity( CDenseMatrix64F mat )
+    {
+        int width = mat.numRows < mat.numCols ? mat.numRows : mat.numCols;
+
+        Arrays.fill(mat.data,0,mat.getDataLength(),0);
+
+        int index = 0;
+        int stride = mat.getRowStride();
+
+        for( int i = 0; i < width; i++ , index += stride + 2) {
+            mat.data[index] = 1;
+        }
+    }
+
+    /**
+     * <p>
+     * Creates a new matrix which is the specified submatrix of 'src'
+     * </p>
+     * <p>
+     * s<sub>i-y0 , j-x0</sub> = o<sub>ij</sub> for all y0 ≤ i < y1 and x0 ≤ j < x1 <br>
+     * <br>
+     * where 's<sub>ij</sub>' is an element in the submatrix and 'o<sub>ij</sub>' is an element in the
+     * original matrix.
+     * </p>
+     *
+     * @param src The original matrix which is to be copied.  Not modified.
+     * @param srcX0 Start column.
+     * @param srcX1 Stop column+1.
+     * @param srcY0 Start row.
+     * @param srcY1 Stop row+1.
+     * @return Extracted submatrix.
+     */
+    public static CDenseMatrix64F extract( CDenseMatrix64F src,
+                                          int srcY0, int srcY1,
+                                          int srcX0, int srcX1 )
+    {
+        if( srcY1 <= srcY0 || srcY0 < 0 || srcY1 > src.numRows )
+            throw new IllegalArgumentException("srcY1 <= srcY0 || srcY0 < 0 || srcY1 > src.numRows");
+        if( srcX1 <= srcX0 || srcX0 < 0 || srcX1 > src.numCols )
+            throw new IllegalArgumentException("srcX1 <= srcX0 || srcX0 < 0 || srcX1 > src.numCols");
+
+        int w = srcX1-srcX0;
+        int h = srcY1-srcY0;
+
+        CDenseMatrix64F dst = new CDenseMatrix64F(h,w);
+
+        extract(src, srcY0, srcY1, srcX0, srcX1, dst, 0, 0);
+
+        return dst;
+    }
+
+    /**
+     * <p>
+     * Extracts a submatrix from 'src' and inserts it in a submatrix in 'dst'.
+     * </p>
+     * <p>
+     * s<sub>i-y0 , j-x0</sub> = o<sub>ij</sub> for all y0 ≤ i < y1 and x0 ≤ j < x1 <br>
+     * <br>
+     * where 's<sub>ij</sub>' is an element in the submatrix and 'o<sub>ij</sub>' is an element in the
+     * original matrix.
+     * </p>
+     *
+     * @param src The original matrix which is to be copied.  Not modified.
+     * @param srcX0 Start column.
+     * @param srcX1 Stop column+1.
+     * @param srcY0 Start row.
+     * @param srcY1 Stop row+1.
+     * @param dst Where the submatrix are stored.  Modified.
+     * @param dstY0 Start row in dst.
+     * @param dstX0 start column in dst.
+     */
+    public static void extract(CDenseMatrix64F src,
+                               int srcY0, int srcY1,
+                               int srcX0, int srcX1,
+                               CDenseMatrix64F dst,
+                               int dstY0, int dstX0 )
+    {
+        int numRows = srcY1 - srcY0;
+        int stride = (srcX1 - srcX0)*2;
+
+        for( int y = 0; y < numRows; y++ ) {
+            int indexSrc = src.getIndex(y+srcY0,srcX0);
+            int indexDst = dst.getIndex(y+dstY0,dstX0);
+            System.arraycopy(src.data,indexSrc,dst.data,indexDst, stride);
+        }
+    }
+
+    /**
+     * Converts the columns in a matrix into a set of vectors.
+     *
+     * @param A Matrix.  Not modified.
+     * @param v Optional storage for columns.
+     * @return An array of vectors.
+     */
+    public static CDenseMatrix64F[] columnsToVector(CDenseMatrix64F A, CDenseMatrix64F[] v)
+    {
+        CDenseMatrix64F []ret;
+        if( v == null || v.length < A.numCols ) {
+            ret = new CDenseMatrix64F[ A.numCols ];
+        } else {
+            ret = v;
+        }
+
+        for( int i = 0; i < ret.length; i++ ) {
+            if( ret[i] == null ) {
+                ret[i] = new CDenseMatrix64F(A.numRows,1);
+            } else {
+                ret[i].reshape(A.numRows,1);
+            }
+
+            CDenseMatrix64F u = ret[i];
+
+            int indexU = 0;
+            for( int j = 0; j < A.numRows; j++ ) {
+                int indexA = A.getIndex(j,i);
+                u.data[indexU++] = A.data[indexA++];
+                u.data[indexU++] = A.data[indexA];
+            }
+        }
+
+        return ret;
+    }
+
+    /**
+     * <p>
+     * Returns the absolute value of the element in the matrix that has the largest absolute value.<br>
+     * <br>
+     * Max{ |a<sub>ij</sub>| } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max abs element value of the matrix.
+     */
+    public static double elementMaxAbs( CDenseMatrix64F a ) {
+        final int size = a.getDataLength();
+
+        double max = 0;
+        for( int i = 0; i < size; i += 2 ) {
+            double real = a.data[i];
+            double imag = a.data[i+1];
+
+            double val = real*real + imag*imag;
+
+            if( val > max ) {
+                max = val;
+            }
+        }
+
+        return Math.sqrt(max);
+    }
+}
diff --git a/main/denseC64/src/org/ejml/ops/CMatrixFeatures.java b/main/denseC64/src/org/ejml/ops/CMatrixFeatures.java
new file mode 100644
index 0000000..e3e3234
--- /dev/null
+++ b/main/denseC64/src/org/ejml/ops/CMatrixFeatures.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.alg.dense.decompose.chol.CholeskyDecompositionInner_CD64;
+import org.ejml.alg.dense.mult.CVectorVectorMult;
+import org.ejml.data.CD1Matrix64F;
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.data.Complex64F;
+import org.ejml.data.ComplexMatrix64F;
+
+/**
+ * <p>
+ * Functions for computing the features of complex matrices
+ * <p>
+ *
+ * @author Peter Abeles
+ */
+public class CMatrixFeatures {
+
+    /**
+     * Checks to see if any element in the matrix is NaN.
+     *
+     * @param m A matrix. Not modified.
+     * @return True if any element in the matrix is NaN.
+     */
+    public static boolean hasNaN( CD1Matrix64F m )
+    {
+        int length = m.getDataLength();
+
+        for( int i = 0; i < length; i++ ) {
+            if( Double.isNaN(m.data[i]))
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Checks to see if any element in the matrix is NaN of Infinite.
+     *
+     * @param m A matrix. Not modified.
+     * @return True if any element in the matrix is NaN of Infinite.
+     */
+    public static boolean hasUncountable( CD1Matrix64F m )
+    {
+        int length = m.getDataLength();
+
+        for( int i = 0; i < length; i++ ) {
+            double a = m.data[i];
+            if( Double.isNaN(a) || Double.isInfinite(a))
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * <p>
+     * Checks to see if each element in the two matrices are equal:
+     * a<sub>ij</sub> == b<sub>ij</sub>
+     * <p>
+     *
+     * <p>
+     * NOTE: If any of the elements are NaN then false is returned.  If two corresponding
+     * elements are both positive or negative infinity then they are equal.
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @param b A matrix. Not modified.
+     * @return true if identical and false otherwise.
+     */
+    public static boolean isEquals( CD1Matrix64F a, CD1Matrix64F b ) {
+        if( a.numRows != b.numRows || a.numCols != b.numCols ) {
+            return false;
+        }
+
+        final int length = a.getDataLength();
+        for( int i = 0; i < length; i++ ) {
+            if( !(a.data[i] == b.data[i]) ) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * <p>
+     * Checks to see if each element in the two matrices are within tolerance of
+     * each other: tol ≥ |a<sub>ij</sub> - b<sub>ij</sub>|.
+     * <p>
+     *
+     * <p>
+     * NOTE: If any of the elements are not countable then false is returned.<br>
+     * NOTE: If a tolerance of zero is passed in this is equivalent to calling
+     * {@link #isEquals(org.ejml.data.CD1Matrix64F, org.ejml.data.CD1Matrix64F)}
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @param b A matrix. Not modified.
+     * @param tol How close to being identical each element needs to be.
+     * @return true if equals and false otherwise.
+     */
+    public static boolean isEquals( CD1Matrix64F a , CD1Matrix64F b , double tol )
+    {
+        if( a.numRows != b.numRows || a.numCols != b.numCols ) {
+            return false;
+        }
+
+        if( tol == 0.0 )
+            return isEquals(a,b);
+
+        final int length = a.getDataLength();
+
+        for( int i = 0; i < length; i++ ) {
+            if( !(tol >= Math.abs(a.data[i] - b.data[i])) ) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * <p>
+     * Checks to see if each corresponding element in the two matrices are
+     * within tolerance of each other or have the some symbolic meaning.  This
+     * can handle NaN and Infinite numbers.
+     * <p>
+     *
+     * <p>
+     * If both elements are countable then the following equality test is used:<br>
+     * |a<sub>ij</sub> - b<sub>ij</sub>| ≤ tol.<br>
+     * Otherwise both numbers must both be Double.NaN, Double.POSITIVE_INFINITY, or
+     * Double.NEGATIVE_INFINITY to be identical.
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @param b A matrix. Not modified.
+     * @param tol Tolerance for equality.
+     * @return true if identical and false otherwise.
+     */
+    public static boolean isIdentical( CD1Matrix64F a, CD1Matrix64F b , double tol ) {
+        if( a.numRows != b.numRows || a.numCols != b.numCols ) {
+            return false;
+        }
+        if( tol < 0 )
+            throw new IllegalArgumentException("Tolerance must be greater than or equal to zero.");
+
+        final int length = a.getDataLength();
+        for( int i = 0; i < length; i++ ) {
+            double valA = a.data[i];
+            double valB = b.data[i];
+
+            // if either is negative or positive infinity the result will be positive infinity
+            // if either is NaN the result will be NaN
+            double diff = Math.abs(valA-valB);
+
+            // diff = NaN == false
+            // diff = infinity == false
+            if( tol >= diff )
+                continue;
+
+            if( Double.isNaN(valA) ) {
+                return Double.isNaN(valB);
+            } else if( Double.isInfinite(valA) ) {
+                return valA == valB;
+            } else {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Checks to see if the provided matrix is within tolerance to an identity matrix.
+     *
+     * @param mat Matrix being examined.  Not modified.
+     * @param tol Tolerance.
+     * @return True if it is within tolerance to an identify matrix.
+     */
+    public static boolean isIdentity( ComplexMatrix64F mat , double tol ) {
+        // see if the result is an identity matrix
+        Complex64F c = new Complex64F();
+        for (int i = 0; i < mat.getNumRows(); i++) {
+            for (int j = 0; j < mat.getNumCols(); j++) {
+                mat.get(i, j, c);
+                if (i == j) {
+                    if (!(Math.abs(c.real - 1) <= tol))
+                        return false;
+                    if (!(Math.abs(c.imaginary) <= tol))
+                        return false;
+                } else {
+                    if (!(Math.abs(c.real) <= tol))
+                        return false;
+                    if (!(Math.abs(c.imaginary) <= tol))
+                        return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Hermitian matrix is a square matrix with complex entries that is equal to its own conjugate transpose.
+     *
+     * @param Q The matrix being tested. Not modified.
+     * @param tol Tolerance.
+     * @return True if it passes the test.
+     */
+    public static boolean isHermitian( CDenseMatrix64F Q , double tol ) {
+        if( Q.numCols != Q.numRows )
+            return false;
+
+        Complex64F a = new Complex64F();
+        Complex64F b = new Complex64F();
+
+
+        for( int i = 0; i < Q.numCols; i++ ) {
+            for( int j = i+1; j < Q.numCols; j++ ) {
+                Q.get(i,j,a);
+                Q.get(j,i,b);
+
+                if( Math.abs(a.real-b.real)>tol)
+                    return false;
+                if( Math.abs(a.imaginary+b.imaginary)>tol)
+                    return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * <p>
+     * Unitary matrices have the following properties:<br><br>
+     * Q*Q<sup>H</sup> = I
+     * </p>
+     * <p>
+     * This is the complex equivalent of orthogonal matrix.
+     * </p>
+     * @param Q The matrix being tested. Not modified.
+     * @param tol Tolerance.
+     * @return True if it passes the test.
+     */
+    public static boolean isUnitary( CDenseMatrix64F Q , double tol ) {
+        if( Q.numRows < Q.numCols ) {
+            throw new IllegalArgumentException("The number of rows must be more than or equal to the number of columns");
+        }
+
+        Complex64F prod = new Complex64F();
+
+        CDenseMatrix64F u[] = CCommonOps.columnsToVector(Q, null);
+
+        for( int i = 0; i < u.length; i++ ) {
+            CDenseMatrix64F a = u[i];
+
+            CVectorVectorMult.innerProdH(a, a, prod);
+
+            if( Math.abs(prod.real-1) > tol)
+                return false;
+            if( Math.abs(prod.imaginary) > tol)
+                return false;
+
+            for( int j = i+1; j < u.length; j++ ) {
+                CVectorVectorMult.innerProdH(a, u[j], prod);
+
+                if( !(prod.getMagnitude2() <= tol*tol))
+                    return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * <p>
+     * Checks to see if the matrix is positive definite.
+     * </p>
+     * <p>
+     * x<sup>T</sup> A x > 0<br>
+     * for all x where x is a non-zero vector and A is a hermitian matrix.
+     * </p>
+     *
+     * @param A square hermitian matrix. Not modified.
+     *
+     * @return True if it is positive definite and false if it is not.
+     */
+    public static boolean isPositiveDefinite( CDenseMatrix64F A ) {
+        if( A.numCols != A.numRows)
+            return false;
+
+        CholeskyDecompositionInner_CD64 chol = new CholeskyDecompositionInner_CD64(true);
+        if( chol.inputModified() )
+            A = A.copy();
+
+        return chol.decompose(A);
+    }
+}
diff --git a/main/denseC64/src/org/ejml/ops/CNormOps.java b/main/denseC64/src/org/ejml/ops/CNormOps.java
new file mode 100644
index 0000000..8e05bc1
--- /dev/null
+++ b/main/denseC64/src/org/ejml/ops/CNormOps.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.data.CDenseMatrix64F;
+
+/**
+ * @author Peter Abeles
+ */
+public class CNormOps {
+    /**
+     * <p>
+     * Computes the Frobenius matrix norm:<br>
+     * <br>
+     * normF = Sqrt{  ∑<sub>i=1:m</sub> ∑<sub>j=1:n</sub> { a<sub>ij</sub><sup>2</sup>}   }
+     * </p>
+     * <p>
+     * This is equivalent to the element wise p=2 norm.  See {@link #fastNormF} for another implementation
+     * that is faster, but more prone to underflow/overflow errors.
+     * </p>
+     *
+     * @param a The matrix whose norm is computed.  Not modified.
+     * @return The norm's value.
+     */
+    public static double normF( CDenseMatrix64F a ) {
+        double total = 0;
+
+        double scale = CCommonOps.elementMaxAbs(a);
+
+        if( scale == 0.0 )
+            return 0.0;
+
+        final int size = a.getDataLength();
+
+        for( int i = 0; i < size; i += 2 ) {
+            double real = a.data[i]/scale;
+            double imag = a.data[i+1]/scale;
+
+            total += real*real + imag*imag;
+        }
+
+        return scale*Math.sqrt(total);
+    }
+}
diff --git a/main/denseC64/src/org/ejml/ops/CRandomMatrices.java b/main/denseC64/src/org/ejml/ops/CRandomMatrices.java
new file mode 100644
index 0000000..ef7e2c7
--- /dev/null
+++ b/main/denseC64/src/org/ejml/ops/CRandomMatrices.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.data.CD1Matrix64F;
+import org.ejml.data.CDenseMatrix64F;
+
+import java.util.Random;
+
+/**
+ * Contains a list of functions for creating random dense complex matrices and vectors with different structures.
+ *
+ * @author Peter Abeles
+ */
+public class CRandomMatrices {
+    /**
+     * <p>
+     * Returns a matrix where all the elements are selected independently from
+     * a uniform distribution between -1 and 1 inclusive.
+     * </p>
+     *
+     * @param numRow Number of rows in the new matrix.
+     * @param numCol Number of columns in the new matrix.
+     * @param rand Random number generator used to fill the matrix.
+     * @return The randomly generated matrix.
+     */
+    public static CDenseMatrix64F createRandom( int numRow , int numCol , Random rand ) {
+        return createRandom(numRow,numCol,-1,1,rand);
+    }
+
+    /**
+     * <p>
+     * Returns a matrix where all the elements are selected independently from
+     * a uniform distribution between 'min' and 'max' inclusive.
+     * </p>
+     *
+     * @param numRow Number of rows in the new matrix.
+     * @param numCol Number of columns in the new matrix.
+     * @param min The minimum value each element can be.
+     * @param max The maximum value each element can be.
+     * @param rand Random number generator used to fill the matrix.
+     * @return The randomly generated matrix.
+     */
+    public static CDenseMatrix64F createRandom( int numRow , int numCol , double min , double max , Random rand ) {
+        CDenseMatrix64F mat = new CDenseMatrix64F(numRow,numCol);
+
+        setRandom(mat,min,max,rand);
+
+        return mat;
+    }
+
+    /**
+     * <p>
+     * Sets each element in the matrix to a value drawn from an uniform distribution from 0 to 1 inclusive.
+     * </p>
+     *
+     * @param mat The matrix who is to be randomized. Modified.
+     * @param rand Random number generator used to fill the matrix.
+     */
+    public static void setRandom( CDenseMatrix64F mat , Random rand )
+    {
+        setRandom(mat,0,1,rand);
+    }
+
+    /**
+     * <p>
+     * Sets each element in the matrix to a value drawn from an uniform distribution from 'min' to 'max' inclusive.
+     * </p>
+     *
+     * @param min The minimum value each element can be.
+     * @param max The maximum value each element can be.
+     * @param mat The matrix who is to be randomized. Modified.
+     * @param rand Random number generator used to fill the matrix.
+     */
+    public static void setRandom( CD1Matrix64F mat , double min , double max , Random rand )
+    {
+        double d[] = mat.getData();
+        int size = mat.getDataLength();
+
+        double r = max-min;
+
+        for( int i = 0; i < size; i++ ) {
+            d[i] = r*rand.nextDouble()+min;
+        }
+    }
+
+    /**
+     * Creates a random symmetric positive definite matrix.
+     *
+     * @param width The width of the square matrix it returns.
+     * @param rand Random number generator used to make the matrix.
+     * @return The random symmetric  positive definite matrix.
+     */
+    public static CDenseMatrix64F createHermPosDef(int width, Random rand) {
+        // This is not formally proven to work.  It just seems to work.
+        CDenseMatrix64F a = CRandomMatrices.createRandom(width,1,rand);
+        CDenseMatrix64F b = new CDenseMatrix64F(1,width);
+        CDenseMatrix64F c = new CDenseMatrix64F(width,width);
+
+        CCommonOps.transposeConjugate(a,b);
+        CCommonOps.mult(a, b, c);
+
+        for( int i = 0; i < width; i++ ) {
+            c.data[2*(i*width+i)] += 1;
+        }
+
+        return c;
+    }
+
+    /**
+     * Creates a random Hermitian matrix with elements from min to max value.
+     *
+     * @param length Width and height of the matrix.
+     * @param min Minimum value an element can have.
+     * @param max Maximum value an element can have.
+     * @param rand Random number generator.
+     * @return A symmetric matrix.
+     */
+    public static CDenseMatrix64F createHermitian(int length, double min, double max, Random rand) {
+        CDenseMatrix64F A = new CDenseMatrix64F(length,length);
+
+        setHermitian(A, min, max, rand);
+
+        return A;
+    }
+
+    /**
+     * Assigns the provided square matrix to be a random Hermitian matrix with elements from min to max value.
+     *
+     * @param A The matrix that is to be modified.  Must be square.  Modified.
+     * @param min Minimum value an element can have.
+     * @param max Maximum value an element can have.
+     * @param rand Random number generator.
+     */
+    public static void setHermitian(CDenseMatrix64F A, double min, double max, Random rand) {
+        if( A.numRows != A.numCols )
+            throw new IllegalArgumentException("A must be a square matrix");
+
+        double range = max-min;
+
+        int length = A.numRows;
+
+        for( int i = 0; i < length; i++ ) {
+            for( int j = i; j < length; j++ ) {
+                double real = rand.nextDouble()*range + min;
+                double imaginary = rand.nextDouble()*range + min;
+                A.set(i,j,real,imaginary);
+                A.set(j,i,real,-imaginary);
+            }
+        }
+    }
+
+//    /**
+//     * Creates an upper triangular matrix whose values are selected from a uniform distribution.  If hessenberg
+//     * is greater than zero then a hessenberg matrix of the specified degree is created instead.
+//     *
+//     * @param dimen Number of rows and columns in the matrix..
+//     * @param hessenberg 0 for triangular matrix and > 0 for hessenberg matrix.
+//     * @param min minimum value an element can be.
+//     * @param max maximum value an element can be.
+//     * @param rand random number generator used.
+//     * @return The randomly generated matrix.
+//     */
+//    public static CDenseMatrix64F createUpperTriangle( int dimen , int hessenberg , double min , double max , Random rand )
+//    {
+//        if( hessenberg < 0 )
+//            throw new RuntimeException("hessenberg must be more than or equal to 0");
+//
+//        double range = max-min;
+//
+//        CDenseMatrix64F A = new CDenseMatrix64F(dimen,dimen);
+//
+//        for( int i = 0; i < dimen; i++ ) {
+//            int start = i <= hessenberg ? 0 : i-hessenberg;
+//
+//            for( int j = start; j < dimen; j++ ) {
+//                double real = rand.nextDouble()*range + min;
+//                double imaginary = rand.nextDouble()*range + min;
+//
+//                A.set(i,j, real, imaginary);
+//            }
+//
+//        }
+//
+//        return A;
+//    }
+}
diff --git a/main/denseC64/src/org/ejml/ops/CSpecializedOps.java b/main/denseC64/src/org/ejml/ops/CSpecializedOps.java
new file mode 100644
index 0000000..1c9b72f
--- /dev/null
+++ b/main/denseC64/src/org/ejml/ops/CSpecializedOps.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.alg.dense.mult.CVectorVectorMult;
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.data.Complex64F;
+
+/**
+ * @author Peter Abeles
+ */
+public class CSpecializedOps {
+    /**
+     * <p>
+     * Creates a pivot matrix that exchanges the rows in a matrix:
+     * <br>
+     * A' = P*A<br>
+     * </p>
+     * <p>
+     * For example, if element 0 in 'pivots' is 2 then the first row in A' will be the 3rd row in A.
+     * </p>
+     *
+     * @param ret If null then a new matrix is declared otherwise the results are written to it.  Is modified.
+     * @param pivots Specifies the new order of rows in a matrix.
+     * @param numPivots How many elements in pivots are being used.
+     * @param transposed If the transpose of the matrix is returned.
+     * @return A pivot matrix.
+     */
+    public static CDenseMatrix64F pivotMatrix(CDenseMatrix64F ret, int pivots[], int numPivots, boolean transposed ) {
+
+        if( ret == null ) {
+            ret = new CDenseMatrix64F(numPivots, numPivots);
+        } else {
+            if( ret.numCols != numPivots || ret.numRows != numPivots )
+                throw new IllegalArgumentException("Unexpected matrix dimension");
+            CCommonOps.fill(ret, 0,0);
+        }
+
+        if( transposed ) {
+            for( int i = 0; i < numPivots; i++ ) {
+                ret.set(pivots[i],i,1,0);
+            }
+        } else {
+            for( int i = 0; i < numPivots; i++ ) {
+                ret.set(i,pivots[i],1,0);
+            }
+        }
+
+        return ret;
+    }
+
+    /**
+     * <p>
+     * Returns the magnitude squared of the complex element along the diagonal with the largest magnitude<br>
+     * <br>
+     * Max{ |a<sub>ij</sub>|^2 } for all i and j<br>
+     * </p>
+     *
+     * @param a A matrix. Not modified.
+     * @return The max magnitude squared
+     */
+    public static double elementDiagMaxMagnitude2(CDenseMatrix64F a) {
+        final int size = Math.min(a.numRows,a.numCols);
+
+        int rowStride = a.getRowStride();
+        double max = 0;
+        for( int i = 0; i < size; i++ ) {
+            int index = i*rowStride + i*2;
+
+            double real = a.data[index];
+            double imaginary = a.data[index+1];
+
+            double m = real*real + imaginary*imaginary;
+
+            if( m > max ) {
+                max = m;
+            }
+        }
+
+        return max;
+    }
+
+    /**
+     * Computes the quality of a triangular matrix, where the quality of a matrix
+     * is defined in {@link org.ejml.interfaces.linsol.LinearSolver#quality()}.  In
+     * this situation the quality is the magnitude of the product of
+     * each diagonal element divided by the magnitude of the largest diagonal element.
+     * If all diagonal elements are zero then zero is returned.
+     *
+     * @return the quality of the system.
+     */
+    public static double qualityTriangular(CDenseMatrix64F T)
+    {
+        int N = Math.min(T.numRows,T.numCols);
+
+        double max = elementDiagMaxMagnitude2(T);
+
+        if( max == 0.0d )
+            return 0.0d;
+
+        max = Math.sqrt(max);
+        int rowStride = T.getRowStride();
+
+        double qualityR = 1.0;
+        double qualityI = 0.0;
+
+        for( int i = 0; i < N; i++ ) {
+            int index = i*rowStride + i*2;
+
+            double real = T.data[index]/max;
+            double imaginary = T.data[index]/max;
+
+            double r = qualityR*real - qualityI*imaginary;
+            double img = qualityR*imaginary + real*qualityI;
+
+            qualityR = r;
+            qualityI = img;
+        }
+
+        return Math.sqrt(qualityR*qualityR + qualityI*qualityI);
+    }
+
+    /**
+     * Q = I - gamma*u*u<sup>H</sup>
+     */
+    public static CDenseMatrix64F householder( CDenseMatrix64F u , double gamma ) {
+        int N = u.getDataLength()/2;
+        // u*u^H
+        CDenseMatrix64F uut = new CDenseMatrix64F(N,N);
+        CVectorVectorMult.outerProdH(u, u, uut);
+        // foo = -gamma*u*u^H
+        CCommonOps.elementMultiply(uut,-gamma,0,uut);
+
+        // I + foo
+        for (int i = 0; i < N; i++) {
+            int index = (i*uut.numCols+i)*2;
+            uut.data[index] = 1 + uut.data[index];
+        }
+
+        return uut;
+    }
+
+    /**
+     * Computes the householder vector used in QR decomposition.
+     *
+     * u = x / max(x)
+     * u(0) = u(0) + |u|
+     * u = u / u(0)
+     *
+     * @param x Input vector.  Unmodified.
+     * @return The found householder reflector vector
+     */
+    public static CDenseMatrix64F householderVector( CDenseMatrix64F x ) {
+        CDenseMatrix64F u = x.copy();
+
+        double max = CCommonOps.elementMaxAbs(u);
+
+        CCommonOps.elementDivide(u, max, 0, u);
+
+        double nx = CNormOps.normF(u);
+        Complex64F c = new Complex64F();
+        u.get(0,0,c);
+
+        double realTau,imagTau;
+
+        if( c.getMagnitude() == 0 ) {
+            realTau = nx;
+            imagTau = 0;
+        } else {
+            realTau = c.real/c.getMagnitude()*nx;
+            imagTau = c.imaginary/c.getMagnitude()*nx;
+        }
+
+        u.set(0,0,c.real + realTau,c.imaginary + imagTau);
+        CCommonOps.elementDivide(u,u.getReal(0,0),u.getImaginary(0,0),u);
+
+        return u;
+    }
+}
diff --git a/main/denseC64/test/org/ejml/alg/dense/decompose/CheckDecompositionInterface_CD64.java b/main/denseC64/test/org/ejml/alg/dense/decompose/CheckDecompositionInterface_CD64.java
new file mode 100644
index 0000000..5bbd2d9
--- /dev/null
+++ b/main/denseC64/test/org/ejml/alg/dense/decompose/CheckDecompositionInterface_CD64.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decompose;
+
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.interfaces.decomposition.DecompositionInterface;
+import org.ejml.ops.CMatrixFeatures;
+import org.ejml.ops.CRandomMatrices;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class CheckDecompositionInterface_CD64 {
+
+
+    /**
+     * Checks to see if the matrix is or is not modified as according to the modified
+     * flag.
+     *
+     * @param decomp
+     */
+    public static void checkModifiedInput( DecompositionInterface decomp ) {
+        CDenseMatrix64F A = CRandomMatrices.createHermPosDef(4, new Random(0x434));
+        CDenseMatrix64F A_orig = A.copy();
+
+        assertTrue(decomp.decompose(A));
+
+        boolean modified = !CMatrixFeatures.isEquals(A, A_orig);
+
+        assertTrue(modified+" "+decomp.inputModified(),decomp.inputModified()==modified);
+    }
+}
diff --git a/main/denseC64/test/org/ejml/alg/dense/decompose/TestCTriangularSolver.java b/main/denseC64/test/org/ejml/alg/dense/decompose/TestCTriangularSolver.java
new file mode 100644
index 0000000..464b9a6
--- /dev/null
+++ b/main/denseC64/test/org/ejml/alg/dense/decompose/TestCTriangularSolver.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decompose;
+
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.ops.CCommonOps;
+import org.ejml.ops.CMatrixFeatures;
+import org.ejml.ops.CRandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestCTriangularSolver {
+
+    Random rand = new Random(234);
+
+    @Test
+    public void solveU() {
+        CDenseMatrix64F U = CRandomMatrices.createRandom(3, 3, -1 ,1 ,rand);
+        for( int i = 0; i < U.numRows; i++ ) {
+            for( int j = 0; j < i; j++ ) {
+                U.set(i,j,0,0);
+            }
+        }
+
+        CDenseMatrix64F X = CRandomMatrices.createRandom(3, 1, -1 ,1 ,rand);
+        CDenseMatrix64F B = new CDenseMatrix64F(3,1);
+
+        CCommonOps.mult(U, X, B);
+
+        CTriangularSolver.solveU(U.data,B.data,3);
+
+        assertTrue(CMatrixFeatures.isIdentical(X, B, 1e-8));
+    }
+
+    @Test
+    public void solveL_diagReal() {
+        for( int N = 1; N <= 4; N++ ) {
+            CDenseMatrix64F L = createLowerTriangleDiagReal(N);
+
+            CDenseMatrix64F X = CRandomMatrices.createRandom(N, 1, -1, 1, rand);
+            CDenseMatrix64F B = new CDenseMatrix64F(N, 1);
+
+            CCommonOps.mult(L, X, B);
+
+            CTriangularSolver.solveL_diagReal(L.data, B.data, N);
+
+            assertTrue(CMatrixFeatures.isIdentical(X, B, 1e-8));
+        }
+    }
+
+    /**
+     * Creates a random complex lower triangular matrix with real diagonal elements
+     */
+    private CDenseMatrix64F createLowerTriangleDiagReal(int n) {
+        CDenseMatrix64F L = CRandomMatrices.createRandom(n, n, -1, 1, rand);
+        for (int i = 0; i < L.numRows; i++) {
+            for (int j = i + 1; j < L.numCols; j++) {
+                L.set(i, j, 0, 0);
+            }
+        }
+        for (int i = 0; i < L.numRows; i++) {
+            L.data[(i*L.numRows+i)*2+1] = 0;
+        }
+        return L;
+    }
+
+    @Test
+    public void solveConjTranL_diagReal() {
+        for( int N = 1; N <= 4; N++ ) {
+            CDenseMatrix64F L = createLowerTriangleDiagReal(N);
+
+            CDenseMatrix64F L_ct = new CDenseMatrix64F(N, N);
+            CCommonOps.transposeConjugate(L,L_ct);
+
+            CDenseMatrix64F X = CRandomMatrices.createRandom(N, 1, -1, 1, rand);
+            CDenseMatrix64F B = new CDenseMatrix64F(N, 1);
+
+            CCommonOps.mult(L_ct, X, B);
+
+            CTriangularSolver.solveConjTranL_diagReal(L.data, B.data, N);
+
+            assertTrue(CMatrixFeatures.isIdentical(X, B, 1e-8));
+        }
+    }
+}
\ No newline at end of file
diff --git a/main/denseC64/test/org/ejml/alg/dense/decompose/chol/GenericCholeskyTests_CD64.java b/main/denseC64/test/org/ejml/alg/dense/decompose/chol/GenericCholeskyTests_CD64.java
new file mode 100644
index 0000000..18e28f8
--- /dev/null
+++ b/main/denseC64/test/org/ejml/alg/dense/decompose/chol/GenericCholeskyTests_CD64.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decompose.chol;
+
+import org.ejml.alg.dense.decompose.CheckDecompositionInterface_CD64;
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.data.Complex64F;
+import org.ejml.factory.CDecompositionFactory;
+import org.ejml.interfaces.decomposition.CholeskyDecomposition;
+import org.ejml.interfaces.decomposition.LUDecomposition;
+import org.ejml.ops.CCommonOps;
+import org.ejml.ops.CMatrixFeatures;
+import org.ejml.ops.CRandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+
+/**
+* @author Peter Abeles
+*/
+// TODO Handle special case of 1x1 matrix
+public abstract class GenericCholeskyTests_CD64 {
+    Random rand = new Random(0x45478);
+
+    boolean canL = true;
+    boolean canR = true;
+
+    public abstract CholeskyDecomposition<CDenseMatrix64F> create( boolean lower );
+
+    @Test
+    public void checkModifyInput() {
+        CheckDecompositionInterface_CD64.checkModifiedInput(create(true));
+        CheckDecompositionInterface_CD64.checkModifiedInput(create(false));
+    }
+
+    /**
+     * If it is not positive definate it should fail
+     */
+    @Test
+    public void testNotPositiveDefinite() {
+        CDenseMatrix64F A = new CDenseMatrix64F(2, 2, true, 1, 0, -1, 0, -1, 0, -2, 0);
+
+        CholeskyDecomposition<CDenseMatrix64F> alg = create(true);
+        assertFalse(alg.decompose(A));
+    }
+
+    /**
+     * Test across several different matrix sizes and upper/lower decompositions using
+     * the definition of cholesky.
+     */
+    @Test
+    public void checkWithDefinition() {
+        for( int i = 0; i < 2; i++ ) {
+            boolean lower = i == 0;
+            if( lower && !canL )
+                continue;
+            if( !lower && !canR )
+                continue;
+
+            for( int size = 1; size < 10; size++ ) {
+                checkWithDefinition(lower, size);
+            }
+        }
+    }
+
+    private void checkWithDefinition(boolean lower, int size) {
+        CDenseMatrix64F A = CRandomMatrices.createHermPosDef(size, rand);
+
+        CholeskyDecomposition<CDenseMatrix64F> cholesky = create(lower);
+        assertTrue(CDecompositionFactory.decomposeSafe(cholesky, A));
+
+        CDenseMatrix64F T = cholesky.getT(null);
+        CDenseMatrix64F T_trans = new CDenseMatrix64F(size,size);
+        CCommonOps.transposeConjugate(T, T_trans);
+        CDenseMatrix64F found = new CDenseMatrix64F(size,size);
+
+        if( lower ) {
+            CCommonOps.mult(T,T_trans,found);
+        } else {
+            CCommonOps.mult(T_trans,T,found);
+        }
+
+        assertTrue(CMatrixFeatures.isIdentical(A, found, 1e-8));
+    }
+
+    @Test
+    public void checkDeterminant() {
+        for( int i = 0; i < 2; i++ ) {
+            boolean lower = i == 0;
+            if( lower && !canL )
+                continue;
+            if( !lower && !canR )
+                continue;
+
+            for( int size = 2; size < 20; size += 2 ) {
+                checkDeterminant(lower, size);
+            }
+        }
+    }
+
+    public void checkDeterminant( boolean lower , int size ) {
+
+        LUDecomposition<CDenseMatrix64F> lu = CDecompositionFactory.lu(size,size);
+        CholeskyDecomposition<CDenseMatrix64F> cholesky = create(lower);
+
+        CDenseMatrix64F A = CRandomMatrices.createHermPosDef(size, rand);
+
+        assertTrue(CDecompositionFactory.decomposeSafe(lu,A));
+        assertTrue(CDecompositionFactory.decomposeSafe(cholesky,A));
+
+        Complex64F expected = lu.computeDeterminant();
+        Complex64F found = cholesky.computeDeterminant();
+
+        assertEquals(expected.real,found.real,1e-8);
+        assertEquals(expected.imaginary,found.imaginary,1e-8);
+    }
+
+    @Test
+    public void failZeros() {
+        CDenseMatrix64F A = new CDenseMatrix64F(3,3);
+
+        assertFalse(create(true).decompose(A));
+        assertFalse(create(false).decompose(A));
+    }
+}
diff --git a/main/denseC64/test/org/ejml/alg/dense/decompose/chol/TestCholeskyDecompositionCommon_CD64.java b/main/denseC64/test/org/ejml/alg/dense/decompose/chol/TestCholeskyDecompositionCommon_CD64.java
new file mode 100644
index 0000000..3224986
--- /dev/null
+++ b/main/denseC64/test/org/ejml/alg/dense/decompose/chol/TestCholeskyDecompositionCommon_CD64.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decompose.chol;
+
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.interfaces.decomposition.CholeskyDecomposition;
+import org.ejml.ops.CMatrixFeatures;
+import org.ejml.ops.CRandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestCholeskyDecompositionCommon_CD64 {
+    Random rand = new Random(234);
+
+    int N = 6;
+
+    /**
+     * The correctness of getT(null) has been tested else where effectively.  This
+     * checks to see if it handles the case where an input is provided correctly.
+     */
+    @Test
+    public void getT() {
+        CDenseMatrix64F A = CRandomMatrices.createHermPosDef(N, rand);
+
+        CholeskyDecomposition<CDenseMatrix64F> cholesky = new Dummy(true);
+
+        CDenseMatrix64F L_null = cholesky.getT(null);
+        CDenseMatrix64F L_provided = CRandomMatrices.createRandom(N, N, rand);
+        assertTrue( L_provided == cholesky.getT(L_provided));
+
+        assertTrue(CMatrixFeatures.isEquals(L_null, L_provided));
+    }
+
+    private class Dummy extends CholeskyDecompositionCommon_CD64 {
+
+        public Dummy(boolean lower) {
+            super(lower);
+            T = CRandomMatrices.createRandom(N,N,rand);
+            n = N;
+        }
+
+        @Override
+        protected boolean decomposeLower() {
+            return true;
+        }
+
+        @Override
+        protected boolean decomposeUpper() {
+            return true;
+        }
+    }
+}
\ No newline at end of file
diff --git a/main/denseC64/test/org/ejml/alg/dense/decompose/chol/TestCholeskyDecompositionInner_CD64.java b/main/denseC64/test/org/ejml/alg/dense/decompose/chol/TestCholeskyDecompositionInner_CD64.java
new file mode 100644
index 0000000..8eb96c1
--- /dev/null
+++ b/main/denseC64/test/org/ejml/alg/dense/decompose/chol/TestCholeskyDecompositionInner_CD64.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decompose.chol;
+
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.interfaces.decomposition.CholeskyDecomposition;
+
+
+/**
+* @author Peter Abeles
+*/
+public class TestCholeskyDecompositionInner_CD64 extends GenericCholeskyTests_CD64 {
+
+    @Override
+    public CholeskyDecomposition<CDenseMatrix64F> create(boolean lower) {
+        return new CholeskyDecompositionInner_CD64(lower);
+    }
+}
diff --git a/main/denseC64/test/org/ejml/alg/dense/decompose/lu/GeneralLuDecompositionChecks_CD64.java b/main/denseC64/test/org/ejml/alg/dense/decompose/lu/GeneralLuDecompositionChecks_CD64.java
new file mode 100644
index 0000000..fcf989c
--- /dev/null
+++ b/main/denseC64/test/org/ejml/alg/dense/decompose/lu/GeneralLuDecompositionChecks_CD64.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decompose.lu;
+
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.interfaces.decomposition.LUDecomposition;
+import org.ejml.ops.CCommonOps;
+import org.ejml.ops.CMatrixFeatures;
+import org.ejml.ops.CRandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * @author Peter Abeles
+ */
+public abstract class GeneralLuDecompositionChecks_CD64 {
+
+    Random rand = new Random(0xff);
+
+    public abstract LUDecomposition<CDenseMatrix64F> create( int numRows , int numCols );
+
+    @Test
+    public void testModifiedInput() {
+        CDenseMatrix64F A = CRandomMatrices.createRandom(4, 4, -1, 1, rand);
+        CDenseMatrix64F A_orig = A.copy();
+
+        LUDecomposition<CDenseMatrix64F> alg = create(4,4);
+        assertTrue(alg.decompose(A));
+
+        boolean modified = !CMatrixFeatures.isEquals(A,A_orig);
+
+        assertEquals(modified, alg.inputModified());
+    }
+
+    @Test
+    public void testAllReal()
+    {
+        CDenseMatrix64F A = new CDenseMatrix64F(3,3, true, 5,0, 2,0, 3,0, 1.5,0, -2,0, 8,0, -3,0, 4.7,0, -0.5,0);
+
+        LUDecomposition<CDenseMatrix64F> alg = create(3,3);
+        assertTrue(alg.decompose(A.copy()));
+
+        assertFalse(alg.isSingular());
+
+        CDenseMatrix64F L = alg.getLower(null);
+        CDenseMatrix64F U = alg.getUpper(null);
+        CDenseMatrix64F P = alg.getPivot(null);
+
+        CDenseMatrix64F P_tran = new CDenseMatrix64F(P.numCols,P.numRows);
+        CDenseMatrix64F PL = new CDenseMatrix64F(P.numRows,P.numCols);
+        CDenseMatrix64F A_found = new CDenseMatrix64F(A.numRows,A.numCols);
+
+        CCommonOps.transpose(P,P_tran);
+        CCommonOps.mult(P_tran, L, PL);
+        CCommonOps.mult(PL, U, A_found);
+
+        assertTrue(CMatrixFeatures.isIdentical(A_found,A,1e-8));
+    }
+
+    @Test
+    public void testDecomposition_square_real()
+    {
+        for( int i = 2; i <= 20; i++ ) {
+            CDenseMatrix64F A = CRandomMatrices.createRandom(i,i,-1,1,rand);
+
+            for (int j = 1; j < A.getDataLength(); j += 2) {
+                A.data[j] = 0;
+            }
+
+            checkDecomposition(A);
+        }
+    }
+
+    @Test
+    public void testDecomposition_square_imaginary()
+    {
+        for( int i = 2; i <= 20; i++ ) {
+            CDenseMatrix64F A = CRandomMatrices.createRandom(i,i,-1,1,rand);
+
+            for (int j = 0; j < A.getDataLength(); j += 2) {
+                A.data[j] = 0;
+            }
+
+            checkDecomposition(A);
+        }
+    }
+
+    @Test
+    public void testDecomposition_square()
+    {
+        for( int i = 2; i <= 20; i++ ) {
+            CDenseMatrix64F A = CRandomMatrices.createRandom(i,i,-1,1,rand);
+
+            checkDecomposition(A);
+        }
+    }
+
+    @Test
+    public void testFat() {
+        CDenseMatrix64F A = CRandomMatrices.createRandom(2,3,-1,1,rand);
+
+        checkDecomposition(A);
+    }
+
+    @Test
+    public void testTall() {
+        CDenseMatrix64F A = CRandomMatrices.createRandom(3,2,rand);
+
+        checkDecomposition(A);
+    }
+
+    @Test
+    public void zeroMatrix() {
+        CDenseMatrix64F A = new CDenseMatrix64F(3,3);
+
+        LUDecomposition<CDenseMatrix64F> alg = create(3,3);
+
+        assertTrue(alg.decompose(A));
+        assertTrue(alg.isSingular());
+
+        CDenseMatrix64F L = alg.getLower(null);
+        CDenseMatrix64F U = alg.getUpper(null);
+
+        CDenseMatrix64F A_found = new CDenseMatrix64F(3,3);
+        CCommonOps.mult(L, U, A_found);
+
+        assertFalse(CMatrixFeatures.hasUncountable(A_found));
+        assertTrue(CMatrixFeatures.isIdentical(A_found,A,1e-8));
+    }
+
+    @Test
+    public void testSingular(){
+        CDenseMatrix64F A = new CDenseMatrix64F(3,3, true, 1,1, 2,2, 3,3, 2,2, 4,4, 6,6, 4,4, 4,4, 0,0);
+
+        LUDecomposition<CDenseMatrix64F> alg = create(3,3);
+        assertTrue(alg.decompose(A));
+        assertTrue(alg.isSingular());
+    }
+
+    @Test
+    public void testNearlySingular(){
+        CDenseMatrix64F A = new CDenseMatrix64F(3,3, true, 1,1, 2,2, 3,3, 2,2, 4,4, 6.1,6.1, 4,4, 4,4, 0,0);
+
+        LUDecomposition<CDenseMatrix64F> alg = create(3,3);
+        assertTrue(alg.decompose(A));
+        assertFalse(alg.isSingular());
+    }
+
+    /**
+     * Checks to see how it handles getLower getUpper functions with and without
+     * a matrix being provided.
+     */
+    @Test
+    public void getLower_getUpper() {
+        CDenseMatrix64F A = CRandomMatrices.createRandom(3,3,rand);
+
+        LUDecomposition<CDenseMatrix64F> alg = create(3,3);
+
+        alg.decompose(A);
+
+        CDenseMatrix64F L_provided = CRandomMatrices.createRandom(3,3,rand);
+        CDenseMatrix64F U_provided = CRandomMatrices.createRandom(3,3,rand);
+
+        assertTrue(L_provided == alg.getLower(L_provided));
+        assertTrue(U_provided == alg.getUpper(U_provided));
+
+        CDenseMatrix64F L_ret = alg.getLower(null);
+        CDenseMatrix64F U_ret = alg.getUpper(null);
+
+        assertTrue(CMatrixFeatures.isEquals(L_provided,L_ret));
+        assertTrue(CMatrixFeatures.isEquals(U_provided,U_ret));
+    }
+
+    private void checkDecomposition(CDenseMatrix64F a) {
+        LUDecomposition<CDenseMatrix64F> alg = create(a.numRows, a.numCols);
+        assertTrue(alg.decompose(a.copy()));
+
+        if( a.numRows <= a.numCols)
+            assertFalse(alg.isSingular());
+
+        CDenseMatrix64F L = alg.getLower(null);
+        CDenseMatrix64F U = alg.getUpper(null);
+        CDenseMatrix64F P = alg.getPivot(null);
+
+        CDenseMatrix64F P_tran  = new CDenseMatrix64F(P.numCols,P.numRows);
+        CDenseMatrix64F PL      = new CDenseMatrix64F(P_tran.numRows,L.numCols);
+        CDenseMatrix64F A_found = new CDenseMatrix64F(a.numRows, a.numCols);
+
+        CCommonOps.transpose(P, P_tran);
+        CCommonOps.mult(P_tran, L, PL);
+        CCommonOps.mult(PL, U, A_found);
+
+        assertTrue(CMatrixFeatures.isIdentical(A_found, a, 1e-8));
+    }
+}
diff --git a/main/denseC64/test/org/ejml/alg/dense/decompose/lu/TestLUDecompositionAlt_CD64.java b/main/denseC64/test/org/ejml/alg/dense/decompose/lu/TestLUDecompositionAlt_CD64.java
new file mode 100644
index 0000000..bc76a82
--- /dev/null
+++ b/main/denseC64/test/org/ejml/alg/dense/decompose/lu/TestLUDecompositionAlt_CD64.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decompose.lu;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestLUDecompositionAlt_CD64 extends GeneralLuDecompositionChecks_CD64 {
+    @Override
+    public LUDecompositionBase_CD64 create(int numRows, int numCols) {
+        return new LUDecompositionAlt_CD64();
+    }
+}
\ No newline at end of file
diff --git a/main/denseC64/test/org/ejml/alg/dense/decompose/lu/TestLUDecompositionBase_CD64.java b/main/denseC64/test/org/ejml/alg/dense/decompose/lu/TestLUDecompositionBase_CD64.java
new file mode 100644
index 0000000..b8bc2f2
--- /dev/null
+++ b/main/denseC64/test/org/ejml/alg/dense/decompose/lu/TestLUDecompositionBase_CD64.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decompose.lu;
+
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.data.Complex64F;
+import org.ejml.ops.CCommonOps;
+import org.ejml.ops.CRandomMatrices;
+import org.ejml.ops.ComplexMath64F;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestLUDecompositionBase_CD64 {
+    Random rand = new Random(0x3344);
+
+    /**
+     * Compare the determinant computed from LU to the value computed from the minor
+     * matrix method.
+     */
+    @Test
+    public void testDeterminant()
+    {
+        Random rand = new Random(0xfff);
+
+        int width = 10;
+
+        CDenseMatrix64F LU = CRandomMatrices.createRandom(width,width,-1,1,rand);
+
+        Complex64F expected = new Complex64F(1,0);
+        Complex64F a = new Complex64F();
+        Complex64F tmp = new Complex64F();
+        for (int i = 0; i < width; i++) {
+            LU.get(i, i, a);
+            ComplexMath64F.multiply(expected,a,tmp);
+            expected.set(tmp);
+        }
+
+        DebugDecompose alg = new DebugDecompose(width);
+        alg.decomposeCommonInit(LU);
+        for( int i = 0; i < width; i++ ) alg.getIndx()[i] = i;
+        alg.setLU(LU);
+
+        Complex64F found = alg.computeDeterminant();
+
+        assertEquals(expected.real,found.real,1e-6);
+        assertEquals(expected.imaginary,found.imaginary,1e-6);
+    }
+
+    @Test
+    public void _solveVectorInternal() {
+        int width = 10;
+        CDenseMatrix64F LU = CRandomMatrices.createRandom(width, width,-1,1, rand);
+
+        CDenseMatrix64F L = new CDenseMatrix64F(width,width);
+        CDenseMatrix64F U = new CDenseMatrix64F(width,width);
+
+        for (int i = 0; i < width; i++) {
+            for (int j = 0; j < width; j++) {
+                double real = LU.getReal(i,j);
+                double imag = LU.getImaginary(i, j);
+
+                if( j <= i ) {
+                    if( j == i )
+                        L.set(i,j,1,0);
+                    else
+                        L.set(i,j,real,imag);
+                }
+                if( i <= j ) {
+                    U.set(i,j,real,imag);
+                }
+            }
+        }
+
+        CDenseMatrix64F x = CRandomMatrices.createRandom(width, 1,-1,1, rand);
+        CDenseMatrix64F tmp = new CDenseMatrix64F(width,1);
+        CDenseMatrix64F b = new CDenseMatrix64F(width,1);
+
+        CCommonOps.mult(U, x, tmp);
+        CCommonOps.mult(L, tmp, b);
+
+        DebugDecompose alg = new DebugDecompose(width);
+        alg.decomposeCommonInit(LU);
+        for( int i = 0; i < width; i++ ) alg.getIndx()[i] = i;
+        alg.setLU(LU);
+
+        alg._solveVectorInternal(b.data);
+
+        for( int i = 0; i < width; i++ ) {
+            assertEquals(x.data[i],b.data[i],1e-6);
+        }
+    }
+
+    @Test
+    public void solveL() {
+        int width = 10;
+        CDenseMatrix64F LU = CRandomMatrices.createRandom(width, width,-1,1, rand);
+
+        CDenseMatrix64F L = new CDenseMatrix64F(width,width);
+
+        for (int i = 0; i < width; i++) {
+            for (int j = 0; j < width; j++) {
+                double real = LU.getReal(i,j);
+                double imag = LU.getImaginary(i, j);
+
+                if( j <= i ) {
+                    if( j == i )
+                        L.set(i,j,1,0);
+                    else
+                        L.set(i,j,real,imag);
+                }
+            }
+        }
+
+        CDenseMatrix64F x = CRandomMatrices.createRandom(width, 1,-1,1, rand);
+        CDenseMatrix64F b = new CDenseMatrix64F(width,1);
+
+        CCommonOps.mult(L, x, b);
+
+        DebugDecompose alg = new DebugDecompose(width);
+        alg.decomposeCommonInit(LU);
+        for( int i = 0; i < width; i++ ) alg.getIndx()[i] = i;
+        alg.setLU(LU);
+
+        alg.solveL(b.data);
+
+        for( int i = 0; i < width; i++ ) {
+            assertEquals(x.data[i],b.data[i],1e-6);
+        }
+    }
+
+    private static class DebugDecompose extends LUDecompositionBase_CD64
+    {
+        public DebugDecompose(int width) {
+            setExpectedMaxSize(width, width);
+            m = n = width;
+        }
+
+        void setLU( CDenseMatrix64F LU ) {
+            this.LU = LU;
+            this.dataLU = LU.data;
+        }
+
+        @Override
+        public boolean decompose(CDenseMatrix64F orig) {
+            return false;
+        }
+    }
+}
\ No newline at end of file
diff --git a/main/denseC64/test/org/ejml/alg/dense/decompose/qr/GenericQrCheck_CD64.java b/main/denseC64/test/org/ejml/alg/dense/decompose/qr/GenericQrCheck_CD64.java
new file mode 100644
index 0000000..e3015d8
--- /dev/null
+++ b/main/denseC64/test/org/ejml/alg/dense/decompose/qr/GenericQrCheck_CD64.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decompose.qr;
+
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.interfaces.decomposition.QRDecomposition;
+import org.ejml.ops.CCommonOps;
+import org.ejml.ops.CMatrixFeatures;
+import org.ejml.ops.CRandomMatrices;
+import org.ejml.ops.EjmlUnitTests;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+
+/**
+* @author Peter Abeles
+*/
+public abstract class GenericQrCheck_CD64 {
+    Random rand = new Random(0xff);
+
+    abstract protected QRDecomposition<CDenseMatrix64F> createQRDecomposition();
+
+    @Test
+    public void testModifiedInput() {
+        QRDecomposition<CDenseMatrix64F> alg = createQRDecomposition();
+
+        CDenseMatrix64F A = CRandomMatrices.createRandom(6, 4, rand);
+        CDenseMatrix64F A_orig = A.copy();
+
+        assertTrue(alg.decompose(A));
+
+        boolean modified = !CMatrixFeatures.isEquals(A,A_orig);
+
+        assertTrue(modified + " " + alg.inputModified(), alg.inputModified() == modified);
+    }
+
+    /**
+     * See if it correctly decomposes a square, tall, or wide matrix.
+     */
+    @Test
+    public void decompositionShape() {
+        checkDecomposition(5, 5 ,false);
+        checkDecomposition(10, 5,false);
+        checkDecomposition(5, 10,false);
+        checkDecomposition(5, 5 ,true);
+        checkDecomposition(10, 5,true);
+        checkDecomposition(5, 10,true);
+    }
+
+    private void checkDecomposition(int height, int width, boolean compact ) {
+        QRDecomposition<CDenseMatrix64F> alg = createQRDecomposition();
+
+        CDenseMatrix64F A = CRandomMatrices.createRandom(height,width,rand);
+
+        assertTrue(alg.decompose(A.copy()));
+
+        int minStride = Math.min(height,width);
+
+        CDenseMatrix64F Q = new CDenseMatrix64F(height,compact ? minStride : height);
+        alg.getQ(Q, compact);
+        CDenseMatrix64F R = new CDenseMatrix64F(compact ? minStride : height,width);
+        alg.getR(R, compact);
+
+        // see if Q has the expected properties
+        assertTrue(CMatrixFeatures.isUnitary(Q, 1e-6));
+
+        // see if it has the expected properties
+        CDenseMatrix64F A_found = new CDenseMatrix64F(Q.numRows,R.numCols);
+        CCommonOps.mult(Q,R,A_found);
+
+        EjmlUnitTests.assertEquals(A,A_found,1e-6);
+        CDenseMatrix64F R_found = new CDenseMatrix64F(R.numRows,R.numCols);
+        CCommonOps.transposeConjugate(Q);
+        CCommonOps.mult(Q, A, R_found);
+    }
+
+    /**
+     * Test a pathological case for computing tau
+     */
+    @Test
+    public void checkZeroInFirstElement() {
+        int width = 4,height = 5;
+
+        QRDecomposition<CDenseMatrix64F> alg = createQRDecomposition();
+
+        CDenseMatrix64F A = CRandomMatrices.createRandom(height,width,rand);
+
+        // cause the pathological situation
+        A.set(0,0,0,0);
+
+        assertTrue(alg.decompose(A.copy()));
+
+        CDenseMatrix64F Q = new CDenseMatrix64F(height,height);
+        alg.getQ(Q, false);
+        CDenseMatrix64F R = new CDenseMatrix64F(height,width);
+        alg.getR(R, false);
+
+        // see if Q has the expected properties
+        assertTrue(CMatrixFeatures.isUnitary(Q, 1e-6));
+
+        // see if it has the expected properties
+        CDenseMatrix64F A_found = new CDenseMatrix64F(Q.numRows,R.numCols);
+        CCommonOps.mult(Q,R,A_found);
+
+        EjmlUnitTests.assertEquals(A,A_found,1e-6);
+        CDenseMatrix64F R_found = new CDenseMatrix64F(R.numRows,R.numCols);
+        CCommonOps.transposeConjugate(Q);
+        CCommonOps.mult(Q, A, R_found);
+    }
+
+    /**
+     * See if passing in a matrix or not providing one to getQ and getR functions
+     * has the same result
+     */
+    @Test
+    public void checkGetNullVersusNot() {
+        int width = 5;
+        int height = 10;
+
+        QRDecomposition<CDenseMatrix64F> alg = createQRDecomposition();
+
+        CDenseMatrix64F A = CRandomMatrices.createRandom(height,width,rand);
+
+        alg.decompose(A);
+
+        // get the results from a provided matrix
+        CDenseMatrix64F Q_provided = CRandomMatrices.createRandom(height,height,rand);
+        CDenseMatrix64F R_provided = CRandomMatrices.createRandom(height,width,rand);
+
+        assertTrue(R_provided == alg.getR(R_provided, false));
+        assertTrue(Q_provided == alg.getQ(Q_provided, false));
+
+        // get the results when no matrix is provided
+        CDenseMatrix64F Q_null = alg.getQ(null, false);
+        CDenseMatrix64F R_null = alg.getR(null,false);
+
+        // see if they are the same
+        assertTrue(CMatrixFeatures.isEquals(Q_provided,Q_null));
+        assertTrue(CMatrixFeatures.isEquals(R_provided,R_null));
+    }
+
+    /**
+     * Depending on if setZero being true or not the size of the R matrix changes
+     */
+    @Test
+    public void checkGetRInputSize()
+    {
+        int width = 5;
+        int height = 10;
+
+        QRDecomposition<CDenseMatrix64F> alg = createQRDecomposition();
+
+        CDenseMatrix64F A = CRandomMatrices.createRandom(height,width,rand);
+
+        alg.decompose(A);
+
+        // check the case where it creates the matrix first
+        assertTrue(alg.getR(null,true).numRows == width);
+        assertTrue(alg.getR(null,false).numRows == height);
+
+        // check the case where a matrix is provided
+        alg.getR(new CDenseMatrix64F(width,width),true);
+        alg.getR(new CDenseMatrix64F(height,width),false);
+
+        // check some negative cases
+        try {
+            alg.getR(new CDenseMatrix64F(height,width),true);
+            fail("Should have thrown an exception");
+        } catch( IllegalArgumentException e ) {}
+
+        try {
+            alg.getR(new CDenseMatrix64F(width-1,width),false);
+            fail("Should have thrown an exception");
+        } catch( IllegalArgumentException e ) {}
+    }
+
+    /**
+     * See if the compact format for Q works
+     */
+    @Test
+    public void checkCompactFormat()
+    {
+        int height = 10;
+        int width = 5;
+
+        QRDecomposition<CDenseMatrix64F> alg = createQRDecomposition();
+
+        CDenseMatrix64F A = CRandomMatrices.createRandom(height,width,rand);
+
+        alg.decompose(A);
+
+        CDenseMatrix64F Q = new CDenseMatrix64F(height,width);
+        alg.getQ(Q, true);
+
+        // see if Q has the expected properties
+        assertEquals(height,Q.numRows);
+        assertEquals(width,Q.numCols);
+        assertTrue(CMatrixFeatures.isUnitary(Q,1e-6));
+
+        // try to extract it with the wrong dimensions
+        Q = new CDenseMatrix64F(height,height);
+        try {
+            alg.getQ(Q, true);
+            fail("Didn't fail");
+        } catch( RuntimeException e ) {}
+    }
+
+}
diff --git a/main/denseC64/test/org/ejml/alg/dense/decompose/qr/TestQRDecompositionHouseholderColumn_CD64.java b/main/denseC64/test/org/ejml/alg/dense/decompose/qr/TestQRDecompositionHouseholderColumn_CD64.java
new file mode 100644
index 0000000..d8dfd52
--- /dev/null
+++ b/main/denseC64/test/org/ejml/alg/dense/decompose/qr/TestQRDecompositionHouseholderColumn_CD64.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decompose.qr;
+
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.data.Complex64F;
+import org.ejml.interfaces.decomposition.QRDecomposition;
+import org.ejml.ops.CCommonOps;
+import org.ejml.ops.CMatrixFeatures;
+import org.ejml.ops.CRandomMatrices;
+import org.ejml.ops.CSpecializedOps;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestQRDecompositionHouseholderColumn_CD64 extends GenericQrCheck_CD64 {
+
+    Random rand = new Random(0xff);
+
+
+    @Override
+    protected QRDecomposition<CDenseMatrix64F> createQRDecomposition() {
+        return new QRDecompositionHouseholderColumn_CD64();
+    }
+
+    /**
+     * Internal several householder operations are performed.  This
+     * checks to see if the householder operations and the expected result for all the
+     * submatrices.
+     */
+    @Test
+    public void householder() {
+        int width = 5;
+
+        for( int i = 0; i < width; i++ ) {
+            checkSubHouse(i , width);
+        }
+    }
+
+    private void checkSubHouse(int w , int width) {
+        DebugQR qr = new DebugQR(width,width);
+
+        CDenseMatrix64F A = CRandomMatrices.createRandom(width,width,rand);
+
+        qr.householder(w,A);
+
+        CDenseMatrix64F U = new CDenseMatrix64F(width-w,1);
+        System.arraycopy(qr.dataQR[w],w*2,U.data,0,(width-w)*2);
+
+        // it already wrote over the first element with tau. Make it 1 + 0i again
+        U.set(0,0,1,0);
+
+        // Q = I - gamma*u*u'
+        CDenseMatrix64F Q = CSpecializedOps.householder(U,qr.getGamma());
+
+        // check the expected properties of Q
+        assertTrue(CMatrixFeatures.isHermitian(Q, 1e-6));
+        assertTrue(CMatrixFeatures.isUnitary(Q, 1e-6));
+
+        CDenseMatrix64F result = new CDenseMatrix64F(Q.numRows,Q.numCols);
+        CDenseMatrix64F Asub = CCommonOps.extract(A, w, width, w, width);
+        CCommonOps.mult(Q, Asub, result);
+
+        Complex64F a = new Complex64F();
+        result.get(0,0,a);
+        assertEquals(-qr.tau.real, a.real, 1e-8);
+        assertEquals(-qr.tau.imaginary,a.imaginary,1e-8);
+
+        for( int i = 1; i < result.numRows; i++ ) {
+            result.get(i,0,a);
+            assertEquals(0, a.getMagnitude2(),1e-5);
+        }
+    }
+
+    /**
+     * Check the results of this function against basic matrix operations
+     * which are equivalent.
+     */
+    @Test
+    public void updateA() {
+        int width = 5;
+
+        for( int i = 0; i < width; i++ )
+            checkSubMatrix(width,i);
+    }
+
+    private void checkSubMatrix(int width , int w ) {
+        DebugQR qr = new DebugQR(width,width);
+
+        double gamma = 0.2;
+
+        CDenseMatrix64F A = CRandomMatrices.createRandom(width,width,rand);
+
+        qr.convertToColumnMajor(A);
+
+        // compute the results using standard matrix operations
+        CDenseMatrix64F u_sub = CCommonOps.extract(A, w, width, w, w+1);
+        CDenseMatrix64F A_sub = CCommonOps.extract(A, w, width, w, width);
+        CDenseMatrix64F expected = new CDenseMatrix64F(u_sub.numRows,u_sub.numRows);
+
+        // Q = I - gamma*u*u'
+        u_sub.set(0,0,1,0);
+        CDenseMatrix64F Q = CSpecializedOps.householder(u_sub,gamma);
+
+        CCommonOps.mult(Q,A_sub,expected);
+
+        qr.updateA(w,gamma);
+
+        double[][] found = qr.getQR();
+
+        Complex64F a = new Complex64F();
+        Complex64F b = new Complex64F();
+
+        for( int i = w; i < width; i++ ) {
+            A.get(i,w,a);
+            b.set(found[w][i*2],found[w][i*2+1]);
+
+            assertEquals(a.real, b.real, 1e-8);
+            assertEquals(a.imaginary,b.imaginary,1e-8);
+        }
+
+        // the right should be the same
+        for( int i = w; i < width; i++ ) {
+            for( int j = w+1; j < width; j++ ) {
+                expected.get(i-w,j-w,a);
+                b.set(found[j][i*2],found[j][i*2+1]);
+//                found.get(i,j,b);
+
+                assertEquals(a.real, b.real, 1e-6);
+                assertEquals(a.imaginary,b.imaginary,1e-6);
+            }
+        }
+    }
+
+    private static class DebugQR extends QRDecompositionHouseholderColumn_CD64
+    {
+
+        public DebugQR( int numRows , int numCols ) {
+            setExpectedMaxSize(numRows,numCols);
+            this.numCols = numCols;
+            this.numRows = numRows;
+        }
+
+        public void householder( int j , CDenseMatrix64F A ) {
+            convertToColumnMajor(A);
+
+            super.householder(j);
+        }
+
+        protected void convertToColumnMajor(CDenseMatrix64F A) {
+            super.convertToColumnMajor(A);
+        }
+
+        public void updateA( int w , double gamma ) {
+            this.gamma = gamma;
+
+            super.updateA(w);
+        }
+
+        public double getGamma() {
+            return gamma;
+        }
+    }
+}
\ No newline at end of file
diff --git a/main/denseC64/test/org/ejml/alg/dense/decompose/qr/TestQRDecompositionHouseholderTran_CD64.java b/main/denseC64/test/org/ejml/alg/dense/decompose/qr/TestQRDecompositionHouseholderTran_CD64.java
new file mode 100644
index 0000000..cc93727
--- /dev/null
+++ b/main/denseC64/test/org/ejml/alg/dense/decompose/qr/TestQRDecompositionHouseholderTran_CD64.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decompose.qr;
+
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.data.Complex64F;
+import org.ejml.interfaces.decomposition.QRDecomposition;
+import org.ejml.ops.CCommonOps;
+import org.ejml.ops.CMatrixFeatures;
+import org.ejml.ops.CRandomMatrices;
+import org.ejml.ops.CSpecializedOps;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestQRDecompositionHouseholderTran_CD64 extends GenericQrCheck_CD64 {
+
+    Random rand = new Random(0xff);
+
+    @Override
+    protected QRDecomposition<CDenseMatrix64F> createQRDecomposition() {
+        return new QRDecompositionHouseholderTran_CD64();
+    }
+
+    /**
+     * Sees if computing Q explicitly and applying Q produces the same results
+     */
+    @Test
+    public void applyQ() {
+        CDenseMatrix64F A = CRandomMatrices.createRandom(5, 4, rand);
+
+        QRDecompositionHouseholderTran_CD64 alg = new QRDecompositionHouseholderTran_CD64();
+
+        assertTrue(alg.decompose(A));
+
+        CDenseMatrix64F Q = alg.getQ(null,false);
+        CDenseMatrix64F B = CRandomMatrices.createRandom(5,2,rand);
+
+        CDenseMatrix64F expected = new CDenseMatrix64F(B.numRows,B.numCols);
+        CCommonOps.mult(Q,B,expected);
+
+        alg.applyQ(B);
+
+        assertTrue(CMatrixFeatures.isIdentical(expected,B,1e-8));
+    }
+
+    /**
+     * Sees if computing Q^H explicitly and applying Q^H produces the same results
+     */
+    @Test
+    public void applyTranQ() {
+        CDenseMatrix64F A = CRandomMatrices.createRandom(5,4,rand);
+
+        QRDecompositionHouseholderTran_CD64 alg = new QRDecompositionHouseholderTran_CD64();
+
+        assertTrue(alg.decompose(A));
+
+        CDenseMatrix64F Q = alg.getQ(null,false);
+        CDenseMatrix64F B = CRandomMatrices.createRandom(5,2,rand);
+
+        CDenseMatrix64F expected = new CDenseMatrix64F(B.numRows,B.numCols);
+        CCommonOps.transposeConjugate(Q);
+        CCommonOps.mult(Q, B, expected);
+
+        alg.applyTranQ(B);
+
+        assertTrue(CMatrixFeatures.isIdentical(expected,B,1e-8));
+    }
+
+    /**
+     * A focused check to see if the internal house holder operations are performed correctly.
+     */
+    @Test
+    public void householder() {
+        int width = 5;
+
+        for( int i = 0; i < width; i++ ) {
+            checkSubHouse(i , width);
+        }
+    }
+
+    private void checkSubHouse(int w , int width) {
+        DebugQR qr = new DebugQR(width,width);
+
+        CDenseMatrix64F A = CRandomMatrices.createRandom(width,width,rand);
+
+        qr.householder(w,A);
+        CDenseMatrix64F U = qr.getU(w);
+
+        // Q = I - gamma*u*u'
+        CDenseMatrix64F Q = CSpecializedOps.householder(U,qr.getGamma());
+
+        // check the expected properties of Q
+        assertTrue(CMatrixFeatures.isHermitian(Q, 1e-6));
+        assertTrue(CMatrixFeatures.isUnitary(Q, 1e-6));
+
+        CDenseMatrix64F result = new CDenseMatrix64F(Q.numRows,Q.numCols);
+        CDenseMatrix64F Asub = CCommonOps.extract(A, w, width, w, width);
+        CCommonOps.mult(Q, Asub, result);
+
+        Complex64F a = new Complex64F();
+        result.get(0,0,a);
+        assertEquals(-qr.tau.real, a.real, 1e-8);
+        assertEquals(-qr.tau.imaginary,a.imaginary,1e-8);
+
+        for( int i = 1; i < result.numRows; i++ ) {
+            result.get(i,0,a);
+            assertEquals(0, a.getMagnitude2(),1e-5);
+        }
+    }
+
+    /**
+     * Check the results of this function against basic matrix operations
+     * which are equivalent.
+     */
+    @Test
+    public void updateA() {
+        int width = 5;
+
+        for( int i = 0; i < width; i++ )
+            checkSubMatrix(width,i);
+    }
+
+    private void checkSubMatrix(int width , int w ) {
+        DebugQR qr = new DebugQR(width,width);
+
+        double gamma = 0.2;
+
+        CDenseMatrix64F A = CRandomMatrices.createRandom(width,width,rand);
+        CCommonOps.transpose(A, qr.QR);
+
+        // compute the results using standard matrix operations
+        CDenseMatrix64F u_sub = CCommonOps.extract(A, w, width, w, w+1);
+        CDenseMatrix64F A_sub = CCommonOps.extract(A, w, width, w, width);
+        CDenseMatrix64F expected = new CDenseMatrix64F(u_sub.numRows,u_sub.numRows);
+
+        // Q = I - gamma*u*u'
+        u_sub.set(0,0,1,0);
+        CDenseMatrix64F Q = CSpecializedOps.householder(u_sub,gamma);
+
+        CCommonOps.mult(Q,A_sub,expected);
+
+        qr.updateA(w,gamma);
+
+        CDenseMatrix64F found = qr.getQR();
+
+        Complex64F a = new Complex64F();
+        Complex64F b = new Complex64F();
+
+        for( int i = w; i < width; i++ ) {
+            A.get(i,w,a);
+            found.get(w,i,b);
+
+            assertEquals(a.real, b.real, 1e-8);
+            assertEquals(a.imaginary,b.imaginary,1e-8);
+        }
+
+        // the right should be the same
+        for( int i = w; i < width; i++ ) {
+            for( int j = w+1; j < width; j++ ) {
+                expected.get(i-w,j-w,a);
+                found.get(j,i,b);
+
+                assertEquals(a.real, b.real, 1e-6);
+                assertEquals(a.imaginary,b.imaginary,1e-6);
+            }
+        }
+    }
+
+    private static class DebugQR extends QRDecompositionHouseholderTran_CD64
+    {
+
+        public DebugQR(int numRows, int numCols) {
+            setExpectedMaxSize(numRows,numCols);
+            this.numRows = numRows;
+            this.numCols = numCols;
+        }
+
+        public void householder( int j , CDenseMatrix64F A ) {
+            CCommonOps.transpose(A, QR);
+
+            super.householder(j);
+        }
+
+        public void updateA( int w , double gamma ) {
+            this.gamma = gamma;
+
+            super.updateA(w);
+        }
+
+        public CDenseMatrix64F getU( int w ) {
+            CDenseMatrix64F U = new CDenseMatrix64F(numRows-w,1);
+
+            System.arraycopy(QR.data,(w*numRows+w)*2,U.data,0,(numRows-w)*2);
+            U.set(0,0,1,0);
+            return U;
+        }
+
+        public double getGamma() {
+            return gamma;
+        }
+    }
+}
diff --git a/main/denseC64/test/org/ejml/alg/dense/decompose/qr/TestQRDecompositionHouseholder_CD64.java b/main/denseC64/test/org/ejml/alg/dense/decompose/qr/TestQRDecompositionHouseholder_CD64.java
new file mode 100644
index 0000000..21deef7
--- /dev/null
+++ b/main/denseC64/test/org/ejml/alg/dense/decompose/qr/TestQRDecompositionHouseholder_CD64.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decompose.qr;
+
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.data.Complex64F;
+import org.ejml.interfaces.decomposition.QRDecomposition;
+import org.ejml.ops.CCommonOps;
+import org.ejml.ops.CMatrixFeatures;
+import org.ejml.ops.CRandomMatrices;
+import org.ejml.ops.CSpecializedOps;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestQRDecompositionHouseholder_CD64 extends GenericQrCheck_CD64 {
+
+    Random rand = new Random(0xff);
+
+
+    @Override
+    protected QRDecomposition<CDenseMatrix64F> createQRDecomposition() {
+        return new QRDecompositionHouseholder_CD64();
+    }
+
+    /**
+     * Internally several house holder operations are performed.  This
+     * checks to see if the householder operations and the expected result for all the
+     * submatrices.
+     */
+    @Test
+    public void householder() {
+        int width = 6;
+
+        for( int i = 0; i < width; i++ ) {
+            checkSubHouse(i, width);
+        }
+    }
+
+    private void checkSubHouse(int w , int width) {
+        DebugQR qr = new DebugQR(width,width);
+
+        CDenseMatrix64F A = CRandomMatrices.createRandom(width,width,rand);
+
+        qr.householder(w,A);
+
+        CDenseMatrix64F U = new CDenseMatrix64F(width-w,1);
+        System.arraycopy(qr.getU(),w*2,U.data,0,(width-w)*2);
+
+        // Q = I - gamma*u*u'
+        CDenseMatrix64F Q = CSpecializedOps.householder(U,qr.getGamma());
+
+        // check the expected properties of Q
+        assertTrue(CMatrixFeatures.isHermitian(Q, 1e-6));
+        assertTrue(CMatrixFeatures.isUnitary(Q, 1e-6));
+
+        CDenseMatrix64F result = new CDenseMatrix64F(Q.numRows,Q.numCols);
+        CDenseMatrix64F Asub = CCommonOps.extract(A,w,width,w,width);
+        CCommonOps.mult(Q, Asub, result);
+
+        Complex64F a = new Complex64F();
+        result.get(0,0,a);
+        assertEquals(-qr.realTau, a.real, 1e-8);
+        assertEquals(-qr.imagTau,a.imaginary,1e-8);
+
+        for( int i = 1; i < result.numRows; i++ ) {
+            result.get(i,0,a);
+            assertEquals(0, a.getMagnitude2(),1e-5);
+        }
+    }
+
+    /**
+     * Check the results of this function against basic matrix operations
+     * which are equivalent.
+     */
+    @Test
+    public void updateA() {
+        int width = 5;
+
+        for( int i = 0; i < width; i++ )
+            checkSubMatrix(width,i);
+    }
+
+    private void checkSubMatrix(int width , int w ) {
+        DebugQR qr = new DebugQR(width,width);
+
+        double gamma = 0.2;
+        double realTau = 0.75;
+        double imagTau = -0.6;
+
+        CDenseMatrix64F U = CRandomMatrices.createRandom(width, 1,rand);
+        CDenseMatrix64F A = CRandomMatrices.createRandom(width,width,rand);
+
+        qr.getQR().set(A);
+
+        // compute the results using standard matrix operations
+        CDenseMatrix64F u_sub = CCommonOps.extract(U, w, width, 0, 1);
+        CDenseMatrix64F A_sub = CCommonOps.extract(A, w, width, w, width);
+        CDenseMatrix64F expected = new CDenseMatrix64F(u_sub.numRows,u_sub.numRows);
+
+        // Q = I - gamma*u*u'
+        CDenseMatrix64F Q = CSpecializedOps.householder(u_sub,gamma);
+
+        CCommonOps.mult(Q,A_sub,expected);
+
+        qr.updateA(w,U.getData(),gamma,realTau,imagTau);
+
+        CDenseMatrix64F found = qr.getQR();
+
+        Complex64F a = new Complex64F();
+        Complex64F b = new Complex64F();
+        found.get(w,w,a);
+
+        assertEquals(-realTau,a.real,1e-8);
+        assertEquals(-imagTau,a.imaginary,1e-8);
+
+        for( int i = w+1; i < width; i++ ) {
+            U.get(i,0,a);
+            found.get(i,w,b);
+
+            assertEquals(a.real, b.real, 1e-8);
+            assertEquals(a.imaginary,b.imaginary,1e-8);
+        }
+
+        // the right should be the same
+        for( int i = w; i < width; i++ ) {
+            for( int j = w+1; j < width; j++ ) {
+                expected.get(i-w,j-w,a);
+                found.get(i,j,b);
+
+                assertEquals(a.real, b.real, 1e-6);
+                assertEquals(a.imaginary,b.imaginary,1e-6);
+            }
+        }
+    }
+
+    private static class DebugQR extends QRDecompositionHouseholder_CD64
+    {
+
+        public DebugQR(int numRows, int numCols) {
+            setExpectedMaxSize(numRows,numCols);
+            this.numRows = numRows;
+            this.numCols = numCols;
+        }
+
+        public void householder( int j , CDenseMatrix64F A ) {
+            this.QR.set(A);
+
+            super.householder(j);
+        }
+
+        public void updateA( int w , double u[] ,
+                             double realGamma, double realTau, double imagTau ) {
+            System.arraycopy(u,0,this.u,0,this.u.length);
+            this.realGamma = realGamma;
+            this.realTau = realTau;
+            this.imagTau = imagTau;
+
+
+            super.updateA(w);
+        }
+
+        public double[] getU() {
+            return u;
+        }
+
+        public double getGamma() {
+            return realGamma;
+        }
+    }
+}
diff --git a/main/denseC64/test/org/ejml/alg/dense/decompose/qr/TestQrHelperFunctions_CD64.java b/main/denseC64/test/org/ejml/alg/dense/decompose/qr/TestQrHelperFunctions_CD64.java
new file mode 100644
index 0000000..c8d2787
--- /dev/null
+++ b/main/denseC64/test/org/ejml/alg/dense/decompose/qr/TestQrHelperFunctions_CD64.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decompose.qr;
+
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.data.Complex64F;
+import org.ejml.ops.CCommonOps;
+import org.ejml.ops.CMatrixFeatures;
+import org.ejml.ops.CRandomMatrices;
+import org.ejml.ops.ComplexMath64F;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestQrHelperFunctions_CD64 {
+
+    Random rand = new Random(234);
+
+    @Test
+    public void findMax() {
+        double u[] = new double[50];
+
+        for (int i = 0; i < u.length; i++) {
+            u[i] = (rand.nextDouble()-0.5)*20;
+        }
+
+        int offset = 4;
+        int length = 5;
+
+        double max = 0;
+        for (int i = 0; i < length; i++) {
+            double real = u[i*2+offset*2];
+            double img = u[i*2+offset*2+1];
+
+            if( real*real + img*img > max ) {
+                max = real*real + img*img;
+            }
+        }
+
+        max = Math.sqrt(max);
+
+        assertEquals(max,QrHelperFunctions_CD64.findMax(u,offset,length),1e-8);
+    }
+
+    @Test
+    public void divideElements_startU() {
+        double u[] = new double[12*2];
+        for (int i = 0; i < u.length; i++ ) {
+            u[i] = (rand.nextDouble()*0.5-1.0)*2;
+        }
+        double found[] = u.clone();
+
+        Complex64F A = new Complex64F(rand.nextDouble(),rand.nextDouble());
+        Complex64F U = new Complex64F();
+        Complex64F expected = new Complex64F();
+
+        int j = 3;
+        int numRows = 8;
+        int startU = 2;
+
+        QrHelperFunctions_CD64.divideElements(j,numRows,found,startU,A.real,A.imaginary);
+
+        for (int i = 0; i < 12; i++) {
+            int index = i * 2;
+
+            if( i >= j+startU && i < numRows+startU ) {
+                U.real = u[index];
+                U.imaginary = u[index + 1];
+                ComplexMath64F.divide(U, A, expected);
+
+                assertEquals(expected.real, found[index], 1e-8);
+                assertEquals(expected.imaginary, found[index + 1], 1e-8);
+            } else {
+                assertEquals(u[index],found[index],1e-8);
+                assertEquals(u[index+1],found[index+1],1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void computeTauGammaAndDivide() {
+        double u[] = new double[12*2];
+        for (int i = 0; i < u.length; i++ ) {
+            u[i] = (rand.nextDouble()*0.5-1.0)*2;
+        }
+
+        double max = 2.0;
+        int j = 2;
+        int numRows = 6;
+
+        Complex64F expectedTau = new Complex64F();
+        double expectedGamma = 0;
+        double[] expectedU = u.clone();
+
+        for (int i = j; i < numRows; i++) {
+            Complex64F U = new Complex64F(u[i*2],u[i*2+1]);
+            Complex64F div = new Complex64F();
+            ComplexMath64F.divide(U,new Complex64F(max,0),div);
+
+            expectedU[i*2] = div.real;
+            expectedU[i*2+1] = div.imaginary;
+        }
+        double normX = 0;
+        for (int i = j; i < numRows; i++) {
+            normX += expectedU[i*2]*expectedU[i*2] + expectedU[i*2+1]*expectedU[i*2+1];
+        }
+        normX = Math.sqrt(normX);
+        double realX0 = expectedU[j*2];
+        double imagX0 = expectedU[j*2+1];
+
+        double magX0 = Math.sqrt(realX0*realX0 + imagX0*imagX0);
+        expectedTau.real      = realX0*normX/magX0;
+        expectedTau.imaginary = imagX0*normX/magX0;
+
+        double realU0 = realX0 + expectedTau.real;
+        double imagU0 = imagX0 + expectedTau.imaginary;
+
+        //
+        double normU = 1;
+        Complex64F B = new Complex64F(realU0,imagU0);
+        for (int i = j+1; i < numRows; i++) {
+            Complex64F A = new Complex64F( expectedU[i*2], expectedU[i*2+1]);
+            Complex64F result = new Complex64F();
+            ComplexMath64F.divide(A,B,result);
+            normU += result.getMagnitude2();
+        }
+        expectedGamma = 2.0/normU;
+
+        Complex64F foundTau = new Complex64F();
+        double[] foundU = u.clone();
+        double foundGamma = QrHelperFunctions_CD64.computeTauGammaAndDivide(j,numRows,foundU,max,foundTau);
+
+        for (int i = 0; i < expectedU.length; i++) {
+            assertEquals(expectedU[i],foundU[i],1e-8);
+        }
+
+        assertEquals(expectedTau.real,foundTau.real,1e-8);
+        assertEquals(expectedTau.imaginary,foundTau.imaginary,1e-8);
+
+        assertEquals(expectedGamma,foundGamma,1e-8);
+    }
+
+    @Test
+    public void rank1UpdateMultR() {
+        double u[] = new double[12*2];
+        double uoff[] = new double[12*2+2];
+        double subU[] = new double[12*2];
+        double _temp[] = new double[u.length];
+        double gamma = 0.6;
+
+        for (int i = 0; i < u.length; i++) {
+            u[i] = uoff[i+2] = (rand.nextDouble()*0.5-1.0)*2;
+        }
+
+        for (int i = 1; i < 12; i++) {
+            CDenseMatrix64F A = CRandomMatrices.createRandom(i,i,rand);
+
+            for (int j = 1; j <= i; j += 2) {
+                CDenseMatrix64F subA = CCommonOps.extract(A,A.numRows-j,A.numRows,A.numRows-j,A.numRows);
+                System.arraycopy(u,(A.numRows-j)*2,subU,0,j*2);
+                CDenseMatrix64F expected = rank1UpdateMultR(subA, gamma,subU);
+
+                CDenseMatrix64F found = A.copy();
+                QrHelperFunctions_CD64.rank1UpdateMultR(found, uoff, 1, gamma,
+                        A.numRows - j, A.numRows - j, A.numRows, _temp);
+
+                CDenseMatrix64F subFound = CCommonOps.extract(found,A.numRows-j,A.numRows,A.numRows-j,A.numRows);
+
+                outsideIdentical(A, found, j);
+                assertTrue(CMatrixFeatures.isEquals(expected, subFound, 1e-8));
+            }
+        }
+    }
+
+    private CDenseMatrix64F rank1UpdateMultR( CDenseMatrix64F A , double gamma,  double u[] ) {
+        CDenseMatrix64F U = new CDenseMatrix64F(A.numCols,1);
+        U.data = u;
+        CDenseMatrix64F Ut = new CDenseMatrix64F(1,A.numCols);
+        CCommonOps.transposeConjugate(U,Ut);
+
+        CDenseMatrix64F UUt = new CDenseMatrix64F(A.numCols,A.numCols);
+        CCommonOps.mult(gamma,0,U,Ut,UUt);
+
+        CDenseMatrix64F I = CCommonOps.identity(A.numCols);
+        CDenseMatrix64F inner = new CDenseMatrix64F(A.numCols,A.numCols);
+        CDenseMatrix64F expected = new CDenseMatrix64F(A.numCols,A.numCols);
+
+        CCommonOps.subtract(I,UUt,inner);
+        CCommonOps.mult(inner,A,expected);
+
+        return expected;
+    }
+
+    @Test
+    public void rank1UpdateMultL() {
+        double u[] = new double[12*2];
+        double subU[] = new double[12*2];
+        Complex64F gamma = new Complex64F(0.5,-0.2);
+
+        for (int i = 0; i < u.length; i++) {
+            u[i] = (rand.nextDouble()*0.5-1.0)*2;
+        }
+
+        for (int i = 1; i < 12; i++) {
+            CDenseMatrix64F A = CRandomMatrices.createRandom(i,i,rand);
+
+            for (int j = 1; j <= i; j += 2) {
+                CDenseMatrix64F subA = CCommonOps.extract(A,A.numRows-j,A.numRows,A.numRows-j,A.numRows);
+                System.arraycopy(u,(A.numRows-j)*2,subU,0,j*2);
+                CDenseMatrix64F expected = rank1UpdateMultL(subA,gamma,subU);
+
+                CDenseMatrix64F found = A.copy();
+                QrHelperFunctions_CD64.rank1UpdateMultL(found,u,gamma.real,gamma.imaginary,
+                        A.numRows-j,A.numRows-j,A.numRows);
+
+                CDenseMatrix64F subFound = CCommonOps.extract(found,A.numRows-j,A.numRows,A.numRows-j,A.numRows);
+
+                outsideIdentical(A, found, j);
+                assertTrue(CMatrixFeatures.isEquals(expected, subFound, 1e-8));
+            }
+        }
+    }
+
+    private CDenseMatrix64F rank1UpdateMultL( CDenseMatrix64F A , Complex64F gamma,  double u[] ) {
+        CDenseMatrix64F U = new CDenseMatrix64F(A.numCols,1);
+        U.data = u;
+        CDenseMatrix64F Ut = new CDenseMatrix64F(1,A.numCols);
+        CCommonOps.transposeConjugate(U,Ut);
+
+        CDenseMatrix64F UUt = new CDenseMatrix64F(A.numCols,A.numCols);
+        CCommonOps.mult(gamma.real,gamma.imaginary,U,Ut,UUt);
+
+        CDenseMatrix64F I = CCommonOps.identity(A.numCols);
+        CDenseMatrix64F inner = new CDenseMatrix64F(A.numCols,A.numCols);
+        CDenseMatrix64F expected = new CDenseMatrix64F(A.numCols,A.numCols);
+
+        CCommonOps.subtract(I,UUt,inner);
+        CCommonOps.mult(A,inner,expected);
+
+        return expected;
+    }
+
+    private void outsideIdentical( CDenseMatrix64F A , CDenseMatrix64F B , int width ) {
+
+        int outside = A.numRows-width;
+
+        for (int i = 0; i < A.numRows; i++) {
+            for (int j = 0; j < A.numCols; j++) {
+                if( i < outside || j < outside ) {
+                    assertEquals(A.getReal(i,j),B.getReal(i,j),1e-8);
+                    assertEquals(A.getImaginary(i, j), B.getImaginary(i,j),1e-8);
+                }
+            }
+        }
+    }
+}
diff --git a/main/denseC64/test/org/ejml/alg/dense/linsol/GenericCLinearSolverChecks.java b/main/denseC64/test/org/ejml/alg/dense/linsol/GenericCLinearSolverChecks.java
new file mode 100644
index 0000000..e7efa2f
--- /dev/null
+++ b/main/denseC64/test/org/ejml/alg/dense/linsol/GenericCLinearSolverChecks.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol;
+
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.data.Complex64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.ejml.ops.CCommonOps;
+import org.ejml.ops.CMatrixFeatures;
+import org.ejml.ops.CRandomMatrices;
+import org.ejml.ops.EjmlUnitTests;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * Contains a series of tests where it solves equations from a known set problems.
+ *
+ * @author Peter Abeles
+ */
+public abstract class GenericCLinearSolverChecks {
+
+    protected Random rand = new Random(0xff);
+
+    // by default have everything run
+    protected boolean shouldFailSingular = true;
+    protected boolean shouldWorkRectangle = true;
+
+    protected double tol = 1e-8;
+
+    @Test
+    public void solve_dimensionCheck() {
+        CDenseMatrix64F A = CRandomMatrices.createRandom(10, 4, rand);
+
+        LinearSolver<CDenseMatrix64F> solver = createSafeSolver(A);
+        assertTrue(solver.setA(A));
+
+        try {
+            CDenseMatrix64F x = CRandomMatrices.createRandom(4,2,rand);
+            CDenseMatrix64F b = CRandomMatrices.createRandom(9,2,rand);
+            solver.solve(b,x);
+            fail("Should have thrown an exception");
+        } catch( RuntimeException ignore ) {}
+
+        try {
+            CDenseMatrix64F x = CRandomMatrices.createRandom(4,3,rand);
+            CDenseMatrix64F b = CRandomMatrices.createRandom(10,2,rand);
+            solver.solve(b,x);
+            fail("Should have thrown an exception");
+        } catch( RuntimeException ignore ) {}
+
+        try {
+            CDenseMatrix64F x = CRandomMatrices.createRandom(5,2,rand);
+            CDenseMatrix64F b = CRandomMatrices.createRandom(10,2,rand);
+            solver.solve(b,x);
+            fail("Should have thrown an exception");
+        } catch( RuntimeException ignore ) {}
+
+
+        try {
+            CDenseMatrix64F x = CRandomMatrices.createRandom(4,2,rand);
+            CDenseMatrix64F b = CRandomMatrices.createRandom(10,1,rand);
+            solver.solve(b,x);
+            fail("Should have thrown an exception");
+        } catch( RuntimeException ignore ) {}
+    }
+
+    /**
+     * Checks to see if the modifyA() flag is set correctly
+     */
+    @Test
+    public void modifiesA() {
+        CDenseMatrix64F A_orig = CRandomMatrices.createRandom(4,4,rand);
+        CDenseMatrix64F A = A_orig.copy();
+
+        LinearSolver<CDenseMatrix64F> solver = createSafeSolver(A);
+
+        assertTrue(solver.setA(A));
+
+        boolean modified = !CMatrixFeatures.isEquals(A_orig,A);
+
+        assertTrue(modified == solver.modifiesA());
+    }
+
+    /**
+     * Checks to see if the modifyB() flag is set correctly
+     */
+    @Test
+    public void modifiesB() {
+        CDenseMatrix64F A = CRandomMatrices.createRandom(4,4,rand);
+
+        LinearSolver<CDenseMatrix64F> solver = createSafeSolver(A);
+
+        assertTrue(solver.setA(A));
+
+        CDenseMatrix64F B = CRandomMatrices.createRandom(4,2,rand);
+        CDenseMatrix64F B_orig = B.copy();
+        CDenseMatrix64F X = new CDenseMatrix64F(A.numRows,B.numCols);
+
+        solver.solve(B,X);
+
+        boolean modified = !CMatrixFeatures.isEquals(B_orig,B);
+
+        assertTrue(modified == solver.modifiesB());
+    }
+
+    /**
+     * See if a matrix that is more singular has a lower quality.
+     */
+    @Test
+    public void checkQuality() {
+        CDenseMatrix64F A_good = CCommonOps.diag(4,0,3,0,2,0,1,0);
+        CDenseMatrix64F A_bad = CCommonOps.diag(4,0,3,0,2,0,0.1,0);
+
+        LinearSolver<CDenseMatrix64F> solver = createSafeSolver(A_good);
+
+        assertTrue(solver.setA(A_good));
+        double q_good;
+        try {
+            q_good = solver.quality();
+        } catch( IllegalArgumentException e ) {
+            // quality is not supported
+            return;
+        }
+
+        assertTrue(solver.setA(A_bad));
+        double q_bad = solver.quality();
+
+        assertTrue(q_bad < q_good);
+
+        assertEquals(q_bad*10.0,q_good,1e-8);
+    }
+
+    /**
+     * See if quality is scale invariant
+     */
+    @Test
+    public void checkQuality_scale() {
+        CDenseMatrix64F A = CCommonOps.diag(4,0,3,0,2,0,10,0);
+        CDenseMatrix64F Asmall = A.copy();
+        CCommonOps.elementMultiply(Asmall, 0.01, 0, Asmall);
+
+        LinearSolver<CDenseMatrix64F> solver = createSafeSolver(A);
+
+        assertTrue(solver.setA(A));
+        double q;
+        try {
+            q = solver.quality();
+        } catch( IllegalArgumentException e ) {
+            // quality is not supported
+            return;
+        }
+
+        assertTrue(solver.setA(Asmall));
+        double q_small = solver.quality();
+
+        assertEquals(q_small,q,1e-8);
+    }
+
+    /**
+     * A very easy matrix to decompose
+     */
+    @Test
+    public void square_trivial() {
+        CDenseMatrix64F A = new CDenseMatrix64F(3,3, true, 5,0, 2,0, 3,0, 1.5,0, -2,0, 8,0, -3,0, 4.7,0, -0.5,0);
+        CDenseMatrix64F b = new CDenseMatrix64F(3,1, true, 18,0, 21.5,0, 4.9000,0);
+        CDenseMatrix64F x = CRandomMatrices.createRandom(3,1,rand);
+
+        LinearSolver<CDenseMatrix64F> solver = createSafeSolver(A);
+        assertTrue(solver.setA(A));
+        solver.solve(b,x);
+
+        CDenseMatrix64F found = new CDenseMatrix64F(3,1);
+        CCommonOps.mult(A,x,found);
+
+        CDenseMatrix64F x_expected = new CDenseMatrix64F(3,1, true, 1,0, 2,0, 3,0);
+
+        EjmlUnitTests.assertEquals(x_expected,x,1e-8);
+    }
+
+    /**
+     * This test checks to see if it can solve a system that will require some algorithms to
+     * perform a pivot.  Pivots can change the data structure and can cause solve to fail if not
+     * handled correctly.
+     */
+    @Test
+    public void square_pivot() {
+        CDenseMatrix64F A = new CDenseMatrix64F(3,3, true, 0,0, 1,0, 2,0, -2,0, 4,0, 9,0, 0.5,0, 0,0, 5,0);
+        CDenseMatrix64F x_expected = new CDenseMatrix64F(3,1, true, 8,-2, 33,1.6, 15.5,-5.7);
+        CDenseMatrix64F x = CRandomMatrices.createRandom(3,1,rand);
+        CDenseMatrix64F b = CRandomMatrices.createRandom(3,1,rand);
+
+        CCommonOps.mult(A,x_expected,b);
+
+        LinearSolver<CDenseMatrix64F> solver = createSafeSolver(A);
+        assertTrue(solver.setA(A));
+        solver.solve(b,x);
+
+        EjmlUnitTests.assertEquals(x_expected,x,1e-8);
+    }
+
+    @Test
+    public void square_singular() {
+        CDenseMatrix64F A = new CDenseMatrix64F(3,3);
+
+        LinearSolver<CDenseMatrix64F> solver = createSafeSolver(A);
+        assertTrue(shouldFailSingular == !solver.setA(A));
+    }
+
+    /**
+     * Have it solve for the coefficients in a polynomial
+     */
+    @Test
+    public void rectangular() {
+        if( !shouldWorkRectangle ) {
+            // skip this test
+            return;
+        }
+
+        double t[] = new double[7*2];
+
+        for (int i = 0; i < t.length; i++) {
+            t[i] = rand.nextDouble()*2-1.0;
+        }
+
+        double vals[] = new double[t.length];
+        Complex64F a = new Complex64F(1,-1);
+        Complex64F b = new Complex64F(2,-0.4);
+        Complex64F c = new Complex64F(3,0.9);
+
+        for( int i = 0; i < t.length; i+= 2 ) {
+            Complex64F T = new Complex64F(t[i],t[i+1]);
+
+            Complex64F result = a.plus( b.times(T) ).plus( c.times(T.times(T)));
+
+            vals[i] = result.real;
+            vals[i+1] = result.imaginary;
+        }
+
+        CDenseMatrix64F B = new CDenseMatrix64F(t.length/2,1, true, vals);
+        CDenseMatrix64F A = createPolyA(t,3);
+        CDenseMatrix64F x = CRandomMatrices.createRandom(3,1,rand);
+
+        LinearSolver<CDenseMatrix64F> solver = createSafeSolver(A);
+        assertTrue(solver.setA(A));
+
+        solver.solve(B,x);
+
+        assertEquals(a.real,     x.getReal(0, 0),tol);
+        assertEquals(a.imaginary,x.getImaginary(0, 0),tol);
+        assertEquals(b.real,     x.getReal(1, 0),tol);
+        assertEquals(b.imaginary,x.getImaginary(1, 0),tol);
+        assertEquals(c.real,     x.getReal(2, 0),tol);
+        assertEquals(c.imaginary,x.getImaginary(2, 0),tol);
+    }
+
+    private CDenseMatrix64F createPolyA( double t[] , int dof ) {
+        CDenseMatrix64F A = new CDenseMatrix64F(t.length/2,dof);
+
+        Complex64F power = new Complex64F();
+        Complex64F T = new Complex64F();
+
+        for( int j = 0; j < A.numRows; j++ ) {
+            T.set(t[j*2],t[j*2+1]);
+            power.set(1,0);
+
+            for( int i = 0; i < dof; i++ ) {
+                A.set(j,i,power.real,power.imaginary);
+                power = power.times(T);
+            }
+        }
+
+        return A;
+    }
+
+    @Test
+    public void inverse() {
+        for (int i = 2; i < 10; i++) {
+            CDenseMatrix64F A = CRandomMatrices.createRandom(i,i,rand);
+            CDenseMatrix64F A_inv = CRandomMatrices.createRandom(i,i,rand);
+
+            LinearSolver<CDenseMatrix64F> solver = createSafeSolver(A);
+
+            assertTrue(solver.setA(A));
+            solver.invert(A_inv);
+
+            CDenseMatrix64F I = CRandomMatrices.createRandom(i,i,rand);
+
+            CCommonOps.mult(A, A_inv, I);
+
+            assertTrue(CMatrixFeatures.isIdentity(I,1e-8));
+        }
+    }
+
+    protected LinearSolver<CDenseMatrix64F>  createSafeSolver( CDenseMatrix64F A ) {
+        return new LinearSolverSafe<CDenseMatrix64F>( createSolver(A));
+    }
+
+    protected abstract LinearSolver<CDenseMatrix64F> createSolver( CDenseMatrix64F A );
+}
diff --git a/main/denseC64/test/org/ejml/alg/dense/linsol/TestCInvertUsingSolve.java b/main/denseC64/test/org/ejml/alg/dense/linsol/TestCInvertUsingSolve.java
new file mode 100644
index 0000000..dfa4da7
--- /dev/null
+++ b/main/denseC64/test/org/ejml/alg/dense/linsol/TestCInvertUsingSolve.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol;
+
+import org.ejml.alg.dense.decompose.lu.LUDecompositionAlt_CD64;
+import org.ejml.alg.dense.linsol.lu.LinearSolverLu_CD64;
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.ejml.ops.CCommonOps;
+import org.ejml.ops.CMatrixFeatures;
+import org.ejml.ops.CRandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestCInvertUsingSolve {
+
+    Random rand = new Random(0xff);
+    double tol = 1e-8;
+
+    /**
+     * See if it can invert a matrix that is known to be invertable.
+     */
+    @Test
+    public void invert() {
+        CDenseMatrix64F A = new CDenseMatrix64F(3,3, true, 0,0, 1,0, 2,0, -2,0, 4,0, 9,0, 0.5,0, 0,0, 5,0);
+        CDenseMatrix64F A_inv = CRandomMatrices.createRandom(3, 3, rand);
+
+        LUDecompositionAlt_CD64 decomp = new LUDecompositionAlt_CD64();
+        LinearSolver<CDenseMatrix64F> solver = new LinearSolverLu_CD64(decomp);
+
+        solver.setA(A);
+        CInvertUsingSolve.invert(solver,A,A_inv);
+
+        CDenseMatrix64F I = CRandomMatrices.createRandom(3,3,rand);
+
+        CCommonOps.mult(A, A_inv, I);
+
+        assertTrue(CMatrixFeatures.isIdentity(I,tol));
+    }
+}
diff --git a/main/denseC64/test/org/ejml/alg/dense/linsol/chol/BaseCholeskySolveTests_CD64.java b/main/denseC64/test/org/ejml/alg/dense/linsol/chol/BaseCholeskySolveTests_CD64.java
new file mode 100644
index 0000000..e3e8ea2
--- /dev/null
+++ b/main/denseC64/test/org/ejml/alg/dense/linsol/chol/BaseCholeskySolveTests_CD64.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.chol;
+
+import org.ejml.alg.dense.linsol.LinearSolverSafe;
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.ejml.ops.CCommonOps;
+import org.ejml.ops.CMatrixFeatures;
+import org.ejml.ops.CRandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+
+/**
+* @author Peter Abeles
+*/
+public abstract class BaseCholeskySolveTests_CD64 {
+
+    Random rand = new Random(0x45);
+
+    public void standardTests() {
+
+        solve_dimensionCheck();
+        testSolve();
+        testInvert();
+        testQuality();
+        testQuality_scale();
+    }
+
+    public abstract LinearSolver<CDenseMatrix64F> createSolver();
+
+    public LinearSolver<CDenseMatrix64F> createSafeSolver() {
+        LinearSolver<CDenseMatrix64F> solver = createSolver();
+        return new LinearSolverSafe<CDenseMatrix64F>(solver);
+    }
+
+    @Test
+    public void setA_dimensionCheck() {
+
+        LinearSolver<CDenseMatrix64F> solver = createSafeSolver();
+
+        try {
+            CDenseMatrix64F A = CRandomMatrices.createRandom(4, 5, rand);
+            assertTrue(solver.setA(A));
+            fail("Should have thrown an exception");
+        } catch( RuntimeException ignore ) {}
+    }
+
+    @Test
+    public void solve_dimensionCheck() {
+
+        LinearSolver<CDenseMatrix64F> solver = createSafeSolver();
+
+        CDenseMatrix64F A = CRandomMatrices.createHermPosDef(4, rand);
+        assertTrue(solver.setA(A));
+
+        try {
+            CDenseMatrix64F x = CRandomMatrices.createRandom(4,3,rand);
+            CDenseMatrix64F b = CRandomMatrices.createRandom(4,2,rand);
+            solver.solve(b,x);
+            fail("Should have thrown an exception");
+        } catch( RuntimeException ignore ) {}
+
+        try {
+            CDenseMatrix64F x = CRandomMatrices.createRandom(5,2,rand);
+            CDenseMatrix64F b = CRandomMatrices.createRandom(4,2,rand);
+            solver.solve(b,x);
+            fail("Should have thrown an exception");
+        } catch( RuntimeException ignore ) {}
+
+        try {
+            CDenseMatrix64F x = CRandomMatrices.createRandom(5,2,rand);
+            CDenseMatrix64F b = CRandomMatrices.createRandom(5,2,rand);
+            solver.solve(b,x);
+            fail("Should have thrown an exception");
+        } catch( RuntimeException ignore ) {}
+    }
+
+    @Test
+    public void testSolve() {
+
+        LinearSolver<CDenseMatrix64F> solver = createSolver();
+
+        for (int N = 1; N <= 4; N++) {
+            CDenseMatrix64F A = CRandomMatrices.createHermPosDef(N,rand);
+            CDenseMatrix64F x = CRandomMatrices.createRandom(N,1,rand);
+            CDenseMatrix64F b = new CDenseMatrix64F(N,1);
+            CDenseMatrix64F x_expected = x.copy();
+
+            CCommonOps.mult(A,x_expected,b);
+
+            CDenseMatrix64F A_orig = A.copy();
+            CDenseMatrix64F B_orig = b.copy();
+
+            assertTrue(solver.setA(A));
+            solver.solve(b,x);
+
+            assertTrue(CMatrixFeatures.isIdentical(x, x_expected, 1e-8));
+
+            // see if input was modified
+            assertEquals(!solver.modifiesA(),CMatrixFeatures.isIdentical(A,A_orig,1e-8));
+            assertEquals(!solver.modifiesB(),CMatrixFeatures.isIdentical(b,B_orig,1e-8));
+        }
+    }
+
+    @Test
+    public void testInvert() {
+
+        LinearSolver<CDenseMatrix64F> solver = createSolver();
+
+        for (int N = 1; N <= 5; N++) {
+            CDenseMatrix64F A = CRandomMatrices.createHermPosDef(N,rand);
+            CDenseMatrix64F A_orig = A.copy();
+            CDenseMatrix64F A_inv = new CDenseMatrix64F(N,N);
+            CDenseMatrix64F found = new CDenseMatrix64F(N,N);
+
+            assertTrue(solver.setA(A));
+            solver.invert(A_inv);
+
+            CCommonOps.mult(A_inv,A_orig,found);
+            assertTrue(CMatrixFeatures.isIdentity(found, 1e-8));
+
+            // see if input was modified
+            assertEquals(!solver.modifiesA(),CMatrixFeatures.isIdentical(A,A_orig,1e-8));
+        }
+    }
+
+    @Test
+    public void testQuality() {
+
+        LinearSolver<CDenseMatrix64F> solver = createSafeSolver();
+
+        CDenseMatrix64F A = CCommonOps.diag(3,0, 2,0, 1,0    );
+        CDenseMatrix64F B = CCommonOps.diag(3,0, 2,0, 0.001,0);
+
+        assertTrue(solver.setA(A));
+        double qualityA = solver.quality();
+
+        assertTrue(solver.setA(B));
+        double qualityB = solver.quality();
+
+        assertTrue(qualityB < qualityA);
+    }
+
+    @Test
+    public void testQuality_scale() {
+
+        LinearSolver<CDenseMatrix64F> solver = createSafeSolver();
+
+        CDenseMatrix64F A = CCommonOps.diag(3,0 ,2,0 ,1,0);
+        CDenseMatrix64F B = A.copy();
+        CCommonOps.elementMultiply(B,0.001,0,B);
+
+        assertTrue(solver.setA(A));
+        double qualityA = solver.quality();
+
+        assertTrue(solver.setA(B));
+        double qualityB = solver.quality();
+
+        assertEquals(qualityB,qualityA,1e-8);
+    }
+}
diff --git a/main/denseC64/test/org/ejml/alg/dense/linsol/chol/TestLinearSolverChol_CD64.java b/main/denseC64/test/org/ejml/alg/dense/linsol/chol/TestLinearSolverChol_CD64.java
new file mode 100644
index 0000000..373b6c9
--- /dev/null
+++ b/main/denseC64/test/org/ejml/alg/dense/linsol/chol/TestLinearSolverChol_CD64.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.chol;
+
+import org.ejml.alg.dense.decompose.chol.CholeskyDecompositionInner_CD64;
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestLinearSolverChol_CD64 extends BaseCholeskySolveTests_CD64 {
+
+    @Override
+    public LinearSolver<CDenseMatrix64F> createSolver() {
+        CholeskyDecompositionInner_CD64 alg = new CholeskyDecompositionInner_CD64(true);
+        return new LinearSolverChol_CD64(alg);
+    }
+}
\ No newline at end of file
diff --git a/main/denseC64/test/org/ejml/alg/dense/linsol/lu/TestLinearSolverLu_CD64.java b/main/denseC64/test/org/ejml/alg/dense/linsol/lu/TestLinearSolverLu_CD64.java
new file mode 100644
index 0000000..5887fbc
--- /dev/null
+++ b/main/denseC64/test/org/ejml/alg/dense/linsol/lu/TestLinearSolverLu_CD64.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.lu;
+
+import org.ejml.alg.dense.decompose.lu.LUDecompositionAlt_CD64;
+import org.ejml.alg.dense.linsol.GenericCLinearSolverChecks;
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestLinearSolverLu_CD64 extends GenericCLinearSolverChecks {
+
+    public TestLinearSolverLu_CD64() {
+        shouldWorkRectangle = false;
+        shouldFailSingular = false;
+    }
+
+    @Override
+    protected LinearSolver<CDenseMatrix64F> createSolver( CDenseMatrix64F A ) {
+        LUDecompositionAlt_CD64 decomp = new LUDecompositionAlt_CD64();
+
+        return new LinearSolverLu_CD64(decomp);
+    }
+}
diff --git a/main/denseC64/test/org/ejml/alg/dense/linsol/qr/TestLinearSolverQrHouseCol_CD64.java b/main/denseC64/test/org/ejml/alg/dense/linsol/qr/TestLinearSolverQrHouseCol_CD64.java
new file mode 100644
index 0000000..fcedabc
--- /dev/null
+++ b/main/denseC64/test/org/ejml/alg/dense/linsol/qr/TestLinearSolverQrHouseCol_CD64.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.qr;
+
+import org.ejml.alg.dense.linsol.GenericCLinearSolverChecks;
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestLinearSolverQrHouseCol_CD64 extends GenericCLinearSolverChecks {
+
+    public TestLinearSolverQrHouseCol_CD64() {
+//         shouldFailSingular = false;
+    }
+
+    @Override
+    protected LinearSolver<CDenseMatrix64F> createSolver( CDenseMatrix64F A ) {
+        return new LinearSolverQrHouseCol_CD64();
+    }
+}
\ No newline at end of file
diff --git a/main/denseC64/test/org/ejml/alg/dense/linsol/qr/TestLinearSolverQrHouseTran_CD64.java b/main/denseC64/test/org/ejml/alg/dense/linsol/qr/TestLinearSolverQrHouseTran_CD64.java
new file mode 100644
index 0000000..1491f75
--- /dev/null
+++ b/main/denseC64/test/org/ejml/alg/dense/linsol/qr/TestLinearSolverQrHouseTran_CD64.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.qr;
+
+import org.ejml.alg.dense.linsol.GenericCLinearSolverChecks;
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestLinearSolverQrHouseTran_CD64 extends GenericCLinearSolverChecks {
+
+    public TestLinearSolverQrHouseTran_CD64() {
+//         shouldFailSingular = false;
+    }
+
+    @Override
+    protected LinearSolver<CDenseMatrix64F> createSolver( CDenseMatrix64F A ) {
+        return new LinearSolverQrHouseTran_CD64();
+    }
+}
\ No newline at end of file
diff --git a/main/denseC64/test/org/ejml/alg/dense/linsol/qr/TestLinearSolverQrHouse_CD64.java b/main/denseC64/test/org/ejml/alg/dense/linsol/qr/TestLinearSolverQrHouse_CD64.java
new file mode 100644
index 0000000..2d21366
--- /dev/null
+++ b/main/denseC64/test/org/ejml/alg/dense/linsol/qr/TestLinearSolverQrHouse_CD64.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.qr;
+
+import org.ejml.alg.dense.linsol.GenericCLinearSolverChecks;
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestLinearSolverQrHouse_CD64 extends GenericCLinearSolverChecks {
+
+    public TestLinearSolverQrHouse_CD64() {
+//         shouldFailSingular = false;
+    }
+
+    @Override
+    protected LinearSolver<CDenseMatrix64F> createSolver( CDenseMatrix64F A ) {
+        return new LinearSolverQrHouse_CD64();
+    }
+}
diff --git a/main/denseC64/test/org/ejml/alg/dense/linsol/qr/TestLinearSolverQr_CD64.java b/main/denseC64/test/org/ejml/alg/dense/linsol/qr/TestLinearSolverQr_CD64.java
new file mode 100644
index 0000000..e87b239
--- /dev/null
+++ b/main/denseC64/test/org/ejml/alg/dense/linsol/qr/TestLinearSolverQr_CD64.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol.qr;
+
+import org.ejml.alg.dense.decompose.qr.QRDecompositionHouseholderColumn_CD64;
+import org.ejml.alg.dense.linsol.GenericCLinearSolverChecks;
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestLinearSolverQr_CD64 extends GenericCLinearSolverChecks {
+
+    public TestLinearSolverQr_CD64() {
+//         shouldFailSingular = false;
+    }
+
+    @Override
+    protected LinearSolver<CDenseMatrix64F> createSolver( CDenseMatrix64F A ) {
+        return new LinearSolverQr_CD64(new QRDecompositionHouseholderColumn_CD64());
+    }
+}
\ No newline at end of file
diff --git a/main/denseC64/test/org/ejml/alg/dense/misc/TestCTransposeAlgs.java b/main/denseC64/test/org/ejml/alg/dense/misc/TestCTransposeAlgs.java
new file mode 100644
index 0000000..b0ebdb4
--- /dev/null
+++ b/main/denseC64/test/org/ejml/alg/dense/misc/TestCTransposeAlgs.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.misc;
+
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.data.Complex64F;
+import org.ejml.ops.CRandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestCTransposeAlgs {
+
+    Random rand = new Random(234);
+
+    @Test
+    public void square() {
+        CDenseMatrix64F a = CRandomMatrices.createRandom(4,4,-1,1,rand);
+        CDenseMatrix64F b = a.copy();
+
+        CTransposeAlgs.square(b);
+
+        Complex64F found = new Complex64F();
+        Complex64F expected = new Complex64F();
+
+        for (int i = 0; i < 4; i++) {
+            for (int j = 0; j < 4; j++) {
+                a.get(j,i,expected);
+                b.get(i,j,found);
+
+                assertEquals(expected.real,found.real,1e-8);
+                assertEquals(expected.imaginary,found.imaginary,1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void squareConjugate() {
+        CDenseMatrix64F a = CRandomMatrices.createRandom(4,4,-1,1,rand);
+        CDenseMatrix64F b = a.copy();
+
+        CTransposeAlgs.squareConjugate(b);
+
+        Complex64F found = new Complex64F();
+        Complex64F expected = new Complex64F();
+
+        for (int i = 0; i < 4; i++) {
+            for (int j = 0; j < 4; j++) {
+                a.get(j,i,expected);
+                b.get(i,j,found);
+
+                assertEquals(expected.real,found.real,1e-8);
+                assertEquals(-expected.imaginary,found.imaginary,1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void standard() {
+        CDenseMatrix64F a = CRandomMatrices.createRandom(4,5,-1,1,rand);
+        CDenseMatrix64F b = CRandomMatrices.createRandom(5, 4, -1, 1, rand);
+
+        CTransposeAlgs.standard(a, b);
+
+        Complex64F found = new Complex64F();
+        Complex64F expected = new Complex64F();
+
+        for (int i = 0; i < 4; i++) {
+            for (int j = 0; j < 5; j++) {
+                a.get(i,j,expected);
+                b.get(j,i,found);
+
+                assertEquals(expected.real,found.real,1e-8);
+                assertEquals(expected.imaginary,found.imaginary,1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void standardConjugate() {
+        CDenseMatrix64F a = CRandomMatrices.createRandom(4,5,-1,1,rand);
+        CDenseMatrix64F b = CRandomMatrices.createRandom(5, 4, -1, 1, rand);
+
+        CTransposeAlgs.standardConjugate(a, b);
+
+        Complex64F found = new Complex64F();
+        Complex64F expected = new Complex64F();
+
+        for (int i = 0; i < 4; i++) {
+            for (int j = 0; j < 5; j++) {
+                a.get(i,j,expected);
+                b.get(j,i,found);
+
+                assertEquals(expected.real,found.real,1e-8);
+                assertEquals(-expected.imaginary,found.imaginary,1e-8);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/main/denseC64/test/org/ejml/alg/dense/mult/TestCMatrixMatrixMult.java b/main/denseC64/test/org/ejml/alg/dense/mult/TestCMatrixMatrixMult.java
new file mode 100644
index 0000000..6c9b8c0
--- /dev/null
+++ b/main/denseC64/test/org/ejml/alg/dense/mult/TestCMatrixMatrixMult.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.mult;
+
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.data.Complex64F;
+import org.ejml.ops.CCommonOps;
+import org.ejml.ops.CMatrixFeatures;
+import org.ejml.ops.CRandomMatrices;
+import org.ejml.ops.ComplexMath64F;
+import org.junit.Test;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestCMatrixMatrixMult {
+
+    @Test
+    public void generalChecks() {
+
+        int numChecked = 0;
+        Method methods[] = CMatrixMatrixMult.class.getMethods();
+
+        for( Method method : methods ) {
+            String name = method.getName();
+
+            // only look at function which perform matrix multiplications
+            if (!name.contains("mult"))
+                continue;
+
+//            System.out.println(name);
+
+            Class[] params = method.getParameterTypes();
+
+            boolean add = name.contains("Add");
+            boolean hasAlpha = double.class == params[0];
+
+            try {
+                check(method,add,hasAlpha);
+            } catch (InvocationTargetException e) {
+                throw new RuntimeException(e);
+            } catch (IllegalAccessException e) {
+                throw new RuntimeException(e);
+            }
+            numChecked++;
+        }
+
+        assertEquals(8,numChecked);
+    }
+
+    public static void check( Method method , boolean isAdd , boolean hasAlpha ) throws InvocationTargetException, IllegalAccessException {
+        Random rand = new Random(234);
+
+        double realAlpha = 2.3;
+        double imgAlpha = 1.3;
+
+        for (int i = 1; i <= 4; i++) {
+            for (int j = 1; j <= 4; j++) {
+                for (int k = 1; k <= 4; k++) {
+                    CDenseMatrix64F A = CRandomMatrices.createRandom(i,j,-1,1,rand);
+                    CDenseMatrix64F B = CRandomMatrices.createRandom(j,k,-1,1,rand);
+                    CDenseMatrix64F C = CRandomMatrices.createRandom(i,k,-1,1,rand);
+
+                    CDenseMatrix64F AB = multiply(A,B);
+                    CDenseMatrix64F expected = new CDenseMatrix64F(i,k);
+
+                    if( hasAlpha ) {
+                        CCommonOps.elementMultiply(AB,realAlpha,imgAlpha,AB);
+                    }
+
+                    if( isAdd ) {
+                        CCommonOps.add(C,AB,expected);
+                    } else {
+                        expected.set(AB);
+                    }
+
+                    invoke(method,realAlpha,imgAlpha,A,B,C);
+
+                    assertTrue(i+" "+j+" "+k,CMatrixFeatures.isEquals(expected,C,1e-8));
+                }
+            }
+        }
+    }
+
+    public static void invoke(Method func,
+                              double realAlpha, double imgAlpha,
+                              CDenseMatrix64F a, CDenseMatrix64F b, CDenseMatrix64F c)
+            throws IllegalAccessException, InvocationTargetException {
+        if( func.getParameterTypes().length == 3 ) {
+            func.invoke(null, a, b, c);
+        } else {
+            if( func.getParameterTypes()[0] == double.class ) {
+                if( func.getParameterTypes().length == 5 )
+                    func.invoke(null,realAlpha, imgAlpha, a, b, c);
+                else
+                    func.invoke(null,realAlpha, imgAlpha, a, b, c,null);
+            } else {
+                func.invoke(null, a, b, c,null);
+            }
+        }
+    }
+
+    public static CDenseMatrix64F multiply( CDenseMatrix64F A , CDenseMatrix64F B ) {
+        CDenseMatrix64F C = new CDenseMatrix64F(A.numRows,B.numCols);
+
+        Complex64F a = new Complex64F();
+        Complex64F b = new Complex64F();
+        Complex64F m = new Complex64F();
+
+        for (int i = 0; i < A.numRows; i++) {
+            for (int j = 0; j < B.numCols; j++) {
+                Complex64F sum = new Complex64F();
+
+                for (int k = 0; k < A.numCols; k++) {
+                    A.get(i,k,a);
+                    B.get(k,j,b);
+
+                    ComplexMath64F.multiply(a,b,m);
+                    sum.real += m.real;
+                    sum.imaginary += m.imaginary;
+                }
+
+                C.set(i,j,sum.real,sum.imaginary);
+            }
+        }
+
+        return C;
+    }
+}
\ No newline at end of file
diff --git a/main/denseC64/test/org/ejml/alg/dense/mult/TestCVectorVectorMult.java b/main/denseC64/test/org/ejml/alg/dense/mult/TestCVectorVectorMult.java
new file mode 100644
index 0000000..01ff8bb
--- /dev/null
+++ b/main/denseC64/test/org/ejml/alg/dense/mult/TestCVectorVectorMult.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.mult;
+
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.data.Complex64F;
+import org.ejml.ops.CCommonOps;
+import org.ejml.ops.CRandomMatrices;
+import org.ejml.ops.EjmlUnitTests;
+import org.junit.Test;
+
+import java.util.Random;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestCVectorVectorMult {
+
+    Random rand = new Random(234);
+
+    @Test
+    public void innerProd() {
+
+        CDenseMatrix64F a = CRandomMatrices.createRandom(1,6,rand);
+        CDenseMatrix64F b = CRandomMatrices.createRandom(6,1,rand);
+
+        CDenseMatrix64F c = new CDenseMatrix64F(1,1);
+
+        CCommonOps.mult(a,b,c);
+
+        Complex64F expected = new Complex64F();
+        c.get(0,0,expected);
+        Complex64F found = CVectorVectorMult.innerProd(a,b,null);
+
+        EjmlUnitTests.assertEquals(expected,found,1e-8);
+    }
+
+    @Test
+    public void innerProdH() {
+
+        CDenseMatrix64F a = CRandomMatrices.createRandom(1,6,rand);
+        CDenseMatrix64F b = CRandomMatrices.createRandom(6,1,rand);
+
+        Complex64F found = CVectorVectorMult.innerProdH(a, b, null);
+
+        CDenseMatrix64F c = new CDenseMatrix64F(1,1);
+
+        CCommonOps.conjugate(b,b);
+        CCommonOps.mult(a,b,c);
+
+        Complex64F expected = new Complex64F();
+        c.get(0,0,expected);
+
+        EjmlUnitTests.assertEquals(expected,found,1e-8);
+    }
+
+    @Test
+    public void outerProd() {
+        CDenseMatrix64F a = CRandomMatrices.createRandom(6,1,rand);
+        CDenseMatrix64F b = CRandomMatrices.createRandom(1,6,rand);
+
+        CDenseMatrix64F expected = new CDenseMatrix64F(6,6);
+        CDenseMatrix64F found = new CDenseMatrix64F(6,6);
+
+        CCommonOps.mult(a,b,expected);
+        CVectorVectorMult.outerProd(a,b,found);
+
+        EjmlUnitTests.assertEquals(expected,found,1e-8);
+    }
+
+    @Test
+    public void outerProdH() {
+        CDenseMatrix64F a = CRandomMatrices.createRandom(6,1,rand);
+        CDenseMatrix64F b = CRandomMatrices.createRandom(1,6,rand);
+
+        CDenseMatrix64F expected = new CDenseMatrix64F(6,6);
+        CDenseMatrix64F found = new CDenseMatrix64F(6,6);
+
+        CVectorVectorMult.outerProdH(a, b, found);
+        CCommonOps.conjugate(b,b);
+        CCommonOps.mult(a, b, expected);
+
+        EjmlUnitTests.assertEquals(expected,found,1e-8);
+    }
+}
\ No newline at end of file
diff --git a/main/denseC64/test/org/ejml/ops/TestCCommonOps.java b/main/denseC64/test/org/ejml/ops/TestCCommonOps.java
new file mode 100644
index 0000000..48850c7
--- /dev/null
+++ b/main/denseC64/test/org/ejml/ops/TestCCommonOps.java
@@ -0,0 +1,707 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.alg.dense.mult.CMatrixMatrixMult;
+import org.ejml.alg.dense.mult.TestCMatrixMatrixMult;
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.data.Complex64F;
+import org.ejml.data.DenseMatrix64F;
+import org.junit.Test;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestCCommonOps {
+
+    Random rand = new Random(234);
+
+    @Test
+    public void identity_one() {
+        CDenseMatrix64F I = CCommonOps.identity(4);
+        assertEquals(4,I.numRows);
+        assertEquals(4,I.numCols);
+
+        assertTrue(CMatrixFeatures.isIdentity(I,1e-8));
+    }
+
+    @Test
+    public void identity_two() {
+        CDenseMatrix64F I = CCommonOps.identity(4,5);
+        assertEquals(4,I.numRows);
+        assertEquals(5,I.numCols);
+
+        assertTrue(CMatrixFeatures.isIdentity(I,1e-8));
+
+        I = CCommonOps.identity(5,4);
+        assertEquals(5,I.numRows);
+        assertEquals(4,I.numCols);
+
+        assertTrue(CMatrixFeatures.isIdentity(I,1e-8));
+    }
+
+    @Test
+    public void diag() {
+        CDenseMatrix64F m = CCommonOps.diag(1,2,3,4,5,6);
+
+        assertEquals(3,m.numRows);
+        assertEquals(3,m.numCols);
+
+        Complex64F a = new Complex64F();
+
+        for (int i = 0; i < 3; i++) {
+            for (int j = 0; j < 3; j++) {
+                m.get(i,j,a);
+
+                if( i == j ) {
+                    assertEquals(2*i+1,a.real,1e-8);
+                    assertEquals(2*i+2,a.imaginary,1e-8);
+                } else {
+                    assertEquals(0,a.real,1e-8);
+                    assertEquals(0,a.imaginary,1e-8);
+                }
+            }
+        }
+    }
+
+    @Test
+    public void convert() {
+        DenseMatrix64F input = RandomMatrices.createRandom(5,7,-1,1,rand);
+        CDenseMatrix64F output = new CDenseMatrix64F(5,7);
+
+        Complex64F a = new Complex64F();
+
+        CCommonOps.convert(input, output);
+
+        for (int i = 0; i < input.numRows; i++) {
+            for (int j = 0; j < input.numCols; j++) {
+                output.get(i,j,a);
+
+                assertEquals(input.get(i,j),a.getReal(),1e-8);
+                assertEquals(0,a.getImaginary(),1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void stripReal() {
+        CDenseMatrix64F input = CRandomMatrices.createRandom(5,7,-1,1,rand);
+        DenseMatrix64F output = new DenseMatrix64F(5,7);
+
+        Complex64F a = new Complex64F();
+
+        CCommonOps.stripReal(input, output);
+
+        for (int i = 0; i < input.numRows; i++) {
+            for (int j = 0; j < input.numCols; j++) {
+                input.get(i,j,a);
+
+                assertEquals(a.getReal(),output.get(i,j),1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void stripImaginary() {
+        CDenseMatrix64F input = CRandomMatrices.createRandom(5,7,-1,1,rand);
+        DenseMatrix64F output = new DenseMatrix64F(5,7);
+
+        Complex64F a = new Complex64F();
+
+        CCommonOps.stripImaginary(input, output);
+
+        for (int i = 0; i < input.numRows; i++) {
+            for (int j = 0; j < input.numCols; j++) {
+                input.get(i,j,a);
+
+                assertEquals(a.getImaginary(),output.get(i,j),1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void magnitude() {
+        CDenseMatrix64F input = CRandomMatrices.createRandom(5,7,-1,1,rand);
+        DenseMatrix64F output = new DenseMatrix64F(5,7);
+
+        Complex64F a = new Complex64F();
+
+        CCommonOps.magnitude(input, output);
+
+        for (int i = 0; i < input.numRows; i++) {
+            for (int j = 0; j < input.numCols; j++) {
+                input.get(i,j,a);
+
+                assertEquals(a.getMagnitude(),output.get(i,j),1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void conjugate() {
+        CDenseMatrix64F matrix = CRandomMatrices.createRandom(5,7,-1,1,rand);
+        CDenseMatrix64F found = CRandomMatrices.createRandom(5,7,-1,1,rand);
+
+        CCommonOps.conjugate(matrix,found);
+
+        for (int i = 0; i < matrix.getDataLength(); i += 2) {
+            double real = matrix.data[i];
+            double img = matrix.data[i+1];
+
+            assertEquals(real, found.data[i],1e-8);
+            assertEquals(img, -found.data[i+1],1e-8);
+        }
+    }
+
+    @Test
+    public void fill() {
+        CDenseMatrix64F matrix = CRandomMatrices.createRandom(5,7,-1,1,rand);
+
+        CCommonOps.fill(matrix,2,-1);
+
+        for (int i = 0; i < matrix.getDataLength(); i += 2) {
+            double real = matrix.data[i];
+            double img = matrix.data[i+1];
+
+            assertEquals(2,real,1e-8);
+            assertEquals(-1,img,1e-8);
+        }
+    }
+
+    @Test
+    public void add() {
+        CDenseMatrix64F matrixA = CRandomMatrices.createRandom(5,7,-1,1,rand);
+        CDenseMatrix64F matrixB = CRandomMatrices.createRandom(5,7,-1,1,rand);
+        CDenseMatrix64F out = CRandomMatrices.createRandom(5,7,-1,1,rand);
+
+        Complex64F a = new Complex64F();
+        Complex64F b = new Complex64F();
+        Complex64F found = new Complex64F();
+        Complex64F expected = new Complex64F();
+
+        CCommonOps.add(matrixA, matrixB, out);
+
+        for (int i = 0; i < matrixA.numRows; i++) {
+            for (int j = 0; j < matrixA.numCols; j++) {
+                matrixA.get(i,j,a);
+                matrixB.get(i,j,b);
+                out.get(i,j,found);
+
+                ComplexMath64F.plus(a, b, expected);
+
+                assertEquals(expected.real,found.real,1e-8);
+                assertEquals(expected.imaginary,found.imaginary,1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void subtract() {
+        CDenseMatrix64F matrixA = CRandomMatrices.createRandom(5,7,-1,1,rand);
+        CDenseMatrix64F matrixB = CRandomMatrices.createRandom(5,7,-1,1,rand);
+        CDenseMatrix64F out = CRandomMatrices.createRandom(5,7,-1,1,rand);
+
+        Complex64F a = new Complex64F();
+        Complex64F b = new Complex64F();
+        Complex64F found = new Complex64F();
+        Complex64F expected = new Complex64F();
+
+        CCommonOps.subtract(matrixA, matrixB, out);
+
+        for (int i = 0; i < matrixA.numRows; i++) {
+            for (int j = 0; j < matrixA.numCols; j++) {
+                matrixA.get(i,j,a);
+                matrixB.get(i,j,b);
+                out.get(i,j,found);
+
+                ComplexMath64F.minus(a, b, expected);
+
+                assertEquals(expected.real,found.real,1e-8);
+                assertEquals(expected.imaginary,found.imaginary,1e-8);
+            }
+        }
+    }
+
+    /**
+     * Make sure the multiplication methods here have the same behavior as the ones in MatrixMatrixMult.
+     */
+    @Test
+    public void checkAllMatrixMult() {
+        int numChecked = 0;
+        Method methods[] = CCommonOps.class.getMethods();
+
+        for (Method method : methods) {
+            String name = method.getName();
+
+            if( !name.startsWith("mult"))
+                continue;
+
+            //            System.out.println(name);
+
+            Class[] params = method.getParameterTypes();
+
+            boolean add = name.contains("Add");
+            boolean hasAlpha = double.class == params[0];
+
+            try {
+                TestCMatrixMatrixMult.check(method, add, hasAlpha);
+            } catch (InvocationTargetException e) {
+                throw new RuntimeException(e);
+            } catch (IllegalAccessException e) {
+                throw new RuntimeException(e);
+            }
+            numChecked++;
+        }
+
+        assertEquals(4,numChecked);
+    }
+
+    @Test
+    public void multiply() {
+        for (int i = 1; i < 10; i++) {
+            for (int j = 1; j < 10; j++) {
+                CDenseMatrix64F A = CRandomMatrices.createRandom(i,j,-1,1,rand);
+                for (int k = 1; k < 10; k++) {
+                    CDenseMatrix64F B = CRandomMatrices.createRandom(j, k, -1, 1, rand);
+                    CDenseMatrix64F found = CRandomMatrices.createRandom(i, k, -1, 1, rand);
+                    CDenseMatrix64F expected = TestCMatrixMatrixMult.multiply(A, B);
+
+                    CMatrixMatrixMult.mult_reorder(A, B, found);
+
+                    assertTrue(i+" "+j+" "+k,CMatrixFeatures.isEquals(expected, found, 1e-8));
+                }
+            }
+        }
+    }
+
+    @Test
+    public void transpose_one() {
+
+        CDenseMatrix64F a = CRandomMatrices.createRandom(4,4,-1,1,rand);
+        CDenseMatrix64F b = a.copy();
+
+        CCommonOps.transpose(b);
+
+        Complex64F found = new Complex64F();
+        Complex64F expected = new Complex64F();
+
+        for (int i = 0; i < 4; i++) {
+            for (int j = 0; j < 4; j++) {
+                a.get(i,j,expected);
+                b.get(j,i,found);
+
+                assertEquals(expected.real,found.real,1e-8);
+                assertEquals(expected.imaginary,found.imaginary,1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void transposeConjugate_one() {
+
+        CDenseMatrix64F a = CRandomMatrices.createRandom(4,4,-1,1,rand);
+        CDenseMatrix64F b = a.copy();
+
+        CCommonOps.transposeConjugate(b);
+
+        Complex64F found = new Complex64F();
+        Complex64F expected = new Complex64F();
+
+        for (int i = 0; i < 4; i++) {
+            for (int j = 0; j < 4; j++) {
+                a.get(i,j,expected);
+                b.get(j,i,found);
+
+                assertEquals(expected.real,found.real,1e-8);
+                assertEquals(-expected.imaginary,found.imaginary,1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void transpose_two() {
+
+        CDenseMatrix64F a = CRandomMatrices.createRandom(4,5,-1,1,rand);
+        CDenseMatrix64F b = CRandomMatrices.createRandom(5,4,-1,1,rand);
+
+        CCommonOps.transpose(a, b);
+
+        Complex64F found = new Complex64F();
+        Complex64F expected = new Complex64F();
+
+        for (int i = 0; i < 4; i++) {
+            for (int j = 0; j < 5; j++) {
+                a.get(i,j,expected);
+                b.get(j,i,found);
+
+                assertEquals(expected.real,found.real,1e-8);
+                assertEquals(expected.imaginary,found.imaginary,1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void transposeConjugate_two() {
+
+        CDenseMatrix64F a = CRandomMatrices.createRandom(4,5,-1,1,rand);
+        CDenseMatrix64F b = CRandomMatrices.createRandom(5,4,-1,1,rand);
+
+        CCommonOps.transposeConjugate(a, b);
+
+        Complex64F found = new Complex64F();
+        Complex64F expected = new Complex64F();
+
+        for (int i = 0; i < 4; i++) {
+            for (int j = 0; j < 5; j++) {
+                a.get(i,j,expected);
+                b.get(j,i,found);
+
+                assertEquals(expected.real,found.real,1e-8);
+                assertEquals(-expected.imaginary,found.imaginary,1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void invert_1() {
+        for (int i = 1; i < 10; i++) {
+            CDenseMatrix64F A = CRandomMatrices.createRandom(i,i,rand);
+            CDenseMatrix64F A_orig = A.copy();
+
+            CDenseMatrix64F I = CRandomMatrices.createRandom(i,i,rand);
+
+            assertTrue(CCommonOps.invert(A));
+            CCommonOps.mult(A_orig,A,I);
+
+            assertTrue(CMatrixFeatures.isIdentity(I, 1e-8));
+        }
+    }
+
+    @Test
+    public void invert_2() {
+        for (int i = 1; i < 10; i++) {
+            CDenseMatrix64F A = CRandomMatrices.createRandom(i, i, rand);
+            CDenseMatrix64F A_orig = A.copy();
+            CDenseMatrix64F A_inv = new CDenseMatrix64F(i, i);
+
+            CDenseMatrix64F I = CRandomMatrices.createRandom(i, i, rand);
+
+            assertTrue(CCommonOps.invert(A, A_inv));
+            CCommonOps.mult(A, A_inv, I);
+
+            assertTrue(CMatrixFeatures.isIdentity(I, 1e-8));
+            assertTrue(CMatrixFeatures.isIdentical(A, A_orig, 0));
+        }
+    }
+
+    @Test
+    public void solve() {
+        // square
+        for (int i = 1; i < 10; i++) {
+            CDenseMatrix64F A = CRandomMatrices.createRandom(i, i, rand);
+            CDenseMatrix64F B = CRandomMatrices.createRandom(i, 1, rand);
+
+            CDenseMatrix64F A_orig = A.copy();
+            CDenseMatrix64F B_orig = B.copy();
+
+            CDenseMatrix64F X = new CDenseMatrix64F(i, 1);
+
+            assertTrue(CCommonOps.solve(A, B, X));
+
+            CDenseMatrix64F found = new CDenseMatrix64F(i, 1);
+
+            CCommonOps.mult(A, X, found);
+
+            assertTrue(CMatrixFeatures.isIdentical(B, found, 1e-8));
+
+            assertTrue(CMatrixFeatures.isIdentical(A, A_orig, 0));
+            assertTrue(CMatrixFeatures.isIdentical(B, B_orig, 0));
+        }
+
+        // rectangular
+        for (int i = 1; i < 10; i++) {
+            CDenseMatrix64F A = CRandomMatrices.createRandom(2*i, i, rand);
+            CDenseMatrix64F X = CRandomMatrices.createRandom(i, 1, rand);
+            CDenseMatrix64F B = new CDenseMatrix64F(2*i,1);
+
+            CCommonOps.mult(A,X,B);
+
+            CDenseMatrix64F A_orig = A.copy();
+            CDenseMatrix64F B_orig = B.copy();
+            CDenseMatrix64F X_expected = X.copy();
+
+            assertTrue(CCommonOps.solve(A, B, X));
+
+            assertTrue(CMatrixFeatures.isIdentical(X, X_expected, 1e-8));
+
+            assertTrue(CMatrixFeatures.isIdentical(B, B_orig, 0));
+            assertTrue(CMatrixFeatures.isIdentical(A, A_orig, 0));
+        }
+    }
+
+    @Test
+    public void det() {
+        CDenseMatrix64F A = new CDenseMatrix64F(3,3,true,
+                0.854634 , 0.445620,  0.082836 , 0.212460 , 0.623783 , 0.037631,
+                0.585408 , 0.768956 , 0.771067 , 0.897763 , 0.125793 , 0.432187,
+                0.303789 , 0.044497 , 0.151182 , 0.034471 , 0.526770 , 0.570333);
+
+        CDenseMatrix64F A_orig = A.copy();
+
+        Complex64F found = CCommonOps.det(A);
+        // from octave
+        Complex64F expected = new Complex64F(-0.40548 , 0.54188);
+
+        assertEquals(expected.real,found.real,1e-3);
+        assertEquals(expected.imaginary,found.imaginary,1e-3);
+
+        assertTrue(CMatrixFeatures.isIdentical(A,A_orig,0));
+    }
+
+    @Test
+    public void elementMultiply() {
+        CDenseMatrix64F in = CRandomMatrices.createRandom(5,7,-1,1,rand);
+        CDenseMatrix64F out = CRandomMatrices.createRandom(5,7,-1,1,rand);
+
+        Complex64F a = new Complex64F(1.2,-0.3);
+        Complex64F b = new Complex64F();
+        Complex64F found = new Complex64F();
+        Complex64F expected = new Complex64F();
+
+        CCommonOps.elementMultiply(in,a.real,a.imaginary,out);
+
+        for (int i = 0; i < in.numRows; i++) {
+            for (int j = 0; j < in.numCols; j++) {
+                in.get(i,j,b);
+                out.get(i,j,found);
+
+                ComplexMath64F.multiply(a,b,expected);
+
+                assertEquals(expected.real,found.real,1e-8);
+                assertEquals(expected.imaginary,found.imaginary,1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void elementDivide_right() {
+        CDenseMatrix64F in = CRandomMatrices.createRandom(5,7,-1,1,rand);
+        CDenseMatrix64F out = CRandomMatrices.createRandom(5,7,-1,1,rand);
+
+        Complex64F a = new Complex64F();
+        Complex64F b = new Complex64F(1.2,-0.3);
+        Complex64F found = new Complex64F();
+        Complex64F expected = new Complex64F();
+
+        CCommonOps.elementDivide(in,b.real,b.imaginary,out);
+
+        for (int i = 0; i < in.numRows; i++) {
+            for (int j = 0; j < in.numCols; j++) {
+                in.get(i,j,a);
+                out.get(i,j,found);
+
+                ComplexMath64F.divide(a,b,expected);
+
+                assertEquals(expected.real,found.real,1e-8);
+                assertEquals(expected.imaginary,found.imaginary,1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void elementDivide_left() {
+        CDenseMatrix64F in = CRandomMatrices.createRandom(5,7,-1,1,rand);
+        CDenseMatrix64F out = CRandomMatrices.createRandom(5,7,-1,1,rand);
+
+        Complex64F a = new Complex64F(1.2,-0.3);
+        Complex64F b = new Complex64F();
+        Complex64F found = new Complex64F();
+        Complex64F expected = new Complex64F();
+
+        CCommonOps.elementDivide(a.real,a.imaginary,in,out);
+
+        for (int i = 0; i < in.numRows; i++) {
+            for (int j = 0; j < in.numCols; j++) {
+                in.get(i,j,b);
+                out.get(i,j,found);
+
+                ComplexMath64F.divide(a,b,expected);
+
+                assertEquals(expected.real,found.real,1e-8);
+                assertEquals(expected.imaginary,found.imaginary,1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void elementMinReal() {
+        CDenseMatrix64F m = new CDenseMatrix64F(3,4);
+        for (int i = 0; i < m.data.length; i++) {
+            m.data[i] = -6 + i;
+        }
+
+        assertEquals(-6, CCommonOps.elementMinReal(m),1e-8);
+    }
+
+    @Test
+    public void elementMinImaginary() {
+        CDenseMatrix64F m = new CDenseMatrix64F(3,4);
+        for (int i = 0; i < m.data.length; i++) {
+            m.data[i] = -6 + i;
+        }
+
+        assertEquals(-5, CCommonOps.elementMinImaginary(m), 1e-8);
+    }
+
+    @Test
+    public void elementMaxReal() {
+        CDenseMatrix64F m = new CDenseMatrix64F(3,4);
+        for (int i = 0; i < m.data.length; i++) {
+            m.data[i] = -6 + i;
+        }
+
+        assertEquals(-6 + 11 * 2, CCommonOps.elementMaxReal(m), 1e-8);
+    }
+
+    @Test
+    public void elementMaxImaginary() {
+        CDenseMatrix64F m = new CDenseMatrix64F(3,4);
+        for (int i = 0; i < m.data.length; i++) {
+            m.data[i] = -6 + i;
+        }
+
+        assertEquals(-5 + 11 * 2, CCommonOps.elementMaxImaginary(m), 1e-8);
+    }
+
+    @Test
+    public void elementMaxMagnitude2() {
+        CDenseMatrix64F m = CRandomMatrices.createRandom(4,5,-2,2,rand);
+        DenseMatrix64F a = new DenseMatrix64F(m.numRows,m.numCols);
+
+        CCommonOps.magnitude(m,a);
+
+        double expected = CommonOps.elementMaxAbs(a);
+        expected *= expected;
+
+        double found = CCommonOps.elementMaxMagnitude2(m);
+
+        assertEquals(expected,found,1e-8);
+    }
+
+    @Test
+    public void setIdentity() {
+        CDenseMatrix64F a = CRandomMatrices.createRandom(4,5,-2,2,rand);
+
+        CCommonOps.setIdentity(a);
+
+        Complex64F c = new Complex64F();
+        for (int i = 0; i < a.numRows; i++) {
+            for (int j = 0; j < a.numCols; j++) {
+                a.get(i,j,c);
+                if( i == j ) {
+                    assertEquals(1,c.real,1e-8);
+                    assertEquals(0,c.imaginary,1e-8);
+                } else {
+                    assertEquals(0,c.real,1e-8);
+                    assertEquals(0,c.imaginary,1e-8);
+                }
+            }
+        }
+    }
+
+    @Test
+    public void extract_simplified() {
+        CDenseMatrix64F a = CRandomMatrices.createRandom(10,12,-2,2,rand);
+        CDenseMatrix64F b = CCommonOps.extract(a,2,5,3,8);
+
+        Complex64F ca = new Complex64F();
+        Complex64F cb = new Complex64F();
+
+        for (int i = 0; i < 3; i++) {
+            for (int j = 0; j < 5; j++) {
+                a.get(2+i,j+3,ca);
+                b.get(  i,j  , cb);
+
+                assertEquals(ca.real,cb.real,1e-8);
+                assertEquals(ca.imaginary,cb.imaginary,1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void extract_complex() {
+        CDenseMatrix64F a = CRandomMatrices.createRandom(10,12,-2,2,rand);
+        CDenseMatrix64F b = new CDenseMatrix64F(6,7);
+
+        Complex64F ca = new Complex64F();
+        Complex64F cb = new Complex64F();
+
+        CCommonOps.extract(a,2,5,3,7,b,1,2);
+
+        for (int i = 0; i < 3; i++) {
+            for (int j = 0; j < 4; j++) {
+                a.get(2+i,j+3,ca);
+                b.get(1 + i, j + 2, cb);
+
+                assertEquals(ca.real,cb.real,1e-8);
+                assertEquals(ca.imaginary,cb.imaginary,1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void columnsToVector() {
+        CDenseMatrix64F a = CRandomMatrices.createRandom(10,12,-2,2,rand);
+        CDenseMatrix64F v[] = CCommonOps.columnsToVector(a,null);
+
+        Complex64F ca = new Complex64F();
+        Complex64F cc = new Complex64F();
+
+        for (int i = 0; i < a.numCols; i++) {
+            CDenseMatrix64F c = v[i];
+
+            assertEquals(c.numRows,a.numRows);
+            assertEquals(1,c.numCols);
+
+            for (int j = 0; j < a.numRows; j++) {
+                a.get(j,i,ca);
+                c.get(j,0,cc);
+
+                EjmlUnitTests.assertEquals(ca,cc,1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void elementMaxAbs() {
+        CDenseMatrix64F a = CRandomMatrices.createRandom(10,12,-2,2,rand);
+        a.set(5,6,10,12);
+
+        double expected = Math.sqrt(10*10 + 12*12);
+        double found = CCommonOps.elementMaxAbs(a);
+        assertEquals(expected,found,1e-8);
+    }
+}
\ No newline at end of file
diff --git a/main/denseC64/test/org/ejml/ops/TestCMatrixFeatures.java b/main/denseC64/test/org/ejml/ops/TestCMatrixFeatures.java
new file mode 100644
index 0000000..91fc467
--- /dev/null
+++ b/main/denseC64/test/org/ejml/ops/TestCMatrixFeatures.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.alg.dense.mult.CVectorVectorMult;
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.data.Complex64F;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestCMatrixFeatures {
+
+    Random rand = new Random(234);
+
+    @Test
+    public void hasUncountable() {
+        CDenseMatrix64F a = new CDenseMatrix64F(4,4);
+
+        // check a negative case first
+        assertFalse(CMatrixFeatures.hasUncountable(a));
+
+        // check two positve cases with different types of uncountables
+        a.set(2,2,Double.NaN,0);
+        assertTrue(CMatrixFeatures.hasUncountable(a));
+
+        a.set(2,2,Double.POSITIVE_INFINITY,0);
+        assertTrue(CMatrixFeatures.hasUncountable(a));
+    }
+
+    @Test
+    public void hasNaN() {
+        CDenseMatrix64F m = new CDenseMatrix64F(3,3);
+        assertFalse(CMatrixFeatures.hasNaN(m));
+
+        m.set(1,2,-Double.NaN,0);
+        assertTrue(CMatrixFeatures.hasNaN(m));
+    }
+
+    @Test
+    public void isEquals() {
+        CDenseMatrix64F m = CRandomMatrices.createRandom(3,4,-1,1,rand);
+        CDenseMatrix64F n = m.copy();
+
+        assertTrue(CMatrixFeatures.isEquals(m,n));
+
+        n.set(2,1,-0.5,-0.6);
+        assertFalse(CMatrixFeatures.isEquals(m,n));
+
+        m.set(2,1,Double.NaN,1);
+        n.set(2,1,Double.NaN,1);
+        assertFalse(CMatrixFeatures.isEquals(m,n));
+        m.set(2,1,Double.POSITIVE_INFINITY,1);
+        n.set(2,1,Double.POSITIVE_INFINITY,1);
+        assertTrue(CMatrixFeatures.isEquals(m,n));
+    }
+
+    @Test
+    public void isEquals_tol() {
+        CDenseMatrix64F m = CRandomMatrices.createRandom(3,4,-1,1,rand);
+        CDenseMatrix64F n = m.copy();
+
+        assertTrue(CMatrixFeatures.isEquals(m,n,1e-6));
+
+        n.data[4] += 1e-25;
+        assertTrue(CMatrixFeatures.isEquals(m,n,1e-6));
+
+        n.data[4] += 1e-2;
+        assertFalse(CMatrixFeatures.isEquals(m,n,1e-6));
+
+        m.set(2,1,Double.NaN,1);
+        n.set(2,1,Double.NaN,1);
+        assertFalse(CMatrixFeatures.isEquals(m,n,1e-6));
+        m.set(2,1,Double.POSITIVE_INFINITY,1);
+        n.set(2,1,Double.POSITIVE_INFINITY,1);
+        assertFalse(CMatrixFeatures.isEquals(m,n,1e-6));
+    }
+
+    @Test
+    public void isIdentical() {
+
+        double values[] = new double[]{1.0,Double.NaN,Double.POSITIVE_INFINITY,Double.NEGATIVE_INFINITY};
+
+        for( int i = 0; i < values.length; i++ ) {
+            for( int j = 0; j < values.length; j++ ) {
+                checkIdentical(values[i],values[j],1e-8,i==j);
+            }
+        }
+
+        checkIdentical(1.0,1.5,1e-8,false);
+        checkIdentical(1.5,1.0,1e-8,false);
+        checkIdentical(1.0,1.0000000001,1e-8,true);
+        checkIdentical(1.0,Double.NaN,1e-8,false);
+        checkIdentical(Double.NaN,1.0,1e-8,false);
+    }
+
+    private void checkIdentical( double valA , double valB , double tol , boolean expected ) {
+        CDenseMatrix64F A = new CDenseMatrix64F(2,2);
+        CDenseMatrix64F B = new CDenseMatrix64F(2,2);
+        CCommonOps.fill(A, valA,0);
+        CCommonOps.fill(B, valB,0);
+
+        assertEquals(expected,CMatrixFeatures.isIdentical(A,B,tol));
+
+        CCommonOps.fill(A, 0,valA);
+        CCommonOps.fill(B, 0,valB);
+
+        assertEquals(expected,CMatrixFeatures.isIdentical(A,B,tol));
+    }
+
+    @Test
+    public void isIdentity() {
+        CDenseMatrix64F m = CCommonOps.diag(1,0,1,0,1,0);
+
+        assertTrue(CMatrixFeatures.isIdentity(m,1e-8));
+
+        m.setImaginary(0,0,1e-12);
+        assertTrue(CMatrixFeatures.isIdentity(m, 1e-8));
+        m.setReal(0, 0, 1 + 1e-12);
+        assertTrue(CMatrixFeatures.isIdentity(m,1e-8));
+
+        assertFalse(CMatrixFeatures.isIdentity(m, 1e-15));
+        assertFalse(CMatrixFeatures.isIdentity(m, 1e-15));
+
+        m.setImaginary(1,0,1e-12);
+        assertTrue(CMatrixFeatures.isIdentity(m,1e-8));
+        m.setReal(1,0,1e-12);
+        assertTrue(CMatrixFeatures.isIdentity(m,1e-8));
+
+        assertFalse(CMatrixFeatures.isIdentity(m,1e-15));
+        assertFalse(CMatrixFeatures.isIdentity(m,1e-15));
+    }
+
+    @Test
+    public void isHermitian() {
+        CDenseMatrix64F A = new CDenseMatrix64F(new double[][]{{1,1.1,2,2.1},{2,-2.1,3,3.1}});
+
+        assertTrue(CMatrixFeatures.isHermitian(A, 1e-8));
+
+        A.set(0,1,5,6);
+
+        assertFalse(CMatrixFeatures.isHermitian(A, 1e-8));
+    }
+
+    @Test
+    public void isUnitary() {
+        // create a reflector since it's unitary
+        CDenseMatrix64F u = CRandomMatrices.createRandom(5,1,rand);
+        Complex64F dot = new Complex64F();
+        CVectorVectorMult.innerProdH(u, u,dot);
+        double gamma = 2.0/dot.real;
+        CDenseMatrix64F A = CSpecializedOps.householder(u,gamma);
+
+        assertTrue(CMatrixFeatures.isUnitary(A, 1e-6f));
+
+        // try a negative case now
+        A.set(0,1,495,400);
+
+        assertFalse(CMatrixFeatures.isUnitary(A, 1e-6f));
+
+        A.set(0,1,Double.NaN,Double.NaN);
+
+        assertFalse(CMatrixFeatures.isUnitary(A, 1e-6f));
+    }
+
+    /**
+     * Check some trial cases.
+     */
+    @Test
+    public void isPositiveDefinite() {
+        CDenseMatrix64F a = new CDenseMatrix64F(2,2,true,2,0,0,0,0,0,2,0);
+        CDenseMatrix64F b = new CDenseMatrix64F(2,2,true,0,0,1,0,1,0,0,0);
+        CDenseMatrix64F c = new CDenseMatrix64F(2,2);
+
+        assertTrue(CMatrixFeatures.isPositiveDefinite(a));
+        assertFalse(CMatrixFeatures.isPositiveDefinite(b));
+        assertFalse(CMatrixFeatures.isPositiveDefinite(c));
+
+        // make sure the input isn't modified
+        assertEquals(2,a.getReal(0, 0),1e-8);
+        assertEquals(2,a.getReal(1,1),1e-8);
+    }
+}
diff --git a/main/denseC64/test/org/ejml/ops/TestCNormOps.java b/main/denseC64/test/org/ejml/ops/TestCNormOps.java
new file mode 100644
index 0000000..c6e6e8e
--- /dev/null
+++ b/main/denseC64/test/org/ejml/ops/TestCNormOps.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.data.Complex64F;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestCNormOps {
+
+    Random rand = new Random(234);
+
+    @Test
+    public void normF() {
+        CDenseMatrix64F a = CRandomMatrices.createRandom(1,7,rand);
+
+        Complex64F b = new Complex64F();
+        double total = 0;
+        for (int i = 0; i < a.numRows; i++) {
+            for (int j = 0; j < a.numCols; j++) {
+                a.get(i,j,b);
+                total += b.real*b.real + b.imaginary*b.imaginary;
+            }
+        }
+
+        double expected = Math.sqrt(total);
+        double found = CNormOps.normF(a);
+
+        assertEquals(expected,found,1e-8);
+    }
+}
\ No newline at end of file
diff --git a/main/denseC64/test/org/ejml/ops/TestCRandomMatrices.java b/main/denseC64/test/org/ejml/ops/TestCRandomMatrices.java
new file mode 100644
index 0000000..c3035cc
--- /dev/null
+++ b/main/denseC64/test/org/ejml/ops/TestCRandomMatrices.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.data.CDenseMatrix64F;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestCRandomMatrices {
+    
+    Random rand = new Random(234);
+
+    @Test
+    public void createRandom_min_max() {
+        CDenseMatrix64F A = CRandomMatrices.createRandom(30,20,-1,1,rand);
+
+        checkRandomRange(A);
+    }
+
+    @Test
+    public void setRandom() {
+        CDenseMatrix64F A = new CDenseMatrix64F(5,4);
+
+        CRandomMatrices.setRandom(A,rand);
+
+        checkRandom1(A);
+    }
+
+    private void checkRandom1(CDenseMatrix64F a) {
+        assertEquals(5, a.numRows);
+        assertEquals(4, a.numCols);
+
+        double totalReal = 0;
+        double totalImg = 0;
+        for( int i = 0; i < a.numRows; i++ ) {
+            for( int j = 0; j < a.numCols; j++ ) {
+                double real = a.getReal(i,j);
+                double img = a.getImaginary(i, j);
+
+                assertTrue( real >= 0);
+                assertTrue( real <= 1);
+                totalReal += real;
+
+                assertTrue( img >= 0);
+                assertTrue( img <= 1);
+                totalImg += img;
+            }
+        }
+
+        assertTrue(totalReal>0);
+        assertTrue(totalImg>0);
+    }
+
+    @Test
+    public void setRandom_min_max() {
+        CDenseMatrix64F A = new CDenseMatrix64F(30,20);
+        CRandomMatrices.setRandom(A,-1,1,rand);
+
+        checkRandomRange(A);
+    }
+
+    private void checkRandomRange(CDenseMatrix64F a) {
+        assertEquals(30, a.numRows);
+        assertEquals(20, a.numCols);
+
+        int numRealNeg = 0;
+        int numRealPos = 0;
+        int numImgNeg = 0;
+        int numImgPos = 0;
+
+        for( int i = 0; i < a.numRows; i++ ) {
+            for( int j = 0; j < a.numCols; j++ ) {
+                double real = a.getReal(i,j);
+                double img = a.getImaginary(i,j);
+
+                if( real < 0 )
+                    numRealNeg++;
+                else
+                    numRealPos++;
+
+                if( Math.abs(real) > 1 )
+                    fail("Out of range");
+
+                if( img < 0 )
+                    numImgNeg++;
+                else
+                    numImgPos++;
+
+                if( Math.abs(img) > 1 )
+                    fail("Out of range");
+            }
+        }
+
+        assertTrue(numRealNeg>0);
+        assertTrue(numRealPos>0);
+
+        assertTrue(numImgNeg>0);
+        assertTrue(numImgPos>0);
+    }
+
+
+    @Test
+    public void createHermPosDef() {
+        for( int i = 1; i < 20; i++ ) {
+            CDenseMatrix64F A = CRandomMatrices.createHermPosDef(i, rand);
+
+            assertTrue(CMatrixFeatures.isPositiveDefinite(A));
+        }
+    }
+
+    @Test
+    public void createHermitian() {
+        CDenseMatrix64F A = CRandomMatrices.createHermitian(10, -1, 1, rand);
+
+        assertTrue(CMatrixFeatures.isHermitian(A, 1e-8));
+
+        // see if it has the expected range of elements
+        double min = CCommonOps.elementMinReal(A);
+        double max = CCommonOps.elementMaxReal(A);
+
+        assertTrue(min < 0 && min >= -1);
+        assertTrue(max > 0 && max <=  1);
+
+        min = CCommonOps.elementMinImaginary(A);
+        max = CCommonOps.elementMaxImaginary(A);
+
+        assertTrue(min < 0 && min >= -1);
+        assertTrue(max > 0 && max <=  1);
+    }
+//
+//    @Test
+//    public void createUpperTriangle() {
+//        for( int hess = 0; hess < 3; hess++ ) {
+//            CDenseMatrix64F A = CRandomMatrices.createUpperTriangle(10,hess,-1,1,rand);
+//
+//            assertTrue(MatrixFeatures.isUpperTriangle(A,hess,1e-8));
+//
+//            // quick sanity check to make sure it could be proper
+//            assertTrue(A.get(hess,0) != 0 );
+//
+//            // see if it has the expected range of elements
+//            double min = CommonOps.elementMin(A);
+//            double max = CommonOps.elementMax(A);
+//
+//            assertTrue(min < 0 && min >= -1);
+//            assertTrue(max > 0 && max <= 1);
+//        }
+//    }
+}
diff --git a/main/denseC64/test/org/ejml/ops/TestCSpecializedOps.java b/main/denseC64/test/org/ejml/ops/TestCSpecializedOps.java
new file mode 100644
index 0000000..4fdb945
--- /dev/null
+++ b/main/denseC64/test/org/ejml/ops/TestCSpecializedOps.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.alg.dense.mult.CVectorVectorMult;
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.data.Complex64F;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestCSpecializedOps {
+
+    Random rand = new Random(234);
+
+    @Test
+    public void pivotMatrix() {
+        int pivots[] = new int[]{1,0,3,2};
+
+        CDenseMatrix64F A = CRandomMatrices.createRandom(4,4,-1,-1,rand);
+        CDenseMatrix64F P = CSpecializedOps.pivotMatrix(null,pivots,4,false);
+        CDenseMatrix64F Pt = CSpecializedOps.pivotMatrix(null,pivots,4,true);
+
+        CDenseMatrix64F B = new CDenseMatrix64F(4,4);
+
+        // see if it swapped the rows
+        CCommonOps.mult(P, A, B);
+
+        for( int i = 0; i < 4; i++ ) {
+            int index = pivots[i];
+            for( int j = 0; j < 4; j++ ) {
+                double real = A.getReal(index,j);
+                double img = A.getImaginary(index, j);
+
+                assertEquals(real,B.getReal(i, j),1e-8);
+                assertEquals(img,B.getImaginary(i, j),1e-8);
+            }
+        }
+
+        // see if it transposed
+        CCommonOps.transpose(P,B);
+
+        assertTrue(CMatrixFeatures.isIdentical(B, Pt, 1e-8));
+    }
+
+
+    @Test
+    public void elementDiagMaxMagnitude2() {
+        CDenseMatrix64F A = CRandomMatrices.createRandom(4,5,-1,1,rand);
+
+        Complex64F a = new Complex64F();
+
+        double expected = 0;
+        for (int i = 0; i < 4; i++) {
+            A.get(i,i,a);
+            if( a.getMagnitude2() > expected )
+                expected = a.getMagnitude2();
+        }
+
+        double found = CSpecializedOps.elementDiagMaxMagnitude2(A);
+        assertEquals(expected, found, 1e-8);
+    }
+
+    @Test
+    public void qualityTriangular() {
+        CDenseMatrix64F A = CRandomMatrices.createRandom(4,4,-1,-1,rand);
+
+        double max = Math.sqrt(CSpecializedOps.elementDiagMaxMagnitude2(A));
+
+        Complex64F a = new Complex64F();
+        Complex64F tmp = new Complex64F();
+        Complex64F total = new Complex64F(1,0);
+        for (int i = 0; i < 4; i++) {
+            A.get(i,i,a);
+            a.real /= max;
+            a.imaginary /= max;
+
+            ComplexMath64F.multiply(total,a,tmp);
+            total.set(tmp);
+        }
+        double expected = total.getMagnitude();
+
+        double found = CSpecializedOps.qualityTriangular(A);
+        assertEquals(expected,found,1e-8);
+    }
+
+    @Test
+    public void householder() {
+        CDenseMatrix64F U = CRandomMatrices.createRandom(6,1,rand);
+        double gamma = 1.6;
+
+        // Q = I - gamma*U*U^H
+        CDenseMatrix64F I = CCommonOps.identity(6);
+        CDenseMatrix64F UUt = new CDenseMatrix64F(6,6);
+        CDenseMatrix64F expected = new CDenseMatrix64F(6,6);
+
+        CVectorVectorMult.outerProdH(U, U, UUt);
+        CCommonOps.elementMultiply(UUt,gamma,0,UUt);
+        CCommonOps.subtract(I,UUt,expected);
+
+        CDenseMatrix64F found = CSpecializedOps.householder(U,gamma);
+
+        assertTrue(CMatrixFeatures.isIdentical(expected,found,1e-8));
+    }
+
+    @Test
+    public void householderVector() {
+        CDenseMatrix64F x = CRandomMatrices.createRandom(6, 1, rand);
+
+//        x.set(0,0,0,0);
+
+        CDenseMatrix64F u = CSpecializedOps.householderVector(x);
+        double gamma = 2.0/Math.pow(CNormOps.normF(u), 2.0);
+
+        // Q = I - gamma*U*U^H
+        CDenseMatrix64F I = CCommonOps.identity(6);
+        CDenseMatrix64F UUt = new CDenseMatrix64F(6,6);
+        CDenseMatrix64F Q = new CDenseMatrix64F(6,6);
+
+        CVectorVectorMult.outerProdH(u, u, UUt);
+        CCommonOps.elementMultiply(UUt,gamma,0,UUt);
+        CCommonOps.subtract(I,UUt,Q);
+
+        CDenseMatrix64F found = new CDenseMatrix64F(x.numRows,x.numCols);
+        CCommonOps.mult(Q,x,found);
+
+        Complex64F c = new Complex64F();
+        found.get(0,0,c);
+        assertTrue(c.real != 0);
+        assertTrue(c.imaginary != 0 );
+
+        for (int i = 1; i < found.numRows; i++) {
+            found.get(i,0,c);
+            assertEquals(0,c.real, 1e-8);
+            assertEquals(0,c.imaginary,1e-8);
+        }
+    }
+}
\ No newline at end of file
diff --git a/main/denseC64/todo.txt b/main/denseC64/todo.txt
new file mode 100644
index 0000000..d81396a
--- /dev/null
+++ b/main/denseC64/todo.txt
@@ -0,0 +1,7 @@
+- Cholesky
+- SVD
+- Symmetric EVD
+- General EVD
+
+- How to handle complex eigenvectors in a real matrix?
+  * Use reflection?
\ No newline at end of file
diff --git a/main/equation/build.gradle b/main/equation/build.gradle
new file mode 100644
index 0000000..676319b
--- /dev/null
+++ b/main/equation/build.gradle
@@ -0,0 +1,11 @@
+dependencies {
+    compile project(':main:core')
+    compile project(':main:dense64')
+    compile project(':main:simple')
+}
+
+idea {
+    module {
+        name = "EJML Equation"
+    }
+}
\ No newline at end of file
diff --git a/main/equation/src/org/ejml/equation/Equation.java b/main/equation/src/org/ejml/equation/Equation.java
new file mode 100644
index 0000000..a4208cc
--- /dev/null
+++ b/main/equation/src/org/ejml/equation/Equation.java
@@ -0,0 +1,1220 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.equation;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.simple.SimpleMatrix;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import static org.ejml.equation.TokenList.Type;
+
+/**
+ * <p>
+ * Equation allows the user to manipulate matrices in a more compact symbolic way, similar to Matlab and Octave.
+ * Aliases are made to Matrices and scalar values which can then be manipulated by specifying an equation in a string.
+ * These equations can either be "pre-compiled" [1] into a sequence of operations or immediately executed.  While the
+ * former is more verbose, when dealing with small matrices it significantly faster and runs close to the speed of
+ * normal hand written code.
+ * </p>
+ * <p>
+ * Each string represents a single line and must have one and only one assignment '=' operator.  Temporary variables
+ * are handled transparently to the user.  Temporary variables are declared at compile time, but resized at runtime.
+ * If the inputs are not resized and the code is precompiled, then no new memory will be declared.  When a matrix
+ * is assigned the results of an operation it is resized so that it can store the results.
+ * </p>
+ * <p>
+ * The compiler currently produces simplistic code.  For example, if it encounters the following equation "a = b*c' it
+ * will not invoke multTransB(b,c,a), but will explicitly transpose c and then call mult().  In the future it
+ * will recognize such short cuts.
+ * </p>
+ *
+ * <p>
+ * Usage example:
+ * <pre>
+ * Equation eq = new Equation();
+ * eq.alias(x,"x", P,"P", Q,"Q");
+ *
+ * eq.process("x = F*x");
+ * eq.process("P = F*P*F' + Q");
+ * </pre>
+ * Which will modify the matrices 'x' amd 'P'.  Sub-matrices and inline matrix construction is also
+ * supported.
+ * <pre>
+ * eq.process("x = [2 1 0; 0 1 3;4 5 6]*x");
+ * eq.process("x(1:3,5:9) = [a ; b]*2");
+ * </pre>
+ * </p>
+ *
+ * <p>
+ * To pre-compile one of the above lines, do the following instead:
+ * <pre>
+ * Sequence predictX = eq.compile("x = F*x");
+ * predictX.perform();
+ * </pre>
+ * Then you can invoke it as much as you want without the "expensive" compilation step.  If you are dealing with
+ * larger matrices (e.g. 100 by 100) then it is likely that the compilation step has an insignificant runtime
+ * cost.
+ * </p>
+ *
+ * <p>
+ * Variables can also be lazily declared and their type inferred under certain conditions.  For example:
+ * <pre>
+ * eq.alias(A,"A", B,"B");
+ * eq.process("C = A*B");
+ * DenseMatrix64F C = eq.lookupMatrix("C");
+ * </pre>
+ * In this case 'C' was lazily declared.  To access the variable, or any others, you can use one of the lookup*()
+ * functions.
+ * </p>
+ *
+ * <p>
+ * Sometimes you don't get the results you expect and it can be helpful to print out the tokens and which operations
+ * the compiler selected.  To do this set the second parameter to eq.compile() or eq.process() to true:
+ * <pre>
+ * Code:
+ * eq.process("C=2.1*B'*A",true);
+ *
+ * Output:
+ * Parsed tokens:
+ * ------------
+ * Word:C
+ * ASSIGN
+ * VarSCALAR
+ * TIMES
+ * VarMATRIX
+ * TRANSPOSE
+ * TIMES
+ * VarMATRIX
+ *
+ * Operations:
+ * ------------
+ * transpose-m
+ * multiply-ms
+ * multiply-mm
+ * copy-mm
+ * </pre>
+ * </p>
+ *
+ * <p>
+ * <h2>Built in Constants</h2>
+ * <pre>
+ * pi = Math.PI
+ * e  = Math.E
+ * </pre>
+ * </p>
+ *
+ * <p>
+ * <h2>Supported functions</h2>
+ * <pre>
+ * eye(N)       Create an identity matrix which is N by N.
+ * eye(A)       Create an identity matrix which is A.numRows by A.numCols
+ * normF(A)     Frobenius normal of the matrix.
+ * det(A)       Determinant of the matrix
+ * inv(A)       Inverse of a matrix
+ * pinv(A)      Pseudo-inverse of a matrix
+ * rref(A)      Reduced row echelon form of A
+ * trace(A)     Trace of the matrix
+ * zeros(r,c)   Matrix full of zeros with r rows and c columns.
+ * ones(r,c)    Matrix full of ones with r rows and c columns.
+ * diag(A)      If a vector then returns a square matrix with diagonal elements filled with vector
+ * diag(A)      If a matrix then it returns the diagonal elements as a column vector
+ * dot(A,B)     Returns the dot product of two vectors as a double.  Does not work on general matrices.
+ * solve(A,B)   Returns the solution X from A*X = B.
+ * kron(A,B)    Kronecker product
+ * abs(A)       Absolute value of A.
+ * max(A)       Element with the largest value in A.
+ * min(A)       Element with the smallest value in A.
+ * pow(a,b)     Computes a to the power of b.  Can also be invoked with "a^b" scalars only.
+ * sqrt(a)      Computes the square root of a.
+ * sin(a)       Math.sin(a) for scalars only
+ * cos(a)       Math.cos(a) for scalars only
+ * atan(a)      Math.atan(a) for scalars only
+ * atan2(a,b)   Math.atan2(a,b) for scalars only
+ * exp(a)       Math.exp(a) for scalars and element-wise matrices
+ * log(a)       Math.log(a) for scalars and element-wise matrices
+ * </pre>
+ * </p>
+ *
+ * <p>
+ * <h2>Supported operations</h2>
+ * <pre>
+ * '*'        multiplication (Matrix-Matrix, Scalar-Matrix, Scalar-Scalar)
+ * '+'        addition (Matrix-Matrix, Scalar-Matrix, Scalar-Scalar)
+ * '-'        subtraction (Matrix-Matrix, Scalar-Matrix, Scalar-Scalar)
+ * '/'        divide (Matrix-Scalar, Scalar-Scalar)
+ * '/'        matrix solve "x=b/A" is equivalent to x=solve(A,b) (Matrix-Matrix)
+ * '^'        Scalar power.  a^b is a to the power of b.
+ * '\'        left-divide.  Same as divide but reversed.  e.g. x=A\b is x=solve(A,b)
+ * '.*'       element-wise multiplication (Matrix-Matrix)
+ * './'       element-wise division (Matrix-Matrix)
+ * '.^'       element-wise power. (scalar-scalar) (matrix-matrix) (scalar-matrix) (matrix-scalar)
+ * '''        matrix transpose
+ * '='        assignment by value (Matrix-Matrix, Scalar-Scalar)
+ * </pre>
+ * Order of operations:  [ ' ] precedes [ ^ .^ ] precedes [ *  /  .*  ./ ] precedes [ +  - ]
+ * </p>
+ *
+ * <p>
+ * <h2>Specialized submatrix and matrix construction syntax</h2>
+ * <pre>
+ * Extracts a sub-matrix from A with rows 1 to 10 (inclusive) and column 3.
+ *               A(1:10,3)
+ * Extracts a sub-matrix from A with rows 2 to numRows-1 (inclusive) and all the columns.
+ *               A(2:,:)
+ * Will concat A and B along their columns and then concat the result with  C along their rows.
+ *                [A,B;C]
+ * Defines a 3x2 matrix.
+ *            [1 2; 3 4; 4 5]
+ * You can also perform operations inside:
+ *            [[2 3 4]';[4 5 6]']
+ * Will assign B to the sub-matrix in A.
+ *             A(1:3,4:8) = B
+ * </pre>
+ * </p>
+ *
+ * <p>
+ * Footnotes:
+ * <pre>
+ * [1] It is not compiled into Java byte-code, but into a sequence of operations stored in a List.
+ * </pre>
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+// TODO Change parsing so that operations specify a pattern.
+// TODO Recycle temporary variables
+// TODO intelligently handle identity matrices
+public class Equation {
+    HashMap<String,Variable> variables = new HashMap<String, Variable>();
+
+    // storage for a single word in the tokenizer
+    char storage[] = new char[1024];
+
+    ManagerFunctions functions = new ManagerFunctions();
+
+    public Equation() {
+        alias(Math.PI,"pi");
+        alias(Math.E,"e");
+    }
+
+    /**
+     * Adds a new Matrix variable.  If one already has the same name it is written over.
+     *
+     * While more verbose for multiple variables, this function doesn't require new memory be declared
+     * each time it's called.
+     *
+     * @param variable Matrix which is to be assigned to name
+     * @param name The name of the variable
+     */
+    public void alias( DenseMatrix64F variable , String name ) {
+        if( isReserved(name))
+            throw new RuntimeException("Reserved word or contains a reserved character");
+        VariableMatrix old = (VariableMatrix)variables.get(name);
+        if( old == null ) {
+            variables.put(name, new VariableMatrix(variable));
+        }else {
+            old.matrix = variable;
+        }
+    }
+
+    public void alias( SimpleMatrix variable , String name ) {
+        alias(variable.getMatrix(),name);
+    }
+
+    /**
+     * Adds a new floating point variable. If one already has the same name it is written over.
+     * @param value Value of the number
+     * @param name Name in code
+     */
+    public void alias( double value , String name ) {
+        if( isReserved(name))
+            throw new RuntimeException("Reserved word or contains a reserved character");
+
+        VariableDouble old = (VariableDouble)variables.get(name);
+        if( old == null ) {
+            variables.put(name, new VariableDouble(value));
+        }else {
+            old.value = value;
+        }
+    }
+
+    /**
+     * Adds a new integer variable. If one already has the same name it is written over.
+     * @param value Value of the number
+     * @param name Name in code
+     */
+    public void alias( int value , String name ) {
+        if( isReserved(name))
+            throw new RuntimeException("Reserved word or contains a reserved character");
+
+        VariableInteger old = (VariableInteger)variables.get(name);
+        if( old == null ) {
+            variables.put(name, new VariableInteger(value));
+        }else {
+            old.value = value;
+        }
+    }
+
+    /**
+     * Creates multiple aliases at once.
+     */
+    public void alias( Object ...args ) {
+        if( args.length % 2 == 1 )
+            throw new RuntimeException("Even number of arguments expected");
+
+        for (int i = 0; i < args.length; i += 2) {
+            if( args[i].getClass() == Integer.class ) {
+                alias(((Integer)args[i]).intValue(),(String)args[i+1]);
+            } else if( args[i].getClass() == Double.class ) {
+                alias(((Double)args[i]).doubleValue(),(String)args[i+1]);
+            } else if( args[i].getClass() == DenseMatrix64F.class ) {
+                alias((DenseMatrix64F)args[i],(String)args[i+1]);
+            } else if( args[i].getClass() == SimpleMatrix.class ) {
+                alias((SimpleMatrix)args[i],(String)args[i+1]);
+            } else {
+                throw new RuntimeException("Unknown value type "+args[i]);
+            }
+        }
+    }
+
+    public Sequence compile( String equation ) {
+        return compile(equation,false);
+    }
+
+    /**
+     * Parses the equation and compiles it into a sequence which can be executed later on
+     * @param equation String in simple equation format.
+     * @param debug if true it will print out debugging information
+     * @return Sequence of operations on the variables
+     */
+    public Sequence compile( String equation , boolean debug ) {
+
+        ManagerTempVariables managerTemp = new ManagerTempVariables();
+        functions.setManagerTemp(managerTemp);
+
+        Sequence sequence = new Sequence();
+        TokenList tokens = extractTokens(equation,managerTemp);
+
+        if( tokens.size() < 3 )
+            throw new RuntimeException("Too few tokens");
+
+        if( debug ) {
+            System.out.println("Parsed tokens:\n------------");
+            tokens.print();
+            System.out.println();
+        }
+
+        TokenList.Token t0 = tokens.getFirst();
+
+        // Get the results variable
+        if( t0.getType() != Type.VARIABLE && t0.getType() != Type.WORD )
+            throw new RuntimeException("Expected variable name first.  Not "+t0);
+
+        // see if it is assign or a range
+        List<Variable> range = parseAssignRange(sequence, tokens, t0);
+
+        TokenList.Token t1 = t0.next;
+        if( t1.getType() != Type.SYMBOL || t1.getSymbol() != Symbol.ASSIGN )
+            throw new RuntimeException("Expected assign next");
+
+        // Parse the right side of the equation
+        TokenList tokensRight = tokens.extractSubList(t1.next,tokens.last);
+        checkForUnknownVariables(tokensRight);
+        handleParentheses( tokensRight ,sequence);
+
+        // see if it needs to be parsed more
+        if( tokensRight.size() != 1 )
+            throw new RuntimeException("BUG");
+        if( tokensRight.getLast().getType() != Type.VARIABLE )
+            throw new RuntimeException("BUG the last token must be a variable");
+
+        // copy the results into the output
+        Variable variableRight = tokensRight.getFirst().getVariable();
+        if( range == null) {
+            // no range, so copy results into the entire output matrix
+            Variable output = createVariableInferred(t0, variableRight);
+            sequence.addOperation(Operation.copy(variableRight, output));
+        } else {
+            // a sub-matrix range is specified.  Copy into that inner part
+            if( t0.getType() == Type.WORD ) {
+                throw new RuntimeException("Can't do lazy variable initialization with submatrices. "+t0.getWord());
+            }
+            sequence.addOperation(Operation.copy(variableRight, t0.getVariable(),range));
+        }
+
+        if( debug ) {
+            System.out.println("Operations:\n------------");
+            for (int i = 0; i < sequence.operations.size(); i++) {
+                System.out.println(sequence.operations.get(i).name());
+            }
+        }
+
+        return sequence;
+    }
+
+    /**
+     * Examines the list of variables for any unknown variables and throws an exception if one is found
+     */
+    private void checkForUnknownVariables(TokenList tokens) {
+        TokenList.Token t = tokens.getFirst();
+        while( t != null ) {
+            if( t.getType() == Type.WORD )
+                throw new RuntimeException("Unknown variable on right side. "+t.getWord());
+            t = t.next;
+        }
+    }
+
+    /**
+     * Infer the type of and create a new output variable using the results from the right side of the equation.
+     * If the type is already known just return that.
+     */
+    private Variable createVariableInferred(TokenList.Token t0, Variable variableRight) {
+        Variable result;
+
+        if( t0.getType() == Type.WORD ) {
+            switch( variableRight.getType()) {
+                case MATRIX:
+                    alias(new DenseMatrix64F(1,1),t0.getWord());
+                    break;
+
+                case SCALAR:
+                    if( variableRight instanceof VariableInteger) {
+                        alias(0,t0.getWord());
+                    } else {
+                        alias(1.0,t0.getWord());
+                    }
+                    break;
+
+                default:
+                    throw new RuntimeException("Unknown type");
+            }
+
+            result = variables.get(t0.getWord());
+        } else {
+            result = t0.getVariable();
+        }
+        return result;
+    }
+
+    /**
+     * See if a range for assignment is specified.  If so return the range, otherwise return null
+     */
+    private List<Variable> parseAssignRange(Sequence sequence, TokenList tokens, TokenList.Token t0) {
+        List<Variable> range;
+        TokenList.Token t1 = t0.next;
+        if( t1.getType() == Type.SYMBOL ) {
+            if( t1.symbol == Symbol.ASSIGN ) {
+                range = null; // copy into the entire matrix
+            } else if( t1.symbol == Symbol.PAREN_LEFT ) {
+                // copy into a specific area
+                range = new ArrayList<Variable>();
+
+                // find the right parentheses
+                TokenList.Token t2 = t1.next;
+                while( t2 != null && t2.symbol != Symbol.PAREN_RIGHT ) {
+                    t2 = t2.next;
+                }
+
+                if( t2 == null )
+                    throw new RuntimeException("Could not find closing )");
+
+                TokenList.Token n = t2.next;
+                TokenList sublist = tokens.extractSubList(t1,t2);
+
+                // remove parentheses
+                sublist.remove(sublist.first);
+                sublist.remove(sublist.last);
+
+                // parse the range
+                parseSubmatrixRange(sublist, sequence, range);
+
+                t1 = n;
+                if( t1.symbol != Symbol.ASSIGN )
+                    throw new RuntimeException("Expected assign after sub-matrix");
+            } else {
+                throw new RuntimeException("Expected assign or submatrix");
+            }
+        } else {
+            throw new RuntimeException("Expecting symbol after first variable");
+        }
+        return range;
+    }
+
+    /**
+     * Searches for pairs of parentheses and processes blocks inside of them.  Embedded parentheses are handled
+     * with no problem.  On output only a single token should be in tokens.
+     * @param tokens List of parsed tokens
+     * @param sequence Sequence of operators
+     */
+    protected void handleParentheses( TokenList tokens, Sequence sequence ) {
+        List<TokenList.Token> left = new ArrayList<TokenList.Token>();
+
+        // find all of them
+        TokenList.Token t = tokens.first;
+        while( t != null ) {
+            TokenList.Token next = t.next;
+            if( t.getType() == Type.SYMBOL ) {
+                if( t.getSymbol() == Symbol.PAREN_LEFT  )
+                    left.add(t);
+                else if( t.getSymbol() == Symbol.PAREN_RIGHT ) {
+                    if( left.isEmpty() )
+                        throw new RuntimeException(") found with no matching (");
+
+                    TokenList.Token a = left.remove(left.size()-1);
+
+                    // remember the element before so the new one can be inserted afterwards
+                    TokenList.Token before = a.previous;
+
+                    TokenList sublist = tokens.extractSubList(a,t);
+                    // remove parentheses
+                    sublist.remove(sublist.first);
+                    sublist.remove(sublist.last);
+
+                    // if its a function before () then the () indicates its an input to a function
+                    if( before != null && before.getType() == Type.FUNCTION ) {
+                        List<TokenList.Token> inputs = parseParameterCommaBlock(sublist, sequence);
+                        if (inputs.isEmpty())
+                            throw new RuntimeException("Empty function input parameters");
+                        else {
+                            createFunction(before, inputs, tokens, sequence);
+                        }
+                    } else if( before != null && before.getType() == Type.VARIABLE ) {
+                        // if it's a variable then that says it's a sub-matrix
+                        TokenList.Token extract = parseSubmatrixToExtract(before,sublist, sequence);
+                        // put in the extract operation
+                        tokens.insert(before,extract);
+                        tokens.remove(before);
+                    } else {
+                        // if null then it was empty inside
+                        TokenList.Token output = parseBlockNoParentheses(sublist,sequence);
+                        if (output != null)
+                            tokens.insert(before, output);
+                    }
+                }
+            }
+            t = next;
+        }
+
+        if( !left.isEmpty())
+            throw new RuntimeException("Dangling ( parentheses");
+
+        if( tokens.size() > 1 ) {
+            parseBlockNoParentheses(tokens, sequence);
+        }
+    }
+
+    /**
+     * Searches for commas in the set of tokens.  Used for inputs to functions
+     *
+     * @return List of output tokens between the commas
+     */
+    protected List<TokenList.Token> parseParameterCommaBlock( TokenList tokens, Sequence sequence ) {
+        // find all the comma tokens
+        List<TokenList.Token> commas = new ArrayList<TokenList.Token>();
+        TokenList.Token token = tokens.first;
+
+        while( token != null ) {
+            if( token.getType() == Type.SYMBOL && token.getSymbol() == Symbol.COMMA ) {
+                commas.add(token);
+            }
+            token = token.next;
+        }
+
+        List<TokenList.Token> output = new ArrayList<TokenList.Token>();
+        if( commas.isEmpty() ) {
+            output.add(parseBlockNoParentheses(tokens, sequence));
+        } else {
+            TokenList.Token before = tokens.first;
+            for (int i = 0; i < commas.size(); i++) {
+                TokenList.Token after = commas.get(i);
+                if( before == after )
+                    throw new RuntimeException("No empty function inputs allowed!");
+                TokenList.Token tmp = after.next;
+                TokenList sublist = tokens.extractSubList(before,after);
+                sublist.remove(after);// remove the comma
+                output.add(parseBlockNoParentheses(sublist, sequence));
+                before = tmp;
+            }
+
+            // if the last character is a comma then after.next above will be null and thus before is null
+            if( before == null )
+                throw new RuntimeException("No empty function inputs allowed!");
+
+            TokenList.Token after = tokens.last;
+            TokenList sublist = tokens.extractSubList(before, after);
+            output.add(parseBlockNoParentheses(sublist, sequence));
+        }
+
+        return output;
+    }
+
+    /**
+     * Converts a submatrix into an extract matrix operation.
+     * @param variableTarget The variable in which the submatrix is extracted from
+     */
+    protected TokenList.Token parseSubmatrixToExtract(TokenList.Token variableTarget,
+                                                      TokenList tokens, Sequence sequence) {
+
+        List<Variable> variables = new ArrayList<Variable>();
+        variables.add(variableTarget.getVariable());
+
+        parseSubmatrixRange(tokens, sequence, variables);
+
+        // first parameter is the matrix it will be extracted from.  rest specify range
+        Operation.Info info;
+
+        // if it is extracting only a single number explicitly then convert it into a scalar
+        if( variables.get(1) == variables.get(2) && variables.get(3) == variables.get(4) ) {
+            // remove some redundant variables
+            variables.remove(4);
+            variables.remove(2);
+            info = functions.create("extractScalar", variables);
+        } else {
+            info = functions.create("extract", variables);
+        }
+
+        sequence.addOperation(info.op);
+
+        return new TokenList.Token(info.output);
+    }
+
+    /**
+     * Parses the range for a sub-matrix and puts the results into variables. List
+     * should be everything inside the parentheses.
+     *
+     * e.g. 1,2 or 2:10,4 or 2:10,3:13
+     *
+     * @param variables Variables which describe the selected range
+     */
+    private void parseSubmatrixRange(TokenList tokens, Sequence sequence,
+                                     List<Variable> variables) {
+        TokenList.Token comma = tokens.first;
+        while( comma != null && comma.getSymbol() != Symbol.COMMA )
+            comma = comma.next;
+
+        if( comma == null )
+            throw new RuntimeException("Can't find comma inside submatrix");
+
+        TokenList listLeft = tokens.extractSubList(tokens.first,comma.previous);
+        TokenList listRight = tokens.extractSubList(comma.next,tokens.last);
+
+        parseValueRange(listLeft,sequence,variables); // rows
+        parseValueRange(listRight,sequence,variables); // columns
+    }
+
+    /**
+     * Parse a range written like 0:10 in which two numbers are separated by a colon.
+     */
+    protected void parseValueRange( TokenList tokens, Sequence sequence , List<Variable> variables ) {
+
+        TokenList.Token[] t = new TokenList.Token[2];
+
+        // range of values are specified with a colon
+        TokenList.Token colon = tokens.first;
+        while( colon != null && colon.getSymbol() != Symbol.COLON ) {
+            colon = colon.next;
+        }
+        if( colon == null ) {
+            // no range, just a single value
+            t[0] = t[1] = parseBlockNoParentheses(tokens,sequence);
+        } else {
+            if( colon.previous == null && colon.next == null) {
+                t[0] = new TokenList.Token(VariableSpecial.Special.ALL);
+            } else if( colon.next == null ) {
+                TokenList listRow0 = tokens.extractSubList(tokens.first,colon.previous);
+                t[0] = parseBlockNoParentheses(listRow0,sequence);
+                t[1] = new TokenList.Token(VariableSpecial.Special.END);
+            } else if( colon.previous == null ) {
+                throw new RuntimeException(":<int> not allowed");
+            } else {
+                TokenList listRow0 = tokens.extractSubList(tokens.first, colon.previous);
+                TokenList listRow1 = tokens.extractSubList(colon.next, tokens.last);
+                t[0] = parseBlockNoParentheses(listRow0, sequence);
+                t[1] = parseBlockNoParentheses(listRow1, sequence);
+            }
+        }
+
+        for (int i = 0; i < t.length; i++) {
+           if(t[i] == null)
+               continue;
+           if( t[i].getType() != Type.VARIABLE ) {
+               throw new RuntimeException("Expected variable inside of range");
+           }
+            variables.add(t[i].getVariable());
+        }
+    }
+
+    /**
+     * Parses a code block with no parentheses and no commas.  After it is done there should be a single token left,
+     * which is returned.
+     */
+    protected TokenList.Token parseBlockNoParentheses(TokenList tokens, Sequence sequence ) {
+        // search for matrix bracket operations
+        parseBracketCreateMatrix(tokens, sequence);
+
+        // process operators depending on their priority
+        parseNegOp(tokens,sequence);
+        parseOperationsL(tokens,sequence);
+        parseOperationsLR(new Symbol[]{Symbol.POWER,Symbol.ELEMENT_POWER}, tokens, sequence);
+        parseOperationsLR(new Symbol[]{Symbol.TIMES, Symbol.RDIVIDE, Symbol.LDIVIDE, Symbol.ELEMENT_TIMES, Symbol.ELEMENT_DIVIDE}, tokens, sequence);
+        parseOperationsLR(new Symbol[]{Symbol.PLUS, Symbol.MINUS}, tokens, sequence);
+
+        if( tokens.size() > 1 )
+            throw new RuntimeException("BUG in parser.  There should only be a single token left");
+
+        return tokens.first;
+    }
+
+    /**
+     * Searches for brackets which are only used to construct new matrices by concatenating
+     * 1 or more matrices together
+     */
+    protected void parseBracketCreateMatrix(TokenList tokens, Sequence sequence) {
+        List<TokenList.Token> left = new ArrayList<TokenList.Token>();
+
+        TokenList.Token t = tokens.getFirst();
+
+        while( t != null ) {
+            TokenList.Token next = t.next;
+            if( t.getSymbol() == Symbol.BRACKET_LEFT ) {
+                left.add(t);
+            } else if( t.getSymbol() == Symbol.BRACKET_RIGHT ) {
+                if( left.isEmpty() )
+                    throw new RuntimeException("No matching left bracket for right");
+
+                TokenList.Token start = left.remove(left.size() - 1);
+                TokenList.Token i = start.next;
+
+                // define the constructor
+                MatrixConstructor constructor = new MatrixConstructor(functions.getManagerTemp());
+
+                TokenList.Token opStart = null;
+
+                while( true ) {
+                    if( i.getType() == Type.VARIABLE ) {
+                        innerMatrixConstructorOp(tokens, sequence, constructor, opStart, i.previous);
+                        opStart = i;
+                    } else if( i.getType() == Type.SYMBOL ) {
+                        boolean finished = false;
+                        boolean ignore = true; // ignore if it's part of an inner expression
+                        if( i.getSymbol() == Symbol.SEMICOLON ) {
+                            ignore = false;
+                        } else if( i.getSymbol() == Symbol.BRACKET_RIGHT ) {
+                            finished = true;
+                            ignore = false;
+                        }
+                        if( !ignore ) {
+                            innerMatrixConstructorOp(tokens, sequence, constructor, opStart, i.previous);
+                            constructor.endRow();
+                            opStart = null;
+
+                            if (finished)
+                                break;
+                        }
+                    } else {
+                        throw new RuntimeException("Unexpected token "+i);
+                    }
+                    i = i.next;
+                }
+
+                Operation.Info info = Operation.matrixConstructor(constructor);
+                sequence.addOperation(info.op);
+
+                // add the new variable to the tokens list
+                tokens.insert(t,new TokenList.Token(info.output));
+
+                // remove used tokens
+                tokens.extractSubList(start,t);
+            }
+
+            t = next;
+        }
+
+        if( !left.isEmpty() )
+            throw new RuntimeException("Dangling [");
+    }
+
+    private void innerMatrixConstructorOp(TokenList tokens, Sequence sequence, MatrixConstructor constructor,
+                                          TokenList.Token opStart, TokenList.Token opEnd) {
+        if( opStart == null )
+            return;
+        if( opStart != opEnd) {
+            TokenList opList = tokens.extractSubList(opStart,opEnd);
+            TokenList.Token var = parseBlockNoParentheses(opList,sequence);
+            constructor.addToRow(var.getVariable());
+        } else {
+            constructor.addToRow(opStart.getVariable());
+        }
+    }
+
+    /**
+     * Searches for cases where a minus sign means negative operator.  That happens when there is a minus
+     * sign with a variable to its right and no variable to its left
+     */
+    protected void parseNegOp(TokenList tokens, Sequence sequence) {
+        if( tokens.size == 0 )
+            return;
+
+        TokenList.Token token = tokens.first;
+
+        while( token != null ) {
+            TokenList.Token next = token.next;
+            escape:
+            if( token.getSymbol() == Symbol.MINUS ) {
+                if( token.previous != null && token.previous.getType() != Type.SYMBOL)
+                    break escape;
+                if( token.next == null || token.next.getType() == Type.SYMBOL)
+                    break escape;
+
+                if( token.next.getType() != Type.VARIABLE )
+                    throw new RuntimeException("Crap bug rethink this function");
+
+                // create the operation
+                Operation.Info info = Operation.neg(token.next.getVariable(),functions.getManagerTemp());
+                // add the operation to the sequence
+                sequence.addOperation(info.op);
+                // update the token list
+                TokenList.Token t = new TokenList.Token(info.output);
+                tokens.insert(token.next,t);
+                tokens.remove(token.next);
+                tokens.remove(token);
+                next = t;
+            }
+            token = next;
+        }
+    }
+
+
+    /**
+     * Parses operations where the input comes from variables to its left only.  Hard coded to only look
+     * for transpose for now
+     *
+     * @param tokens List of all the tokens
+     * @param sequence List of operation sequence
+     */
+    protected void parseOperationsL(TokenList tokens, Sequence sequence) {
+
+        if( tokens.size == 0 )
+            return;
+
+        TokenList.Token token = tokens.first;
+
+        if( token.getType() != Type.VARIABLE )
+            throw new RuntimeException("The first token in an equation needs to be a variable and not "+token);
+
+        while( token != null ) {
+            if( token.getType() == Type.FUNCTION ) {
+                throw new RuntimeException("Function encountered with no parentheses");
+            } else if( token.getType() == Type.SYMBOL && token.getSymbol() == Symbol.TRANSPOSE) {
+                if( token.previous.getType() == Type.VARIABLE )
+                    token = insertTranspose(token.previous,tokens,sequence);
+                else
+                    throw new RuntimeException("Expected variable before tranpose");
+            }
+            token = token.next;
+        }
+    }
+
+    /**
+     * Parses operations where the input comes from variables to its left and right
+     *
+     * @param ops List of operations which should be parsed
+     * @param tokens List of all the tokens
+     * @param sequence List of operation sequence
+     */
+    protected void parseOperationsLR(Symbol ops[], TokenList tokens, Sequence sequence) {
+
+        if( tokens.size == 0 )
+            return;
+
+        TokenList.Token token = tokens.first;
+
+        if( token.getType() != Type.VARIABLE )
+            throw new RuntimeException("The first token in an equation needs to be a variable and not "+token);
+
+        boolean hasLeft = false;
+        while( token != null ) {
+            if( token.getType() == Type.FUNCTION ) {
+                throw new RuntimeException("Function encountered with no parentheses");
+            } else if( token.getType() == Type.VARIABLE ) {
+                if( hasLeft ) {
+                    if( token.previous.getType() == Type.VARIABLE ) {
+                        throw new RuntimeException("Two variables next to each other");
+                    }
+                    if( isTargetOp(token.previous,ops)) {
+                        token = createOp(token.previous.previous,token.previous,token,tokens,sequence);
+                    }
+                } else {
+                    hasLeft = true;
+                }
+            } else {
+                if( token.previous.getType() == Type.SYMBOL ) {
+                    throw new RuntimeException("Two symbols next to each other. "+token.previous+" and "+token);
+                }
+            }
+            token = token.next;
+        }
+    }
+
+    /**
+     * Adds a new operation to the list from the operation and two variables.  The inputs are removed
+     * from the token list and replaced by their output.
+     */
+    protected TokenList.Token insertTranspose( TokenList.Token variable ,
+                                               TokenList tokens , Sequence sequence )
+    {
+        Operation.Info info = functions.create('\'',variable.getVariable());
+
+        sequence.addOperation(info.op);
+
+        // replace the symbols with their output
+        TokenList.Token t = new TokenList.Token(info.output);
+        // remove the transpose symbol
+        tokens.remove(variable.next);
+        // replace the variable with its transposed version
+        tokens.replace(variable,t);
+        return t;
+
+    }
+
+    /**
+     * Adds a new operation to the list from the operation and two variables.  The inputs are removed
+     * from the token list and replaced by their output.
+     */
+    protected TokenList.Token createOp( TokenList.Token left , TokenList.Token op , TokenList.Token right ,
+                                      TokenList tokens , Sequence sequence )
+    {
+        Operation.Info info = functions.create(op.symbol, left.getVariable(), right.getVariable());
+
+        sequence.addOperation(info.op);
+
+        // replace the symbols with their output
+        TokenList.Token t = new TokenList.Token(info.output);
+        tokens.remove(left);
+        tokens.remove(right);
+        tokens.replace(op,t);
+        return t;
+
+    }
+
+    /**
+     * Adds a new operation to the list from the operation and two variables.  The inputs are removed
+     * from the token list and replaced by their output.
+     */
+    protected TokenList.Token createFunction( TokenList.Token name , List<TokenList.Token> inputs , TokenList tokens , Sequence sequence )
+    {
+        Operation.Info info;
+        if( inputs.size() == 1 )
+            info = functions.create(name.getFunction().getName(),inputs.get(0).getVariable());
+        else {
+            List<Variable> vars = new ArrayList<Variable>();
+            for (int i = 0; i < inputs.size(); i++) {
+                vars.add(inputs.get(i).getVariable());
+            }
+            info = functions.create(name.getFunction().getName(), vars );
+        }
+
+        sequence.addOperation(info.op);
+
+        // replace the symbols with the function's output
+        TokenList.Token t = new TokenList.Token(info.output);
+        tokens.replace(name, t);
+        return t;
+
+    }
+
+    /**
+     * Looks up a variable given its name.  If none is found then return null.
+     */
+    public <T extends Variable> T lookupVariable(String token) {
+        Variable result = variables.get(token);
+        return (T)result;
+    }
+
+    public DenseMatrix64F lookupMatrix(String token) {
+        return ((VariableMatrix)variables.get(token)).matrix;
+    }
+
+    public int lookupInteger(String token) {
+        return ((VariableInteger)variables.get(token)).value;
+    }
+
+    public double lookupDouble(String token) {
+        Variable v = variables.get(token);
+
+        if( v instanceof VariableMatrix ) {
+            DenseMatrix64F m = ((VariableMatrix)v).matrix;
+            if( m.numCols == 1 && m.numRows == 1 ) {
+                return m.get(0,0);
+            } else {
+                throw new RuntimeException("Can only return 1x1 real matrices as doubles");
+            }
+        }
+        return ((VariableScalar)variables.get(token)).getDouble();
+    }
+
+    /**
+     * Parses the text string to extract tokens.
+     */
+    protected TokenList extractTokens(String equation , ManagerTempVariables managerTemp ) {
+        TokenList tokens = new TokenList();
+
+        int length = 0;
+        boolean again; // process the same character twice
+        TokenType type = TokenType.UNKNOWN;
+        for( int i = 0; i < equation.length(); i++ ) {
+            again = false;
+            char c = equation.charAt(i);
+            if( type == TokenType.WORD ) {
+                if (isLetter(c)) {
+                    storage[length++] = c;
+                } else {
+                    // add the variable/function name to token list
+                    String name = new String(storage, 0, length);
+                    Variable v = lookupVariable(name);
+                    if (v == null) {
+                        if (functions.isFunctionName(name)) {
+                            tokens.add(new Function(name));
+                        } else {
+                            // see if it's type can be inferred later on
+                            tokens.add(name);
+                        }
+                    } else {
+                        tokens.add(v);
+                    }
+
+                    type = TokenType.UNKNOWN;
+                    again = true; // process unexpected character a second time
+                }
+            } else if( type == TokenType.INTEGER ) { // Handle integer numbers.  Until proven to be a float
+                if( c == '.' ) {
+                    type = TokenType.FLOAT;
+                    storage[length++] = c;
+                } else if( c == 'e' || c == 'E' ) {
+                    type = TokenType.FLOAT_EXP;
+                    storage[length++] = c;
+                } else if( Character.isDigit(c) ) {
+                    storage[length++] = c;
+                } else if( isSymbol(c) || Character.isWhitespace(c) ) {
+                    int value = Integer.parseInt( new String(storage, 0, length));
+                    tokens.add(managerTemp.createInteger(value));
+                    type = TokenType.UNKNOWN;
+                    again = true; // process unexpected character a second time
+                } else {
+                    throw new RuntimeException("Unexpected character at the end of an integer "+c);
+                }
+            } else if( type == TokenType.FLOAT ) { // Handle floating point numbers
+                if( c == '.') {
+                    throw new RuntimeException("Unexpected '.' in a float");
+                } else if( c == 'e' || c == 'E' ) {
+                    storage[length++] = c;
+                    type = TokenType.FLOAT_EXP;
+                } else if( Character.isDigit(c) ) {
+                    storage[length++] = c;
+                } else if( isSymbol(c) || Character.isWhitespace(c) ) {
+                    double value = Double.parseDouble( new String(storage, 0, length));
+                    tokens.add(managerTemp.createDouble(value));
+                    type = TokenType.UNKNOWN;
+                    again = true; // process unexpected character a second time
+                } else {
+                    throw new RuntimeException("Unexpected character at the end of an float "+c);
+                }
+            } else if( type == TokenType.FLOAT_EXP ) { // Handle floating point numbers in exponential format
+                boolean end = false;
+                if( c == '-' ) {
+                    char p = storage[length-1];
+                    if( p == 'e' || p == 'E') {
+                        storage[length++] = c;
+                    } else {
+                        end = true;
+                    }
+                } else if( Character.isDigit(c) ) {
+                    storage[length++] = c;
+                } else if( isSymbol(c) || Character.isWhitespace(c) ) {
+                    end = true;
+                } else {
+                    throw new RuntimeException("Unexpected character at the end of an float "+c);
+                }
+
+                if( end ) {
+                    double value = Double.parseDouble( new String(storage, 0, length));
+                    tokens.add(managerTemp.createDouble(value));
+                    type = TokenType.UNKNOWN;
+                    again = true; // process the current character again since it was unexpected
+                }
+            } else {
+                if( isSymbol(c) ) {
+                    boolean special = false;
+                    if( c == '-' ) {
+                        // need to handle minus symbols carefully since it can be part of a number of a minus operator
+                        // if next to a number it should be negative sign, unless there is no operator to its left
+                        // then its a minus sign.
+                        if( i+1 < equation.length() && Character.isDigit(equation.charAt(i+1)) &&
+                                (tokens.last == null || isOperatorLR(tokens.last.getSymbol()))) {
+                            type = TokenType.INTEGER;
+                            storage[0] = c;
+                            length = 1;
+                            special = true;
+                        }
+                    }
+                    if( !special ) {
+                        TokenList.Token t = tokens.add(Symbol.lookup(c));
+                        if (t.previous != null && t.previous.getType() == Type.SYMBOL) {
+                            // there should only be two symbols in a row if its an element-wise operation
+                            if (t.previous.getSymbol() == Symbol.PERIOD) {
+                                tokens.remove(t.previous);
+                                tokens.remove(t);
+                                tokens.add(Symbol.lookupElementWise(c));
+                            }
+                        }
+                    }
+                } else if( Character.isWhitespace(c) ) {
+                    continue;// ignore white space
+                } else {
+                    // start adding to the word
+                    if( Character.isDigit(c) ) {
+                        type = TokenType.INTEGER;
+                    } else {
+                        type = TokenType.WORD;
+                    }
+                    storage[0] = c;
+                    length = 1;
+                }
+            }
+            // see if it should process the same character again
+            if( again )
+                i--;
+        }
+        if( type == TokenType.WORD ) {
+            String word = new String(storage,0,length);
+            Variable v = lookupVariable(word);
+            if( v == null )
+                throw new RuntimeException("Unknown variable "+word);
+            tokens.add( v );
+        } else if( type == TokenType.INTEGER ) {
+            tokens.add(managerTemp.createInteger(Integer.parseInt( new String(storage, 0, length))));
+        } else if( type == TokenType.FLOAT || type == TokenType.FLOAT_EXP ) {
+            tokens.add(managerTemp.createDouble(Double.parseDouble( new String(storage, 0, length))));
+        }
+
+        return tokens;
+    }
+
+    protected static enum TokenType
+    {
+        WORD,
+        INTEGER,
+        FLOAT,
+        FLOAT_EXP,
+        UNKNOWN
+    }
+
+    /**
+     * Checks to see if the token is in the list of allowed character operations.  Used to apply order of operations
+     * @param token Token being checked
+     * @param ops List of allowed character operations
+     * @return true for it being in the list and false for it not being in the list
+     */
+    protected static boolean isTargetOp( TokenList.Token token , Symbol[] ops ) {
+        Symbol c = token.symbol;
+        for (int i = 0; i < ops.length; i++) {
+            if( c == ops[i])
+                return true;
+        }
+        return false;
+    }
+
+    protected static boolean isSymbol(char c) {
+        return c == '*' || c == '/' || c == '+' || c == '-' || c == '(' || c == ')' || c == '[' || c == ']' ||
+               c == '=' || c == '\'' || c == '.' || c == ',' || c == ':' || c == ';' || c == '\\' || c == '^';
+    }
+
+    /**
+     * Operators which affect the variables to its left and right
+     */
+    protected static boolean isOperatorLR( Symbol s ) {
+        if( s == null )
+            return false;
+
+        switch( s ) {
+            case ELEMENT_DIVIDE:
+            case ELEMENT_TIMES:
+            case ELEMENT_POWER:
+            case RDIVIDE:
+            case LDIVIDE:
+            case TIMES:
+            case POWER:
+            case PLUS:
+            case MINUS:
+            case ASSIGN:
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if the character is a valid letter for use in a variable name
+     */
+    protected static boolean isLetter( char c ) {
+        return !(isSymbol(c) || Character.isWhitespace(c));
+    }
+
+    /**
+     * Returns true if the specified name is NOT allowed.  It isn't allowed if it matches a built in operator
+     * or if it contains a restricted character.
+     */
+    protected boolean isReserved( String name ) {
+        if( functions.isFunctionName(name))
+            return true;
+
+        for (int i = 0; i < name.length(); i++) {
+            if( !isLetter(name.charAt(i)) )
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Compiles and performs the provided equation.
+     *
+     * @param equation String in simple equation format
+     */
+    public void process( String equation ) {
+        compile(equation).perform();
+    }
+
+    /**
+     * Compiles and performs the provided equation.
+     *
+     * @param equation String in simple equation format
+     */
+    public void process( String equation , boolean debug ) {
+        compile(equation,debug).perform();
+    }
+
+    /**
+     * Returns the functions manager
+     */
+    public ManagerFunctions getFunctions() {
+        return functions;
+    }
+}
diff --git a/main/equation/src/org/ejml/equation/Function.java b/main/equation/src/org/ejml/equation/Function.java
new file mode 100644
index 0000000..d8304fa
--- /dev/null
+++ b/main/equation/src/org/ejml/equation/Function.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.equation;
+
+/**
+ * A function is an operator with the following syntax "<Name>( Input )"
+ *
+ * @author Peter Abeles
+ */
+public class Function {
+    /**
+     * Name of operator and the string it looks for when parsing
+     */
+    public String name;
+
+    public Function(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public String toString() {
+        return "Function{" +
+                "name='" + name + '\'' +
+                '}';
+    }
+}
diff --git a/main/equation/src/org/ejml/equation/ManagerFunctions.java b/main/equation/src/org/ejml/equation/ManagerFunctions.java
new file mode 100644
index 0000000..42b887d
--- /dev/null
+++ b/main/equation/src/org/ejml/equation/ManagerFunctions.java
@@ -0,0 +1,361 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.equation;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Centralized place to create new instances of operations and functions.  Must call
+ * {@link #setManagerTemp} before any other functions.
+ *
+ * @author Peter Abeles
+ */
+public class ManagerFunctions {
+
+    // List of functions which take in N inputs
+    Map<String,Input1> input1 = new HashMap<String,Input1>();
+    Map<String,InputN> inputN = new HashMap<String,InputN>();
+
+    // Reference to temporary variable manager
+    protected ManagerTempVariables managerTemp;
+
+    public ManagerFunctions() {
+        addBuiltIn();
+    }
+
+    /**
+     * Returns true if the string matches the name of a function
+     */
+    public boolean isFunctionName( String s ) {
+        if( input1.containsKey(s))
+            return true;
+        if( inputN.containsKey(s))
+            return true;
+
+        return false;
+    }
+
+    /**
+     * Create a new instance of single input functions
+     * @param name function name
+     * @param var0 Input variable
+     * @return Resulting operation
+     */
+    public Operation.Info create( String name , Variable var0 ) {
+        Input1 func = input1.get(name);
+        if( func == null )
+            return null;
+        return func.create(var0,managerTemp);
+    }
+
+    /**
+     * Create a new instance of single input functions
+     * @param name function name
+     * @param vars Input variables
+     * @return Resulting operation
+     */
+    public Operation.Info create( String name , List<Variable> vars ) {
+        InputN func = inputN.get(name);
+        if( func == null )
+            return null;
+        return func.create(vars,managerTemp);
+    }
+
+    /**
+     * Create a new instance of a single input function from an operator character
+     * @param op Which operation
+     * @param input Input variable
+     * @return Resulting operation
+     */
+    public Operation.Info create( char op , Variable input ) {
+        switch( op ) {
+            case '\'':
+                return Operation.transpose(input, managerTemp);
+
+            default:
+                throw new RuntimeException("Unknown operation " + op);
+        }
+    }
+
+    /**
+     * Create a new instance of a two input function from an operator character
+     * @param op Which operation
+     * @param left Input variable on left
+     * @param right Input variable on right
+     * @return Resulting operation
+     */
+    public Operation.Info create( Symbol op , Variable left , Variable right ) {
+        switch( op ) {
+            case PLUS:
+                return Operation.add(left, right, managerTemp);
+
+            case MINUS:
+                return Operation.subtract(left, right, managerTemp);
+
+            case TIMES:
+                return Operation.multiply(left, right, managerTemp);
+
+            case RDIVIDE:
+                return Operation.divide(left, right, managerTemp);
+
+            case LDIVIDE:
+                return Operation.divide(right, left, managerTemp);
+
+            case POWER:
+                return Operation.pow(left, right, managerTemp);
+
+            case ELEMENT_DIVIDE:
+                return Operation.elementDivision(left, right, managerTemp);
+
+            case ELEMENT_TIMES:
+                return Operation.elementMult(left, right, managerTemp);
+
+            case ELEMENT_POWER:
+                return Operation.elementPow(left, right, managerTemp);
+
+            default:
+                throw new RuntimeException("Unknown operation " + op);
+        }
+    }
+
+    /**
+     *
+     * @param managerTemp
+     */
+
+    public void setManagerTemp(ManagerTempVariables managerTemp) {
+        this.managerTemp = managerTemp;
+    }
+
+    /**
+     * Adds a function, with a single input, to the list
+     * @param name Name of function
+     * @param function Function factory
+     */
+    public void add( String name , Input1 function ) {
+       input1.put(name, function);
+    }
+
+    /**
+     * Adds a function, with a two inputs, to the list
+     * @param name Name of function
+     * @param function Function factory
+     */
+    public void add( String name , InputN function ) {
+        inputN.put(name,function);
+    }
+
+    /**
+     * Adds built in functions
+     */
+    private void addBuiltIn( ) {
+        input1.put("inv",new Input1() {
+            @Override
+            public Operation.Info create(Variable A, ManagerTempVariables manager) {
+                return Operation.inv(A,manager);
+            }
+        });
+        input1.put("pinv",new Input1() {
+            @Override
+            public Operation.Info create(Variable A, ManagerTempVariables manager) {
+                return Operation.pinv(A, manager);
+            }
+        });
+        input1.put("rref",new Input1() {
+            @Override
+            public Operation.Info create(Variable A, ManagerTempVariables manager) {
+                return Operation.rref(A, manager);
+            }
+        });
+        input1.put("eye",new Input1() {
+            @Override
+            public Operation.Info create(Variable A, ManagerTempVariables manager) {
+                return Operation.eye(A, manager);
+            }
+        });
+        input1.put("det",new Input1() {
+            @Override
+            public Operation.Info create(Variable A, ManagerTempVariables manager) {
+                return Operation.det(A, manager);
+            }
+        });
+        input1.put("normF",new Input1() {
+            @Override
+            public Operation.Info create(Variable A, ManagerTempVariables manager) {
+                return Operation.normF(A, manager);
+            }
+        });
+        input1.put("trace",new Input1() {
+            @Override
+            public Operation.Info create(Variable A, ManagerTempVariables manager) {
+                return Operation.trace(A, manager);
+            }
+        });
+        input1.put("diag",new Input1() {
+            @Override
+            public Operation.Info create(Variable A, ManagerTempVariables manager) {
+                return Operation.diag(A, manager);
+            }
+        });
+        input1.put("min",new Input1() {
+            @Override
+            public Operation.Info create(Variable A, ManagerTempVariables manager) {
+                return Operation.min(A, manager);
+            }
+        });
+        input1.put("max",new Input1() {
+            @Override
+            public Operation.Info create(Variable A, ManagerTempVariables manager) {
+                return Operation.max(A, manager);
+            }
+        });
+        input1.put("abs",new Input1() {
+            @Override
+            public Operation.Info create(Variable A, ManagerTempVariables manager) {
+                return Operation.abs(A, manager);
+            }
+        });
+        input1.put("sin",new Input1() {
+            @Override
+            public Operation.Info create(Variable A, ManagerTempVariables manager) {
+                return Operation.sin(A, manager);
+            }
+        });
+        input1.put("cos",new Input1() {
+            @Override
+            public Operation.Info create(Variable A, ManagerTempVariables manager) {
+                return Operation.cos(A, manager);
+            }
+        });
+        input1.put("atan",new Input1() {
+            @Override
+            public Operation.Info create(Variable A, ManagerTempVariables manager) {
+                return Operation.atan(A, manager);
+            }
+        });
+        input1.put("exp",new Input1() {
+            @Override
+            public Operation.Info create(Variable A, ManagerTempVariables manager) {
+                return Operation.exp(A, manager);
+            }
+        });
+        input1.put("log",new Input1() {
+            @Override
+            public Operation.Info create(Variable A, ManagerTempVariables manager) {
+                return Operation.log(A, manager);
+            }
+        });
+        input1.put("sqrt",new Input1() {
+            @Override
+            public Operation.Info create(Variable A, ManagerTempVariables manager) {
+                return Operation.sqrt(A, manager);
+            }
+        });
+
+        inputN.put("zeros",new InputN() {
+            @Override
+            public Operation.Info create(List<Variable> inputs, ManagerTempVariables manager) {
+                if( inputs.size() != 2 ) throw new RuntimeException("Two inputs expected");
+                return Operation.zeros(inputs.get(0), inputs.get(1), manager);
+            }
+        });
+
+        inputN.put("ones",new InputN() {
+            @Override
+            public Operation.Info create(List<Variable> inputs, ManagerTempVariables manager) {
+                if( inputs.size() != 2 ) throw new RuntimeException("Two inputs expected");
+                return Operation.ones(inputs.get(0), inputs.get(1), manager);
+            }
+        });
+
+        inputN.put("kron",new InputN() {
+            @Override
+            public Operation.Info create(List<Variable> inputs, ManagerTempVariables manager) {
+                if( inputs.size() != 2 ) throw new RuntimeException("Two inputs expected");
+                return Operation.kron(inputs.get(0), inputs.get(1), manager);
+            }
+        });
+
+        inputN.put("dot",new InputN() {
+            @Override
+            public Operation.Info create(List<Variable> inputs, ManagerTempVariables manager) {
+                if( inputs.size() != 2 ) throw new RuntimeException("Two inputs expected");
+                return Operation.dot(inputs.get(0), inputs.get(1), manager);
+            }
+        });
+
+        inputN.put("pow",new InputN() {
+            @Override
+            public Operation.Info create(List<Variable> inputs, ManagerTempVariables manager) {
+                if( inputs.size() != 2 ) throw new RuntimeException("Two inputs expected");
+                return Operation.pow(inputs.get(0), inputs.get(1), manager);
+            }
+        });
+
+        inputN.put("atan2",new InputN() {
+            @Override
+            public Operation.Info create(List<Variable> inputs, ManagerTempVariables manager) {
+                if( inputs.size() != 2 ) throw new RuntimeException("Two inputs expected");
+                return Operation.atan2(inputs.get(0), inputs.get(1), manager);
+            }
+        });
+
+        inputN.put("solve",new InputN() {
+            @Override
+            public Operation.Info create(List<Variable> inputs, ManagerTempVariables manager) {
+                if( inputs.size() != 2 ) throw new RuntimeException("Two inputs expected");
+                return Operation.solve(inputs.get(0), inputs.get(1), manager);
+            }
+        });
+
+        inputN.put("extract",new InputN() {
+            @Override
+            public Operation.Info create(List<Variable> inputs, ManagerTempVariables manager) {
+                return Operation.extract(inputs, manager);
+            }
+        });
+        inputN.put("extractScalar",new InputN() {
+            @Override
+            public Operation.Info create(List<Variable> inputs, ManagerTempVariables manager) {
+                if( inputs.size() != 3 ) throw new RuntimeException("Three inputs expected");
+                return Operation.extractScalar(inputs, manager);
+            }
+        });
+    }
+
+    public ManagerTempVariables getManagerTemp() {
+        return managerTemp;
+    }
+
+    /**
+     * Creates new instances of functions from a single variable
+     */
+    public static interface Input1 {
+        Operation.Info create( Variable A , ManagerTempVariables manager );
+    }
+
+    /**
+     * Creates a new instance of functions from two variables
+     */
+    public static interface InputN {
+        Operation.Info create( List<Variable> inputs , ManagerTempVariables manager );
+    }
+}
diff --git a/main/equation/src/org/ejml/equation/ManagerTempVariables.java b/main/equation/src/org/ejml/equation/ManagerTempVariables.java
new file mode 100644
index 0000000..f595c14
--- /dev/null
+++ b/main/equation/src/org/ejml/equation/ManagerTempVariables.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.equation;
+
+/**
+ * Manages the creation and recycling of temporary variables used to store intermediate results.  The user
+ * cannot directly access these variables
+ *
+ * @author Peter Abeles
+ */
+// TODO add function to purge temporary variables.  basicaly resize and redeclare their array to size 1
+public class ManagerTempVariables {
+
+    public VariableMatrix createMatrix() {
+        return VariableMatrix.createTemp();
+    }
+
+    public VariableDouble createDouble() {
+        return new VariableDouble(0);
+    }
+
+    public VariableDouble createDouble( double value ) {
+        return new VariableDouble(value);
+    }
+
+    public VariableInteger createInteger() {
+        return createInteger(0);
+    }
+
+    public VariableInteger createInteger( int value ) {
+        return new VariableInteger(value);
+    }
+}
diff --git a/main/equation/src/org/ejml/equation/MatrixConstructor.java b/main/equation/src/org/ejml/equation/MatrixConstructor.java
new file mode 100644
index 0000000..f808fff
--- /dev/null
+++ b/main/equation/src/org/ejml/equation/MatrixConstructor.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.equation;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * matrix used to construct a matrix from a sequence of concatenations.
+ *
+ * @author Peter Abeles
+ */
+public class MatrixConstructor {
+
+    VariableMatrix output;
+    List<Item> items = new ArrayList<Item>();
+
+    List<VariableScalar> tmp = new ArrayList<VariableScalar>();
+
+    public MatrixConstructor(ManagerTempVariables manager) {
+        this.output = manager.createMatrix();
+    }
+
+    public void addToRow(Variable variable) {
+       items.add( new Item(variable));
+    }
+
+    public void endRow() {
+        items.add(new Item());
+    }
+
+    public void construct() {
+        // make sure the last item is and end row
+        if( !items.get(items.size()-1).endRow )
+            endRow();
+
+        setToRequiredSize(output.matrix);
+
+        int matrixRow = 0;
+        List<Item> row = new ArrayList<Item>();
+        for (int i = 0; i < items.size(); i++) {
+            Item item = items.get(i);
+
+            if( item.endRow ) {
+                Item v = row.get(0);
+                int numRows = v.getRows();
+                int numCols = v.getColumns();
+                if( v.matrix ) {
+                    CommonOps.insert(v.getMatrix(),output.matrix,matrixRow,0);
+                } else {
+                    output.matrix.set(matrixRow,0,v.getValue());
+                }
+                for (int j = 1; j < row.size(); j++) {
+                    v = row.get(j);
+                    if( v.getRows() != numRows)
+                        throw new RuntimeException("Row miss-matched. "+numRows+" "+v.getRows());
+                    if( v.matrix ) {
+                        CommonOps.insert(v.getMatrix(),output.matrix,matrixRow,numCols);
+                    } else {
+                        output.matrix.set(matrixRow, numCols, v.getValue());
+                    }
+                    numCols += v.getColumns();
+                }
+                matrixRow += numRows;
+                row.clear();
+            } else {
+                row.add(item);
+            }
+        }
+
+    }
+
+    public VariableMatrix getOutput() {
+        return output;
+    }
+
+    protected void setToRequiredSize( DenseMatrix64F matrix ) {
+
+
+        int matrixRow = 0;
+        int matrixCol = 0;
+        List<Item> row = new ArrayList<Item>();
+        for (int i = 0; i < items.size(); i++) {
+            Item item = items.get(i);
+
+            if( item.endRow ) {
+                Item v = row.get(0);
+                int numRows = v.getRows();
+                int numCols = v.getColumns();
+                for (int j = 1; j < row.size(); j++) {
+                    v = row.get(j);
+                    if( v.getRows() != numRows)
+                        throw new RuntimeException("Row miss-matched. "+numRows+" "+v.getRows());
+                    numCols += v.getColumns();
+                }
+                matrixRow += numRows;
+
+                if( matrixCol == 0 )
+                    matrixCol = numCols;
+                else if( matrixCol != numCols )
+                    throw new RuntimeException("Unexpected number of columns");
+
+                row.clear();
+            } else {
+                row.add(item);
+            }
+        }
+
+        matrix.reshape(matrixRow,matrixCol);
+    }
+
+
+    private static class Item
+    {
+        Variable variable;
+        boolean endRow;
+        boolean matrix;
+
+        private Item(Variable variable) {
+            this.variable = variable;
+            matrix = variable instanceof VariableMatrix;
+        }
+
+        private Item() {
+            endRow = true;
+        }
+
+        public int getRows() {
+            if( matrix ) {
+                return ((VariableMatrix)variable).matrix.numRows;
+            } else {
+                return 1;
+            }
+        }
+
+        public int getColumns() {
+            if( matrix ) {
+                return ((VariableMatrix)variable).matrix.numCols;
+            } else {
+                return 1;
+            }
+        }
+
+        public DenseMatrix64F getMatrix() {
+            return ((VariableMatrix)variable).matrix;
+        }
+
+        public double getValue() {
+            return ((VariableScalar)variable).getDouble();
+        }
+    }
+}
diff --git a/main/equation/src/org/ejml/equation/Operation.java b/main/equation/src/org/ejml/equation/Operation.java
new file mode 100644
index 0000000..1e7e114
--- /dev/null
+++ b/main/equation/src/org/ejml/equation/Operation.java
@@ -0,0 +1,1401 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.equation;
+
+import org.ejml.alg.dense.mult.VectorVectorMult;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.LinearSolverFactory;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.NormOps;
+
+import java.util.List;
+
+/**
+ * Performs math operations.
+ *
+ * @author Peter Abeles
+ */
+public abstract class Operation {
+
+    String name;
+
+    protected Operation(String name) {
+        this.name = name;
+    }
+
+    public abstract void process();
+
+    public String name() {
+        return name;
+    }
+
+    /**
+     * If the variable is a local temporary variable it will be resized so that the operation can complete.  If not
+     * temporary then it will not be reshaped
+     * @param mat Variable containing the matrix
+     * @param numRows Desired number of rows
+     * @param numCols Desired number of columns
+     */
+    protected void resize( VariableMatrix mat , int numRows , int numCols ) {
+        if( mat.isTemp() ) {
+            mat.matrix.reshape(numRows,numCols);
+        }
+    }
+
+    public static Info multiply(final Variable A, final Variable B, ManagerTempVariables manager) {
+
+        Info ret = new Info();
+
+        if( A instanceof VariableMatrix && B instanceof VariableMatrix ) {
+            final VariableMatrix output = manager.createMatrix();
+            ret.output = output;
+            ret.op = new Operation("multiply-mm") {
+                @Override
+                public void process() {
+                    VariableMatrix mA = (VariableMatrix)A;
+                    VariableMatrix mB = (VariableMatrix)B;
+
+                    resize(output,mA.matrix.numRows,mB.matrix.numCols);
+                    CommonOps.mult(mA.matrix,mB.matrix,output.matrix);
+                }
+            };
+        } else if( A instanceof VariableInteger && B instanceof VariableInteger ) {
+            final VariableInteger output = manager.createInteger();
+            ret.output = output;
+            ret.op = new Operation("multiply-ii") {
+                @Override
+                public void process() {
+                    VariableInteger mA = (VariableInteger)A;
+                    VariableInteger mB = (VariableInteger)B;
+
+                    output.value = mA.value*mB.value;
+                }
+            };
+        } else if( A instanceof VariableScalar && B instanceof VariableScalar ) {
+            final VariableDouble output = manager.createDouble();
+            ret.output = output;
+            ret.op = new Operation("multiply-ss") {
+                @Override
+                public void process() {
+                    VariableScalar mA = (VariableScalar)A;
+                    VariableScalar mB = (VariableScalar)B;
+
+                    output.value = mA.getDouble()*mB.getDouble();
+                }
+            };
+        } else {
+            final VariableMatrix output = manager.createMatrix();
+            ret.output = output;
+            final VariableMatrix m;
+            final VariableScalar s;
+
+            if( A instanceof VariableMatrix ) {
+                m = (VariableMatrix)A;
+                s = (VariableScalar)B;
+            } else {
+                m = (VariableMatrix)B;
+                s = (VariableScalar)A;
+            }
+
+            ret.op = new Operation("multiply-ms") {
+                @Override
+                public void process() {
+                    output.matrix.reshape(m.matrix.numRows,m.matrix.numCols);
+                    CommonOps.scale(s.getDouble(),m.matrix,output.matrix);
+                }
+            };
+        }
+
+        return ret;
+    }
+
+    public static Info divide(final Variable A, final Variable B, ManagerTempVariables manager) {
+
+        Info ret = new Info();
+
+        if( A instanceof VariableMatrix && B instanceof VariableMatrix ) {
+            return solve(B,A,manager);
+        } else if( A instanceof VariableMatrix && B instanceof VariableScalar ) {
+            final VariableMatrix output = manager.createMatrix();
+            final VariableMatrix m = (VariableMatrix)A;
+            final VariableScalar s = (VariableScalar)B;
+            ret.output = output;
+            ret.op = new Operation("divide-ma") {
+                @Override
+                public void process() {
+                    output.matrix.reshape(m.matrix.numRows,m.matrix.numCols);
+                    CommonOps.divide(m.matrix,s.getDouble(),output.matrix);
+                }
+            };
+        } else if( A instanceof VariableScalar && B instanceof VariableMatrix ) {
+            final VariableMatrix output = manager.createMatrix();
+            final VariableMatrix m = (VariableMatrix)B;
+            final VariableScalar s = (VariableScalar)A;
+            ret.output = output;
+            ret.op = new Operation("divide-ma") {
+                @Override
+                public void process() {
+                    output.matrix.reshape(m.matrix.numRows,m.matrix.numCols);
+                    CommonOps.divide(s.getDouble(),m.matrix,output.matrix);
+                }
+            };
+        } else if( A instanceof VariableInteger && B instanceof VariableInteger ) {
+            final VariableInteger output = manager.createInteger();
+            ret.output = output;
+            ret.op = new Operation("divide-ii") {
+                @Override
+                public void process() {
+                    VariableInteger mA = (VariableInteger)A;
+                    VariableInteger mB = (VariableInteger)B;
+
+                    output.value = mA.value/mB.value;
+                }
+            };
+        } else {
+            final VariableDouble output = manager.createDouble();
+            ret.output = output;
+            ret.op = new Operation("divide-ss") {
+                @Override
+                public void process() {
+                    VariableScalar mA = (VariableScalar)A;
+                    VariableScalar mB = (VariableScalar)B;
+
+                    output.value = mA.getDouble()/mB.getDouble();
+                }
+            };
+        }
+
+        return ret;
+    }
+
+    /**
+     * Returns the negative of the input variable
+     */
+    public static Info neg(final Variable A, ManagerTempVariables manager) {
+        Info ret = new Info();
+
+        if( A instanceof VariableInteger  ) {
+            final VariableInteger output = manager.createInteger();
+            ret.output = output;
+            ret.op = new Operation("neg-i") {
+                @Override
+                public void process() {
+                    output.value = -((VariableInteger)A).value;
+                }
+            };
+        } else if( A instanceof VariableScalar  ) {
+            final VariableDouble output = manager.createDouble();
+            ret.output = output;
+            ret.op = new Operation("neg-s") {
+                @Override
+                public void process() {
+                    output.value = -((VariableScalar)A).getDouble();
+                }
+            };
+        } else if( A instanceof VariableMatrix  ) {
+            final VariableMatrix output = manager.createMatrix();
+            ret.output = output;
+            ret.op = new Operation("neg-m") {
+                @Override
+                public void process() {
+                    DenseMatrix64F a = ((VariableMatrix)A).matrix;
+                    output.matrix.reshape(a.numRows, a.numCols);
+                    CommonOps.changeSign(a, output.matrix);
+                }
+            };
+        } else {
+            throw new RuntimeException("Unsupported variable "+A);
+        }
+
+        return ret;
+    }
+
+    public static Info pow(final Variable A, final Variable B, ManagerTempVariables manager) {
+        Info ret = new Info();
+        final VariableDouble output = manager.createDouble();
+        ret.output = output;
+
+        if( A instanceof VariableScalar && B instanceof VariableScalar ) {
+
+            ret.op = new Operation("pow-ss") {
+                @Override
+                public void process() {
+                    double a = ((VariableScalar)A).getDouble();
+                    double b = ((VariableScalar)B).getDouble();
+
+                    output.value = Math.pow(a,b);
+                }
+            };
+        } else {
+            throw new RuntimeException("Only scalar to scalar power supported");
+        }
+
+        return ret;
+    }
+
+    public static Info atan2(final Variable A, final Variable B, ManagerTempVariables manager) {
+        Info ret = new Info();
+        final VariableDouble output = manager.createDouble();
+        ret.output = output;
+
+        if( A instanceof VariableScalar && B instanceof VariableScalar ) {
+
+            ret.op = new Operation("atan2-ss") {
+                @Override
+                public void process() {
+                    double a = ((VariableScalar)A).getDouble();
+                    double b = ((VariableScalar)B).getDouble();
+
+                    output.value = Math.atan2(a, b);
+                }
+            };
+        } else {
+            throw new RuntimeException("Only scalar to scalar atan2 supported");
+        }
+
+        return ret;
+    }
+
+    public static Info sqrt(final Variable A, ManagerTempVariables manager) {
+        Info ret = new Info();
+        final VariableDouble output = manager.createDouble();
+        ret.output = output;
+
+        if( A instanceof VariableScalar  ) {
+
+            ret.op = new Operation("sqrt-s") {
+                @Override
+                public void process() {
+                    double a = ((VariableScalar)A).getDouble();
+
+                    output.value = Math.sqrt(a);
+                }
+            };
+        } else {
+            throw new RuntimeException("Only scalars are supported");
+        }
+
+        return ret;
+    }
+
+    public static Info sin(final Variable A, ManagerTempVariables manager) {
+        Info ret = new Info();
+        final VariableDouble output = manager.createDouble();
+        ret.output = output;
+
+        if( A instanceof VariableScalar  ) {
+
+            ret.op = new Operation("sin-s") {
+                @Override
+                public void process() {
+                    output.value = Math.sin(((VariableScalar) A).getDouble());
+                }
+            };
+        } else {
+            throw new RuntimeException("Only scalars are supported");
+        }
+
+        return ret;
+    }
+
+    public static Info cos(final Variable A, ManagerTempVariables manager) {
+        Info ret = new Info();
+        final VariableDouble output = manager.createDouble();
+        ret.output = output;
+
+        if( A instanceof VariableScalar  ) {
+
+            ret.op = new Operation("cos-s") {
+                @Override
+                public void process() {
+                    output.value = Math.cos(((VariableScalar) A).getDouble());
+                }
+            };
+        } else {
+            throw new RuntimeException("Only scalars are supported");
+        }
+
+        return ret;
+    }
+
+    public static Info atan(final Variable A, ManagerTempVariables manager) {
+        Info ret = new Info();
+        final VariableDouble output = manager.createDouble();
+        ret.output = output;
+
+        if( A instanceof VariableScalar  ) {
+
+            ret.op = new Operation("atan-s") {
+                @Override
+                public void process() {
+                    output.value = Math.atan(((VariableScalar) A).getDouble());
+                }
+            };
+        } else {
+            throw new RuntimeException("Only scalars are supported");
+        }
+
+        return ret;
+    }
+
+    public static Info exp(final Variable A, ManagerTempVariables manager) {
+        final Info ret = new Info();
+
+
+        if( A instanceof VariableScalar  ) {
+            final VariableDouble output = manager.createDouble();
+            ret.output = output;
+            ret.op = new Operation("exp-s") {
+                @Override
+                public void process() {
+                    output.value = Math.exp(((VariableScalar) A).getDouble());
+                }
+            };
+        } else if( A instanceof VariableMatrix  ) {
+            final VariableMatrix output = manager.createMatrix();
+            ret.output = output;
+            ret.op = new Operation("exp-m") {
+                @Override
+                public void process() {
+                    DenseMatrix64F a = ((VariableMatrix)A).matrix;
+                    DenseMatrix64F out = ((VariableMatrix)ret.output).matrix;
+                    out.reshape(a.numRows,a.numCols);
+                    CommonOps.elementExp(a, out);
+                }
+            };
+        } else {
+            throw new RuntimeException("Only scalars are supported");
+        }
+
+        return ret;
+    }
+
+    public static Info log(final Variable A, ManagerTempVariables manager) {
+        final Info ret = new Info();
+
+        if( A instanceof VariableScalar  ) {
+            final VariableDouble output = manager.createDouble();
+            ret.output = output;
+            ret.op = new Operation("log-s") {
+                @Override
+                public void process() {
+                    output.value = Math.log(((VariableScalar) A).getDouble());
+                }
+            };
+        } else if( A instanceof VariableMatrix  ) {
+            final VariableMatrix output = manager.createMatrix();
+            ret.output = output;
+            ret.op = new Operation("log-m") {
+                @Override
+                public void process() {
+                    DenseMatrix64F a = ((VariableMatrix)A).matrix;
+                    DenseMatrix64F out = ((VariableMatrix)ret.output).matrix;
+                    out.reshape(a.numRows,a.numCols);
+                    CommonOps.elementLog(a,out);
+                }
+            };
+        } else {
+            throw new RuntimeException("Only scalars are supported");
+        }
+
+        return ret;
+    }
+
+    public static Info add(final Variable A, final Variable B, ManagerTempVariables manager) {
+        Info ret = new Info();
+
+        if( A instanceof VariableMatrix && B instanceof VariableMatrix ) {
+            final VariableMatrix output = manager.createMatrix();
+            ret.output = output;
+            ret.op = new Operation("add-mm") {
+                @Override
+                public void process() {
+                    VariableMatrix mA = (VariableMatrix)A;
+                    VariableMatrix mB = (VariableMatrix)B;
+
+                    resize(output, mA.matrix.numRows, mA.matrix.numCols);
+                    CommonOps.add(mA.matrix, mB.matrix, output.matrix);
+                }
+            };
+        } else if( A instanceof VariableInteger && B instanceof VariableInteger ) {
+            final VariableInteger output = manager.createInteger(0);
+            ret.output = output;
+            ret.op = new Operation("add-ii") {
+                @Override
+                public void process() {
+                    VariableInteger mA = (VariableInteger)A;
+                    VariableInteger mB = (VariableInteger)B;
+
+                    output.value = mA.value + mB.value;
+                }
+            };
+        } else if( A instanceof VariableScalar && B instanceof VariableScalar ) {
+            final VariableDouble output = manager.createDouble();
+            ret.output = output;
+            ret.op = new Operation("add-ss") {
+                @Override
+                public void process() {
+                    VariableScalar mA = (VariableScalar)A;
+                    VariableScalar mB = (VariableScalar)B;
+
+                    output.value = mA.getDouble() + mB.getDouble();
+                }
+            };
+        } else {
+            final VariableMatrix output = manager.createMatrix();
+            ret.output = output;
+            final VariableMatrix m;
+            final VariableScalar s;
+
+            if( A instanceof VariableMatrix ) {
+                m = (VariableMatrix)A;
+                s = (VariableScalar)B;
+            } else {
+                m = (VariableMatrix)B;
+                s = (VariableScalar)A;
+            }
+
+            ret.op = new Operation("add-ms") {
+                @Override
+                public void process() {
+                    output.matrix.reshape(m.matrix.numRows,m.matrix.numCols);
+                    CommonOps.add(m.matrix, s.getDouble(), output.matrix);
+                }
+            };
+        }
+
+        return ret;
+    }
+
+    public static Info subtract(final Variable A, final Variable B, ManagerTempVariables manager) {
+        Info ret = new Info();
+
+        if( A instanceof VariableMatrix && B instanceof VariableMatrix ) {
+            final VariableMatrix output = manager.createMatrix();
+            ret.output = output;
+            ret.op = new Operation("subtract-mm") {
+                @Override
+                public void process() {
+                    VariableMatrix mA = (VariableMatrix)A;
+                    VariableMatrix mB = (VariableMatrix)B;
+
+                    resize(output, mA.matrix.numRows, mA.matrix.numCols);
+                    CommonOps.subtract(mA.matrix, mB.matrix, output.matrix);
+                }
+            };
+        } else if( A instanceof VariableInteger && B instanceof VariableInteger ) {
+            final VariableInteger output = manager.createInteger(0);
+            ret.output = output;
+            ret.op = new Operation("subtract-ii") {
+                @Override
+                public void process() {
+                    VariableInteger mA = (VariableInteger)A;
+                    VariableInteger mB = (VariableInteger)B;
+
+                    output.value = mA.value - mB.value;
+                }
+            };
+        } else if( A instanceof VariableScalar && B instanceof VariableScalar ) {
+            final VariableDouble output = manager.createDouble();
+            ret.output = output;
+            ret.op = new Operation("subtract-ss") {
+                @Override
+                public void process() {
+                    VariableScalar mA = (VariableScalar)A;
+                    VariableScalar mB = (VariableScalar)B;
+
+                    output.value = mA.getDouble() - mB.getDouble();
+                }
+            };
+        } else {
+            final VariableMatrix output = manager.createMatrix();
+            ret.output = output;
+
+            if( A instanceof VariableMatrix ) {
+                ret.op = new Operation("subtract-ms") {
+                    @Override
+                    public void process() {
+                        DenseMatrix64F m = ((VariableMatrix)A).matrix;
+                        double v = ((VariableScalar)B).getDouble();
+                        output.matrix.reshape(m.numRows, m.numCols);
+                        CommonOps.subtract(m, v, output.matrix);
+                    }
+                };
+            } else {
+                ret.op = new Operation("subtract-sm") {
+                    @Override
+                    public void process() {
+                        DenseMatrix64F m = ((VariableMatrix)B).matrix;
+                        double v = ((VariableScalar)A).getDouble();
+                        output.matrix.reshape(m.numRows, m.numCols);
+                        CommonOps.subtract(v, m, output.matrix);
+                    }
+                };
+            }
+        }
+
+        return ret;
+    }
+
+    public static Info elementMult( final Variable A , final Variable B , ManagerTempVariables manager ) {
+        Info ret = new Info();
+
+        if( A instanceof VariableMatrix && B instanceof VariableMatrix ) {
+            final VariableMatrix output = manager.createMatrix();
+            ret.output = output;
+            ret.op = new Operation("elementMult-mm") {
+                @Override
+                public void process() {
+                    VariableMatrix mA = (VariableMatrix)A;
+                    VariableMatrix mB = (VariableMatrix)B;
+
+                    resize(output, mA.matrix.numRows, mA.matrix.numCols);
+                    CommonOps.elementMult(mA.matrix, mB.matrix, output.matrix);
+                }
+            };
+        } else {
+            throw new RuntimeException("Both inputs must be matrices for element wise multiplication");
+        }
+
+        return ret;
+    }
+
+    public static Info elementDivision( final Variable A , final Variable B , ManagerTempVariables manager ) {
+        Info ret = new Info();
+
+        if( A instanceof VariableMatrix && B instanceof VariableMatrix ) {
+            final VariableMatrix output = manager.createMatrix();
+            ret.output = output;
+            ret.op = new Operation("elementDivision-mm") {
+                @Override
+                public void process() {
+                    VariableMatrix mA = (VariableMatrix)A;
+                    VariableMatrix mB = (VariableMatrix)B;
+
+                    resize(output, mA.matrix.numRows, mA.matrix.numCols);
+                    CommonOps.elementDiv(mA.matrix, mB.matrix, output.matrix);
+                }
+            };
+        } else {
+            throw new RuntimeException("Both inputs must be matrices for element wise multiplication");
+        }
+
+        return ret;
+    }
+
+    public static Info elementPow(final Variable A, final Variable B, ManagerTempVariables manager) {
+        Info ret = new Info();
+
+
+        if( A instanceof VariableScalar && B instanceof VariableScalar ) {
+
+            final VariableDouble output = manager.createDouble();
+            ret.output = output;
+
+            ret.op = new Operation("elementPow-ss") {
+                @Override
+                public void process() {
+                    double a = ((VariableScalar) A).getDouble();
+                    double b = ((VariableScalar) B).getDouble();
+
+                    output.value = Math.pow(a, b);
+                }
+            };
+        } else if( A instanceof VariableMatrix && B instanceof VariableMatrix ) {
+
+            final VariableMatrix output = manager.createMatrix();
+            ret.output = output;
+
+            ret.op = new Operation("elementPow-mm") {
+                @Override
+                public void process() {
+                    DenseMatrix64F a = ((VariableMatrix) A).matrix;
+                    DenseMatrix64F b = ((VariableMatrix) B).matrix;
+
+                    resize(output, a.numRows, a.numCols);
+                    CommonOps.elementPower(a, b, output.matrix);
+                }
+            };
+        } else if( A instanceof VariableMatrix && B instanceof VariableScalar ) {
+
+            final VariableMatrix output = manager.createMatrix();
+            ret.output = output;
+
+            ret.op = new Operation("elementPow-ms") {
+                @Override
+                public void process() {
+                    DenseMatrix64F a = ((VariableMatrix) A).matrix;
+                    double b = ((VariableScalar) B).getDouble();
+
+                    resize(output, a.numRows, a.numCols);
+                    CommonOps.elementPower(a, b, output.matrix);
+                }
+            };
+        } else if( A instanceof VariableScalar && B instanceof VariableMatrix ) {
+
+            final VariableMatrix output = manager.createMatrix();
+            ret.output = output;
+
+            ret.op = new Operation("elementPow-sm") {
+                @Override
+                public void process() {
+                    double a = ((VariableScalar) A).getDouble();
+                    DenseMatrix64F b = ((VariableMatrix) B).matrix;
+
+                    resize(output, b.numRows, b.numCols);
+                    CommonOps.elementPower(a, b, output.matrix);
+                }
+            };
+        } else {
+            throw new RuntimeException("Unsupport element-wise power input types");
+        }
+
+        return ret;
+    }
+
+    public static Operation copy( final Variable src , final Variable dst ) {
+
+        if( src instanceof VariableMatrix  ) {
+            if( dst instanceof VariableMatrix ) {
+                return new Operation("copy-mm") {
+                    @Override
+                    public void process() {
+                        DenseMatrix64F d = ((VariableMatrix) dst).matrix;
+                        DenseMatrix64F s = ((VariableMatrix) src).matrix;
+                        d.reshape(s.numRows, s.numCols);
+                        d.set(((VariableMatrix) src).matrix);
+                    }
+                };
+            } else if( dst instanceof VariableDouble ) {
+                return new Operation("copy-sm1") {
+                    @Override
+                    public void process() {
+                        DenseMatrix64F s = ((VariableMatrix) src).matrix;
+                        if( s.numRows != 1 || s.numCols != 1 ) {
+                            throw new RuntimeException("Attempting to assign a non 1x1 matrix to a double");
+                        }
+                        ((VariableDouble) dst).value = s.unsafe_get(0,0);
+
+                    }
+                };
+            }
+        }
+        if( src instanceof VariableInteger && dst instanceof VariableInteger ) {
+            return new Operation("copy-ii") {
+                @Override
+                public void process() {
+                    ((VariableInteger)dst).value = ((VariableInteger)src).value;
+                }
+            };
+        }
+        if( src instanceof VariableScalar && dst instanceof VariableDouble ) {
+            return new Operation("copy-ss") {
+                @Override
+                public void process() {
+                    ((VariableDouble)dst).value = ((VariableScalar)src).getDouble();
+                }
+            };
+        }
+        throw new RuntimeException("Copy type miss-match src = "+src.getClass().getSimpleName()+" dst = "+dst.getClass().getSimpleName());
+    }
+
+    public static Operation copy( final Variable src , final Variable dst , final List<Variable> range ) {
+        if( src instanceof VariableMatrix && dst instanceof VariableMatrix ) {
+            return new Operation("copyR-mm") {
+                Extents extents = new Extents();
+
+                @Override
+                public void process() {
+
+                    DenseMatrix64F msrc = ((VariableMatrix) src).matrix;
+                    DenseMatrix64F mdst = ((VariableMatrix) dst).matrix;
+
+                    findExtents(mdst, range, 0, extents);
+
+                    if (extents.col1 - extents.col0 != msrc.numCols)
+                        throw new RuntimeException("Columns don't match");
+                    if (extents.row1 - extents.row0 != msrc.numRows)
+                        throw new RuntimeException("Rows don't match");
+
+                    CommonOps.insert(msrc, mdst, extents.row0, extents.col0);
+                }
+            };
+        } else if( src instanceof VariableScalar && dst instanceof VariableMatrix ) {
+            return new Operation("copyR-sm") {
+                Extents extents = new Extents();
+
+                @Override
+                public void process() {
+
+                    double msrc = ((VariableScalar)src).getDouble();
+                    DenseMatrix64F mdst = ((VariableMatrix)dst).matrix;
+
+                    findExtents(mdst,range,0,extents);
+
+                    if( !mdst.isInBounds(extents.row0,extents.col0))
+                        throw new RuntimeException("Submatrix out of bounds. Lower extent");
+                    if( !mdst.isInBounds(extents.row1-1,extents.col1-1))
+                        throw new RuntimeException("Submatrix out of bounds. Upper extent");
+
+                    for (int i = extents.row0; i < extents.row1; i++) {
+                        int index = i*mdst.numCols + extents.col0;
+                        for (int j = extents.col0; j < extents.col1; j++) {
+                            mdst.data[index++] = msrc;
+                        }
+                    }
+                }
+            };
+        } else {
+            throw new RuntimeException("Both variables must be of type VariableMatrix");
+        }
+    }
+
+    public static Info transpose( final Variable A , ManagerTempVariables manager) {
+        Info ret = new Info();
+
+        if( A instanceof VariableMatrix ) {
+            final VariableMatrix output = manager.createMatrix();
+            ret.output = output;
+            ret.op = new Operation("transpose-m") {
+                @Override
+                public void process() {
+                    VariableMatrix mA = (VariableMatrix)A;
+                    output.matrix.reshape(mA.matrix.numCols, mA.matrix.numRows);
+                    CommonOps.transpose(mA.matrix, output.matrix);
+                }
+            };
+        } else {
+            throw new RuntimeException("Transpose only makes sense for a matrix");
+        }
+        return ret;
+    }
+
+    /**
+     * Matrix inverse
+     */
+    public static Info inv( final Variable A , ManagerTempVariables manager) {
+        Info ret = new Info();
+
+        if( A instanceof VariableMatrix ) {
+            final VariableMatrix output = manager.createMatrix();
+            ret.output = output;
+            ret.op = new Operation("inv-m") {
+                @Override
+                public void process() {
+                    VariableMatrix mA = (VariableMatrix)A;
+                    output.matrix.reshape(mA.matrix.numRows,mA.matrix.numCols);
+                    if( !CommonOps.invert(mA.matrix,output.matrix) )
+                        throw new RuntimeException("Inverse failed!");
+                }
+            };
+        } else {
+            final VariableDouble output = manager.createDouble();
+            ret.output = output;
+            ret.op = new Operation("inv-s") {
+                @Override
+                public void process() {
+                    VariableScalar mA = (VariableScalar)A;
+                    output.value = 1.0/mA.getDouble();
+                }
+            };
+        }
+
+        return ret;
+    }
+
+    /**
+     * Matrix pseudo-inverse
+     */
+    public static Info pinv( final Variable A , ManagerTempVariables manager) {
+        Info ret = new Info();
+
+        if( A instanceof VariableMatrix ) {
+            final VariableMatrix output = manager.createMatrix();
+            ret.output = output;
+            ret.op = new Operation("pinv-m") {
+                @Override
+                public void process() {
+                    VariableMatrix mA = (VariableMatrix)A;
+                    output.matrix.reshape(mA.matrix.numCols, mA.matrix.numRows);
+                    CommonOps.pinv(mA.matrix, output.matrix);
+                }
+            };
+        } else {
+            final VariableDouble output = manager.createDouble();
+            ret.output = output;
+            ret.op = new Operation("pinv-s") {
+                @Override
+                public void process() {
+                    VariableScalar mA = (VariableScalar)A;
+                    output.value = 1.0/mA.getDouble();
+                }
+            };
+        }
+
+        return ret;
+    }
+
+    public static Info rref( final Variable A , ManagerTempVariables manager) {
+        Info ret = new Info();
+
+        if( A instanceof VariableMatrix ) {
+            final VariableMatrix output = manager.createMatrix();
+            ret.output = output;
+            ret.op = new Operation("rref-m") {
+                @Override
+                public void process() {
+                    DenseMatrix64F a = ((VariableMatrix)A).matrix;
+                    output.matrix.reshape(a.numRows,a.numCols);
+                    CommonOps.rref(a, -1, output.matrix);
+                }
+            };
+        } else {
+            final VariableDouble output = manager.createDouble();
+            ret.output = output;
+            ret.op = new Operation("rref-s") {
+                @Override
+                public void process() {
+                    double a = ((VariableScalar)A).getDouble();
+                    output.value = a == 0 ? 0 : 1;
+                }
+            };
+        }
+
+        return ret;
+    }
+
+    /**
+     * Matrix determinant
+     */
+    public static Info det( final Variable A , ManagerTempVariables manager) {
+        Info ret = new Info();
+
+        final VariableDouble output = manager.createDouble();
+        ret.output = output;
+
+        if( A instanceof VariableMatrix ) {
+            ret.op = new Operation("det-m") {
+                @Override
+                public void process() {
+                    VariableMatrix mA = (VariableMatrix)A;
+                    output.value = CommonOps.det(mA.matrix);
+                }
+            };
+        } else {
+            ret.op = new Operation("det-s") {
+                @Override
+                public void process() {
+                    VariableScalar mA = (VariableScalar)A;
+                    output.value = mA.getDouble();
+                }
+            };
+        }
+
+        return ret;
+    }
+
+    public static Info trace( final Variable A , ManagerTempVariables manager) {
+        Info ret = new Info();
+        final VariableDouble output = manager.createDouble();
+        ret.output = output;
+
+        if( A instanceof VariableMatrix ) {
+            ret.op = new Operation("trace-m") {
+                @Override
+                public void process() {
+                    VariableMatrix mA = (VariableMatrix)A;
+                    output.value=CommonOps.trace(mA.matrix);
+                }
+            };
+        } else {
+            ret.op = new Operation("trace-s") {
+                @Override
+                public void process() {
+                    VariableScalar mA = (VariableScalar)A;
+                    output.value = mA.getDouble();
+                }
+            };
+        }
+
+        return ret;
+    }
+
+    public static Info normF( final Variable A , ManagerTempVariables manager) {
+        Info ret = new Info();
+        final VariableDouble output = manager.createDouble();
+        ret.output = output;
+
+        if( A instanceof VariableMatrix ) {
+            ret.op = new Operation("normF-m") {
+                @Override
+                public void process() {
+                    output.value= NormOps.normF(((VariableMatrix) A).matrix);
+                }
+            };
+        } else {
+            ret.op = new Operation("normF-s") {
+                @Override
+                public void process() {
+                    output.value = Math.abs(((VariableScalar) A).getDouble());
+                }
+            };
+        }
+
+        return ret;
+    }
+
+
+    public static Info max( final Variable A , ManagerTempVariables manager) {
+        Info ret = new Info();
+
+        if( A instanceof VariableMatrix ) {
+            final VariableDouble output = manager.createDouble();
+            ret.output = output;
+            ret.op = new Operation("max-m") {
+                @Override
+                public void process() {
+                    output.value = CommonOps.elementMax(((VariableMatrix) A).matrix);
+                }
+            };
+        } else if( A instanceof VariableInteger ) {
+            final VariableInteger output = manager.createInteger();
+            ret.output = output;
+            ret.op = new Operation("max-i") {
+                @Override
+                public void process() {
+                    output.value = ((VariableInteger)A).value;
+                }
+            };
+        } else if( A instanceof VariableScalar ) {
+            final VariableDouble output = manager.createDouble();
+            ret.output = output;
+            ret.op = new Operation("max-s") {
+                @Override
+                public void process() {
+                    output.value = ((VariableDouble)A).getDouble();
+                }
+            };
+        }
+
+        return ret;
+    }
+
+    public static Info min( final Variable A , ManagerTempVariables manager) {
+        Info ret = new Info();
+
+        if( A instanceof VariableMatrix ) {
+            final VariableDouble output = manager.createDouble();
+            ret.output = output;
+            ret.op = new Operation("min-m") {
+                @Override
+                public void process() {
+                    output.value = CommonOps.elementMin(((VariableMatrix) A).matrix);
+                }
+            };
+        } else if( A instanceof VariableInteger ) {
+            final VariableInteger output = manager.createInteger();
+            ret.output = output;
+            ret.op = new Operation("min-i") {
+                @Override
+                public void process() {
+                    output.value = ((VariableInteger)A).value;
+                }
+            };
+        } else if( A instanceof VariableScalar ) {
+            final VariableDouble output = manager.createDouble();
+            ret.output = output;
+            ret.op = new Operation("min-s") {
+                @Override
+                public void process() {
+                    output.value = ((VariableDouble)A).getDouble();
+                }
+            };
+        }
+
+        return ret;
+    }
+
+    public static Info abs( final Variable A , ManagerTempVariables manager) {
+        Info ret = new Info();
+
+        if( A instanceof VariableMatrix ) {
+            final VariableMatrix output = manager.createMatrix();
+            ret.output = output;
+            ret.op = new Operation("abs-m") {
+                @Override
+                public void process() {
+                    DenseMatrix64F a = ((VariableMatrix)A).matrix;
+                    output.matrix.reshape(a.numRows,a.numCols);
+                    int N = a.getNumElements();
+                    for (int i = 0; i < N; i++) {
+                        output.matrix.data[i] = Math.abs(a.data[i]);
+                    }
+                }
+            };
+        } else if( A instanceof VariableInteger ) {
+            final VariableInteger output = manager.createInteger();
+            ret.output = output;
+            ret.op = new Operation("abs-i") {
+                @Override
+                public void process() {
+                    output.value = Math.abs(((VariableInteger)A).value);
+                }
+            };
+        } else if( A instanceof VariableScalar ) {
+            final VariableDouble output = manager.createDouble();
+            ret.output = output;
+            ret.op = new Operation("abs-s") {
+                @Override
+                public void process() {
+                    output.value = Math.abs((((VariableDouble) A).getDouble()));
+                }
+            };
+        }
+
+        return ret;
+    }
+
+    /**
+     * Returns an identity matrix
+     */
+    public static Info eye( final Variable A , ManagerTempVariables manager) {
+        Info ret = new Info();
+        final VariableMatrix output = manager.createMatrix();
+        ret.output = output;
+
+        if( A instanceof VariableMatrix ) {
+            ret.op = new Operation("eye-m") {
+                @Override
+                public void process() {
+                    DenseMatrix64F mA = ((VariableMatrix)A).matrix;
+                    output.matrix.reshape(mA.numRows,mA.numCols);
+                    CommonOps.setIdentity(output.matrix);
+                }
+            };
+        } else if( A instanceof VariableInteger ) {
+            ret.op = new Operation("eye-i") {
+                @Override
+                public void process() {
+                    int N = ((VariableInteger)A).value;
+                    output.matrix.reshape(N,N);
+                    CommonOps.setIdentity(output.matrix);
+                }
+            };
+        } else {
+            throw new RuntimeException("Unsupported variable type "+A);
+        }
+
+        return ret;
+    }
+
+    public static Info diag( final Variable A , ManagerTempVariables manager) {
+        Info ret = new Info();
+
+        if( A instanceof VariableMatrix ) {
+            final VariableMatrix output = manager.createMatrix();
+            ret.output = output;
+            ret.op = new Operation("diag-m") {
+                @Override
+                public void process() {
+                    DenseMatrix64F mA = ((VariableMatrix)A).matrix;
+
+                    if(MatrixFeatures.isVector(mA)) {
+                        int N = mA.getNumElements();
+                        output.matrix.reshape(N,N);
+                        CommonOps.diag(output.matrix,N,mA.data);
+                    } else {
+                        int N = Math.min(mA.numCols,mA.numRows);
+                        output.matrix.reshape(N,1);
+                        for (int i = 0; i < N; i++) {
+                            output.matrix.data[i] = mA.unsafe_get(i,i);
+                        }
+                    }
+                }
+            };
+        } else {
+            throw new RuntimeException("diag requires a matrix as input");
+        }
+        return ret;
+    }
+
+    /**
+     * Returns a matrix full of zeros
+     */
+    public static Info zeros( final Variable A , final Variable B , ManagerTempVariables manager) {
+        Info ret = new Info();
+        final VariableMatrix output = manager.createMatrix();
+        ret.output = output;
+
+        if( A instanceof VariableInteger && B instanceof VariableInteger ) {
+            ret.op = new Operation("zeros-ii") {
+                @Override
+                public void process() {
+                    int numRows = ((VariableInteger)A).value;
+                    int numCols = ((VariableInteger)B).value;
+                    output.matrix.reshape(numRows,numCols);
+                    CommonOps.fill(output.matrix,0);
+                    //not sure if this is necessary.  Can its value every be modified?
+                }
+            };
+        } else {
+            throw new RuntimeException("Expected two integers got "+A+" "+B);
+        }
+
+        return ret;
+    }
+
+    /**
+     * Returns a matrix full of ones
+     */
+    public static Info ones( final Variable A , final Variable B , ManagerTempVariables manager) {
+        Info ret = new Info();
+        final VariableMatrix output = manager.createMatrix();
+        ret.output = output;
+
+        if( A instanceof VariableInteger && B instanceof VariableInteger ) {
+            ret.op = new Operation("ones-ii") {
+                @Override
+                public void process() {
+                    int numRows = ((VariableInteger)A).value;
+                    int numCols = ((VariableInteger)B).value;
+                    output.matrix.reshape(numRows,numCols);
+                    CommonOps.fill(output.matrix,1);
+                }
+            };
+        } else {
+            throw new RuntimeException("Expected two integers got "+A+" "+B);
+        }
+
+        return ret;
+    }
+
+    /**
+     * Kronecker product
+     */
+    public static Info kron( final Variable A , final Variable B, ManagerTempVariables manager) {
+        Info ret = new Info();
+        final VariableMatrix output = manager.createMatrix();
+        ret.output = output;
+
+        if( A instanceof VariableMatrix && B instanceof VariableMatrix ) {
+            ret.op = new Operation("kron-mm") {
+                @Override
+                public void process() {
+                    DenseMatrix64F mA = ((VariableMatrix)A).matrix;
+                    DenseMatrix64F mB = ((VariableMatrix)B).matrix;
+                    output.matrix.reshape(mA.numRows * mB.numRows, mA.numCols * mB.numCols);
+                    CommonOps.kron(mA, mB, output.matrix);
+                }
+            };
+        } else {
+            throw new RuntimeException("Both inputs must be matrices ");
+        }
+
+        return ret;
+    }
+
+    /**
+     * If input is two vectors then it returns the dot product as a double.
+     */
+    public static Info dot( final Variable A , final Variable B , ManagerTempVariables manager) {
+        Info ret = new Info();
+        final VariableDouble output = manager.createDouble();
+        ret.output = output;
+
+        if( A instanceof VariableMatrix && B instanceof VariableMatrix ) {
+            ret.op = new Operation("dot-mm") {
+                @Override
+                public void process() {
+                    DenseMatrix64F a = ((VariableMatrix)A).matrix;
+                    DenseMatrix64F b = ((VariableMatrix)B).matrix;
+
+                    if( !MatrixFeatures.isVector(a) || !MatrixFeatures.isVector(b))
+                        throw new RuntimeException("Both inputs to dot() must be vectors");
+
+                    output.value = VectorVectorMult.innerProd(a,b);
+                }
+            };
+        } else {
+            throw new RuntimeException("Expected two matrices got "+A+" "+B);
+        }
+
+        return ret;
+    }
+
+    /**
+     * If input is two vectors then it returns the dot product as a double.
+     */
+    public static Info solve( final Variable A , final Variable B , ManagerTempVariables manager) {
+        Info ret = new Info();
+        final VariableMatrix output = manager.createMatrix();
+        ret.output = output;
+
+        if( A instanceof VariableMatrix && B instanceof VariableMatrix ) {
+            ret.op = new Operation("solve-mm") {
+                LinearSolver<DenseMatrix64F> solver;
+                @Override
+                public void process() {
+
+                    DenseMatrix64F a = ((VariableMatrix)A).matrix;
+                    DenseMatrix64F b = ((VariableMatrix)B).matrix;
+
+                    if( solver == null ) {
+                        solver = LinearSolverFactory.leastSquares(a.numRows,a.numCols);
+                    }
+
+                    if( !solver.setA(a))
+                        throw new RuntimeException("Solver failed!");
+
+                    output.matrix.reshape(a.numCols,b.numCols);
+                    solver.solve(b, output.matrix);
+                }
+            };
+        } else {
+            throw new RuntimeException("Expected two matrices got "+A+" "+B);
+        }
+
+        return ret;
+    }
+
+    public static Info extract( final List<Variable> inputs, ManagerTempVariables manager) {
+        Info ret = new Info();
+        final VariableMatrix output = manager.createMatrix();
+        ret.output = output;
+
+        if(  !(inputs.get(0) instanceof VariableMatrix))
+            throw new RuntimeException("First parameter must be a matrix.");
+
+        for (int i = 1; i < inputs.size(); i++) {
+            if( !(inputs.get(i) instanceof VariableInteger) && !(inputs.get(i) instanceof VariableSpecial) )
+                throw new RuntimeException("Last parameters must be integers or special for sub");
+        }
+
+        ret.op = new Operation("extract") {
+
+            Extents extents = new Extents();
+
+            @Override
+            public void process() {
+
+                DenseMatrix64F A = ((VariableMatrix)inputs.get(0)).matrix;
+
+                findExtents(A,inputs,1,extents);
+
+                output.matrix.reshape(extents.row1-extents.row0,extents.col1-extents.col0);
+                CommonOps.extract(A,extents.row0,extents.row1,extents.col0,extents.col1,output.matrix,0,0);
+            }
+        };
+
+        return ret;
+    }
+
+    public static Info extractScalar( final List<Variable> inputs, ManagerTempVariables manager) {
+        Info ret = new Info();
+        final VariableDouble output = manager.createDouble();
+        ret.output = output;
+
+        if(  !(inputs.get(0) instanceof VariableMatrix))
+            throw new RuntimeException("First parameter must be a matrix.");
+
+        for (int i = 1; i < 3; i++) {
+            if( !(inputs.get(i) instanceof VariableInteger) )
+                throw new RuntimeException("Parameters must be integers for extract scalar");
+        }
+
+        ret.op = new Operation("extractScalar") {
+
+            Extents extents = new Extents();
+
+            @Override
+            public void process() {
+
+                DenseMatrix64F A = ((VariableMatrix)inputs.get(0)).matrix;
+
+                int row = ((VariableInteger)inputs.get(1)).value;
+                int col = ((VariableInteger)inputs.get(2)).value;
+
+                output.value = A.get(row,col);
+            }
+        };
+
+        return ret;
+    }
+
+    /**
+     * Parses the inputs to figure out the range of a sub-matrix.  Special variables are handled to set
+     * relative values
+     */
+    protected static void findExtents( DenseMatrix64F A, List<Variable> inputs , int where ,Extents e ) {
+        if( inputs.get(where).getType() == VariableType.SPECIAL ) {
+            e.row0 = 0; e.row1 = A.numRows; where++;
+        } else {
+            e.row0 = ((VariableInteger)inputs.get(where++)).value;
+            if( inputs.get(where).getType() == VariableType.SPECIAL ) {
+                e.row1 = A.numRows;
+            } else {
+                e.row1 = ((VariableInteger)inputs.get(where)).value+1;
+            }
+            where++;
+        }
+
+        if( inputs.get(where).getType() == VariableType.SPECIAL ) {
+            e.col0 = 0; e.col1 = A.numCols; where++;
+        } else {
+            e.col0 = ((VariableInteger)inputs.get(where++)).value;
+            if( inputs.get(where).getType() == VariableType.SPECIAL ) {
+                e.col1 = A.numCols;
+            } else {
+                e.col1 = ((VariableInteger)inputs.get(where)).value+1;
+            }
+            where++;
+        }
+
+        if( where != inputs.size())
+            throw new RuntimeException("Unexpected number of inputs");
+    }
+
+    public static Info matrixConstructor( final MatrixConstructor m ) {
+        Info ret = new Info();
+        ret.output = m.getOutput();
+
+        ret.op = new Operation("matrixConstructor") {
+
+            @Override
+            public void process() {
+                m.construct();
+            }
+        };
+
+        return ret;
+    }
+
+    public static class Extents
+    {
+        int row0,row1;
+        int col0,col1;
+    }
+
+    public static class Info
+    {
+        public Operation op;
+        public Variable output;
+    }
+}
diff --git a/main/equation/src/org/ejml/equation/Sequence.java b/main/equation/src/org/ejml/equation/Sequence.java
new file mode 100644
index 0000000..167722a
--- /dev/null
+++ b/main/equation/src/org/ejml/equation/Sequence.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.equation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Contains a sequence of operations.  This is the final result of compiling the equation.  Once created it can
+ * be invoked an arbitrary number of times by invoking {@link #perform()}.
+ *
+ * @author Peter Abeles
+ */
+public class Sequence {
+    // List of in sequence operations which the equation string described
+    List<Operation> operations = new ArrayList<Operation>();
+
+    public void addOperation( Operation operation ) {
+        operations.add(operation);
+    }
+
+    /**
+     * Executes the sequence of operations
+     */
+    public void perform() {
+        for (int i = 0; i < operations.size(); i++) {
+            operations.get(i).process();
+        }
+    }
+}
diff --git a/main/equation/src/org/ejml/equation/Symbol.java b/main/equation/src/org/ejml/equation/Symbol.java
new file mode 100644
index 0000000..444bec0
--- /dev/null
+++ b/main/equation/src/org/ejml/equation/Symbol.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.equation;
+
+/**
+ * Types of low level operators which can be applied in the code
+ *
+ * @author Peter Abeles
+ */
+public enum Symbol {
+    PLUS,
+    MINUS,
+    TIMES,
+    LDIVIDE,
+    RDIVIDE,
+    POWER,
+    PERIOD,
+    ELEMENT_TIMES,
+    ELEMENT_DIVIDE,
+    ELEMENT_POWER,
+    ASSIGN,
+    PAREN_LEFT,
+    PAREN_RIGHT,
+    BRACKET_LEFT,
+    BRACKET_RIGHT,
+    COMMA,
+    TRANSPOSE,
+    COLON,
+    SEMICOLON;
+
+    public static Symbol lookup( char c ) {
+        switch( c ) {
+            case '.': return PERIOD;
+            case ',': return COMMA;
+            case '\'': return TRANSPOSE;
+            case '+': return PLUS;
+            case '-': return MINUS;
+            case '*': return TIMES;
+            case '\\': return LDIVIDE;
+            case '/': return RDIVIDE;
+            case '^': return POWER;
+            case '=': return ASSIGN;
+            case '(': return PAREN_LEFT;
+            case ')': return PAREN_RIGHT;
+            case '[': return BRACKET_LEFT;
+            case ']': return BRACKET_RIGHT;
+            case ':': return COLON;
+            case ';': return SEMICOLON;
+        }
+        throw new RuntimeException("Unknown type "+c);
+    }
+
+    public static Symbol lookupElementWise( char c ) {
+        switch( c ) {
+            case '*': return ELEMENT_TIMES;
+            case '/': return ELEMENT_DIVIDE;
+            case '^': return ELEMENT_POWER;
+        }
+        throw new RuntimeException("Unknown element-wise type "+c);
+    }
+}
diff --git a/main/equation/src/org/ejml/equation/TokenList.java b/main/equation/src/org/ejml/equation/TokenList.java
new file mode 100644
index 0000000..86b80f8
--- /dev/null
+++ b/main/equation/src/org/ejml/equation/TokenList.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.equation;
+
+/**
+ * Linked-list of tokens parsed from the equations string.
+ *
+ * @author Peter Abeles
+ */
+class TokenList {
+
+    Token first;
+    Token last;
+    int size = 0;
+
+    public TokenList() {
+    }
+
+    /**
+     * Creates a list from the two given tokens.  These tokens are assumed to form a linked list starting at 'first'
+     * and ending at 'last'
+     * @param first First element in the new list
+     * @param last Last element in the new list
+     */
+    public TokenList(Token first, Token last) {
+        this.first = first;
+        this.last = last;
+
+        Token t = first;
+        while( t != null ) {
+            size++;
+            t = t.next;
+        }
+    }
+
+    /**
+     * Adds a function to the end of the token list
+     * @param function Function which is to be added
+     * @return The new Token created around function
+     */
+    public Token add( Function function ) {
+        Token t = new Token(function);
+        push( t );
+        return t;
+    }
+
+    /**
+     * Adds a variable to the end of the token list
+     * @param variable Variable which is to be added
+     * @return The new Token created around variable
+     */
+    public Token add( Variable variable ) {
+        Token t = new Token(variable);
+        push( t );
+        return t;
+    }
+
+    /**
+     * Adds a symbol to the end of the token list
+     * @param symbol Symbol which is to be added
+     * @return The new Token created around symbol
+     */
+    public Token add( Symbol symbol ) {
+        Token t = new Token(symbol);
+        push( t );
+        return t;
+    }
+
+    /**
+     * Adds a word to the end of the token list
+     * @param word word which is to be added
+     * @return The new Token created around symbol
+     */
+    public Token add( String word ) {
+        Token t = new Token(word);
+        push( t );
+        return t;
+    }
+
+    /**
+     * Adds a new Token to the end of the linked list
+     */
+    public void push( Token token ) {
+        size++;
+        if( first == null ) {
+            first = token;
+            last = token;
+            token.previous = null;
+            token.next = null;
+        } else {
+            last.next = token;
+            token.previous = last;
+            token.next = null;
+            last = token;
+        }
+    }
+
+    /**
+     * Inserts 'token' after 'where'.  if where is null then it is inserted to the beginning of the list.
+     * @param where Where 'token' should be inserted after.  if null the put at it at the beginning
+     * @param token The token that is to be inserted
+     */
+    public void insert( Token where , Token token ) {
+        if( where == null ) {
+            // put at the front of the list
+            if( size == 0 )
+                push(token);
+            else {
+                first.previous = token;
+                token.previous = null;
+                token.next = first;
+                first = token;
+                size++;
+            }
+        } else if( where == last || null == last ) {
+            push(token);
+        } else {
+            token.next = where.next;
+            token.previous = where;
+            where.next.previous = token;
+            where.next = token;
+            size++;
+        }
+    }
+
+    /**
+     * Removes the token from the list
+     * @param token Token which is to be removed
+     */
+    public void remove( Token token ) {
+        if( token == first ) {
+            first = first.next;
+        }
+        if( token == last ) {
+            last = last.previous;
+        }
+        if( token.next != null ) {
+            token.next.previous = token.previous;
+        }
+        if( token.previous != null ) {
+            token.previous.next = token.next;
+        }
+
+        token.next = token.previous = null;
+        size--;
+    }
+
+    /**
+     * Removes 'original' and places 'target' at the same location
+     */
+    public void replace( Token original , Token target  ) {
+        if( first == original )
+            first = target;
+        if( last == original )
+            last = target;
+
+        target.next = original.next;
+        target.previous = original.previous;
+
+        if( original.next != null )
+            original.next.previous = target;
+        if( original.previous != null )
+            original.previous.next = target;
+
+        original.next = original.previous = null;
+    }
+
+    /**
+     * Removes elements from begin to end from the list, inclusive.  Returns a new list which
+     * is composed of the removed elements
+     */
+    public TokenList extractSubList( Token begin , Token end ) {
+        if( begin == end ) {
+            remove(begin);
+            return new TokenList(begin,begin);
+        } else {
+            if( first == begin ) {
+                first = end.next;
+            }
+            if( last == end ) {
+                last = begin.previous;
+            }
+            if( begin.previous != null ) {
+                begin.previous.next = end.next;
+            }
+            if( end.next != null ) {
+                end.next.previous = begin.previous;
+            }
+            begin.previous = null;
+            end.next = null;
+
+            TokenList ret = new TokenList(begin,end);
+            size -= ret.size();
+            return ret;
+        }
+    }
+
+    /**
+     * Prints the list of tokens
+     */
+    public String toString() {
+        String ret = "";
+        Token t = first;
+        while( t != null ) {
+            ret += t +" ";
+            t = t.next;
+        }
+        return ret;
+    }
+
+    /**
+     * First token in the list
+     */
+    public Token getFirst() {
+        return first;
+    }
+
+    /**
+     * Last token in the list
+     */
+    public Token getLast() {
+        return last;
+    }
+
+    /**
+     * Number of tokens in the list
+     */
+    public int size() {
+        return size;
+    }
+
+    /**
+     * The token class contains a reference to parsed data (e.g. function, variable, or symbol) and reference
+     * to list elements before and after it.
+     */
+    public static class Token {
+        /**
+         * Next element in the list.  If null then it's at the end of the list
+         */
+        public Token next;
+        /**
+         * Previous element in the list.  If null then it's the first element in the list
+         */
+        public Token previous;
+
+        public Function function;
+        public Variable variable;
+        public Symbol symbol;
+        public String word;
+
+        public Token(Function function) {
+            this.function = function;
+        }
+
+        public Token(Variable variable) {
+            this.variable = variable;
+        }
+
+        public Token(VariableSpecial.Special special) {
+            this.variable = new VariableSpecial(special);
+        }
+
+        public Token(Symbol symbol) {
+            this.symbol = symbol;
+        }
+
+        public Token(String word) {
+            this.word = word;
+        }
+
+        public Type getType() {
+            if( function != null )
+                return Type.FUNCTION;
+            else if( variable != null )
+                return Type.VARIABLE;
+            else if( word != null )
+                return Type.WORD;
+            else
+                return Type.SYMBOL;
+        }
+
+        public Variable getVariable() {
+            return variable;
+        }
+
+        public Function getFunction() {
+            return function;
+        }
+
+        public Symbol getSymbol() {
+            return symbol;
+        }
+
+        public String getWord() {
+            return word;
+        }
+
+        public String toString() {
+            switch( getType() ) {
+                case FUNCTION:
+                    return "Func:"+function.getName();
+                case SYMBOL:
+                    return ""+symbol;
+                case VARIABLE:
+                    return variable.toString();
+                case WORD:
+                    return "Word:"+word;
+            }
+            throw new RuntimeException("Unknown type");
+        }
+    }
+
+    public void print() {
+        Token t = first;
+        while( t != null ) {
+            System.out.println(t);
+            t = t.next;
+        }
+    }
+
+    /**
+     * Specifies the type of data stored in a Token.
+     */
+    public static enum Type
+    {
+        FUNCTION,
+        VARIABLE,
+        SYMBOL,
+        WORD
+    }
+}
diff --git a/main/equation/src/org/ejml/equation/Variable.java b/main/equation/src/org/ejml/equation/Variable.java
new file mode 100644
index 0000000..7a5102f
--- /dev/null
+++ b/main/equation/src/org/ejml/equation/Variable.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.equation;
+
+/**
+ * Instance of a variable created at compile time.  This base class only specifies the type of variable which it is.
+ *
+ * @author Peter Abeles
+ */
+public class Variable {
+    public VariableType type;
+
+    protected Variable(VariableType type) {
+        this.type = type;
+    }
+
+    public VariableType getType() {
+        return type;
+    }
+
+    public String toString() {
+        return "Var"  + type;
+    }
+}
diff --git a/main/equation/src/org/ejml/equation/VariableDouble.java b/main/equation/src/org/ejml/equation/VariableDouble.java
new file mode 100644
index 0000000..e52f217
--- /dev/null
+++ b/main/equation/src/org/ejml/equation/VariableDouble.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.equation;
+
+/**
+ * Variable which stores an instance of double.
+ *
+ * @author Peter Abeles
+ */
+public class VariableDouble extends VariableScalar {
+    public double value;
+
+    public VariableDouble(double value) {
+        this.value = value;
+    }
+
+    @Override
+    public double getDouble() {
+        return value;
+    }
+}
diff --git a/main/equation/src/org/ejml/equation/VariableInteger.java b/main/equation/src/org/ejml/equation/VariableInteger.java
new file mode 100644
index 0000000..b968d69
--- /dev/null
+++ b/main/equation/src/org/ejml/equation/VariableInteger.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.equation;
+
+/**
+ * Variable which stores an instance of int.
+ *
+ * @author Peter Abeles
+ */
+public class VariableInteger extends VariableScalar {
+    public int value;
+
+    public VariableInteger(int value) {
+        this.value = value;
+    }
+
+    @Override
+    public double getDouble() {
+        return value;
+    }
+}
diff --git a/main/equation/src/org/ejml/equation/VariableMatrix.java b/main/equation/src/org/ejml/equation/VariableMatrix.java
new file mode 100644
index 0000000..8d931a7
--- /dev/null
+++ b/main/equation/src/org/ejml/equation/VariableMatrix.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.equation;
+
+import org.ejml.data.DenseMatrix64F;
+
+/**
+ * Storage for {@link org.ejml.data.DenseMatrix64F matrix} type variables.
+ *
+ * @author Peter Abeles
+ */
+public class VariableMatrix extends Variable {
+    public DenseMatrix64F matrix;
+
+    /**
+     * If true then the matrix is dynamically resized to match the output of a function
+     */
+    public boolean temp;
+
+    /**
+     * Initializes the matrix variable.  If null then the variable will be a reference one.  If not null then
+     * it will be assignment.
+     * @param matrix Matrix.
+     */
+    public VariableMatrix(DenseMatrix64F matrix) {
+        super(VariableType.MATRIX);
+        this.matrix = matrix;
+    }
+
+    public static VariableMatrix createTemp() {
+        VariableMatrix ret = new VariableMatrix(new DenseMatrix64F(1,1));
+        ret.setTemp(true);
+        return ret;
+    }
+
+    public boolean isTemp() {
+        return temp;
+    }
+
+    public void setTemp(boolean temp) {
+        this.temp = temp;
+    }
+}
diff --git a/main/equation/src/org/ejml/equation/VariableScalar.java b/main/equation/src/org/ejml/equation/VariableScalar.java
new file mode 100644
index 0000000..b8fe5da
--- /dev/null
+++ b/main/equation/src/org/ejml/equation/VariableScalar.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.equation;
+
+/**
+ * Variable for storing primitive scalar data types, e.g. int and double.
+ *
+ * @author Peter Abeles
+ */
+public abstract class VariableScalar extends Variable {
+
+    public VariableScalar() {
+        super(VariableType.SCALAR);
+    }
+
+    public abstract double getDouble();
+}
diff --git a/main/equation/src/org/ejml/equation/VariableSpecial.java b/main/equation/src/org/ejml/equation/VariableSpecial.java
new file mode 100644
index 0000000..f1a37e1
--- /dev/null
+++ b/main/equation/src/org/ejml/equation/VariableSpecial.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.equation;
+
+/**
+ * Variables which have special meanings depending on other matrices
+ *
+ * @author Peter Abeles
+ */
+public class VariableSpecial extends Variable {
+
+    Special type;
+
+    protected VariableSpecial( Special special) {
+        super(VariableType.SPECIAL);
+        this.type = special;
+    }
+
+    public Special getValue() {
+        return type;
+    }
+
+    public static enum Special {
+        /** Maximum possible value */
+        END,
+        /** All values */
+        ALL
+    }
+}
diff --git a/main/equation/src/org/ejml/equation/VariableType.java b/main/equation/src/org/ejml/equation/VariableType.java
new file mode 100644
index 0000000..102fdc4
--- /dev/null
+++ b/main/equation/src/org/ejml/equation/VariableType.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.equation;
+
+/**
+ * List of the types of variables.
+ *
+ * @author Peter Abeles
+ */
+public enum VariableType {
+    MATRIX,
+    SCALAR,
+    SPECIAL
+}
diff --git a/main/equation/test/org/ejml/equation/TestEquation.java b/main/equation/test/org/ejml/equation/TestEquation.java
new file mode 100644
index 0000000..cf14594
--- /dev/null
+++ b/main/equation/test/org/ejml/equation/TestEquation.java
@@ -0,0 +1,815 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.equation;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.simple.SimpleMatrix;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.ejml.equation.TokenList.Type;
+import static org.junit.Assert.*;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestEquation {
+
+    Random rand = new Random(234);
+
+    /**
+     * Basic test which checks ability parse basic operators and order of operation
+     */
+    @Test
+    public void compile_basic() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = new SimpleMatrix(5, 6);
+        SimpleMatrix B = SimpleMatrix.random(5, 6, -1, 1, rand);
+        SimpleMatrix C = SimpleMatrix.random(5, 4, -1, 1, rand);
+        SimpleMatrix D = SimpleMatrix.random(4, 6, -1, 1, rand);
+
+        eq.alias(A, "A");
+        eq.alias(B, "B");
+        eq.alias(C, "C");
+        eq.alias(D, "D");
+
+        Sequence sequence = eq.compile("A=B+C*D-B");
+        SimpleMatrix expected = C.mult(D);
+        sequence.perform();
+        assertTrue(expected.isIdentical(A,1e-15));
+    }
+
+    /**
+     * Output is included in input
+     */
+    @Test
+    public void compile_output() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = SimpleMatrix.random(6, 6, -1, 1, rand);
+        SimpleMatrix B = SimpleMatrix.random(6, 6, -1, 1, rand);
+
+        eq.alias(A, "A");
+        eq.alias(B, "B");
+
+        Sequence sequence = eq.compile("A=A*B");
+        SimpleMatrix expected = A.mult(B);
+        sequence.perform();
+        assertTrue(expected.isIdentical(A,1e-15));
+    }
+
+    /**
+     * Results are assigned to a sub-matrix
+     */
+    @Test
+    public void compile_assign_submatrix() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = SimpleMatrix.random(6, 6, -1, 1, rand);
+        SimpleMatrix B = SimpleMatrix.random(2, 5, -1, 1, rand);
+
+        SimpleMatrix A_orig = A.copy();
+
+        eq.alias(A, "A");
+        eq.alias(B, "B");
+
+        Sequence sequence = eq.compile("A(2:3,0:4)=B");
+        sequence.perform();
+
+        for (int y = 0; y < 6; y++) {
+            for (int x = 0; x < 6; x++) {
+                if( x < 5 && y >= 2 && y <= 3 ) {
+                    assertTrue(A.get(y,x) == B.get(y-2,x));
+                } else {
+                    assertTrue(x+" "+y,A.get(y,x) == A_orig.get(y,x));
+                }
+            }
+        }
+    }
+
+    @Test
+    public void compile_assign_submatrix_special() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = SimpleMatrix.random(6, 5, -1, 1, rand);
+        SimpleMatrix B = SimpleMatrix.random(4, 5, -1, 1, rand);
+
+        SimpleMatrix A_orig = A.copy();
+
+        eq.alias(A, "A");
+        eq.alias(B, "B");
+
+        eq.process("A(2:,:)=B");
+
+        for (int y = 0; y < 6; y++) {
+            for (int x = 0; x < 5; x++) {
+                if( y >= 2 ) {
+                    assertTrue(A.get(y,x) == B.get(y-2,x));
+                } else {
+                    assertTrue(x+" "+y,A.get(y,x) == A_orig.get(y,x));
+                }
+            }
+        }
+    }
+
+    @Test
+    public void compile_assign_submatrix_scalar() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = SimpleMatrix.random(6, 5, -1, 1, rand);
+
+        eq.alias(A, "A");
+
+        // single element
+        eq.process("A(1,2)=0.5");
+
+        assertEquals(A.get(1, 2), 0.5, 1e-8);
+
+        // multiple elements
+        eq.process("A(1:2,2:4)=0.5");
+
+        for (int i = 1; i <= 2; i++) {
+            for (int j = 2; j <= 4; j++) {
+                assertEquals(A.get(i, j), 0.5, 1e-8);
+            }
+        }
+    }
+
+    /**
+     * Lazily declare a variable.  Which means it is not explicitly aliased
+     */
+    @Test
+    public void assign_lazy() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = SimpleMatrix.random(6, 5, -1, 1, rand);
+        eq.alias(A, "A");
+        eq.process("B=A");
+
+        DenseMatrix64F B = eq.lookupMatrix("B");
+        assertTrue(A.getMatrix()!=B);
+        assertTrue(MatrixFeatures.isEquals(A.getMatrix(),B));
+    }
+
+    /**
+     * Place an unknown variable on the right and see if it blows up
+     */
+    @Test(expected = RuntimeException.class)
+    public void assign_lazy_right() {
+        new Equation().process("B=A");
+    }
+
+    /**
+     * See if matrices are automatically resized when assinged a value
+     */
+    @Test
+    public void assign_resize_lazy() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = SimpleMatrix.random(6, 5, -1, 1, rand);
+        SimpleMatrix B = SimpleMatrix.random(2, 3, -1, 1, rand);
+        eq.alias(A, "A");
+        eq.alias(B, "B");
+        eq.process("B=A");
+
+        assertTrue(A.isIdentical(B,1e-8));
+    }
+
+    @Test
+    public void compile_parentheses() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = SimpleMatrix.random(6, 6, -1, 1, rand);
+        SimpleMatrix B = SimpleMatrix.random(6, 6, -1, 1, rand);
+        SimpleMatrix C = SimpleMatrix.random(6, 6, -1, 1, rand);
+        SimpleMatrix R = new SimpleMatrix(6, 6);
+
+        eq.alias(A, "A");
+        eq.alias(B, "B");
+        eq.alias(C, "C");
+        eq.alias(R, "R");
+
+        eq.process("R=A*(B+C)");
+        SimpleMatrix expected = A.mult(B.plus(C));
+        assertTrue(expected.isIdentical(R, 1e-15));
+
+        // try again with pointless ones
+        eq.process("R=(A*((B+(C))))");
+        assertTrue(expected.isIdentical(R,1e-15));
+    }
+
+    @Test
+    public void compile_parentheses_extract() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = SimpleMatrix.random(6, 6, -1, 1, rand);
+        SimpleMatrix B = SimpleMatrix.random(8, 8, -1, 1, rand);
+
+        eq.alias(A, "A");
+        eq.alias(B, "B");
+
+        Sequence sequence = eq.compile("A=B(2:7,1:6)");
+        sequence.perform();
+        assertTrue(A.isIdentical(B.extractMatrix(2,8,1,7), 1e-15));
+
+        // get single values now
+        A = SimpleMatrix.random(6, 1, -1, 1, rand);
+        eq.alias(A, "A");
+        sequence = eq.compile("A=B(2:7,3)");
+        sequence.perform();
+        assertTrue(A.isIdentical(B.extractMatrix(2,8,3,4), 1e-15));
+
+        // multiple in a row
+        A = SimpleMatrix.random(1, 2, -1, 1, rand);
+        eq.alias(A, "A");
+        sequence = eq.compile("A=(B(2:7,3:6))(0:0,1:2)");
+        sequence.perform();
+        assertTrue(A.isIdentical(B.extractMatrix(2,3,4,6), 1e-15));
+    }
+
+    @Test
+    public void compile_parentheses_extractSpecial() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = SimpleMatrix.random(6, 8, -1, 1, rand);
+        SimpleMatrix B = SimpleMatrix.random(8, 8, -1, 1, rand);
+
+        eq.alias(A, "A");
+        eq.alias(B, "B");
+
+        eq.process("A=B(2:,:)");
+        assertTrue(A.isIdentical(B.extractMatrix(2,8,0,8), 1e-15));
+
+        B = SimpleMatrix.random(6, 10, -1, 1, rand);
+        eq.alias(B, "B");
+        eq.process("A=B(:,2:)");
+        assertTrue(A.isIdentical(B.extractMatrix(0,6,2,10), 1e-15));
+    }
+
+    @Test
+    public void compile_parentheses_extractScalar() {
+        Equation eq = new Equation();
+
+        SimpleMatrix B = SimpleMatrix.random(8, 8, -1, 1, rand);
+
+        eq.alias(B, "B");
+
+        eq.process("A=B(1,2)");
+        Variable v = eq.lookupVariable("A");
+        assertTrue(v instanceof VariableDouble);
+        assertEquals(eq.lookupDouble("A"),B.get(1,2),1e-8);
+    }
+
+    @Test
+    public void compile_neg() {
+        Equation eq = new Equation();
+
+        eq.alias(1, "A",2, "B");
+
+        eq.process("A=-B");
+        assertEquals(-2, eq.lookupInteger("A"));
+
+        eq.process("A=B--B");
+        assertEquals(4, eq.lookupInteger("A"));
+        eq.process("A=B+-B");
+        assertEquals(0,eq.lookupInteger("A"));
+        eq.process("A=B---5");
+        assertEquals(2-5,eq.lookupInteger("A"));
+        eq.process("A=B--5");
+        assertEquals(2+5,eq.lookupInteger("A"));
+    }
+
+    @Test
+    public void compile_constructMatrix_scalars() {
+        Equation eq = new Equation();
+
+        SimpleMatrix expected = new SimpleMatrix(new double[][]{{0,1,2,3},{4,5,6,7},{8,1,1,1}});
+        SimpleMatrix A = new SimpleMatrix(3,4);
+
+        eq.alias(A, "A");
+        Sequence sequence = eq.compile("A=[0 1 2 3; 4 5 6 7;8 1 1 1]");
+        sequence.perform();
+        assertTrue(A.isIdentical(expected, 1e-8));
+    }
+
+    @Test
+    public void compile_constructMatrix_MatrixAndScalar() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = new SimpleMatrix(new double[][]{{0,1,2,3}});
+        SimpleMatrix found = new SimpleMatrix(1,5);
+
+        eq.alias(A, "A");
+        eq.alias(found, "found");
+        Sequence sequence = eq.compile("found=[A 4]");
+        sequence.perform();
+        for (int i = 0; i < 5; i++) {
+            assertEquals(found.get(0,i),i,1e-4);
+        }
+    }
+
+    @Test
+    public void compile_constructMatrix_Operations() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = new SimpleMatrix(new double[][]{{0,1,2,3}});
+        SimpleMatrix found = new SimpleMatrix(5,1);
+
+        eq.alias(A, "A");
+        eq.alias(found, "found");
+        Sequence sequence = eq.compile("found=[A' ; 4]");
+        sequence.perform();
+        for (int i = 0; i < 5; i++) {
+            assertEquals(found.get(i,0),i,1e-4);
+        }
+    }
+
+    @Test
+    public void compile_constructMatrix_Inner() {
+        Equation eq = new Equation();
+
+        SimpleMatrix found = new SimpleMatrix(3,2);
+
+        eq.alias(found, "found");
+        Sequence sequence = eq.compile("found=[[1 2 3]' [4 5 [6]]']");
+        sequence.perform();
+        int index = 1;
+        for (int x = 0; x < 2; x++) {
+            for (int y = 0; y < 3; y++) {
+                assertEquals(x+" "+y,found.get(y,x),index++,1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void compile_transpose() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = SimpleMatrix.random(6, 6, -1, 1, rand);
+        SimpleMatrix B = SimpleMatrix.random(6, 6, -1, 1, rand);
+        SimpleMatrix C = SimpleMatrix.random(6, 6, -1, 1, rand);
+        SimpleMatrix R = new SimpleMatrix(6, 6);
+
+        eq.alias(A, "A");
+        eq.alias(B, "B");
+        eq.alias(C, "C");
+        eq.alias(R, "R");
+
+        Sequence sequence = eq.compile("R=A'*(B'+C)'+inv(B)'");
+        SimpleMatrix expected = A.transpose().mult(B.transpose().plus(C).transpose()).plus(B.invert().transpose());
+        sequence.perform();
+        assertTrue(expected.isIdentical(R, 1e-15));
+    }
+
+    @Test
+    public void compile_elementWise() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = SimpleMatrix.random(6, 6, -1, 1, rand);
+        SimpleMatrix B = SimpleMatrix.random(6, 6, -1, 1, rand);
+        SimpleMatrix C = SimpleMatrix.random(6, 6, -1, 1, rand);
+        SimpleMatrix R = new SimpleMatrix(6, 6);
+
+        eq.alias(A, "A");
+        eq.alias(B, "B");
+        eq.alias(C, "C");
+        eq.alias(R, "R");
+
+        Sequence sequence = eq.compile("R=A.*(B./C)");
+        SimpleMatrix expected = A.elementMult(B.elementDiv(C));
+        sequence.perform();
+        assertTrue(expected.isIdentical(R, 1e-15));
+    }
+
+    @Test
+    public void compile_double() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = SimpleMatrix.random(6, 6, -1, 1, rand);
+        SimpleMatrix B = SimpleMatrix.random(6, 6, -1, 1, rand);
+        double C = 2.5;
+        double D = 1.7;
+
+        eq.alias(A, "A");
+        eq.alias(B, "B");
+        eq.alias(D, "D");
+        eq.alias(0.0, "E");
+
+        VariableDouble E = eq.lookupVariable("E");
+
+        Sequence sequence = eq.compile("A=2.5*B");
+        SimpleMatrix expected = B.scale(C);
+        sequence.perform();
+        assertTrue(expected.isIdentical(A, 1e-15));
+
+        sequence = eq.compile("A=B*2.5");
+        sequence.perform();
+        assertTrue(expected.isIdentical(A, 1e-15));
+
+        sequence = eq.compile("E=2.5*D");
+        sequence.perform();
+        assertEquals(C * D, E.value, 1e-8);
+
+        // try exponential formats
+        sequence = eq.compile("E=2.001e-6*1e3");
+        sequence.perform();
+        assertEquals(2.001e-6*1e3, E.value, 1e-8);
+    }
+
+    /**
+     * Function with one input
+     */
+    @Test
+    public void compile_function_one() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = SimpleMatrix.random(6, 6, -1, 1, rand);
+        SimpleMatrix B = SimpleMatrix.random(6, 6, -1, 1, rand);
+        SimpleMatrix C = SimpleMatrix.random(6, 6, -1, 1, rand);
+        SimpleMatrix R = new SimpleMatrix(6, 6);
+
+        eq.alias(A, "A");
+        eq.alias(B, "B");
+        eq.alias(C, "C");
+        eq.alias(R, "R");
+
+        // easy case
+        Sequence sequence = eq.compile("R=inv(A)");
+        SimpleMatrix expected = A.invert();
+        sequence.perform();
+        assertTrue(expected.isIdentical(R, 1e-15));
+
+        // harder case
+        sequence = eq.compile("R=inv(A)+det((A+B)*C)*B");
+        expected = A.invert().plus( B.scale(A.plus(B).mult(C).determinant()));
+        sequence.perform();
+        assertTrue(expected.isIdentical(R, 1e-15));
+
+        // this should throw an exception
+        try {
+            eq.compile("R=inv*B");
+            fail("Implement");
+        } catch( RuntimeException ignore ){}
+    }
+
+    /**
+     * Function with two input
+     */
+    @Test
+    public void compile_function_N() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = SimpleMatrix.random(3, 4, -1, 1, rand);
+        SimpleMatrix B = SimpleMatrix.random(4, 5, -1, 1, rand);
+        SimpleMatrix R = new SimpleMatrix(12, 20);
+
+        eq.alias(A, "A");
+        eq.alias(B, "B");
+        eq.alias(R, "R");
+
+        eq.process("R=kron(A,B)");
+        SimpleMatrix expected = A.kron(B);
+        assertTrue(expected.isIdentical(R, 1e-15));
+
+        eq.process("R=kron(A+(A')',(B+B))");
+        expected = A.plus(A).kron(B.plus(B));
+        assertTrue(expected.isIdentical(R, 1e-15));
+    }
+
+    @Test
+    public void handleParentheses() {
+        Equation eq = new Equation();
+        ManagerTempVariables managerTemp = new ManagerTempVariables();
+        eq.functions.setManagerTemp( managerTemp );
+
+        eq.alias(new DenseMatrix64F(1, 1), "A");
+        eq.alias(new DenseMatrix64F(1, 1), "B");
+        eq.alias(new DenseMatrix64F(1, 1), "C");
+
+        // handle empty case
+        Sequence sequence = new Sequence();
+        TokenList tokens = eq.extractTokens("((()))()",managerTemp);
+        eq.handleParentheses(tokens,sequence);
+        assertEquals(0,sequence.operations.size());
+        assertEquals(0,tokens.size);
+
+        // embedded with just one variable
+        sequence = new Sequence();
+        tokens = eq.extractTokens("(((A)))",managerTemp);
+        eq.handleParentheses(tokens,sequence);
+        assertEquals(0,sequence.operations.size());
+        assertEquals(1,tokens.size);
+        assertTrue(tokens.first.getType() == Type.VARIABLE);
+
+        // pointless
+        sequence = new Sequence();
+        tokens = eq.extractTokens("((A)*(B)+(C))",managerTemp);
+        eq.handleParentheses(tokens,sequence);
+        assertEquals(2,sequence.operations.size());
+        assertEquals(1,tokens.size);
+        assertTrue(tokens.first.getType() == Type.VARIABLE);
+    }
+
+    @Test
+    public void parseOperations() {
+        Equation eq = new Equation();
+        ManagerTempVariables managerTemp = new ManagerTempVariables();
+        eq.functions.setManagerTemp( managerTemp );
+
+        eq.alias(new DenseMatrix64F(1, 1), "A");
+        eq.alias(new DenseMatrix64F(1, 1), "B");
+        eq.alias(new DenseMatrix64F(1, 1), "C");
+
+        // give it an empty list
+        TokenList tokens = eq.extractTokens("",managerTemp);
+        Sequence sequence = new Sequence();
+
+        eq.parseOperationsLR(new Symbol[]{Symbol.TIMES}, tokens, sequence);
+        assertEquals(0,sequence.operations.size());
+        assertEquals(0,tokens.size);
+
+        // other cases
+        tokens = eq.extractTokens("B+B-A*B*A",managerTemp);
+        sequence = new Sequence();
+
+        eq.parseOperationsLR(new Symbol[]{Symbol.TIMES}, tokens, sequence);
+
+        assertEquals(2,sequence.operations.size());
+        assertEquals(5,tokens.size);
+        assertTrue(tokens.last.getType() == Type.VARIABLE);
+        assertTrue(Symbol.MINUS==tokens.last.previous.getSymbol());
+
+        tokens = eq.extractTokens("B+B*B*A-B",managerTemp);
+        sequence = new Sequence();
+
+        eq.parseOperationsLR(new Symbol[]{Symbol.PLUS, Symbol.MINUS}, tokens, sequence);
+
+        assertEquals(2,sequence.operations.size());
+        assertEquals(5,tokens.size);
+        assertTrue(tokens.last.getType() == Type.VARIABLE);
+        assertTrue(Symbol.TIMES == tokens.last.previous.getSymbol());
+        assertTrue(Symbol.TIMES == tokens.first.next.next.next.getSymbol());
+    }
+
+    @Test
+    public void createOp() {
+        Equation eq = new Equation();
+        ManagerTempVariables managerTemp = new ManagerTempVariables();
+        eq.functions.setManagerTemp( managerTemp );
+
+        eq.alias(new DenseMatrix64F(1, 1), "A");
+        eq.alias(new DenseMatrix64F(1, 1), "B");
+
+        TokenList tokens = eq.extractTokens("A=A*B",managerTemp);
+
+        TokenList.Token t0 = tokens.first.next.next;
+        TokenList.Token t1 = t0.next;
+        TokenList.Token t2 = t1.next;
+
+        Sequence sequence = new Sequence();
+
+        TokenList.Token found = eq.createOp(t0,t1,t2,tokens,sequence);
+        assertTrue(found.getType() == Type.VARIABLE);
+        assertEquals(3, tokens.size);
+        assertTrue(Symbol.ASSIGN == tokens.first.next.getSymbol());
+        assertTrue(found==tokens.last);
+        assertEquals(1, sequence.operations.size());
+    }
+
+    @Test
+    public void lookupVariable() {
+        Equation eq = new Equation();
+        eq.alias(new DenseMatrix64F(1,1),"A");
+        eq.alias(new DenseMatrix64F(1,1),"BSD");
+
+        eq.lookupVariable("A");
+        eq.lookupVariable("BSD");
+        assertTrue( null == eq.lookupVariable("dDD") );
+    }
+
+    @Test
+    public void extractTokens() {
+        Equation eq = new Equation();
+        ManagerTempVariables managerTemp = new ManagerTempVariables();
+
+        eq.alias(new DenseMatrix64F(1,1),"A");
+        eq.alias(new DenseMatrix64F(1,1),"BSD");
+
+        Variable v0 = eq.lookupVariable("A");
+        Variable v1 = eq.lookupVariable("BSD");
+
+
+        TokenList list = eq.extractTokens("A = A*A + BSD*(A+BSD) -A*BSD",managerTemp);
+
+        TokenList.Token t = list.getFirst();
+
+        assertTrue(v0==t.getVariable()); t = t.next;
+        assertTrue(Symbol.ASSIGN==t.getSymbol()); t = t.next;
+        assertTrue(v0==t.getVariable()); t = t.next;
+        assertTrue(Symbol.TIMES==t.getSymbol()); t = t.next;
+        assertTrue(v0==t.getVariable()); t = t.next;
+        assertTrue(Symbol.PLUS==t.getSymbol()); t = t.next;
+        assertTrue(v1==t.getVariable()); t = t.next;
+        assertTrue(Symbol.TIMES==t.getSymbol()); t = t.next;
+        assertTrue(Symbol.PAREN_LEFT==t.getSymbol()); t = t.next;
+        assertTrue(v0==t.getVariable()); t = t.next;
+        assertTrue(Symbol.PLUS==t.getSymbol()); t = t.next;
+        assertTrue(v1==t.getVariable()); t = t.next;
+        assertTrue(Symbol.PAREN_RIGHT==t.getSymbol()); t = t.next;
+        assertTrue(Symbol.MINUS==t.getSymbol()); t = t.next;
+        assertTrue(v0==t.getVariable()); t = t.next;
+        assertTrue(Symbol.TIMES==t.getSymbol()); t = t.next;
+        assertTrue(v1==t.getVariable()); t = t.next;
+    }
+
+    @Test
+    public void extractTokens_elementWise() {
+        Equation eq = new Equation();
+        ManagerTempVariables managerTemp = new ManagerTempVariables();
+
+        eq.alias(new DenseMatrix64F(1,1),"A");
+        eq.alias(new DenseMatrix64F(1,1),"BSD");
+
+        Variable v0 = eq.lookupVariable("A");
+        Variable v1 = eq.lookupVariable("BSD");
+
+
+        TokenList list = eq.extractTokens("A = (A.*A)./BSD",managerTemp);
+
+        TokenList.Token t = list.getFirst();
+
+        assertTrue(v0==t.getVariable()); t = t.next;
+        assertTrue(Symbol.ASSIGN==t.getSymbol()); t = t.next;
+        assertTrue(Symbol.PAREN_LEFT==t.getSymbol()); t = t.next;
+        assertTrue(v0==t.getVariable()); t = t.next;
+        assertTrue(Symbol.ELEMENT_TIMES==t.getSymbol()); t = t.next;
+        assertTrue(v0==t.getVariable()); t = t.next;
+        assertTrue(Symbol.PAREN_RIGHT==t.getSymbol()); t = t.next;
+        assertTrue(Symbol.ELEMENT_DIVIDE==t.getSymbol()); t = t.next;
+        assertTrue(v1==t.getVariable()); t = t.next;
+    }
+
+    @Test
+    public void extractTokens_integers() {
+        Equation eq = new Equation();
+        ManagerTempVariables managerTemp = new ManagerTempVariables();
+
+        eq.alias(new DenseMatrix64F(1,1),"A");
+        eq.alias(new DenseMatrix64F(1,1),"BSD");
+
+        Variable v0 = eq.lookupVariable("A");
+        Variable v1 = eq.lookupVariable("BSD");
+
+        TokenList list = eq.extractTokens("A*2 + 345 + 56*BSD*934",managerTemp);
+
+        TokenList.Token t = list.getFirst();
+
+        assertTrue(v0 == t.getVariable()); t = t.next;
+        assertTrue(Symbol.TIMES == t.getSymbol()); t = t.next;
+        assertEquals(2, ((VariableInteger) t.getVariable()).value); t = t.next;
+        assertTrue(Symbol.PLUS == t.getSymbol()); t = t.next;
+        assertEquals(345, ((VariableInteger) t.getVariable()).value); t = t.next;
+        assertTrue(Symbol.PLUS==t.getSymbol()); t = t.next;
+        assertEquals(56, ((VariableInteger) t.getVariable()).value); t = t.next;
+        assertTrue(Symbol.TIMES==t.getSymbol()); t = t.next;
+        assertTrue(v1==t.getVariable()); t = t.next;
+        assertTrue(Symbol.TIMES==t.getSymbol()); t = t.next;
+        assertEquals(934, ((VariableInteger) t.getVariable()).value); t = t.next;
+    }
+
+    @Test
+    public void extractTokens_doubles() {
+        Equation eq = new Equation();
+        ManagerTempVariables managerTemp = new ManagerTempVariables();
+
+        eq.alias(new DenseMatrix64F(1,1),"A");
+        eq.alias(new DenseMatrix64F(1,1),"BSD");
+
+        Variable v0 = eq.lookupVariable("A");
+        Variable v1 = eq.lookupVariable("BSD");
+
+        TokenList list = eq.extractTokens("A*2. + 345.034 + 0.123*BSD*5.1",managerTemp);
+
+        TokenList.Token t = list.getFirst();
+
+        assertTrue(v0==t.getVariable()); t = t.next;
+        assertTrue(Symbol.TIMES==t.getSymbol()); t = t.next;
+        assertTrue(2 == ((VariableDouble) t.getVariable()).value); t = t.next;
+        assertTrue(Symbol.PLUS==t.getSymbol()); t = t.next;
+        assertTrue(345.034 == ((VariableDouble) t.getVariable()).value); t = t.next;
+        assertTrue(Symbol.PLUS==t.getSymbol()); t = t.next;
+        assertTrue(0.123 == ((VariableDouble) t.getVariable()).value); t = t.next;
+        assertTrue(Symbol.TIMES==t.getSymbol()); t = t.next;
+        assertTrue(v1==t.getVariable()); t = t.next;
+        assertTrue(Symbol.TIMES==t.getSymbol()); t = t.next;
+        assertTrue(5.1==((VariableDouble)t.getVariable()).value);
+        assertTrue(t.next == null);
+    }
+
+    /**
+     * See if the minus symbol is handled correctly.  It's meaning can very depending on the situation.
+     */
+    @Test
+    public void extractTokens_minus() {
+        Equation eq = new Equation();
+        ManagerTempVariables managerTemp = new ManagerTempVariables();
+
+        TokenList list = eq.extractTokens("- 1.2",managerTemp);
+        TokenList.Token t = list.getFirst();
+        assertTrue(Symbol.MINUS==t.getSymbol()); t = t.next;
+        assertTrue(1.2 == ((VariableDouble) t.getVariable()).value);
+        assertTrue(t.next==null);
+
+        list = eq.extractTokens("-1.2",managerTemp);
+        t = list.getFirst();
+        assertTrue(-1.2 == ((VariableDouble) t.getVariable()).value);
+        assertTrue(t.next==null);
+
+        list = eq.extractTokens("2.1-1.2",managerTemp);
+        t = list.getFirst();
+        assertTrue(2.1 == ((VariableDouble) t.getVariable()).value); t = t.next;
+        assertTrue(Symbol.MINUS==t.getSymbol()); t = t.next;
+        assertTrue(1.2 == ((VariableDouble) t.getVariable()).value);
+        assertTrue(t.next==null);
+
+        list = eq.extractTokens("2.1 -1.2",managerTemp);
+        t = list.getFirst();
+        assertTrue(2.1 == ((VariableDouble) t.getVariable()).value); t = t.next;
+        assertTrue(Symbol.MINUS==t.getSymbol()); t = t.next;
+        assertTrue(1.2 == ((VariableDouble) t.getVariable()).value);
+        assertTrue(t.next==null);
+
+        list = eq.extractTokens("2.1 - -1.2",managerTemp);
+        t = list.getFirst();
+        assertTrue(2.1 == ((VariableDouble) t.getVariable()).value); t = t.next;
+        assertTrue(Symbol.MINUS==t.getSymbol()); t = t.next;
+        assertTrue(-1.2 == ((VariableDouble) t.getVariable()).value);
+        assertTrue(t.next==null);
+
+        list = eq.extractTokens("inv(2.1) -1.2",managerTemp);
+        t = list.getFirst();
+        assertTrue(t.getFunction().getName().equals("inv")); t = t.next;
+        assertTrue(Symbol.PAREN_LEFT==t.getSymbol()); t = t.next;
+        assertTrue(2.1 == ((VariableDouble) t.getVariable()).value); t = t.next;
+        assertTrue(Symbol.PAREN_RIGHT==t.getSymbol()); t = t.next;
+        assertTrue(Symbol.MINUS==t.getSymbol()); t = t.next;
+        assertTrue(1.2 == ((VariableDouble) t.getVariable()).value);
+        assertTrue(t.next==null);
+
+        list = eq.extractTokens("= -1.2",managerTemp);
+        t = list.getFirst();
+        assertTrue(Symbol.ASSIGN==t.getSymbol()); t = t.next;
+        assertTrue(-1.2 == ((VariableDouble) t.getVariable()).value);
+        assertTrue(t.next==null);
+
+        list = eq.extractTokens("= - 1.2",managerTemp);
+        t = list.getFirst();
+        assertTrue(Symbol.ASSIGN==t.getSymbol()); t = t.next;
+        assertTrue(Symbol.MINUS==t.getSymbol()); t = t.next;
+        assertTrue(1.2 == ((VariableDouble) t.getVariable()).value);
+        assertTrue(t.next==null);
+    }
+
+    @Test
+    public void isTargetOp() {
+        Symbol[] targets = new Symbol[]{Symbol.PERIOD,Symbol.TIMES,Symbol.TRANSPOSE};
+        assertTrue(Equation.isTargetOp(new TokenList.Token(Symbol.TIMES),targets));
+        assertFalse(Equation.isTargetOp(new TokenList.Token(Symbol.RDIVIDE), targets));
+    }
+
+    @Test
+    public void isLetter() {
+        assertTrue(Equation.isLetter('a'));
+        assertTrue(Equation.isLetter('_'));
+        assertTrue(Equation.isLetter('5'));
+
+        assertFalse(Equation.isLetter(' '));
+        assertFalse(Equation.isLetter('\t'));
+        assertFalse(Equation.isLetter('*'));
+        assertFalse(Equation.isLetter('+'));
+        assertFalse(Equation.isLetter('-'));
+        assertFalse(Equation.isLetter('('));
+        assertFalse(Equation.isLetter(')'));
+        assertFalse(Equation.isLetter('='));
+    }
+
+}
diff --git a/main/equation/test/org/ejml/equation/TestMatrixConstructor.java b/main/equation/test/org/ejml/equation/TestMatrixConstructor.java
new file mode 100644
index 0000000..417529e
--- /dev/null
+++ b/main/equation/test/org/ejml/equation/TestMatrixConstructor.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.equation;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestMatrixConstructor {
+
+    Random rand = new Random(234);
+
+    @Test
+    public void basicTest() {
+        DenseMatrix64F A = RandomMatrices.createRandom(10,8,rand);
+
+        DenseMatrix64F B = CommonOps.extract(A,0,5,0,3);
+        DenseMatrix64F C = CommonOps.extract(A,0,5,3,8);
+        DenseMatrix64F D = CommonOps.extract(A,5,10,0,8);
+
+        MatrixConstructor alg = new MatrixConstructor(new ManagerTempVariables());
+
+        alg.addToRow(new VariableMatrix(B));
+        alg.addToRow(new VariableMatrix(C));
+        alg.endRow();
+        alg.addToRow(new VariableMatrix(D));
+
+        alg.construct();
+
+        DenseMatrix64F found = alg.getOutput().matrix;
+        assertTrue(MatrixFeatures.isIdentical(A, found, 1e-8));
+    }
+
+    @Test
+    public void setToRequiredSize_matrix() {
+        MatrixConstructor alg = new MatrixConstructor(new ManagerTempVariables());
+
+        alg.addToRow(new VariableMatrix(new DenseMatrix64F(2, 3)));
+        alg.addToRow(new VariableMatrix(new DenseMatrix64F(2, 4)));
+        alg.endRow();
+        alg.addToRow(new VariableMatrix(new DenseMatrix64F(1, 7)));
+        alg.endRow();
+
+        DenseMatrix64F a = new DenseMatrix64F(1,1);
+
+        alg.setToRequiredSize(a);
+
+        assertEquals(7,a.numCols);
+        assertEquals(3,a.numRows);
+    }
+
+    @Test
+    public void setToRequiredSize_scalar() {
+        MatrixConstructor alg = new MatrixConstructor(new ManagerTempVariables());
+
+        alg.addToRow(new VariableDouble(123));
+        alg.addToRow(new VariableDouble(1));
+        alg.endRow();
+
+        DenseMatrix64F a = new DenseMatrix64F(1,1);
+
+        alg.setToRequiredSize(a);
+
+        assertEquals(2,a.numCols);
+        assertEquals(1,a.numRows);
+    }
+}
\ No newline at end of file
diff --git a/main/equation/test/org/ejml/equation/TestOperation.java b/main/equation/test/org/ejml/equation/TestOperation.java
new file mode 100644
index 0000000..426336e
--- /dev/null
+++ b/main/equation/test/org/ejml/equation/TestOperation.java
@@ -0,0 +1,1064 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.equation;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.simple.SimpleMatrix;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestOperation {
+
+    Random rand = new Random(234);
+
+    @Test
+    public void divide_matrix_scalar() {
+        Equation eq = new Equation();
+
+        SimpleMatrix x = SimpleMatrix.random(5, 3, -1, 1, rand);
+        SimpleMatrix b = SimpleMatrix.random(5, 3, -1, 1, rand);
+
+        eq.alias(2.5, "A");
+        eq.alias(b, "b");
+        eq.alias(x, "x");
+
+        eq.process("x=b/A");
+
+        assertTrue(b.divide(2.5).isIdentical(x, 1e-8));
+    }
+
+    @Test
+    public void divide_scalar_matrix() {
+        Equation eq = new Equation();
+
+        SimpleMatrix x = SimpleMatrix.random(5, 3, -1, 1, rand);
+        SimpleMatrix b = SimpleMatrix.random(5, 3, -1, 1, rand);
+
+        eq.alias(2.5, "A");
+        eq.alias(b, "b");
+        eq.alias(x, "x");
+
+        eq.process("x=A/b");
+
+        DenseMatrix64F tmp = new DenseMatrix64F(5,3);
+        CommonOps.divide(2.5,b.getMatrix(),tmp);
+
+        assertTrue(MatrixFeatures.isIdentical(tmp, x.getMatrix(), 1e-8));
+    }
+
+    @Test
+    public void divide_int_int() {
+        Equation eq = new Equation();
+
+        eq.alias(4, "A");
+        eq.alias(13, "b");
+        eq.alias(-1, "x");
+
+        eq.process("x=b/A");
+
+        int found = eq.lookupInteger("x");
+
+        assertEquals(13 / 4, found, 1e-8);
+    }
+
+    @Test
+    public void divide_scalar_scalar() {
+        Equation eq = new Equation();
+
+        eq.alias(5, "A");
+        eq.alias(4.2, "b");
+        eq.alias(-1.0, "x");
+
+        eq.process("x=b/A");
+
+        double found = eq.lookupDouble("x");
+
+        assertEquals(4.2 / 5.0, found, 1e-8);
+    }
+
+    @Test
+    public void divide_matrix_matrix() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = SimpleMatrix.random(6, 5, -1, 1, rand);
+        SimpleMatrix x = SimpleMatrix.random(5, 3, -1, 1, rand);
+        SimpleMatrix b = SimpleMatrix.random(6, 3, -1, 1, rand);
+
+        eq.alias(A, "A");
+        eq.alias(b, "b");
+        eq.alias(x, "x");
+
+        eq.process("x=b/A");
+
+        assertTrue(A.solve(b).isIdentical(x, 1e-8));
+    }
+
+    @Test
+    public void ldivide_matrix_matrix() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = SimpleMatrix.random(6, 5, -1, 1, rand);
+        SimpleMatrix x = SimpleMatrix.random(5, 3, -1, 1, rand);
+        SimpleMatrix b = SimpleMatrix.random(6, 3, -1, 1, rand);
+
+        eq.alias(A, "A");
+        eq.alias(b, "b");
+        eq.alias(x, "x");
+
+        eq.process("x=A\\b");
+
+        assertTrue(A.solve(b).isIdentical(x, 1e-8));
+    }
+
+    @Test
+    public void multiply_matrix_scalar() {
+        Equation eq = new Equation();
+
+        SimpleMatrix x = SimpleMatrix.random(5, 3, -1, 1, rand);
+        SimpleMatrix b = SimpleMatrix.random(5, 3, -1, 1, rand);
+
+        eq.alias(2.5, "A");
+        eq.alias(b, "b");
+        eq.alias(x, "x");
+
+        eq.process("x=b*A");
+        assertTrue(b.scale(2.5).isIdentical(x, 1e-8));
+        eq.process("x=A*b");
+        assertTrue(b.scale(2.5).isIdentical(x, 1e-8));
+    }
+
+    @Test
+    public void multiply_int_int() {
+        Equation eq = new Equation();
+
+        eq.alias(4, "A");
+        eq.alias(13, "b");
+        eq.alias(-1, "x");
+
+        eq.process("x=b*A");
+
+        int found = eq.lookupInteger("x");
+
+        assertEquals(13 * 4, found, 1e-8);
+    }
+
+    @Test
+    public void multiply_scalar_scalar() {
+        Equation eq = new Equation();
+
+        eq.alias(5, "A");
+        eq.alias(4.2, "b");
+        eq.alias(-1.0, "x");
+
+        eq.process("x=b*A");
+
+        double found = eq.lookupDouble("x");
+
+        assertEquals(4.2 * 5.0, found, 1e-8);
+    }
+
+    @Test
+    public void multiply_matrix_matrix() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = SimpleMatrix.random(6, 5, -1, 1, rand);
+        SimpleMatrix x = SimpleMatrix.random(5, 3, -1, 1, rand);
+        SimpleMatrix b = SimpleMatrix.random(6, 3, -1, 1, rand);
+
+        eq.alias(A, "A");
+        eq.alias(b, "b");
+        eq.alias(x, "x");
+
+        eq.process("b=A*x");
+
+        assertTrue(A.mult(x).isIdentical(b, 1e-8));
+    }
+
+    @Test
+    public void elementMult_matrix() {
+        Equation eq = new Equation();
+
+        SimpleMatrix a = SimpleMatrix.random(6, 5, -1, 1, rand);
+        SimpleMatrix b = SimpleMatrix.random(6, 5, -1, 1, rand);
+        SimpleMatrix c = SimpleMatrix.random(6, 5, -1, 1, rand);
+
+        eq.alias(a,"a",b,"b",c,"c");
+
+        eq.process("c=a.*b");
+
+        assertTrue(a.elementMult(b).isIdentical(c, 1e-8));
+    }
+
+    @Test
+    public void elementDivide_matrix() {
+        Equation eq = new Equation();
+
+        SimpleMatrix a = SimpleMatrix.random(6, 5, -1, 1, rand);
+        SimpleMatrix b = SimpleMatrix.random(6, 5, -1, 1, rand);
+        SimpleMatrix c = SimpleMatrix.random(6, 5, -1, 1, rand);
+
+        eq.alias(a,"a",b,"b",c,"c");
+
+        eq.process("c=a./b");
+
+        assertTrue(a.elementDiv(b).isIdentical(c, 1e-8));
+    }
+
+    @Test
+    public void elementPower_mm() {
+        Equation eq = new Equation();
+
+        SimpleMatrix a = SimpleMatrix.random(6, 5, 0, 1, rand);
+        SimpleMatrix b = SimpleMatrix.random(6, 5, 0, 1, rand);
+        SimpleMatrix c = SimpleMatrix.random(6, 5, 0, 1, rand);
+
+        eq.alias(a,"a",b,"b",c,"c");
+
+        eq.process("c=a.^b");
+
+        assertTrue(a.elementPower(b).isIdentical(c, 1e-8));
+    }
+
+    @Test
+    public void elementPower_ms() {
+        Equation eq = new Equation();
+
+        SimpleMatrix a = SimpleMatrix.random(6, 5, 0, 1, rand);
+        double b = 1.1;
+        SimpleMatrix c = SimpleMatrix.random(6, 5, 0, 1, rand);
+
+        eq.alias(a,"a",b,"b",c,"c");
+
+        eq.process("c=a.^b");
+
+        assertTrue(a.elementPower(b).isIdentical(c, 1e-8));
+    }
+
+    @Test
+    public void elementPower_sm() {
+        Equation eq = new Equation();
+
+        double a = 1.1;
+        SimpleMatrix b = SimpleMatrix.random(6, 5, 0, 1, rand);
+        SimpleMatrix c = SimpleMatrix.random(6, 5, 0, 1, rand);
+
+        eq.alias(a,"a",b,"b",c,"c");
+
+        eq.process("c=a.^b");
+
+        SimpleMatrix expected = new SimpleMatrix(6,5);
+        CommonOps.elementPower(a,b.getMatrix(),expected.getMatrix());
+        assertTrue(expected.isIdentical(c, 1e-8));
+    }
+
+    @Test
+    public void elementPower_ss() {
+        Equation eq = new Equation();
+
+        double a = 1.1;
+        double b = 0.7;
+
+        eq.alias(a,"a",b,"b");
+
+        eq.process("c=a.^b");
+
+        double found = eq.lookupDouble("c");
+
+        assertEquals(Math.pow(a,b),found,1e-8);
+    }
+
+    @Test
+    public void kron_matrix_matrix() {
+        Equation eq = new Equation();
+
+        SimpleMatrix a = SimpleMatrix.random(2, 3, -1, 1, rand);
+        SimpleMatrix b = SimpleMatrix.random(3, 2, -1, 1, rand);
+        SimpleMatrix c = SimpleMatrix.random(6, 5, -1, 1, rand);
+
+        eq.alias(a,"a",b,"b",c,"c");
+
+        eq.process("c=kron(a,b)");
+
+        assertTrue(a.kron(b).isIdentical(c, 1e-8));
+    }
+
+    @Test
+    public void power_double_double() {
+        Equation eq = new Equation();
+
+        eq.alias(1.1,"a");
+        eq.process("a=2.3^4.2");
+
+        assertEquals(Math.pow(2.3, 4.2), eq.lookupDouble("a"), 1e-8);
+    }
+
+    @Test
+    public void power_int_int() {
+        Equation eq = new Equation();
+
+        eq.alias(1.1,"a");
+        eq.process("a=2^4");
+
+        assertEquals(Math.pow(2,4),eq.lookupDouble("a"),1e-8);
+    }
+
+    @Test
+    public void sqrt_int() {
+        Equation eq = new Equation();
+
+        eq.process("a=sqrt(5)");
+
+        assertEquals(Math.sqrt(5),eq.lookupDouble("a"),1e-8);
+    }
+
+    @Test
+    public void sqrt_double() {
+        Equation eq = new Equation();
+
+        eq.process("a=sqrt(5.7)");
+
+        assertEquals(Math.sqrt(5.7),eq.lookupDouble("a"),1e-8);
+    }
+
+
+    @Test
+    public void atan2_scalar() {
+        Equation eq = new Equation();
+
+        eq.alias(1.1,"a");
+        eq.process("a=atan2(1.1,0.5)");
+
+        assertEquals(Math.atan2(1.1, 0.5), eq.lookupDouble("a"), 1e-8);
+    }
+
+    @Test
+    public void neg_int() {
+        Equation eq = new Equation();
+
+        eq.alias(2,"a");
+        eq.alias(3,"b");
+        eq.process("a=-b");
+
+        assertEquals(-3, eq.lookupInteger("a"));
+    }
+
+    @Test
+    public void neg_scalar() {
+        Equation eq = new Equation();
+
+        eq.alias(2.1,"a");
+        eq.alias(3.1,"b");
+        eq.process("a=-b");
+
+        assertEquals(-3.1, eq.lookupDouble("a"), 1e-8);
+    }
+
+    @Test
+    public void neg_matrix() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = SimpleMatrix.random(1, 1, -1, 1, rand);
+        SimpleMatrix B = SimpleMatrix.random(5, 3, -1, 1, rand);
+
+        eq.alias(A, "A");
+        eq.alias(B, "B");
+
+        eq.process("A=-B");
+
+        for (int i = 0; i < A.getNumElements(); i++) {
+            assertEquals(-A.get(i),B.get(i),1e-8);
+        }
+    }
+
+    @Test
+    public void sin() {
+        Equation eq = new Equation();
+
+        eq.alias(1.1,"a");
+        eq.process("a=sin(2.1)");
+
+        assertEquals(Math.sin(2.1), eq.lookupDouble("a"), 1e-8);
+    }
+
+    @Test
+    public void cos() {
+        Equation eq = new Equation();
+
+        eq.alias(1.1,"a");
+        eq.process("a=cos(2.1)");
+
+        assertEquals(Math.cos(2.1),eq.lookupDouble("a"),1e-8);
+    }
+
+    @Test
+    public void atan() {
+        Equation eq = new Equation();
+
+        eq.alias(1.1,"a");
+        eq.process("a=atan(2.1)");
+
+        assertEquals(Math.atan(2.1), eq.lookupDouble("a"), 1e-8);
+    }
+
+    @Test
+    public void exp_s() {
+        Equation eq = new Equation();
+
+        eq.alias(1.1,"a");
+        eq.process("a=exp(2.1)");
+
+        assertEquals(Math.exp(2.1),eq.lookupDouble("a"),1e-8);
+    }
+
+    @Test
+    public void exp_m() {
+        Equation eq = new Equation();
+
+        SimpleMatrix a = SimpleMatrix.random(3,4,0,1,rand);
+        SimpleMatrix b = SimpleMatrix.random(3,4,0,1,rand);
+
+        eq.alias(a,"a",b,"b");
+        eq.process("b=exp(a)");
+
+        SimpleMatrix expected = a.elementExp();
+
+        assertTrue(expected.isIdentical(b,1e-8));
+    }
+
+    @Test
+    public void log_s() {
+        Equation eq = new Equation();
+
+        eq.alias(1.1,"a");
+        eq.process("a=log(2.1)");
+
+        assertEquals(Math.log(2.1),eq.lookupDouble("a"),1e-8);
+    }
+
+    @Test
+    public void log_m() {
+        Equation eq = new Equation();
+
+        SimpleMatrix a = SimpleMatrix.random(3,4,0,1,rand);
+        SimpleMatrix b = SimpleMatrix.random(3,4,0,1,rand);
+
+        eq.alias(a,"a",b,"b");
+        eq.process("b=log(a)");
+
+        SimpleMatrix expected = a.elementLog();
+
+        assertTrue(expected.isIdentical(b,1e-8));
+    }
+
+    @Test
+    public void add_int_int() {
+        Equation eq = new Equation();
+
+        eq.alias(1,"a");
+        eq.process("a=2 + 3");
+
+        assertEquals(5,eq.lookupInteger("a"),1e-8);
+    }
+
+    @Test
+    public void add_scalar_scalar() {
+        Equation eq = new Equation();
+
+        eq.alias(1.2,"a");
+        eq.process("a= 2.3 + 3");
+
+        assertEquals(5.3, eq.lookupDouble("a"), 1e-8);
+    }
+
+    @Test
+    public void add_matrix_matrix() {
+        Equation eq = new Equation();
+
+        SimpleMatrix a = SimpleMatrix.random(3,4,-1,1,rand);
+        SimpleMatrix b = SimpleMatrix.random(3,4,-1,1,rand);
+        SimpleMatrix c = SimpleMatrix.random(3,4,-1,1,rand);
+
+        eq.alias(a,"a",b,"b",c,"c");
+        eq.process("a=b+c");
+
+        assertTrue(b.plus(c).isIdentical(a, 1e-8));
+    }
+
+    @Test
+    public void add_matrix_scalar() {
+        Equation eq = new Equation();
+
+        SimpleMatrix a = SimpleMatrix.random(3,4,-1,1,rand);
+        SimpleMatrix b = SimpleMatrix.random(3,4,-1,1,rand);
+
+        eq.alias(a,"a",b,"b");
+
+        eq.process("a=b+2.2");
+        assertTrue(b.plus(2.2).isIdentical(a, 1e-8));
+
+        eq.process("a=2.2+b");
+        assertTrue(b.plus(2.2).isIdentical(a, 1e-8));
+    }
+
+    @Test
+    public void subtract_int_int() {
+        Equation eq = new Equation();
+
+        eq.alias(1,"a");
+        eq.process("a=2 - 3");
+
+        assertEquals(-1, eq.lookupInteger("a"), 1e-8);
+    }
+
+    @Test
+    public void subtract_scalar_scalar() {
+        Equation eq = new Equation();
+
+        eq.alias(1.2,"a");
+        eq.process("a= 2.3 - 3");
+
+        assertEquals(2.3 - 3.0, eq.lookupDouble("a"), 1e-8);
+    }
+
+    @Test
+    public void subtract_matrix_matrix() {
+        Equation eq = new Equation();
+
+        SimpleMatrix a = SimpleMatrix.random(3,4,-1,1,rand);
+        SimpleMatrix b = SimpleMatrix.random(3,4,-1,1,rand);
+        SimpleMatrix c = SimpleMatrix.random(3,4,-1,1,rand);
+
+        eq.alias(a, "a", b, "b", c, "c");
+        eq.process("a=b-c");
+
+        assertTrue(b.minus(c).isIdentical(a, 1e-8));
+    }
+
+    @Test
+    public void subtract_matrix_scalar() {
+        Equation eq = new Equation();
+
+        SimpleMatrix a = SimpleMatrix.random(3,4,-1,1,rand);
+        SimpleMatrix b = SimpleMatrix.random(3,4,-1,1,rand);
+
+        eq.alias(a,"a",b,"b");
+
+        eq.process("a=b-2.2");
+        assertTrue(b.plus(-2.2).isIdentical(a, 1e-8));
+
+        eq.process("a=2.2-b");
+
+        DenseMatrix64F expected = new DenseMatrix64F(3,4);
+        CommonOps.subtract(2.2,b.getMatrix(),expected);
+        assertTrue(SimpleMatrix.wrap(expected).isIdentical(a, 1e-8));
+    }
+
+    @Test
+    public void copy_matrix_matrix() {
+        Equation eq = new Equation();
+
+        SimpleMatrix a = SimpleMatrix.random(3,4,-1,1,rand);
+        SimpleMatrix b = SimpleMatrix.random(3,4,-1,1,rand);
+
+        eq.alias(a,"a",b,"b");
+        eq.process("b=a");
+
+        assertTrue(a.isIdentical(b, 1e-8));
+    }
+
+    @Test
+    public void copy_double_matrix() {
+        Equation eq = new Equation();
+
+        DenseMatrix64F src = new DenseMatrix64F(1,1,true,2.5);
+        eq.alias(1.2,"a");
+        eq.alias(src,"b");
+
+        eq.process("a=b");
+
+        assertEquals(2.5, eq.lookupDouble("a"), 1e-8);
+
+        // pass in a none 1x1 matrix
+        eq.alias(new DenseMatrix64F(2,1),"b");
+        try {
+            eq.process("a=b");
+            fail("Exception should have been thrown");
+        } catch( RuntimeException e ){}
+    }
+
+    @Test
+    public void copy_int_int() {
+        Equation eq = new Equation();
+
+        eq.alias(2,"a");
+        eq.alias(3,"b");
+
+        eq.process("a=b");
+
+        assertEquals(3, eq.lookupInteger("a"));
+    }
+
+    @Test
+    public void copy_double_scalar() {
+        Equation eq = new Equation();
+
+        // int to double
+        eq.alias(2.2,"a");
+        eq.alias(3,"b");
+
+        eq.process("a=b");
+        assertEquals(3, eq.lookupDouble("a"),1e-8);
+
+        // double to double
+        eq.alias(3.5,"c");
+        eq.process("a=c");
+        assertEquals(3.5, eq.lookupDouble("a"),1e-8);
+    }
+
+    @Test
+    public void copy_submatrix_matrix() {
+        Equation eq = new Equation();
+
+        SimpleMatrix a = SimpleMatrix.random(2,3,-1,1,rand);
+        SimpleMatrix b = SimpleMatrix.random(3,4,-1,1,rand);
+
+        eq.alias(a,"a",b,"b");
+        eq.process("b(1:2,1:3)=a");
+
+        assertTrue(a.isIdentical(b.extractMatrix(1, 3, 1, 4), 1e-8));
+    }
+
+    @Test
+    public void copy_submatrix_scalar() {
+        Equation eq = new Equation();
+
+        SimpleMatrix b = SimpleMatrix.random(3,4,-1,1,rand);
+
+        eq.alias(b,"b");
+        eq.process("b(2,3)=4.5");
+        eq.process("b(0,0)=3.5");
+
+        assertEquals(3.5, b.get(0, 0),1e-8);
+        assertEquals(4.5, b.get(2, 3),1e-8);
+    }
+
+    @Test
+    public void transpose_matrix() {
+        Equation eq = new Equation();
+
+        SimpleMatrix a = SimpleMatrix.random(3,4,-1,1,rand);
+        SimpleMatrix b = SimpleMatrix.random(3,4,-1,1,rand);
+
+        eq.alias(a,"a",b,"b");
+        eq.process("b=a'");
+
+        assertTrue(a.transpose().isIdentical(b, 1e-8));
+    }
+
+    @Test
+    public void inv_matrix() {
+        Equation eq = new Equation();
+
+        SimpleMatrix a = SimpleMatrix.random(3,3,-1,1,rand);
+        SimpleMatrix b = SimpleMatrix.random(3,3,-1,1,rand);
+
+        eq.alias(a,"a",b,"b");
+        eq.process("b=inv(a)");
+
+        assertTrue(a.invert().isIdentical(b, 1e-8));
+    }
+
+    @Test
+    public void inv_scalar() {
+        Equation eq = new Equation();
+
+        eq.alias(2.2,"a",3.3,"b");
+        eq.process("b=inv(a)");
+
+        assertEquals(1.0 / 2.2, eq.lookupDouble("b"), 1e-8);
+    }
+
+    @Test
+    public void pinv_matrix() {
+        Equation eq = new Equation();
+
+        SimpleMatrix a = SimpleMatrix.random(4,3,-1,1,rand);
+        SimpleMatrix b = SimpleMatrix.random(1,1,-1,1,rand);
+
+        eq.alias(a,"a",b,"b");
+        eq.process("b=pinv(a)");
+
+        assertTrue(a.pseudoInverse().isIdentical(b, 1e-8));
+    }
+
+    @Test
+    public void pinv_scalar() {
+        Equation eq = new Equation();
+
+        eq.alias(2.2,"a",3.3,"b");
+        eq.process("b=pinv(a)");
+
+        assertEquals(1.0 / 2.2, eq.lookupDouble("b"), 1e-8);
+    }
+
+    @Test
+    public void rref_matrix() {
+        Equation eq = new Equation();
+
+        SimpleMatrix a = SimpleMatrix.random(4,3,-1,1,rand);
+        SimpleMatrix b = SimpleMatrix.random(1,1,-1,1,rand);
+
+        eq.alias(a,"a",b,"b");
+        eq.process("b=rref(a)");
+
+        DenseMatrix64F expected = new DenseMatrix64F(4,3);
+        CommonOps.rref(a.getMatrix(),-1,expected);
+
+        assertTrue(MatrixFeatures.isIdentical(expected,b.getMatrix(),1e-8));
+    }
+
+    @Test
+    public void rref_scalar() {
+        Equation eq = new Equation();
+
+        eq.process("a=rref(2.3)");
+        assertEquals(1,eq.lookupDouble("a"),1e-8);
+
+        eq.process("a=rref(0)");
+        assertEquals(0,eq.lookupDouble("a"),1e-8);
+
+        eq.process("a=rref(-1.2)");
+        assertEquals(1,eq.lookupDouble("a"),1e-8);
+    }
+
+    @Test
+    public void det_matrix() {
+        Equation eq = new Equation();
+
+        SimpleMatrix a = SimpleMatrix.random(4,4,-1,1,rand);
+
+        eq.alias(a,"a");
+        eq.process("b=det(a)");
+
+        assertEquals(a.determinant(),eq.lookupDouble("b"),1e-8);
+    }
+
+    @Test
+    public void det_scalar() {
+        Equation eq = new Equation();
+
+        eq.process("b=det(5.6)");
+
+        assertEquals(5.6, eq.lookupDouble("b"), 1e-8);
+    }
+
+    @Test
+    public void trace_matrix() {
+        Equation eq = new Equation();
+
+        SimpleMatrix a = SimpleMatrix.random(3,4,-1,1,rand);
+
+        eq.alias(a,"a");
+        eq.process("b=trace(a)");
+
+        assertEquals(a.trace(), eq.lookupDouble("b"), 1e-8);
+    }
+
+    @Test
+    public void normF_matrix() {
+        Equation eq = new Equation();
+
+        SimpleMatrix a = SimpleMatrix.random(3,4,-1,1,rand);
+
+        eq.alias(a,"a");
+        eq.process("b=normF(a)");
+
+        assertEquals(a.normF(), eq.lookupDouble("b"), 1e-8);
+    }
+
+    @Test
+    public void normF_scalar() {
+        Equation eq = new Equation();
+
+        eq.process("b=normF(5.6)");
+
+        assertEquals(5.6, eq.lookupDouble("b"), 1e-8);
+    }
+
+    @Test
+    public void eye() {
+        Equation eq = new Equation();
+
+        SimpleMatrix a = SimpleMatrix.random(3,4,-1,1,rand);
+
+        eq.alias(a,"a");
+        eq.process("a=eye(3)");
+
+        assertTrue(SimpleMatrix.identity(3).isIdentical(a, 1e-8));
+    }
+
+    @Test
+    public void abs_matrix() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = SimpleMatrix.random(6, 5, -1, 1, rand);
+        SimpleMatrix B = SimpleMatrix.random(6, 5, -1, 1, rand);
+
+        eq.alias(A, "A");
+        eq.alias(B, "B");
+
+        eq.process("B=abs(A)");
+
+        for (int i = 0; i < A.numRows(); i++) {
+            for (int j = 0; j < A.numCols(); j++) {
+                assertTrue(B.get(i,j)==Math.abs(A.get(i,j)));
+            }
+        }
+    }
+
+    @Test
+    public void abs_int() {
+        Equation eq = new Equation();
+
+        eq.alias(-4, "A");
+        eq.alias(1, "B");
+
+        eq.process("B=abs(A)");
+
+        int found = eq.lookupInteger("B");
+        assertEquals(4,found,1e-8);
+    }
+
+    @Test
+    public void abs_scalar() {
+        Equation eq = new Equation();
+
+        eq.alias(-4.6, "A");
+        eq.alias(1.1, "B");
+
+        eq.process("B=abs(A)");
+
+        double found = eq.lookupDouble("B");
+        assertEquals(4.6,found,1e-8);
+    }
+
+    @Test
+    public void max_matrix() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = SimpleMatrix.random(6, 5, -1, 1, rand);
+
+        eq.alias(A, "A");
+        eq.alias(1.0, "B");
+
+        eq.process("B=max(A)");
+
+        double found = eq.lookupDouble("B");
+        double expected = CommonOps.elementMax(A.getMatrix());
+        assertEquals(expected,found,1e-8);
+    }
+
+    @Test
+    public void max_int() {
+        Equation eq = new Equation();
+
+        eq.alias(4, "A");
+        eq.alias(1, "B");
+
+        eq.process("B=max(A)");
+
+        int found = eq.lookupInteger("B");
+        assertEquals(4,found,1e-8);
+    }
+
+    @Test
+    public void max_scalar() {
+        Equation eq = new Equation();
+
+        eq.alias(4.6, "A");
+        eq.alias(1.1, "B");
+
+        eq.process("B=max(A)");
+
+        double found = eq.lookupDouble("B");
+        assertEquals(4.6,found,1e-8);
+    }
+
+    @Test
+    public void min_matrix() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = SimpleMatrix.random(6, 5, -1, 1, rand);
+
+        eq.alias(A, "A");
+        eq.alias(1.0, "B");
+
+        eq.process("B=min(A)");
+
+        double found = eq.lookupDouble("B");
+        double expected = CommonOps.elementMin(A.getMatrix());
+        assertEquals(expected,found,1e-8);
+    }
+
+    @Test
+    public void min_int() {
+        Equation eq = new Equation();
+
+        eq.alias(4, "A");
+        eq.alias(1, "B");
+
+        eq.process("B=min(A)");
+
+        int found = eq.lookupInteger("B");
+        assertEquals(4,found,1e-8);
+    }
+
+    @Test
+    public void min_scalar() {
+        Equation eq = new Equation();
+
+        eq.alias(4.6, "A");
+        eq.alias(1.1, "B");
+
+        eq.process("B=min(A)");
+
+        double found = eq.lookupDouble("B");
+        assertEquals(4.6,found,1e-8);
+    }
+
+    @Test
+    public void zeros() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = SimpleMatrix.random(6, 8, -1, 1, rand);
+
+        eq.alias(A, "A");
+
+        eq.process("A=zeros(6,8)");
+
+        for (int i = 0; i < 6; i++) {
+            for (int j = 0; j < 8; j++) {
+                assertEquals(0,A.get(i,j),1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void ones() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = SimpleMatrix.random(6, 8, -1, 1, rand);
+
+        eq.alias(A, "A");
+
+        eq.process("A=ones(6,8)");
+
+        for (int i = 0; i < 6; i++) {
+            for (int j = 0; j < 8; j++) {
+                assertEquals(1,A.get(i,j),1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void diag_vector() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = SimpleMatrix.random(6, 6, -1, 1, rand);
+        SimpleMatrix B = SimpleMatrix.random(6, 1, -1, 1, rand);
+
+
+        eq.alias(A, "A");
+        eq.alias(B, "B");
+
+        eq.process("A=diag(B)");
+
+        for (int i = 0; i < 6; i++) {
+            for (int j = 0; j < 6; j++) {
+                if( i == j )
+                    assertEquals(B.get(i,0),A.get(i,j),1e-8);
+                else
+                    assertEquals(0,A.get(i,j),1e-8);
+            }
+        }
+    }
+
+    @Test
+    public void diag_matrix() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = SimpleMatrix.random(6, 8, -1, 1, rand);
+        SimpleMatrix B = SimpleMatrix.random(6, 1, -1, 1, rand);
+
+        eq.alias(A, "A");
+        eq.alias(B, "B");
+
+        eq.process("B=diag(A)");
+
+        assertEquals(6,B.numRows());
+        assertEquals(1,B.numCols());
+
+        for (int i = 0; i < 6; i++) {
+            assertEquals(A.get(i,i),B.get(i,0),1e-8);
+        }
+    }
+
+    @Test
+    public void dot() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = SimpleMatrix.random(6, 1, -1, 1, rand);
+        SimpleMatrix B = SimpleMatrix.random(6, 1, -1, 1, rand);
+
+        eq.alias(A, "A");
+        eq.alias(B, "B");
+        eq.alias(1.0, "found");
+
+        eq.process("found=dot(A,B)");
+
+        double found = ((VariableDouble)eq.lookupVariable("found")).value;
+
+        assertEquals(A.dot(B),found,1e-8);
+    }
+
+    @Test
+    public void solve() {
+        Equation eq = new Equation();
+
+        SimpleMatrix A = SimpleMatrix.random(6, 5, -1, 1, rand);
+        SimpleMatrix x = SimpleMatrix.random(5, 3, -1, 1, rand);
+        SimpleMatrix b = SimpleMatrix.random(6, 3, -1, 1, rand);
+
+        eq.alias(A, "A");
+        eq.alias(b, "b");
+        eq.alias(x, "x");
+
+        eq.process("x=solve(A,b)");
+
+        assertTrue(A.solve(b).isIdentical(x, 1e-8));
+    }
+}
diff --git a/main/equation/test/org/ejml/equation/TestSequence.java b/main/equation/test/org/ejml/equation/TestSequence.java
new file mode 100644
index 0000000..2c22fe7
--- /dev/null
+++ b/main/equation/test/org/ejml/equation/TestSequence.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.equation;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestSequence {
+
+    int total;
+
+    /**
+     * Checks the order in which operations are run
+     */
+    @Test
+    public void order() {
+        Sequence s = new Sequence();
+        s.addOperation(new Foo("a",0));
+        s.addOperation(new Foo("b",1));
+
+        total = 0;
+        s.perform();
+        assertEquals(2,total);
+    }
+
+    public class Foo extends Operation {
+
+        int expected;
+
+        protected Foo(String name , int expected ) {
+            super(name);
+            this.expected = expected;
+        }
+
+        @Override
+        public void process() {
+            assertEquals(expected, total);
+            total++;
+        }
+    }
+
+}
diff --git a/main/equation/test/org/ejml/equation/TestTokenList.java b/main/equation/test/org/ejml/equation/TestTokenList.java
new file mode 100644
index 0000000..c9cc459
--- /dev/null
+++ b/main/equation/test/org/ejml/equation/TestTokenList.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.equation;
+
+import org.junit.Test;
+
+import static org.ejml.equation.TokenList.Token;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestTokenList {
+
+    @Test
+    public void push() {
+        TokenList list = new TokenList();
+
+        assertTrue(null==list.getFirst());
+        assertTrue(null==list.getLast());
+        assertEquals(0,list.size());
+
+        list.add(Symbol.MINUS);
+        Token a = list.getFirst();
+        assertTrue(null!=a);
+        assertTrue(a==list.getLast());
+        assertEquals(1,list.size());
+
+
+        list.add(Symbol.PAREN_LEFT);
+        Token b = list.getLast();
+        assertTrue(a!=b);
+        assertTrue(b!=list.getFirst());
+        assertEquals(2,list.size());
+
+        list.add(Symbol.PAREN_RIGHT);
+        Token c = list.getLast();
+        assertTrue(a!=c);
+        assertTrue(b!=c);
+        assertTrue(c!=list.getFirst());
+        assertEquals(3,list.size());
+    }
+
+    @Test
+    public void insert() {
+        TokenList list = new TokenList();
+
+        list.insert(null,new Token(Symbol.MINUS));
+        assertEquals(1, list.size());
+        assertTrue(list.first == list.last && list.first != null);
+
+        list.insert(null,new Token(Symbol.PLUS));
+        assertEquals(2, list.size());
+        assertEquals(Symbol.PLUS,list.first.getSymbol());
+        assertEquals(Symbol.MINUS, list.first.next.getSymbol());
+
+        list.insert(list.last,new Token(Symbol.TIMES));
+        assertEquals(3, list.size());
+        assertEquals(Symbol.PLUS,list.first.getSymbol());
+        assertEquals(Symbol.MINUS,list.first.next.getSymbol());
+        assertEquals(Symbol.TIMES,list.last.getSymbol());
+
+        list.insert(list.first.next,new Token(Symbol.ASSIGN));
+        assertEquals(4, list.size());
+        assertEquals(Symbol.MINUS, list.first.next.getSymbol());
+        assertEquals(Symbol.ASSIGN, list.first.next.next.getSymbol());
+        assertEquals(Symbol.TIMES, list.first.next.next.next.getSymbol());
+    }
+
+    @Test
+    public void remove() {
+        TokenList list = new TokenList();
+        Token A,B,C;
+
+        list.add(Symbol.MINUS);
+        list.remove(list.first);
+        assertEquals(0,list.size);
+        assertTrue(null==list.getFirst());
+        assertTrue(null==list.getLast());
+
+        A = list.add(Symbol.MINUS);
+        list.add(Symbol.PLUS);
+        list.remove(list.last);
+        assertEquals(1, list.size);
+        assertTrue(A == list.getFirst());
+        assertTrue(A==list.getLast());
+        assertTrue(A.next==null);
+        assertTrue(A.previous==null);
+        B = list.add(Symbol.PLUS);
+        list.remove(list.first);
+        assertEquals(1, list.size);
+        assertTrue(B == list.getFirst());
+        assertTrue(B==list.getLast());
+        assertTrue(B.next==null);
+        assertTrue(B.previous==null);
+
+
+        list.remove(list.first);
+        A = list.add(Symbol.MINUS);
+        B = list.add(Symbol.PLUS);
+        C = list.add(Symbol.TIMES);
+        list.remove(list.first.next);
+        assertEquals(2, list.size);
+        assertTrue(A == list.getFirst());
+        assertTrue(C==list.getLast());
+        assertTrue(A.next==C);
+        assertTrue(A==C.previous);
+
+    }
+
+    @Test
+    public void replace() {
+        TokenList list = new TokenList();
+        Token A,B,C,D;
+
+        A = list.add(Symbol.MINUS);
+        B = new Token(Symbol.PLUS);
+        list.replace(A, B);
+        assertEquals(1,list.size);
+        assertTrue(B==list.getFirst());
+        assertTrue(B == list.getLast());
+
+        list.remove(B);
+
+        A = list.add(Symbol.MINUS);
+        B = list.add(Symbol.PLUS);
+        C = new Token(Symbol.TIMES);
+
+        list.replace(A, C);
+        assertEquals(2, list.size);
+        assertTrue(C==list.getFirst());
+        assertTrue(B==list.getLast());
+        assertTrue(C.previous==null);
+        assertTrue(C.next==B);
+        assertTrue(B.previous==C);
+        assertTrue(B.next==null);
+
+        list = new TokenList();
+        A=list.add(Symbol.MINUS);
+        B=list.add(Symbol.PLUS);
+        C=list.add(Symbol.TIMES);
+        D=new Token(Symbol.RDIVIDE);
+
+        list.replace(B,D);
+        assertEquals(3, list.size);
+        assertTrue(A == list.getFirst());
+        assertTrue(C==list.getLast());
+        assertTrue(A.next==D);
+        assertTrue(A==D.previous);
+        assertTrue(C.previous==D);
+        assertTrue(C==D.next);
+    }
+
+    @Test
+    public void extractSubList() {
+
+        // Check when the input list has a size of 1
+        TokenList list = new TokenList();
+        list.add(Symbol.MINUS);
+
+        TokenList found = list.extractSubList(list.first,list.first);
+        assertEquals(0, list.size);
+        assertEquals(1, found.size);
+        assertTrue(null == list.first && null == list.last);
+        assertTrue(found.first==found.last && found.first != null);
+
+        // Remove entire thing
+        list = new TokenList();
+        list.add(Symbol.MINUS);
+        list.add(Symbol.PLUS);
+        list.add(Symbol.RDIVIDE);
+        found = list.extractSubList(list.first,list.last);
+        assertEquals(0,list.size);
+        assertEquals(3,found.size);
+        assertEquals(Symbol.MINUS,found.first.getSymbol());
+        assertEquals(Symbol.RDIVIDE,found.last.getSymbol());
+
+        // Remove stuff in the middle
+        list = new TokenList();
+        list.add(Symbol.MINUS);
+        list.add(Symbol.PLUS);
+        list.add(Symbol.RDIVIDE);
+        list.add(Symbol.TIMES);
+        found = list.extractSubList(list.first.next,list.last.previous);
+        assertEquals(2,list.size);
+        assertEquals(2,found.size);
+        assertEquals(Symbol.MINUS,list.first.getSymbol());
+        assertEquals(Symbol.TIMES,list.last.getSymbol());
+        assertEquals(Symbol.PLUS,found.first.getSymbol());
+        assertEquals(Symbol.RDIVIDE,found.last.getSymbol());
+    }
+
+    @Test
+    public void Token_getType() {
+        assertTrue(new Token(new VariableMatrix(null)).getType() == TokenList.Type.VARIABLE);
+        assertTrue(new Token(Symbol.PLUS).getType() == TokenList.Type.SYMBOL);
+        assertTrue(new Token(new Function("foo")).getType() == TokenList.Type.FUNCTION);
+    }
+}
diff --git a/main/experimental/benchmarks/src/org/ejml/BenchmarkElement.java b/main/experimental/benchmarks/src/org/ejml/BenchmarkElement.java
new file mode 100644
index 0000000..d5a0e5d
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/BenchmarkElement.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class BenchmarkElement {
+    static Random rand = new Random(234);
+
+    public static void main( String args[] ) {
+        long N = 10000000;
+
+        double num = 2.5;
+
+        DenseMatrix64F A = RandomMatrices.createRandom(10,10,rand);
+
+        long timeBefore = System.currentTimeMillis();
+        for( int i = 0; i < N; i++ ) {
+            CommonOps.divide(A,num);
+        }
+        long timeAfter = System.currentTimeMillis();
+
+        System.out.println("div = "+(timeAfter-timeBefore));
+
+        A = RandomMatrices.createRandom(10,10,rand);
+
+        timeBefore = System.currentTimeMillis();
+        for( int i = 0; i < N; i++ ) {
+            CommonOps.scale(num,A);
+        }
+        timeAfter = System.currentTimeMillis();
+
+        System.out.println("scale = "+(timeAfter-timeBefore));
+    }
+}
diff --git a/main/experimental/benchmarks/src/org/ejml/BenchmarkGenerics.java b/main/experimental/benchmarks/src/org/ejml/BenchmarkGenerics.java
new file mode 100644
index 0000000..f38191b
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/BenchmarkGenerics.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml;
+
+import org.ejml.data.D1Matrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * Checks to see if using generics slows things down.
+ *
+ * @author Peter Abeles
+ */
+public class BenchmarkGenerics {
+
+
+    private static class ImplDense implements ScaleDense
+    {
+        @Override
+        public void scale(double s, DenseMatrix64F mat) {
+            for( int i = 0; i < mat.data.length; i++ ) {
+                mat.data[i] *= s;
+            }
+        }
+    }
+
+    private static class ImplGeneric implements ScaleGeneric<DenseMatrix64F>
+    {
+        @Override
+        public void scale(double s, DenseMatrix64F mat) {
+            for( int i = 0; i < mat.data.length; i++ ) {
+                mat.data[i] *= s;
+            }
+        }
+    }
+
+    private static interface ScaleDense
+    {
+        public void scale( double s , DenseMatrix64F mat );
+    }
+
+    private static interface ScaleGeneric<T extends D1Matrix64F>
+    {
+        public void scale( double s , T mat );
+    }
+
+    public static long benchmarkDense( DenseMatrix64F A , double scale , int trials ) {
+        ScaleDense s = new ImplDense();
+
+        long before = System.currentTimeMillis();
+
+        for( int i = 0; i < trials; i++ ) {
+            s.scale(scale,A);
+            s.scale(1.0/scale,A);
+        }
+
+        return System.currentTimeMillis() - before;
+    }
+
+    public static long benchmarkGeneric( DenseMatrix64F A , double scale , int trials ) {
+        ImplGeneric s = new ImplGeneric();
+
+        long before = System.currentTimeMillis();
+
+        for( int i = 0; i < trials; i++ ) {
+            s.scale(scale,A);
+            s.scale(1.0/scale,A);
+        }
+
+        return System.currentTimeMillis() - before;
+    }
+
+
+    public static void main( String []args ) {
+        DenseMatrix64F A = RandomMatrices.createRandom(10,10,new Random(234));
+
+        int N = 10000000;
+
+        System.out.println("dense   = "+benchmarkDense(A,2.5,N));
+        System.out.println("generic = "+benchmarkGeneric(A,2.5,N));
+    }
+}
diff --git a/main/experimental/benchmarks/src/org/ejml/BenchmarkInheritanceCall.java b/main/experimental/benchmarks/src/org/ejml/BenchmarkInheritanceCall.java
new file mode 100644
index 0000000..776263a
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/BenchmarkInheritanceCall.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml;
+
+import org.ejml.data.D1Matrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * Benchmark that tests to see if referring the parent of the class versus the actual class
+ * has any performance difference.  The function used internally is matrix multiplication in "ikj" order.
+ *
+ * @author Peter Abeles
+ */
+public class BenchmarkInheritanceCall {
+
+    public static void multParent( D1Matrix64F a , D1Matrix64F b , D1Matrix64F c )
+    {
+        double dataA[] = a.data;
+        double dataB[] = b.data;
+        double dataC[] = c.data;
+
+        double valA;
+        int indexCbase= 0;
+        int endOfKLoop = b.numRows*b.numCols;
+
+        for( int i = 0; i < a.numRows; i++ ) {
+            int indexA = i*a.numCols;
+
+            // need to assign dataC to a value initially
+            int indexB = 0;
+            int indexC = indexCbase;
+            int end = indexB + b.numCols;
+
+            valA = dataA[indexA++];
+
+            while( indexB < end ) {
+                dataC[indexC++] = valA*dataB[indexB++];
+            }
+
+            // now add to it
+            while( indexB != endOfKLoop ) { // k loop
+                indexC = indexCbase;
+                end = indexB + b.numCols;
+
+                valA = dataA[indexA++];
+
+                while( indexB < end ) { // j loop
+                    dataC[indexC++] += valA*dataB[indexB++];
+                }
+            }
+            indexCbase += c.numCols;
+        }
+    }
+
+    public static void multParent_wrap( D1Matrix64F a , D1Matrix64F b , D1Matrix64F c )
+    {
+        double valA;
+        int indexCbase= 0;
+        int endOfKLoop = b.numRows*b.numCols;
+
+        for( int i = 0; i < a.numRows; i++ ) {
+            int indexA = i*a.numCols;
+
+            // need to assign dataC to a value initially
+            int indexB = 0;
+            int indexC = indexCbase;
+            int end = indexB + b.numCols;
+
+            valA = a.get(indexA++);
+
+            while( indexB < end ) {
+                c.set( indexC++ , valA*b.get(indexB++));
+            }
+
+            // now add to it
+            while( indexB != endOfKLoop ) { // k loop
+                indexC = indexCbase;
+                end = indexB + b.numCols;
+
+                valA = a.get(indexA++);
+
+                while( indexB < end ) { // j loop
+                    c.plus( indexC++ , valA*b.get(indexB++));
+                }
+            }
+            indexCbase += c.numCols;
+        }
+    }
+
+    public static void multChild( DenseMatrix64F a , DenseMatrix64F b , DenseMatrix64F c )
+    {
+        double dataA[] = a.data;
+        double dataB[] = b.data;
+        double dataC[] = c.data;
+
+        double valA;
+        int indexCbase= 0;
+        int endOfKLoop = b.numRows*b.numCols;
+
+        for( int i = 0; i < a.numRows; i++ ) {
+            int indexA = i*a.numCols;
+
+            // need to assign dataC to a value initially
+            int indexB = 0;
+            int indexC = indexCbase;
+            int end = indexB + b.numCols;
+
+            valA = dataA[indexA++];
+
+            while( indexB < end ) {
+                dataC[indexC++] = valA*dataB[indexB++];
+            }
+
+            // now add to it
+            while( indexB != endOfKLoop ) { // k loop
+                indexC = indexCbase;
+                end = indexB + b.numCols;
+
+                valA = dataA[indexA++];
+
+                while( indexB < end ) { // j loop
+                    dataC[indexC++] += valA*dataB[indexB++];
+                }
+            }
+            indexCbase += c.numCols;
+        }
+    }
+
+    public static void main( String args[] ) {
+        Random rand = new Random(23234);
+
+        DenseMatrix64F A = RandomMatrices.createRandom(2,2,rand);
+        DenseMatrix64F B = RandomMatrices.createRandom(2,2,rand);
+        DenseMatrix64F C = new DenseMatrix64F(2,2);
+
+        int N = 40000000;
+
+        long before = System.currentTimeMillis();
+        for( int i = 0; i < N; i++ ) {
+            multParent(A,B,C);
+        }
+        long after = System.currentTimeMillis();
+
+        System.out.println("Parent:       "+(after-before));
+
+        before = System.currentTimeMillis();
+        for( int i = 0; i < N; i++ ) {
+            multParent_wrap(A,B,C);
+        }
+        after = System.currentTimeMillis();
+
+        System.out.println("Parent func:  "+(after-before));
+
+        before = System.currentTimeMillis();
+        for( int i = 0; i < N; i++ ) {
+            multChild(A,B,C);
+        }
+        after = System.currentTimeMillis();
+
+        System.out.println("Child:        "+(after-before));
+    }
+}
diff --git a/main/experimental/benchmarks/src/org/ejml/BenchmarkInliningGetSet.java b/main/experimental/benchmarks/src/org/ejml/BenchmarkInliningGetSet.java
new file mode 100644
index 0000000..7558172
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/BenchmarkInliningGetSet.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * Test to see how well set and get are inlined in DenseMatrix64F.
+ *
+ * @author Peter Abeles
+ */
+public class BenchmarkInliningGetSet {
+
+    /**
+     * Bounds checks are performed on get(i,j)
+     */
+    public static long benchGet( DenseMatrix64F A , int n ) {
+
+        long before = System.currentTimeMillis();
+
+        double total = 0;
+
+        for( int iter = 0; iter < n; iter++ ) {
+
+            for( int i = 0; i < A.numRows; i++ ) {
+                for( int j = 0; j < A.numCols; j++ ) {
+                    total += A.get(i,j);
+                }
+            }
+        }
+
+        long after = System.currentTimeMillis();
+
+        // print to ensure that ensure that an overly smart compiler does not optimize out
+        // the whole function and to show that both produce the same results.
+        System.out.println(total);
+
+        return after-before;
+    }
+
+    /**
+     * Unsafe version of get(i,j) with no bounds checking
+     */
+    public static long getUnsafeGet( DenseMatrix64F A , int n ) {
+
+        long before = System.currentTimeMillis();
+
+        double total = 0;
+
+        for( int iter = 0; iter < n; iter++ ) {
+
+            for( int i = 0; i < A.numRows; i++ ) {
+                for( int j = 0; j < A.numCols; j++ ) {
+                    total += A.unsafe_get(i,j);
+                }
+            }
+        }
+
+        long after = System.currentTimeMillis();
+
+        // print to ensure that ensure that an overly smart compiler does not optimize out
+        // the whole function and to show that both produce the same results.
+        System.out.println(total);
+
+        return after-before;
+    }
+
+    /**
+     * Get by index is used here.
+     */
+    public static long get1D( DenseMatrix64F A , int n ) {
+
+        long before = System.currentTimeMillis();
+
+        double total = 0;
+
+        for( int iter = 0; iter < n; iter++ ) {
+
+            int index = 0;
+            for( int i = 0; i < A.numRows; i++ ) {
+                int end = index+A.numCols;
+                while( index != end ) {
+                    total += A.get(index++);
+                }
+            }
+        }
+
+        long after = System.currentTimeMillis();
+
+        // print to ensure that ensure that an overly smart compiler does not optimize out
+        // the whole function and to show that both produce the same results.
+        System.out.println(total);
+
+        return after-before;
+    }
+
+    /**
+     * Hand inlined version of get(i,j)
+     */
+    public static long inlined( DenseMatrix64F A , int n ) {
+
+        long before = System.currentTimeMillis();
+
+        double total = 0;
+
+        for( int iter = 0; iter < n; iter++ ) {
+
+            for( int i = 0; i < A.numRows; i++ ) {
+                for( int j = 0; j < A.numCols; j++ ) {
+                    total += A.data[i*A.numCols + j];
+                }
+            }
+        }
+
+        long after = System.currentTimeMillis();
+
+        // print to ensure that ensure that an overly smart compiler does not optimize out
+        // the whole function and to show that both produce the same results.
+        System.out.println(total);
+
+        return after-before;
+    }
+
+    public static void main( String args[] ) {
+        DenseMatrix64F A = RandomMatrices.createRandom(1000,1000,new Random());
+
+        int N = 2000;
+
+        long time1D = get1D(A,N);
+        long timeInlined = inlined(A,N);
+        long timeGet = benchGet(A,N);
+        long timeUnsafeGet = getUnsafeGet(A,N);
+        
+        System.out.println("get = "+timeGet+"  Inlined "+timeInlined+" unsafe_get "+timeUnsafeGet+" get1D "+time1D);
+    }
+}
diff --git a/main/experimental/benchmarks/src/org/ejml/BenchmarkInstanceOf.java b/main/experimental/benchmarks/src/org/ejml/BenchmarkInstanceOf.java
new file mode 100644
index 0000000..b0f938d
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/BenchmarkInstanceOf.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml;
+
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.D1Matrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.data.RealMatrix64F;
+import org.ejml.ops.CommonOps;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class BenchmarkInstanceOf {
+
+    public static final double SCALE = 1.1;
+
+    public static final StuffA stuff = new StuffA();
+
+    public interface Stuff
+    {
+        public void process( Stuff a, RealMatrix64F M );
+    }
+
+    public static class StuffA implements Stuff
+    {
+
+        @Override
+        public void process(Stuff a, RealMatrix64F M) {
+
+            if( M instanceof BlockMatrix64F) {
+                CommonOps.scale(1.0,(BlockMatrix64F)M);
+            } else if( M instanceof DenseMatrix64F) {
+                CommonOps.scale(SCALE,(DenseMatrix64F)M);
+//                CommonOps.scale(0.5,(DenseMatrix64F)M);
+            } else if(M instanceof D1Matrix64F) {
+                CommonOps.scale(1.0,(D1Matrix64F)M);
+            } else {
+               throw new IllegalArgumentException("Who knows");
+            }
+        }
+    }
+
+    public static void withIfStatement( DenseMatrix64F M )
+    {
+        if( M.numCols > 10 ) {
+            CommonOps.scale(2.0,M);
+        } else if( M.numRows > 12 ) {
+            CommonOps.scale(2.0,M);
+        } else {
+            CommonOps.scale(SCALE,M);
+//            CommonOps.scale(0.5,M);
+        }
+    }
+
+
+    public static long processInstanceOf( DenseMatrix64F M , int N ) {
+
+        long before = System.currentTimeMillis();
+
+        for( int i = 0; i < N; i++ ) {
+            stuff.process(null,M);
+        }
+
+        return System.currentTimeMillis() - before;
+    }
+
+    public static long processDirect( DenseMatrix64F M , int N ) {
+
+        long before = System.currentTimeMillis();
+
+        for( int i = 0; i < N; i++ ) {
+            CommonOps.scale(SCALE,M);
+//            CommonOps.scale(0.5,M);
+        }
+
+        return System.currentTimeMillis() - before;
+    }
+
+    public static long processIf( DenseMatrix64F M , int N ) {
+
+        long before = System.currentTimeMillis();
+
+        for( int i = 0; i < N; i++ ) {
+            withIfStatement(M);
+        }
+
+        return System.currentTimeMillis() - before;
+    }
+
+
+    public static void main( String args[] ) {
+        DenseMatrix64F A = new DenseMatrix64F(2,2,true,0.1,0.5,0.7,10.0);
+
+        int N = 200000000;
+
+        System.out.println("instanceof "+processInstanceOf(A,N));
+        System.out.println("direct     "+processDirect(A,N));
+        System.out.println("if         "+processIf(A,N));
+    }
+}
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/block/BenchmarkBlockTranspose.java b/main/experimental/benchmarks/src/org/ejml/alg/block/BenchmarkBlockTranspose.java
new file mode 100644
index 0000000..bf137b5
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/block/BenchmarkBlockTranspose.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.block;
+
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * Compare block against other transpose for DenseMatrix64F
+ *
+ *  @author Peter Abeles
+ */
+public class BenchmarkBlockTranspose {
+
+    static Random rand = new Random(234);
+
+    public static long transposeDenseInPlace( DenseMatrix64F mat , int numTrials) {
+
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CommonOps.transpose(mat);
+        }
+        long curr = System.currentTimeMillis();
+
+        return curr-prev;
+    }
+
+    public static long transposeDense( DenseMatrix64F mat , int numTrials) {
+
+
+        DenseMatrix64F tran = new DenseMatrix64F(mat.numCols,mat.numRows);
+
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CommonOps.transpose(mat,tran);
+        }
+        long curr = System.currentTimeMillis();
+
+        return curr-prev;
+    }
+
+    public static long transposeBlock( DenseMatrix64F mat , int numTrials) {
+
+        BlockMatrix64F A = new BlockMatrix64F(mat.numRows,mat.numCols);
+        BlockMatrix64F A_t = new BlockMatrix64F(mat.numCols,mat.numRows);
+
+        BlockMatrixOps.convert(mat,A);
+
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            BlockMatrixOps.transpose(A,A_t);
+        }
+        long curr = System.currentTimeMillis();
+
+        return curr-prev;
+    }
+
+    public static void main( String args[] ) {
+
+        DenseMatrix64F A = RandomMatrices.createRandom(5000,5000,rand);
+
+        int N = 5;
+
+        System.out.println("In place  : "+transposeDenseInPlace(A,N));
+        System.out.println("Standard  : "+transposeDense(A,N));
+        System.out.println("Block     : "+transposeBlock(A,N));
+    }
+}
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/complex/mult/BenchmarkMatrixMatrixMult.java b/main/experimental/benchmarks/src/org/ejml/alg/complex/mult/BenchmarkMatrixMatrixMult.java
new file mode 100644
index 0000000..a7144d4
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/complex/mult/BenchmarkMatrixMatrixMult.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.complex.mult;
+
+import org.ejml.alg.block.BlockMatrixOps;
+import org.ejml.alg.blockd3.BlockD3MatrixOps;
+import org.ejml.alg.dense.mult.CMatrixMatrixMult;
+import org.ejml.data.BlockD3Matrix64F;
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CCommonOps;
+import org.ejml.ops.CRandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ *
+ * Some notes:
+ *
+ * Other libraries implement there multiplication the same as my aux implementation, but theirs run faster.
+ * That is because they use 2D arrays, this allows them to only increment one variable in their inner
+ * most loop.  While in mine I have to increment two.  Thus there is an additional N^3 addition operations.
+ *
+ * @author Peter Abeles
+ */
+public class BenchmarkMatrixMatrixMult {
+
+    static Random rand = new Random(234234);
+
+    static int TRIALS_MULT = 10000000;
+
+    public static long multiply( CDenseMatrix64F matA , CDenseMatrix64F matB ,
+                             CDenseMatrix64F matResult , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CCommonOps.mult(matA, matB, matResult);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long multSmall( CDenseMatrix64F matA , CDenseMatrix64F matB ,
+                                  CDenseMatrix64F matResult , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CMatrixMatrixMult.mult_small(matA,matB,matResult);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+//    public static long multAux( DenseMatrix64F matA , DenseMatrix64F matB ,
+//                             DenseMatrix64F matResult , int numTrials) {
+//        long prev = System.currentTimeMillis();
+//
+//        for( int i = 0; i < numTrials; i++ ) {
+//            MatrixMatrixMult.mult_aux(matA,matB,matResult,null);
+//        }
+//
+//        long curr = System.currentTimeMillis();
+//        return curr-prev;
+//    }
+
+    public static long multReorder( CDenseMatrix64F matA , CDenseMatrix64F matB ,
+                                    CDenseMatrix64F matResult , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CMatrixMatrixMult.mult_reorder(matA, matB, matResult);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long multBlockNative( DenseMatrix64F matA , DenseMatrix64F matB ,
+                                        DenseMatrix64F matResult , int numTrials) {
+        BlockMatrix64F blockA = BlockMatrixOps.convert(matA);
+        BlockMatrix64F blockB = BlockMatrixOps.convert(matB);
+        BlockMatrix64F blockC = new BlockMatrix64F(matResult.numRows,matResult.numCols);
+
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            BlockMatrixOps.mult(blockA,blockB,blockC);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long multBlockD3Native( DenseMatrix64F matA , DenseMatrix64F matB ,
+                                          DenseMatrix64F matResult , int numTrials) {
+        BlockD3Matrix64F blockA = BlockD3MatrixOps.convert(matA);
+        BlockD3Matrix64F blockB = BlockD3MatrixOps.convert(matB);
+        BlockD3Matrix64F blockC = new BlockD3Matrix64F(matResult.numRows,matResult.numCols);
+
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            BlockD3MatrixOps.mult(blockA,blockB,blockC);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+
+    public static void performTests( int numRows , int numCols , int numK,
+                                     int numTrials )
+    {
+        System.out.println("M = "+numRows+" N = "+numCols+" K = "+numK);
+        CDenseMatrix64F matA = CRandomMatrices.createRandom(numRows,numCols,-1,1,rand);
+        CDenseMatrix64F matB = CRandomMatrices.createRandom(numCols,numK,-1,1,rand);
+        CDenseMatrix64F matResult = CRandomMatrices.createRandom(numRows, numK,-1,1, rand);
+
+        System.out.printf("Mult: %7d  Small %7d  Aux %7d  Reord %7d  Block %7d  BlockD3 %7d\n",
+                0,//mult(matA,matB,matResult,numTrials),
+                multSmall(matA,matB,matResult,numTrials),
+                0,//multAux(matA,matB,matResult,numTrials),
+                multReorder(matA,matB,matResult,numTrials),
+                0,//multBlockNative(matA,matB,matResult,numTrials),
+                0);//multBlockD3Native(matA,matB,matResult,numTrials));
+        System.gc();
+    }
+
+    public static void main( String args[] ) {
+        int size[] = new int[]{2,4,10,15,20,50,100,200,500,1000,2000,4000,10000};
+        int count[] = new int[]{40000000,10000000,1000000,300000,100000,10000,1000,100,8,2,1,1,1};
+
+        int sizeTall[] = new int[]{1,2,4,10,20,50,100,200,500,1000,2000,5000,10000};
+        int countTall[] = new int[]{3000,2400,1500,1000,200,200,100,50,10,5,2,1,1};
+
+        int N = size.length;
+
+        System.out.println("******* Square:\n");
+        for( int i = 2; i < N; i++ ) {
+            System.out.println();
+
+            performTests(size[i],size[i],size[i],count[i]);
+        }
+
+        N = sizeTall.length;
+        System.out.println("\n******* Wide A:");
+        for( int i = 0; i < N; i++ ) {
+            System.out.println();
+
+            performTests(sizeTall[i],1500,100,countTall[i]);
+        }
+
+        System.out.println("\n******* Tall A:");
+        for( int i = 7; i < N; i++ ) {
+            System.out.println();
+
+            performTests(1500,sizeTall[i],100,countTall[i]);
+        }
+
+        System.out.println("\n******* Wide B:");
+        for( int i = 7; i < N; i++ ) {
+            System.out.println();
+
+            performTests(100,sizeTall[i],1500,countTall[i]);
+        }
+
+        System.out.println("\n******* Tall B:");
+        for( int i = 7; i < N; i++ ) {
+            System.out.println();
+
+            performTests(100,1500,sizeTall[i],countTall[i]);
+        }
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/bidiagonal/BenchmarkBidiagonalDecomposition.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/bidiagonal/BenchmarkBidiagonalDecomposition.java
new file mode 100644
index 0000000..8ba1faf
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/bidiagonal/BenchmarkBidiagonalDecomposition.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.bidiagonal;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.BidiagonalDecomposition;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * Compare the speed of various algorithms at inverting square matrices
+ *
+ * @author Peter Abeles
+ */
+public class BenchmarkBidiagonalDecomposition {
+
+
+    public static long evaluate( BidiagonalDecomposition<DenseMatrix64F> alg , DenseMatrix64F orig , int numTrials ) {
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            if( !alg.decompose(orig.copy()) ) {
+                throw new RuntimeException("Bad matrix");
+            }
+//            alg.getU(null,false,false);
+//            alg.getV(null,false,false);
+
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    private static void runAlgorithms( DenseMatrix64F mat , int numTrials )
+    {
+        if( numTrials <= 0 ) return;
+        System.out.println("row               = "+ evaluate(new BidiagonalDecompositionRow_D64(),mat,numTrials));
+        System.out.println("tall              = "+ evaluate(new BidiagonalDecompositionTall_D64(),mat,numTrials));
+    }
+
+    public static void main( String args [] ) {
+        Random rand = new Random(23423);
+
+        int size[] = new int[]{2,4,10,100,500,1000,2000,5000,10000};
+        int trials[] = new int[]{(int)4e6,(int)1e6,(int)1e5,200,1,1,1,1,1};
+//        int trials[] = new int[]{(int)1e6,(int)2e5,(int)2e4,50,1,1,1,1,1};
+
+        System.out.println("Square matrix");
+        // results vary significantly depending if it starts from a small or large matrix
+        for( int i = 0; i < size.length; i++ ) {
+            int w = size[i];
+
+            System.out.printf("Decomposition size %3d for %12d trials\n",w,trials[i]);
+
+            System.out.print("* Creating matrix ");
+            DenseMatrix64F mat = RandomMatrices.createRandom(w,w,rand);
+            System.out.println("  Done.");
+            runAlgorithms(mat,trials[i]);
+        }
+
+        System.out.println("Tall matrix");
+        // results vary significantly depending if it starts from a small or large matrix
+        for( int i = 0; i < size.length; i++ ) {
+            int w = size[i];
+            int h = w*3;
+
+            int t = trials[i];
+
+            if( t == 0 ) continue;
+
+            System.out.printf("Decomposition size w=%3d h=%3d for %12d trials\n",w,h,t);
+
+            System.out.print("* Creating matrix ");
+            DenseMatrix64F mat = RandomMatrices.createRandom(h,w,rand);
+            System.out.println("  Done.");
+            runAlgorithms(mat,t);
+        }
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/bidiagonal/StabilityBidiagonalDecomposition.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/bidiagonal/StabilityBidiagonalDecomposition.java
new file mode 100644
index 0000000..3c0b913
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/bidiagonal/StabilityBidiagonalDecomposition.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.bidiagonal;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.BidiagonalDecomposition;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.RandomMatrices;
+import org.ejml.simple.SimpleMatrix;
+
+import java.util.Random;
+
+
+/**
+ * Compare the speed of various algorithms at inverting square matrices
+ *
+ * @author Peter Abeles
+ */
+public class StabilityBidiagonalDecomposition {
+
+
+    public static double evaluate( BidiagonalDecomposition<DenseMatrix64F> alg , DenseMatrix64F orig ) {
+
+        if( !alg.decompose(orig.copy())) {
+            return Double.NaN;
+        }
+
+        SimpleMatrix U = SimpleMatrix.wrap(alg.getU(null,false,true));
+        SimpleMatrix B = SimpleMatrix.wrap(alg.getB(null,true));
+        SimpleMatrix Vt = SimpleMatrix.wrap(alg.getV(null,true,true));
+
+        SimpleMatrix A_found = U.mult(B).mult(Vt);
+        SimpleMatrix A = SimpleMatrix.wrap(orig);
+
+        double top = A_found.minus(A).normF();
+        double bottom = A.normF();
+
+        return top/bottom;
+    }
+
+    private static void runAlgorithms( DenseMatrix64F mat  )
+    {
+        System.out.println("row               = "+ evaluate(new BidiagonalDecompositionRow_D64(),mat));
+        System.out.println("tall              = "+ evaluate(new BidiagonalDecompositionTall_D64(),mat));
+    }
+
+    public static void main( String args [] ) {
+        Random rand = new Random(23423);
+
+        int size = 10;
+        double scales[] = new double[]{1,0.1,1e-20,1e-100,1e-200,1e-300,1e-304,1e-308,1e-319,1e-320,1e-321,Double.MIN_VALUE};
+
+        System.out.println("Square matrix: Scale");
+        // results vary significantly depending if it starts from a small or large matrix
+        for( int i = 0; i < scales.length; i++ ) {
+            System.out.printf("Decomposition size %3d for %e scale\n",size,scales[i]);
+
+            DenseMatrix64F mat = RandomMatrices.createRandom(size,size,-1,1,rand);
+            CommonOps.scale(scales[i],mat);
+            runAlgorithms(mat);
+        }
+
+        System.out.println("Square Matrix: Singular");
+
+        double sv[] = new double[size];
+        for( int i = 0; i < size; i++ )
+            sv[i] = 2*i+5;
+
+        for( int i = 0; i < 10; i++ ) {
+            sv[0] = (9.0-i)/10.0;
+
+            System.out.printf("Decomposition size %3d for %e singular\n",size,sv[0]);
+
+//            System.out.print("* Creating matrix ");
+            DenseMatrix64F mat = RandomMatrices.createSingularValues(size,size,rand,sv);
+            CommonOps.scale(scales[i],mat);
+//            System.out.println("  Done.");
+            runAlgorithms(mat);
+        }
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/chol/BenchmarkCholeskyDecomposition.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/chol/BenchmarkCholeskyDecomposition.java
new file mode 100644
index 0000000..9af5243
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/chol/BenchmarkCholeskyDecomposition.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.chol;
+
+import org.ejml.EjmlParameters;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.DecompositionFactory;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * Compare the speed of various algorithms at inverting square matrices
+ *
+ * @author Peter Abeles
+ */
+public class BenchmarkCholeskyDecomposition {
+
+
+    public static long choleskyL( DenseMatrix64F orig , int numTrials ) {
+
+        CholeskyDecompositionInner_D64 alg = new CholeskyDecompositionInner_D64(true);
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            if( !DecompositionFactory.decomposeSafe(alg,orig) ) {
+                throw new RuntimeException("Bad matrix");
+            }
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    public static long choleskyU( DenseMatrix64F orig , int numTrials ) {
+
+        CholeskyDecompositionInner_D64 alg = new CholeskyDecompositionInner_D64(false);
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            if( !DecompositionFactory.decomposeSafe(alg,orig) ) {
+                throw new RuntimeException("Bad matrix");
+            }
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    public static long choleskyL_block( DenseMatrix64F orig , int numTrials ) {
+
+        CholeskyDecompositionBlock_D64 alg = new CholeskyDecompositionBlock_D64(
+                EjmlParameters.BLOCK_WIDTH_CHOL);
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            if( !DecompositionFactory.decomposeSafe(alg,orig) ) {
+                throw new RuntimeException("Bad matrix");
+            }
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+
+    public static long choleskyBlockU( DenseMatrix64F orig , int numTrials ) {
+
+        CholeskyDecomposition_B64_to_D64 alg = new CholeskyDecomposition_B64_to_D64(false);
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            if( !DecompositionFactory.decomposeSafe(alg,orig) ) {
+                throw new RuntimeException("Bad matrix");
+            }
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    public static long choleskyBlockL( DenseMatrix64F orig , int numTrials ) {
+
+        CholeskyDecomposition_B64_to_D64 alg = new CholeskyDecomposition_B64_to_D64(true);
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            if( !DecompositionFactory.decomposeSafe(alg,orig)) {
+                throw new RuntimeException("Bad matrix");
+            }
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    public static long choleskyLDL( DenseMatrix64F orig , int numTrials ) {
+
+        long prev = System.currentTimeMillis();
+
+        CholeskyDecompositionLDL_D64 alg = new CholeskyDecompositionLDL_D64();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            if( !DecompositionFactory.decomposeSafe(alg,orig) ) {
+                throw new RuntimeException("Bad matrix");
+            }
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    private static void runAlgorithms( DenseMatrix64F mat , int numTrials )
+    {
+        System.out.println("Lower            = "+ choleskyL(mat,numTrials));
+//        System.out.println("Upper            = "+ choleskyU(mat,numTrials));
+        System.out.println("Lower Block      = "+ choleskyL_block(mat,numTrials));
+//        System.out.println("LDL              = "+ choleskyLDL(mat,numTrials));
+//        System.out.println("Real Block U     = "+ choleskyBlockU(mat,numTrials));
+        System.out.println("Real Block L     = "+ choleskyBlockL(mat,numTrials));
+    }
+
+    public static void main( String args [] ) {
+        Random rand = new Random(23423);
+
+        int size[] = new int[]{2,4,10,100,500,1000,2000,4000,10000};
+        int trials[] = new int[]{(int)2e7,(int)5e6,(int)1e6,1000,40,3,1,1,1};
+
+        // results vary significantly depending if it starts from a small or large matrix
+        for( int i = 4; i < size.length; i++ ) {
+            int w = size[i];
+
+            System.out.printf("Decomposition size %3d for %12d trials\n",w,trials[i]);
+
+            System.out.print("* Creating matrix ");
+            DenseMatrix64F symMat = RandomMatrices.createSymmPosDef(w,rand);
+            System.out.println("  Done.");
+            runAlgorithms(symMat,trials[i]);
+        }
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/chol/StabilityCholeksyDecomposition.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/chol/StabilityCholeksyDecomposition.java
new file mode 100644
index 0000000..125298c
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/chol/StabilityCholeksyDecomposition.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.chol;
+
+import org.ejml.EjmlParameters;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.DecompositionFactory;
+import org.ejml.interfaces.decomposition.CholeskyDecomposition;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.RandomMatrices;
+import org.ejml.simple.SimpleMatrix;
+
+import java.util.Random;
+
+
+/**
+ * Compare the speed of various algorithms at inverting square matrices
+ *
+ * @author Peter Abeles
+ */
+public class StabilityCholeksyDecomposition {
+
+
+    public static double evaluate( CholeskyDecomposition<DenseMatrix64F> alg , DenseMatrix64F orig ) {
+
+        if( !DecompositionFactory.decomposeSafe(alg,orig)) {
+            return Double.NaN;
+        }
+
+        SimpleMatrix T = SimpleMatrix.wrap(alg.getT(null));
+
+        SimpleMatrix A_found = T.mult(T.transpose());
+        SimpleMatrix A = SimpleMatrix.wrap(orig);
+
+        double top = A_found.minus(A).normF();
+        double bottom = A.normF();
+
+        return top/bottom;
+    }
+
+    private static void runAlgorithms( DenseMatrix64F mat  )
+    {
+        System.out.println("basic             = "+ evaluate(new CholeskyDecompositionInner_D64(),mat));
+        System.out.println("block             = "+ evaluate(new CholeskyDecompositionBlock_D64(EjmlParameters.BLOCK_WIDTH_CHOL),mat));
+        System.out.println("block64           = "+ evaluate(new CholeskyDecomposition_B64_to_D64(true),mat));
+
+    }
+
+    public static void main( String args [] ) {
+        Random rand = new Random(23423);
+
+        EjmlParameters.BLOCK_SIZE = 5;
+
+        for( int size = 5; size <= 15; size += 5 ) {
+            double scales[] = new double[]{1,0.1,1e-20,1e-100,1e-200,1e-300,1e-304,1e-308,1e-319,1e-320,1e-321,Double.MIN_VALUE};
+
+            // results vary significantly depending if it starts from a small or large matrix
+            for( int i = 0; i < scales.length; i++ ) {
+                System.out.printf("Decomposition size %3d for %e scale\n",size,scales[i]);
+                DenseMatrix64F mat = RandomMatrices.createSymmPosDef(size,rand);
+                CommonOps.scale(scales[i],mat);
+                runAlgorithms(mat);
+            }
+        }
+        System.out.println("  Done.");
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/eig/BenchmarkEigenDecomposition.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/eig/BenchmarkEigenDecomposition.java
new file mode 100644
index 0000000..bc60c2b
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/eig/BenchmarkEigenDecomposition.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.eig;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class BenchmarkEigenDecomposition {
+    public static long watched( DenseMatrix64F orig , int numTrials ) {
+
+        long prev = System.currentTimeMillis();
+
+        WatchedDoubleStepQRDecomposition_D64 alg = new WatchedDoubleStepQRDecomposition_D64(true);
+
+        for( long i = 0; i < numTrials; i++ ) {
+            if( !alg.decompose(orig) ) {
+                throw new RuntimeException("Bad matrix");
+            }
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    private static void runAlgorithms( DenseMatrix64F mat , int numTrials )
+    {
+        System.out.println("Watched            = "+ watched(mat,numTrials));
+    }
+
+    public static void main( String args [] ) {
+        Random rand = new Random(23423);
+
+        int size[] = new int[]{2,4,10,100,200};
+        int trials[] = new int[]{200000,40000,8000,50,10};
+
+        // results vary significantly depending if it starts from a small or large matrix
+        for( int i = 0; i < size.length; i++ ) {
+            int w = size[i];
+
+            System.out.printf("Decomposing size %3d for %12d trials\n",w,trials[i]);
+
+            DenseMatrix64F symMat = RandomMatrices.createRandom(w,w,rand);
+
+            runAlgorithms(symMat,trials[i]);
+        }
+    }
+}
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/eig/RealEigenDecompositionStressTest.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/eig/RealEigenDecompositionStressTest.java
new file mode 100644
index 0000000..9c1121f
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/eig/RealEigenDecompositionStressTest.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.eig;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class RealEigenDecompositionStressTest {
+}
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/eig/symm/BenchmarkSymmetricEigenDecomposition.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/eig/symm/BenchmarkSymmetricEigenDecomposition.java
new file mode 100644
index 0000000..564a97c
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/eig/symm/BenchmarkSymmetricEigenDecomposition.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.eig.symm;
+
+import org.ejml.alg.dense.decomposition.eig.SymmetricQRAlgorithmDecomposition_D64;
+import org.ejml.alg.dense.decomposition.hessenberg.TridiagonalDecompositionHouseholder_D64;
+import org.ejml.alg.dense.decomposition.hessenberg.TridiagonalDecomposition_B64_to_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.DecompositionFactory;
+import org.ejml.interfaces.decomposition.EigenDecomposition;
+import org.ejml.interfaces.decomposition.TridiagonalSimilarDecomposition;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class BenchmarkSymmetricEigenDecomposition {
+    public static long symmTogether( DenseMatrix64F orig , int numTrials ) {
+
+        long prev = System.currentTimeMillis();
+
+        TridiagonalSimilarDecomposition<DenseMatrix64F> decomp =  DecompositionFactory.tridiagonal(orig.numRows);
+        SymmetricQRAlgorithmDecomposition_D64 alg = new SymmetricQRAlgorithmDecomposition_D64(decomp,true);
+
+        alg.setComputeVectorsWithValues(true);
+
+        for( long i = 0; i < numTrials; i++ ) {
+            if( !DecompositionFactory.decomposeSafe(alg,orig) ) {
+                throw new RuntimeException("Bad matrix");
+            }
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    public static long symmSeparate( DenseMatrix64F orig , int numTrials ) {
+
+        long prev = System.currentTimeMillis();
+
+        TridiagonalSimilarDecomposition<DenseMatrix64F> decomp =  DecompositionFactory.tridiagonal(orig.numRows);
+        SymmetricQRAlgorithmDecomposition_D64 alg = new SymmetricQRAlgorithmDecomposition_D64(decomp,true);
+
+        alg.setComputeVectorsWithValues(false);
+
+        for( long i = 0; i < numTrials; i++ ) {
+            if( !DecompositionFactory.decomposeSafe(alg,orig) ) {
+                throw new RuntimeException("Bad matrix");
+            }
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    public static long standardTridiag( DenseMatrix64F orig , int numTrials ) {
+        TridiagonalSimilarDecomposition<DenseMatrix64F> decomp = new TridiagonalDecompositionHouseholder_D64();
+        SymmetricQRAlgorithmDecomposition_D64 alg = new SymmetricQRAlgorithmDecomposition_D64(decomp,true);
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            if( !DecompositionFactory.decomposeSafe(alg,orig) ) {
+                throw new RuntimeException("Bad matrix");
+            }
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    public static long blockTridiag( DenseMatrix64F orig , int numTrials ) {
+
+        TridiagonalSimilarDecomposition<DenseMatrix64F> decomp = new TridiagonalDecomposition_B64_to_D64();
+        SymmetricQRAlgorithmDecomposition_D64 alg = new SymmetricQRAlgorithmDecomposition_D64(decomp,true);
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            if( !DecompositionFactory.decomposeSafe(alg,orig) ) {
+                throw new RuntimeException("Bad matrix");
+            }
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    public static long defaultSymm( DenseMatrix64F orig , int numTrials ) {
+
+        EigenDecomposition<DenseMatrix64F> alg = DecompositionFactory.eig(orig.numCols, true, true);
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            if( !DecompositionFactory.decomposeSafe(alg,orig) ) {
+                throw new RuntimeException("Bad matrix");
+            }
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+
+    private static void runAlgorithms( DenseMatrix64F mat , int numTrials )
+    {
+//        System.out.println("Together            = "+ symmTogether(mat,numTrials));
+//        System.out.println("Separate            = "+ symmSeparate(mat,numTrials));
+        System.out.println("Standard            = "+ standardTridiag(mat,numTrials));
+        System.out.println("Block               = "+ blockTridiag(mat,numTrials));
+        System.out.println("Default             = "+ defaultSymm(mat,numTrials));
+    }
+
+    public static void main( String args [] ) {
+        Random rand = new Random(232423);
+
+        int size[] = new int[]{2,4,10,100,200,500,1000,2000,5000};
+        int trials[] = new int[]{2000000,400000,80000,300,40,4,1,1,1};
+
+        for( int i = 0; i < size.length; i++ ) {
+            int w = size[i];
+
+            System.out.printf("Decomposing size %3d for %12d trials\n",w,trials[i]);
+
+            DenseMatrix64F symMat = RandomMatrices.createSymmetric(w,-1,1,rand);
+
+            runAlgorithms(symMat,trials[i]);
+        }
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/eig/symm/StabilitySymmEigen.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/eig/symm/StabilitySymmEigen.java
new file mode 100644
index 0000000..1c0181e
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/eig/symm/StabilitySymmEigen.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.eig.symm;
+
+import org.ejml.alg.dense.decomposition.eig.SymmetricQRAlgorithmDecomposition_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.DecompositionFactory;
+import org.ejml.interfaces.decomposition.EigenDecomposition;
+import org.ejml.interfaces.decomposition.TridiagonalSimilarDecomposition;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * Compare the speed of various algorithms at inverting square matrices
+ *
+ * @author Peter Abeles
+ */
+public class StabilitySymmEigen {
+
+
+    public static double evaluate( EigenDecomposition<DenseMatrix64F> alg , DenseMatrix64F orig ) {
+
+        if( !alg.decompose(orig)) {
+            return Double.NaN;
+        }
+
+        return DecompositionFactory.quality(orig,alg);
+    }
+
+    private static void runAlgorithms( DenseMatrix64F mat  )
+    {
+        TridiagonalSimilarDecomposition<DenseMatrix64F> decomp = DecompositionFactory.tridiagonal(0);
+        System.out.println("qr ult           = "+ evaluate(new SymmetricQRAlgorithmDecomposition_D64(decomp,true),mat));
+    }
+
+    public static void main( String args [] ) {
+        Random rand = new Random(239454923);
+
+        int size = 10;
+        double scales[] = new double[]{1,0.1,1e-20,1e-100,1e-200,1e-300,1e-304,1e-308,1e-310,1e-312,1e-319,1e-320,1e-321,Double.MIN_VALUE};
+
+        System.out.println("Square matrix");
+        DenseMatrix64F orig = RandomMatrices.createSymmetric(size,-1,1,rand);
+        DenseMatrix64F mat = orig.copy();
+        // results vary significantly depending if it starts from a small or large matrix
+        for( int i = 0; i < scales.length; i++ ) {
+            System.out.printf("Decomposition size %3d for %e scale\n",size,scales[i]);
+            CommonOps.scale(scales[i],orig,mat);
+            runAlgorithms(mat);
+        }
+
+        System.out.println("  Done.");
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/eig/symm/SymmetricEigenStressTest.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/eig/symm/SymmetricEigenStressTest.java
new file mode 100644
index 0000000..24ddc7f
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/eig/symm/SymmetricEigenStressTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.eig.symm;
+
+import org.ejml.UtilEjml;
+import org.ejml.data.Complex64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.DecompositionFactory;
+import org.ejml.interfaces.decomposition.EigenDecomposition;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.RandomMatrices;
+import org.ejml.ops.SpecializedOps;
+
+import java.util.Date;
+import java.util.Random;
+
+
+/**
+ * Provides a more rigorous and time consuming series of tests to check the correctness of a
+ * symmetric matrix eigenvalue decomposition.
+ *
+ * @author Peter Abeles
+ */
+public class SymmetricEigenStressTest {
+
+    Random rand = new Random(234234);
+
+    public void checkMatrix( int N , long seed ) {
+        DenseMatrix64F A = new DenseMatrix64F(N,N);
+
+        Random localRand = new Random(seed);
+        RandomMatrices.createSymmetric(A,-1,1,localRand);
+
+        EigenDecomposition<DenseMatrix64F> decomp = DecompositionFactory.eig(A.numRows,true);
+
+        System.out.println("Decomposing...");
+
+        if( !decomp.decompose(A) ) {
+            throw new RuntimeException("Decomposition failed");
+        }
+
+        DenseMatrix64F L = new DenseMatrix64F(N,1);
+        DenseMatrix64F R = new DenseMatrix64F(N,1);
+
+        for( int i = 0; i < N; i++ ) {
+            Complex64F value = decomp.getEigenvalue(i);
+
+            DenseMatrix64F vector = decomp.getEigenVector(i);
+
+            if( !value.isReal())
+                throw new RuntimeException("Complex eigenvalue");
+
+            CommonOps.mult(A,vector,L);
+            CommonOps.scale(value.real,vector,R);
+
+            double diff = SpecializedOps.diffNormF(L,R)/N;
+
+            if( diff > UtilEjml.EPS*1000 )
+                System.out.println("["+i+"] value = "+value.real+" error = "+diff);
+        }
+        System.out.println("done");
+    }
+
+    public void checkRandomMatrices( int N ) {
+        System.out.println("N = "+N);
+        EigenDecomposition decomp = DecompositionFactory.eig(N,true);
+
+        DenseMatrix64F A = new DenseMatrix64F(N,N);
+
+        for( int i = 0; i < 1000; i++ ) {
+            long seed = rand.nextLong();
+            System.out.print("Date = "+new Date()+" Seed = "+seed);
+
+            Random localRand = new Random(seed);
+
+            RandomMatrices.createSymmetric(A,-1,1,localRand);
+
+            if( !decomp.decompose(A) ) {
+                System.out.println("Decomposition failed");
+                return;
+            }
+
+            double error = DecompositionFactory.quality(A,decomp);
+            System.out.println("      error = "+error);
+            if( error > 0.05 || Double.isNaN(error) || Double.isInfinite(error)) {
+                System.out.println("   Large Error");
+                return;
+            }
+        }
+    }
+
+    public static void main( String args[] ) {
+        SymmetricEigenStressTest stress = new SymmetricEigenStressTest();
+
+        stress.checkRandomMatrices(1000);
+
+//        stress.checkMatrix(1000,5878413318033397987L);
+    }
+}
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/eig/watched/WatchDoubleStepQR.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/eig/watched/WatchDoubleStepQR.java
new file mode 100644
index 0000000..d7d1ae0
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/eig/watched/WatchDoubleStepQR.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.eig.watched;
+
+import org.ejml.data.Complex64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: pja
+ * Date: Dec 26, 2009
+ * Time: 6:20:51 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class WatchDoubleStepQR {
+
+    public static void watchFindEigen( DenseMatrix64F A ) {
+        WatchedDoubleStepQREigenvalue alg = new WatchedDoubleStepQREigenvalue();
+
+//        alg.implicitQR.printFound = true;
+//        alg.implicitQR.normalize = true;
+        alg.implicitQR.checkHessenberg = true;
+
+//        CommonOps.scale(1e305,A);
+        alg.process(A);
+
+        System.out.println("Eigenvalues.");
+        for( int i = 0; i < A.numRows; i++ ) {
+            Complex64F c = alg.implicitQR.eigenvalues[i];
+
+            System.out.printf("(%8.5e  img = %8.5e ) in steps %3d \n",c.real,c.imaginary,alg.implicitQR.numStepsFind[i]);
+        }
+
+        System.out.println("Number of exceptional steps = "+alg.implicitQR.numExceptional);
+
+        // finding eigen vectors now
+
+        WatchedDoubleStepQREigenvector algVector = new WatchedDoubleStepQREigenvector();
+
+        algVector.process(alg.implicitQR,A,null);
+
+        System.out.println("Eigenvectors.");
+        for( int i = 0; i < A.numRows; i++ ) {
+            DenseMatrix64F v = algVector.eigenvectors[i];
+
+            if( v != null )
+                v.print("%8.3e");
+            else
+                System.out.println("i = "+i+"  is null");
+        }
+    }
+
+    public static void watchImplicitDouble( DenseMatrix64F A ) {
+        WatchedDoubleStepQREigen alg = new WatchedDoubleStepQREigen();
+
+//        alg.printHumps = true;
+
+        alg.setup(A);
+
+        alg.implicitDoubleStep(0,4);
+
+        for( int i = 0; i < 20; i++ ) {
+            System.out.println("-----------------------------------");
+            alg.A.print();
+            System.out.println();
+            alg.implicitDoubleStep(0,4);
+            System.out.println();
+        }
+    }
+
+    public static void watchImplicitSingle( DenseMatrix64F A ) {
+        WatchedDoubleStepQREigen alg = new WatchedDoubleStepQREigen();
+
+//        alg.printHumps = true;
+
+        alg.setup(A);
+
+        double ev =  -7.801;
+
+//        alg.implicitSingleStep(0,4);
+        alg.performImplicitSingleStep(0,4,ev);
+
+        for( int i = 0; i < 20; i++ ) {
+            System.out.println("-----------------------------------");
+            alg.A.print();
+            System.out.println();
+//            alg.implicitSingleStep(0,4);
+            alg.performImplicitSingleStep(0,4,ev);
+//            System.out.println();
+        }
+    }
+
+    public static void main( String args[]) {
+        Random rand = new Random(23475);
+//        Random rand = new Random(235);
+
+        DenseMatrix64F A = RandomMatrices.createUpperTriangle(5,1,2,3,rand);
+//        DenseMatrix64F A = RandomMatrices.createUpperTriangle(50,1,-2,2,rand);
+//        DenseMatrix64F A = new DenseMatrix64F(5,5,new double[]{0,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0},true);
+//        DenseMatrix64F A = UtilEjml.parseMatrix("-0.951  0.845 -0.171 \n" +
+//                " 0.573 -0.720  0.264 \n" +
+//                " 0.000  0.552 -0.100",3);
+
+        System.out.println("--------- Original Matrix -----------");
+        A.print();
+        System.out.println("-------------------------------------");
+
+        watchFindEigen(A);
+//        watchImplicitDouble(A);
+//        watchImplicitSingle(A);
+    }
+}
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/hessenberg/BenchmarkHessenberg.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/hessenberg/BenchmarkHessenberg.java
new file mode 100644
index 0000000..b9ff7f7
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/hessenberg/BenchmarkHessenberg.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.hessenberg;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * Compare the speed of various algorithms at inverting square matrices
+ *
+ * @author Peter Abeles
+ */
+public class BenchmarkHessenberg {
+
+
+    public static long basic( DenseMatrix64F orig , int numTrials ) {
+
+        HessenbergSimilarDecomposition_D64 alg = new HessenbergSimilarDecomposition_D64();
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            if( !alg.decompose(orig) ) {
+                throw new RuntimeException("Bad matrix");
+            }
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+//    public static long alt( DenseMatrix64F orig , int numTrials ) {
+//
+//        HessenbergSimilarDecompositionAlt alg = new HessenbergSimilarDecompositionAlt();
+//
+//        long prev = System.currentTimeMillis();
+//
+//        for( long i = 0; i < numTrials; i++ ) {
+//            if( !alg.decompose(orig) ) {
+//                throw new RuntimeException("Bad matrix");
+//            }
+//        }
+//
+//        return System.currentTimeMillis() - prev;
+//    }
+
+    private static void runAlgorithms( DenseMatrix64F mat , int numTrials )
+    {
+        System.out.println("basic            = "+ basic(mat,numTrials));
+//        System.out.println("alt              = "+ alt(mat,numTrials));
+    }
+
+    public static void main( String args [] ) {
+        Random rand = new Random(23423);
+
+        int size[] = new int[]{2,4,10,100,500,1000,2000};
+        int trials[] = new int[]{(int)2e6,(int)5e5,(int)1e5,400,15,3,1,1};
+
+        // results vary significantly depending if it starts from a small or large matrix
+        for( int i = 0; i < size.length; i++ ) {
+            int w = size[i];
+
+            System.out.printf("Decompositing size %3d for %12d trials\n",w,trials[i]);
+
+            System.out.print("* Creating matrix ");
+            DenseMatrix64F mat = RandomMatrices.createRandom(w,w,rand);
+            System.out.println("  Done.");
+            runAlgorithms(mat,trials[i]);
+        }
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/hessenberg/BenchmarkTridiagonal.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/hessenberg/BenchmarkTridiagonal.java
new file mode 100644
index 0000000..3ffc1f1
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/hessenberg/BenchmarkTridiagonal.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.hessenberg;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * Compare the speed of various algorithms at inverting square matrices
+ *
+ * @author Peter Abeles
+ */
+public class BenchmarkTridiagonal {
+
+
+    public static long basic( DenseMatrix64F orig , int numTrials ) {
+
+        TridiagonalDecompositionHouseholder_D64 alg = new TridiagonalDecompositionHouseholder_D64();
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            if( alg.inputModified())
+                alg.decompose(orig.copy());
+            else
+                alg.decompose(orig);
+
+            alg.getQ(null,false);
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    public static long alt( DenseMatrix64F orig , int numTrials ) {
+
+        TridiagonalDecompositionHouseholderOrig_D64 alg = new TridiagonalDecompositionHouseholderOrig_D64();
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            alg.decompose(orig);
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    public static long block( DenseMatrix64F orig , int numTrials ) {
+
+
+        TridiagonalDecomposition_B64_to_D64 alg = new TridiagonalDecomposition_B64_to_D64();
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            if( alg.inputModified())
+                alg.decompose(orig.copy());
+            else
+                alg.decompose(orig);
+
+            alg.getQ(null,false);
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    private static void runAlgorithms( DenseMatrix64F mat , int numTrials )
+    {
+        System.out.println("basic            = "+ basic(mat,numTrials));
+//        System.out.println("alt              = "+ alt(mat,numTrials));
+        System.out.println("block            = "+ block(mat,numTrials));
+    }
+
+    public static void main( String args [] ) {
+        Random rand = new Random(23423);
+
+        int size[] = new int[]{2,4,10,100,500,1000,2000,5000};
+        int trials[] = new int[]{(int)8e6,(int)2e6,(int)2e5,600,12,3,1,1,1};
+
+        // results vary significantly depending if it starts from a small or large matrix
+        for( int i = 3; i < size.length; i++ ) {
+            int w = size[i];
+
+            System.out.printf("Processing size %3d for %12d trials\n",w,trials[i]);
+
+            System.out.print("* Creating matrix ");
+            DenseMatrix64F mat = RandomMatrices.createRandom(w,w,rand);
+            System.out.println("  Done.");
+            runAlgorithms(mat,trials[i]);
+        }
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/hessenberg/StabilityTridiagonal.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/hessenberg/StabilityTridiagonal.java
new file mode 100644
index 0000000..7eb56c7
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/hessenberg/StabilityTridiagonal.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.hessenberg;
+
+import org.ejml.EjmlParameters;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.DecompositionFactory;
+import org.ejml.interfaces.decomposition.TridiagonalSimilarDecomposition;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.RandomMatrices;
+import org.ejml.simple.SimpleMatrix;
+
+import java.util.Random;
+
+
+/**
+ * Compare the speed of various algorithms at inverting square matrices
+ *
+ * @author Peter Abeles
+ */
+public class StabilityTridiagonal {
+
+
+    public static double evaluate( TridiagonalSimilarDecomposition<DenseMatrix64F> alg , DenseMatrix64F orig ) {
+
+        if( !DecompositionFactory.decomposeSafe(alg,orig)) {
+            throw new RuntimeException("Decomposition failed");
+        }
+
+        SimpleMatrix O = SimpleMatrix.wrap(alg.getQ(null,false));
+        SimpleMatrix T = SimpleMatrix.wrap(alg.getT(null));
+
+        SimpleMatrix A_found = O.mult(T).mult(O.transpose());
+        SimpleMatrix A = SimpleMatrix.wrap(orig);
+
+        double top = A_found.minus(A).normF();
+        double bottom = A.normF();
+
+        return top/bottom;
+    }
+
+    private static void runAlgorithms( DenseMatrix64F mat  )
+    {
+        System.out.println("tri             = "+ evaluate(new TridiagonalDecompositionHouseholder_D64(),mat));
+        System.out.println("block           = "+ evaluate(new TridiagonalDecomposition_B64_to_D64(),mat));
+    }
+
+    public static void main( String args [] ) {
+        EjmlParameters.BLOCK_SIZE = 10;
+
+        Random rand = new Random(239454923);
+
+        for( int size = 5; size <= 15; size += 5 ) {
+            double scales[] = new double[]{1,0.1,1e-20,1e-100,1e-200,1e-300,1e-304,1e-308,1e-310,1e-312,1e-319,1e-320,1e-321,Double.MIN_VALUE};
+
+            System.out.println("Square matrix");
+            DenseMatrix64F orig = RandomMatrices.createSymmetric(size,-1,1,rand);
+            DenseMatrix64F mat = orig.copy();
+            // results vary significantly depending if it starts from a small or large matrix
+            for( int i = 0; i < scales.length; i++ ) {
+                System.out.printf("Decomposition size %3d for %e scale\n",size,scales[i]);
+                CommonOps.scale(scales[i],orig,mat);
+                runAlgorithms(mat);
+            }
+        }
+
+        System.out.println("  Done.");
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/lu/BenchmarkLuDecomposition.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/lu/BenchmarkLuDecomposition.java
new file mode 100644
index 0000000..b0db564
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/lu/BenchmarkLuDecomposition.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.lu;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.LUDecomposition;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * Compare the speed of various algorithms at decomposing square matrices
+ *
+ * @author Peter Abeles
+ */
+public class BenchmarkLuDecomposition {
+
+
+    public static void benchmark( LUDecomposition<DenseMatrix64F> lu , DenseMatrix64F orig , int numTrials ) {
+
+        long prev = System.currentTimeMillis();
+        for( long i = 0; i < numTrials; i++ ) {
+            if( !lu.decompose(orig) ) {
+                throw new RuntimeException("Bad matrix");
+            }
+        }
+
+        long time = System.currentTimeMillis() - prev;
+        System.out.printf(" %20s time = %7d\n",lu.getClass().getSimpleName(),time);
+    }
+
+
+    private static void runAlgorithms( DenseMatrix64F mat , int numTrials )
+    {
+        benchmark(new LUDecompositionAlt_D64(),mat,numTrials);
+        benchmark(new LUDecompositionNR_D64(),mat,numTrials);
+    }
+
+    public static void main( String args [] ) {
+        Random rand = new Random(23423);
+
+        int size[] = new int[]{2,4,10,100,500,1000,2000,4000};
+        int trials[] = new int[]{(int)1e7,(int)5e6,(int)1e6,2000,40,3,1,1};
+
+        // results vary significantly depending if it starts from a small or large matrix
+        for( int i = 0; i < size.length; i++ ) {
+            int w = size[i];
+
+            System.out.printf("Decomposing size %3d for %12d trials\n",w,trials[i]);
+
+//            System.out.print("* Creating matrix ");
+            DenseMatrix64F symMat = RandomMatrices.createRandom(w,w,-1,1,rand);
+//            System.out.println("  Done.");
+            runAlgorithms(symMat,trials[i]);
+        }
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/qr/BenchmarkQrDecomposition_CD64.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/qr/BenchmarkQrDecomposition_CD64.java
new file mode 100644
index 0000000..2865ddf
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/qr/BenchmarkQrDecomposition_CD64.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.qr;
+
+import org.ejml.alg.dense.decompose.qr.QRDecompositionHouseholderColumn_CD64;
+import org.ejml.alg.dense.decompose.qr.QRDecompositionHouseholderTran_CD64;
+import org.ejml.alg.dense.decompose.qr.QRDecompositionHouseholder_CD64;
+import org.ejml.data.CDenseMatrix64F;
+import org.ejml.interfaces.decomposition.QRDecomposition;
+import org.ejml.ops.CRandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * Compare the speed of various algorithms at inverting square matrices
+ *
+ * @author Peter Abeles
+ */
+public class BenchmarkQrDecomposition_CD64 {
+
+    public static long generic(QRDecomposition<CDenseMatrix64F> alg,  CDenseMatrix64F orig , int numTrials ) {
+
+        long prev = System.currentTimeMillis();
+        CDenseMatrix64F B;
+        for( long i = 0; i < numTrials; i++ ) {
+            if( alg.inputModified())
+                B = orig.copy();
+            else
+                B = orig;
+            if( !alg.decompose(B) ) {
+                throw new RuntimeException("Bad matrix");
+            }
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    private static void runAlgorithms( CDenseMatrix64F mat , int numTrials )
+    {
+        System.out.println("basic            = "+ generic( new QRDecompositionHouseholder_CD64(), mat,numTrials));
+        System.out.println("column           = "+ generic( new QRDecompositionHouseholderColumn_CD64() ,mat,numTrials));
+        System.out.println("tran             = "+ generic( new QRDecompositionHouseholderTran_CD64() , mat,numTrials));
+//        System.out.println("pivot column     = "+ generic( new QRColPivDecompositionHouseholderColumn_CD64() , mat,numTrials));
+
+//        System.out.println("block  native    = "+ block(mat,numTrials));
+//        System.out.println("block wrapper    = "+ generic( new QRDecomposition_B64_to_D64() , mat,numTrials));
+    }
+
+    public static void main( String args [] ) {
+        Random rand = new Random(23423);
+
+        int size[] = new int[]{2,4,10,100,500,1000,2000,4000};
+        int trials[] = new int[]{(int)2e6,(int)5e5,(int)1e4,200,2,1,1,1,1};
+
+        // results vary significantly depending if it starts from a small or large matrix
+        for( int i = 0; i < size.length; i++ ) {
+            int w = size[i];
+            CDenseMatrix64F mat = CRandomMatrices.createRandom(w * 4, w / 1, rand);
+             System.out.printf("Decomposing size [ %5d  , %5d ] for %12d trials\n",mat.numRows,mat.numCols,trials[i]);
+            runAlgorithms(mat,trials[i]);
+        }
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/qr/BenchmarkQrDecomposition_D64.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/qr/BenchmarkQrDecomposition_D64.java
new file mode 100644
index 0000000..2f3d786
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/qr/BenchmarkQrDecomposition_D64.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.qr;
+
+import org.ejml.alg.block.BlockMatrixOps;
+import org.ejml.alg.block.decomposition.qr.QRDecompositionHouseholder_B64;
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.QRDecomposition;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * Compare the speed of various algorithms at inverting square matrices
+ *
+ * @author Peter Abeles
+ */
+public class BenchmarkQrDecomposition_D64 {
+
+    public static long generic(QRDecomposition<DenseMatrix64F> alg,  DenseMatrix64F orig , int numTrials ) {
+
+        long prev = System.currentTimeMillis();
+        DenseMatrix64F B;
+        for( long i = 0; i < numTrials; i++ ) {
+            if( alg.inputModified())
+                B = orig.copy();
+            else
+                B = orig;
+            if( !alg.decompose(B) ) {
+                throw new RuntimeException("Bad matrix");
+            }
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    public static long block( DenseMatrix64F orig , int numTrials ) {
+
+        BlockMatrix64F A = BlockMatrixOps.convert(orig);
+        QRDecompositionHouseholder_B64 alg = new QRDecompositionHouseholder_B64();
+
+        BlockMatrix64F B;
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            if( alg.inputModified())
+                B = A.copy();
+            else
+                B = A;
+            if( !alg.decompose(B) ) {
+                throw new RuntimeException("Bad matrix");
+            }
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    private static void runAlgorithms( DenseMatrix64F mat , int numTrials )
+    {
+//        System.out.println("basic            = "+ generic( new QRDecompositionHouseholder(), mat,numTrials));
+        System.out.println("column           = "+ generic( new QRDecompositionHouseholderColumn_D64() ,mat,numTrials));
+        System.out.println("tran             = "+ generic( new QRDecompositionHouseholderTran_D64() , mat,numTrials));
+        System.out.println("pivot column     = "+ generic( new QRColPivDecompositionHouseholderColumn_D64() , mat,numTrials));
+
+//        System.out.println("block  native    = "+ block(mat,numTrials));
+        System.out.println("block wrapper    = "+ generic( new QRDecomposition_B64_to_D64() , mat,numTrials));
+    }
+
+    public static void main( String args [] ) {
+        Random rand = new Random(23423);
+
+        int size[] = new int[]{2,4,10,100,500,1000,2000,4000};
+        int trials[] = new int[]{(int)2e6,(int)5e5,(int)1e5,400,5,1,1,1,1};
+
+        // results vary significantly depending if it starts from a small or large matrix
+        for( int i = 0; i < size.length; i++ ) {
+            int w = size[i];
+            DenseMatrix64F mat = RandomMatrices.createRandom(w*4,w/1,rand);
+             System.out.printf("Decomposing size [ %5d  , %5d ] for %12d trials\n",mat.numRows,mat.numCols,trials[i]);
+            runAlgorithms(mat,trials[i]);
+        }
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/qr/StabilityQRDecomposition.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/qr/StabilityQRDecomposition.java
new file mode 100644
index 0000000..39e0070
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/qr/StabilityQRDecomposition.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.qr;
+
+import org.ejml.EjmlParameters;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.QRDecomposition;
+import org.ejml.interfaces.decomposition.QRPDecomposition;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.RandomMatrices;
+import org.ejml.simple.SimpleMatrix;
+
+import java.util.Random;
+
+import static org.ejml.factory.DecompositionFactory.decomposeSafe;
+
+
+/**
+ * Compare the speed of various algorithms at inverting square matrices
+ *
+ * @author Peter Abeles
+ */
+public class StabilityQRDecomposition {
+
+
+    public static double evaluate( QRDecomposition<DenseMatrix64F> alg , DenseMatrix64F orig ) {
+
+        if( !decomposeSafe(alg,orig)) {
+            return Double.NaN;
+        }
+
+        SimpleMatrix Q = SimpleMatrix.wrap(alg.getQ(null,true));
+        SimpleMatrix R = SimpleMatrix.wrap(alg.getR(null,true));
+
+        SimpleMatrix A_found = Q.mult(R);
+        SimpleMatrix A = SimpleMatrix.wrap(orig);
+
+        return A.minus(A_found).normF()/A.normF();
+    }
+
+    public static double evaluate( QRPDecomposition<DenseMatrix64F> alg , DenseMatrix64F orig ) {
+
+        if( !decomposeSafe(alg,orig)) {
+            return Double.NaN;
+        }
+
+        SimpleMatrix Q = SimpleMatrix.wrap(alg.getQ(null,true));
+        SimpleMatrix R = SimpleMatrix.wrap(alg.getR(null,true));
+        SimpleMatrix P = SimpleMatrix.wrap(alg.getPivotMatrix(null));
+
+        SimpleMatrix A_found = Q.mult(R);
+        SimpleMatrix A = SimpleMatrix.wrap(orig);
+
+        return A.mult(P).minus(A_found).normF()/A.normF();
+    }
+
+    private static void runAlgorithms( DenseMatrix64F mat  )
+    {
+        System.out.println("qr               = "+ evaluate(new QRDecompositionHouseholder_D64(),mat));
+        System.out.println("qr col           = "+ evaluate(new QRDecompositionHouseholderColumn_D64(),mat));
+        System.out.println("qr pivot col     = "+ evaluate(new QRColPivDecompositionHouseholderColumn_D64(),mat));
+        System.out.println("qr tran          = "+ evaluate(new QRDecompositionHouseholderTran_D64(),mat));
+        System.out.println("qr block         = "+ evaluate(new QRDecomposition_B64_to_D64(),mat));
+    }
+
+    public static void main( String args [] ) {
+
+        // set the block size so that it will get triggered at a smaller size
+        EjmlParameters.BLOCK_SIZE = 10;
+
+        Random rand = new Random(239454923);
+
+        for( int size = 5; size <= 15; size += 5 ) {
+            double scales[] = new double[]{1,0.1,1e-20,1e-100,1e-200,1e-300,1e-304,1e-308,1e-310,1e-312,1e-319,1e-320,1e-321,Double.MIN_VALUE};
+
+            System.out.println("Square matrix");
+            DenseMatrix64F orig = RandomMatrices.createRandom(2*size,size,-1,1,rand);
+            DenseMatrix64F mat = orig.copy();
+            // results vary significantly depending if it starts from a small or large matrix
+            for( int i = 0; i < scales.length; i++ ) {
+                System.out.printf("Decomposition size %3d for %e scale\n",size,scales[i]);
+                CommonOps.scale(scales[i],orig,mat);
+                runAlgorithms(mat);
+            }
+        }
+
+        System.out.println("  Done.");
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/svd/BenchmarkSvd.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/svd/BenchmarkSvd.java
new file mode 100644
index 0000000..abadde7
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/svd/BenchmarkSvd.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.svd;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.SingularValueDecomposition;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * Compare the speed of various algorithms at inverting square matrices
+ *
+ * @author Peter Abeles
+ */
+public class BenchmarkSvd {
+
+
+    public static String evaluate( SingularValueDecomposition<DenseMatrix64F> alg , DenseMatrix64F orig , int numTrials ) {
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            if( !alg.decompose(orig) ) {
+                throw new RuntimeException("Bad matrix");
+            }
+        }
+
+        long diff =  System.currentTimeMillis() - prev;
+        return diff+" (ms)  "+(numTrials/(diff/1000.0))+" (ops/sec)";
+    }
+
+    private static void runAlgorithms( DenseMatrix64F mat , int numTrials )
+    {
+//        mat.print("%f");
+        if( numTrials <= 0 ) return;
+        System.out.println("qr               = "+ evaluate(new SvdImplicitQrDecompose_D64(true,true,true,true),mat,numTrials));
+//        System.out.println("qr smart         = "+ evaluate(new SvdImplicitQrDecompose_UltimateS(true,true,true),mat,numTrials));
+        System.out.println("qr separate      = "+ evaluate(new SvdImplicitQrDecompose_Ultimate(true,true,true),mat,numTrials));
+//        System.out.println("qr               = "+ evaluate(new SvdImplicitQrDecompose(true,true,true),mat,numTrials));
+//        System.out.println("qr no U          = "+ evaluate(new SvdImplicitQrDecompose(true,false,true),mat,numTrials));
+//        System.out.println("qr no U and V    = "+ evaluate(new SvdImplicitQrDecompose(true,false,false),mat,numTrials));
+//        System.out.println("alt              = "+ evaluate(new SvdNumericalRecipes(),mat,numTrials));
+    }
+
+    public static void main( String args [] ) {
+        Random rand = new Random(23423);
+
+        int size[] = new int[]{2,4,10,100,500,1000,2000,3000};
+        int trials[] = new int[]{(int)7e5,(int)1e5,(int)5e4,100,2,1,1,1};
+
+        System.out.println("Square matrix");
+        // results vary significantly depending if it starts from a small or large matrix
+        for( int i = 0; i < size.length; i++ ) {
+            int w = size[i];
+
+            System.out.printf("Decomposition size %3d for %12d trials\n",w,trials[i]);
+
+            System.out.print("* Creating matrix ");
+            DenseMatrix64F mat = RandomMatrices.createRandom(w,w,rand);
+            System.out.println("  Done.");
+            runAlgorithms(mat,trials[i]);
+        }
+
+        System.out.println("Tall matrix");
+        // results vary significantly depending if it starts from a small or large matrix
+        for( int i = 0; i < size.length; i++ ) {
+            int w = size[i];
+
+            int t = trials[i]*3/5;
+
+            if( t == 0 ) continue;
+
+            System.out.printf("Decomposition size %3d for %12d trials\n",w,t);
+
+            System.out.print("* Creating matrix ");
+            DenseMatrix64F mat = RandomMatrices.createRandom(2*w,w,rand);
+            System.out.println("  Done.");
+            runAlgorithms(mat,t);
+        }
+
+        System.out.println("Wide matrix");
+        // results vary significantly depending if it starts from a small or large matrix
+        for( int i = 0; i < size.length; i++ ) {
+            int w = size[i];
+
+            int t = trials[i]*3/5;
+
+            if( t == 0 ) continue;
+
+            System.out.printf("Decomposition size %3d for %12d trials\n",w,t);
+
+            System.out.print("* Creating matrix ");
+            DenseMatrix64F mat = RandomMatrices.createRandom(w,2*w,rand);
+            System.out.println("  Done.");
+            runAlgorithms(mat,trials[i]);
+        }
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/svd/StabilitySvdlDecomposition.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/svd/StabilitySvdlDecomposition.java
new file mode 100644
index 0000000..6650612
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/decomposition/svd/StabilitySvdlDecomposition.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.svd;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.DecompositionFactory;
+import org.ejml.interfaces.decomposition.SingularValueDecomposition;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * Compare the speed of various algorithms at inverting square matrices
+ *
+ * @author Peter Abeles
+ */
+public class StabilitySvdlDecomposition {
+
+    private static boolean compact = false;
+    // These two options need to be true to compute the quality
+    private static boolean computeU = true;
+    private static boolean computeV = true;
+
+
+    public static double evaluate( SingularValueDecomposition<DenseMatrix64F> alg , DenseMatrix64F orig ) {
+
+        DenseMatrix64F U=null;
+        DenseMatrix64F W;
+        DenseMatrix64F Vt=null;
+
+        if( !alg.decompose(orig.copy())) {
+            return Double.NaN;
+        }
+
+        W = alg.getW(null);
+        if( computeU )
+            U = alg.getU(null,false).copy();
+        if( computeV )
+            Vt = alg.getV(null,true).copy();
+
+        // I'm not sure how to test quality of U or V is not computed.
+
+        return DecompositionFactory.quality(orig, U,W,Vt);
+    }
+
+    private static void runAlgorithms( DenseMatrix64F mat )
+    {
+        System.out.println("qr               = "+ evaluate(new SvdImplicitQrDecompose_D64(compact,computeU,computeV,false),mat));
+        System.out.println("qr ult           = "+ evaluate(new SvdImplicitQrDecompose_Ultimate(compact,computeU,computeV),mat));
+    }
+
+    public static void main( String args [] ) {
+        Random rand = new Random(239454923);
+
+        int numCols = 10;
+        int numRows = 30;
+        double scales[] = new double[]{1,0.1,1e-20,1e-100,1e-200,1e-300,1e-304,1e-308,1e-310,1e-312,1e-319,1e-320,1e-321,Double.MIN_VALUE};
+
+
+        DenseMatrix64F orig = RandomMatrices.createRandom(numRows,numCols,-1,1,rand);
+        DenseMatrix64F mat = orig.copy();
+        // results vary significantly depending if it starts from a small or large matrix
+        for( int i = 0; i < scales.length; i++ ) {
+            System.out.printf("  Decomposition size %3d %d for %e scale\n",numRows,numCols,scales[i]);
+            CommonOps.scale(scales[i],orig,mat);
+            runAlgorithms(mat);
+        }
+
+        System.out.println();
+        System.out.println("Done.");
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/linsol/BenchmarkInverseStability.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/linsol/BenchmarkInverseStability.java
new file mode 100644
index 0000000..5115301
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/linsol/BenchmarkInverseStability.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol;
+
+import org.ejml.alg.dense.linsol.qr.LinearSolverQrHouseCol_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.LinearSolverFactory;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+
+/**
+ * Feeds the algorithms matrices that are closer and closer to being singular
+ * and sees at which point they break.
+ *
+ * @author Peter Abeles
+ */
+public class BenchmarkInverseStability {
+
+    private DenseMatrix64F b = new DenseMatrix64F(3,3);
+
+
+    private double evaluateInverse( DenseMatrix64F A , DenseMatrix64F A_inv )
+    {
+        CommonOps.mult(A,A_inv,b);
+
+        double total = 0;
+
+        for( int y = 0; y < 3; y++ ) {
+            for( int x = 0; x < 3; x++ ) {
+                if( x == y ) {
+                    total += Math.abs(b.get(x,y)-1.0);
+                } else {
+                    total += Math.abs(b.get(x,y));
+                }
+            }
+        }
+
+        return total;
+    }
+
+    public void evaluateAll()
+    {
+        List<LinearSolver<DenseMatrix64F>> solvers = new ArrayList<LinearSolver<DenseMatrix64F>>();
+        List<String> names = new ArrayList<String>();
+
+//        solvers.add(new GaussJordanNoPivot());
+//        names.add("GJ NP");
+//        solvers.add(new GaussJordan(3));
+//        names.add("GJ");
+//        solvers.add(new LinearSolverLu(new LUDecompositionAlt()));
+//        names.add("LU ALT");
+//        solvers.add(new LinearSolverLu(new LUDecompositionNR()));
+//        names.add("LU NR");
+//        solvers.add(new LinearSolverLuKJI(new LUDecompositionAlt()));
+//        names.add("LU B");
+//        solvers.add(new LinearSolverLu(new LUDecompositionAlt(),true));
+//        names.add("LU A Imp");
+        solvers.add(new LinearSolverQrHouseCol_D64());
+        names.add("QR");
+//        solvers.add(new LinearSolverSvdNR(new SvdNumericalRecipes()));
+//        names.add("SVD NR");
+//        solvers.add(new LinearSolverUnrolled());
+//        names.add("Unrolled");
+        solvers.add(LinearSolverFactory.leastSquaresQrPivot(true, true));
+        names.add("P'QR compute Q");
+        solvers.add(LinearSolverFactory.leastSquaresQrPivot(true,false));
+        names.add("P'QR householder");
+        solvers.add(LinearSolverFactory.pseudoInverse(true));
+        names.add("PINV SVD");
+
+        allTheBreaks(solvers,names);
+    }
+
+    private void allTheBreaks( List<LinearSolver<DenseMatrix64F>> solvers , List<String> names )
+    {
+        System.out.println("Testing singular:");
+        for( int i = 0; i < solvers.size(); i++ ) {
+            breakNearlySingluar(names.get(i),solvers.get(i));
+        }
+
+        System.out.println("Testing overflow:");
+        for( int i = 0; i < solvers.size(); i++ ) {
+            breakOverUnderFlow(names.get(i),solvers.get(i),true);
+        }
+
+        System.out.println("Testing underflow:");
+        for( int i = 0; i < solvers.size(); i++ ) {
+            breakOverUnderFlow(names.get(i),solvers.get(i),false);
+        }
+    }
+
+    private void breakNearlySingluar( String name , LinearSolver<DenseMatrix64F> alg ) {
+        double breakDelta = -1;
+        int breakW = -1;
+
+        alg = new LinearSolverSafe<DenseMatrix64F>(alg);
+
+        escape: for( int i = 0; i < 40; i++ ) {
+//            System.out.println("i = "+i);
+            double delta = Math.pow(0.1,i);
+            for( int w = 0; w < 6; w++ ) {
+                DenseMatrix64F A = new DenseMatrix64F(3,3, true, 1, 2, 3, 2, 4, 6, 4, 6, -2);
+                DenseMatrix64F A_inv = new DenseMatrix64F(3,3);
+
+                A.plus(w,  delta);
+
+                try {
+                    if( !alg.setA(A) ) {
+                        breakDelta = delta;
+                        breakW = w;
+                        break escape;
+                    }
+                    alg.invert(A_inv);
+                    if(evaluateInverse(A,A_inv) > 1.0 ) {
+                        breakDelta = delta;
+                        breakW = w;
+                        break escape;
+                    }
+                } catch( RuntimeException e ) {
+                    breakDelta = delta;
+                    breakW = w;
+                    break escape;
+                }
+            }
+        }
+
+        System.out.printf("%20s broke at %E  w = %d\n",name,
+                breakDelta,breakW);
+//        System.out.println(alg.getClass().getSimpleName()+" broke at "+breakDelta+" w = "+breakW);
+    }
+
+    private void breakOverUnderFlow( String name , LinearSolver<DenseMatrix64F> alg , boolean overflow ) {
+        boolean madeBad= false;
+
+        DenseMatrix64F A_orig = RandomMatrices.createRandom(3,3,new Random(0x14));
+
+        int i;
+        for( i = 0; i < 3000; i++ ) {
+//            System.out.println("i = "+i);
+            DenseMatrix64F A = new DenseMatrix64F(A_orig);
+            if( overflow )
+                CommonOps.scale(Math.pow(2,i),A);
+            else
+                CommonOps.scale(Math.pow(1.0/2,i),A);
+
+            DenseMatrix64F A_inv = new DenseMatrix64F(A.numRows,A.numCols);
+
+            if(MatrixFeatures.hasUncountable(A)) {
+                madeBad = true;
+                break;
+            }
+
+            try {
+                if( !alg.setA(A) ) {
+                    break;
+                }
+                alg.invert(A_inv);
+                if(MatrixFeatures.hasUncountable(A_inv)) {
+                    break;
+                }
+            } catch( RuntimeException e ) {
+                break;
+            }
+
+            if(evaluateInverse(A,A_inv) > 1.0 ) {
+                break;
+            }
+        }
+
+        if( madeBad ) {
+            System.out.printf("%20s never broke. (%d)\n",name,i);
+        } else {
+            System.out.printf("%20s broke at %d.\n",name,i);
+        }
+    }
+
+    public static void main( String arg[] ) {
+        BenchmarkInverseStability eval = new BenchmarkInverseStability();
+
+        eval.evaluateAll();
+    }
+}
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/linsol/BenchmarkInvertSquare.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/linsol/BenchmarkInvertSquare.java
new file mode 100644
index 0000000..9d911d1
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/linsol/BenchmarkInvertSquare.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol;
+
+import org.ejml.alg.dense.misc.UnrolledInverseFromMinor;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * Compare the speed of various algorithms at inverting square matrices
+ *
+ * @author Peter Abeles
+ */
+// TODO generate random stuff per trial
+public class BenchmarkInvertSquare {
+
+
+    public static long invertBenchmark( LinearSolver solver , DenseMatrix64F orig , int numTrials ) {
+        DenseMatrix64F A = new DenseMatrix64F(orig.numRows,orig.numCols);
+
+        long prev = System.currentTimeMillis();
+
+        solver.setA(orig);
+
+        for( long i = 0; i < numTrials; i++ ) {
+            solver.invert(A);
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    public static long invertUnrolledBenchmark( DenseMatrix64F orig , int numTrials ) {
+        DenseMatrix64F A = new DenseMatrix64F(orig.numRows,orig.numCols);
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            UnrolledInverseFromMinor.inv(orig,A);
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    public static long invertOpsBenchmark( DenseMatrix64F orig , int numTrials ) {
+        DenseMatrix64F A = new DenseMatrix64F(orig.numRows,orig.numCols);
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            CommonOps.invert(orig,A);
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    private static void runAlgorithms( DenseMatrix64F mat , int numTrials )
+    {
+//        System.out.println("invert GJ No Pivot     = "+ invertBenchmark(
+//                new GaussJordanNoPivot(),mat,numTrials));
+//        System.out.println("invert GJ              = "+ invertBenchmark(
+//                new GaussJordan(mat.numRows),mat,numTrials));
+//        System.out.println("invert LU              = "+ invertBenchmark(
+//                new LinearSolverLu(new LUDecompositionAlt_D64()),mat,numTrials));
+//        System.out.println("invert LU  NR          = "+ invertBenchmark(
+//                new LinearSolverLu(new LUDecompositionNR()),mat,numTrials));
+//        System.out.println("invert Ops             = "+
+//                invertOpsBenchmark(mat,numTrials));
+        System.out.println("unrolled               = "+
+                invertUnrolledBenchmark(mat,numTrials));
+    }
+
+    public static void main( String args [] ) {
+        Random rand = new Random(23423);
+
+        int size[] = new int[]{5,4,6,10,100,1000,2000,5000,10000};
+        int trials[] = new int[]{(int)8e6,(int)5e6,(int)2e6,(int)1e6,1000,3,1,1,1};
+
+        for( int i = 0; i < 1; i++ ) {
+            int w = size[i];
+
+            System.out.printf("Inverting size %3d for %12d trials\n",w,trials[i]);
+            DenseMatrix64F mat = RandomMatrices.createRandom(w,w,rand);
+
+            runAlgorithms(mat,trials[i]);
+        }
+
+    }
+}
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/linsol/BenchmarkInvertSymPosDef.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/linsol/BenchmarkInvertSymPosDef.java
new file mode 100644
index 0000000..057ce5f
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/linsol/BenchmarkInvertSymPosDef.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol;
+
+import org.ejml.EjmlParameters;
+import org.ejml.alg.dense.decomposition.chol.CholeskyDecompositionBlock_D64;
+import org.ejml.alg.dense.decomposition.chol.CholeskyDecompositionInner_D64;
+import org.ejml.alg.dense.linsol.chol.LinearSolverChol_B64;
+import org.ejml.alg.dense.linsol.chol.LinearSolverChol_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.ejml.ops.CovarianceOps;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * Compare the speed of various algorithms at inverting square matrices
+ *
+ * @author Peter Abeles
+ */
+public class BenchmarkInvertSymPosDef {
+
+    public static long invertCovar( DenseMatrix64F orig , int numTrials ) {
+
+        DenseMatrix64F A = new DenseMatrix64F(orig.numRows,orig.numCols);
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            CovarianceOps.invert(orig,A);
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    public static long invertCholesky( LinearSolver<DenseMatrix64F> alg , DenseMatrix64F orig , int numTrials ) {
+
+        alg = new LinearSolverSafe<DenseMatrix64F>(alg);
+        DenseMatrix64F A = new DenseMatrix64F(orig.numRows,orig.numCols);
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            if( !alg.setA(orig) ) {
+                throw new RuntimeException("Bad matrix");
+            }
+            alg.invert(A);
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+
+    private static void runAlgorithms( DenseMatrix64F mat , int numTrials )
+    {
+
+        System.out.println("invert covariance         = "+ invertCovar(mat,numTrials));
+//        System.out.println("invert GJ No Pivot     = "+ invertGJ_NoPivot(mat,numTrials));
+//        System.out.println("invert GJ               = "+ invertGJ(mat,numTrials));
+//        System.out.println("invert LU-NR            = "+ invertLU_nr(mat,numTrials));
+//        System.out.println("invert LU-Alt           = "+ invertLU_alt(mat,numTrials));
+        System.out.println("invert Cholesky Inner       = "+ invertCholesky(
+                new LinearSolverChol_D64(new CholeskyDecompositionInner_D64( true)),
+                mat,numTrials));
+        System.out.println("invert Cholesky Block Dense = "+ invertCholesky(
+                new LinearSolverChol_D64(new CholeskyDecompositionBlock_D64( EjmlParameters.BLOCK_WIDTH_CHOL)),
+                mat,numTrials));
+//        System.out.println("invert default              = "+ invertCholesky(
+//                LinearSolverFactory.symmetric(mat.numRows),
+//                mat,numTrials));
+//        System.out.println("invert CholeskyLDL          = "+ invertCholesky(
+//                new LinearSolverCholLDL(new CholeskyDecompositionLDL()),
+//                mat,numTrials));
+        System.out.println("invert CholeskyBlock64      = "+ invertCholesky(
+                new LinearSolverChol_B64(),
+                mat,numTrials));
+    }
+
+    public static void main( String args [] ) {
+        Random rand = new Random(23423);
+
+        int size[] = new int[]{2,4,6,10,100,1000,2000,4000,8000};
+        int trials[] = new int[]{(int)1e7,(int)3e6,(int)1e6,(int)4e5,1000,3,1,1,1};
+
+        for( int i = 0; i < size.length; i++ ) {
+            int w = size[i];
+
+            System.out.printf("Inverting size %3d for %12d trials\n",w,trials[i]);
+
+            System.out.print("* Creating matrix ");
+            DenseMatrix64F symMat = RandomMatrices.createSymmPosDef(w,rand);
+            System.out.println("  Done.");
+            runAlgorithms(symMat,trials[i]);
+        }
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/linsol/BenchmarkRectSolve.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/linsol/BenchmarkRectSolve.java
new file mode 100644
index 0000000..4f979b8
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/linsol/BenchmarkRectSolve.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol;
+
+import org.ejml.alg.dense.linsol.qr.LinearSolverQrHouseCol_D64;
+import org.ejml.alg.dense.linsol.qr.LinearSolverQrHouse_D64;
+import org.ejml.alg.dense.linsol.svd.SolvePseudoInverseSvd;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class BenchmarkRectSolve {
+    private static final long SEED = 6;
+    private static final Random rand = new Random();
+    private static DenseMatrix64F A;
+    private static DenseMatrix64F B;
+
+    private static final boolean includeSet = true;
+
+    public static long solveBenchmark( LinearSolver<DenseMatrix64F> solver , int numTrials ) {
+        rand.setSeed(SEED);
+        DenseMatrix64F X = new DenseMatrix64F(A.numCols,B.numCols);
+        RandomMatrices.setRandom(A,rand);
+        RandomMatrices.setRandom(B,rand);
+
+        if( !includeSet ) solver.setA(A);
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            if(includeSet) solver.setA(A);
+            solver.solve(B,X);
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    private static void runAlgorithms( int numTrials )
+    {
+
+        System.out.println("Pseudo Inverse  = "+ solveBenchmark(
+                new SolvePseudoInverseSvd(A.numRows,A.numCols),numTrials));
+        System.out.println("QR house        = "+ solveBenchmark(
+                new LinearSolverQrHouse_D64(),numTrials));
+        System.out.println("QR house Col    = "+ solveBenchmark(
+                new LinearSolverQrHouseCol_D64(),numTrials));
+    }
+
+    public static void main( String args [] ) {
+        int size[] = new int[]{2,4,10,100,1000,2000};
+        int trials[] = new int[]{(int)2e6,(int)8e5,(int)3e5,800,3,1};
+        int trialsX[] = new int[]{(int)1e5,(int)6e4,(int)1e4,(int)5e3,1000,500};
+
+        System.out.println("Increasing matrix A size");
+        for( int i = 0; i < size.length; i++ ) {
+            int w = size[i];
+
+            System.out.printf("Solving A size %3d for %12d trials\n",w,trials[i]);
+            A = RandomMatrices.createRandom(w*2,w,rand);
+            B = new DenseMatrix64F(w*2,2);
+
+            runAlgorithms(trials[i]);
+        }
+
+        System.out.println("Increasing matrix B size");
+        for( int i = 0; i < size.length; i++ ) {
+            int w = size[i];
+
+            System.out.printf("Solving B size %3d for %12d trials\n",w,trialsX[i]);
+            A = RandomMatrices.createRandom(200,100,rand);
+            B = new DenseMatrix64F(200,w);
+
+            runAlgorithms(trialsX[i]/80);
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/linsol/BenchmarkSolveEq.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/linsol/BenchmarkSolveEq.java
new file mode 100644
index 0000000..428e7e2
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/linsol/BenchmarkSolveEq.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol;
+
+import org.ejml.alg.dense.decomposition.lu.LUDecompositionAlt_D64;
+import org.ejml.alg.dense.linsol.lu.LinearSolverLuKJI_D64;
+import org.ejml.alg.dense.linsol.lu.LinearSolverLu_D64;
+import org.ejml.alg.dense.linsol.qr.LinearSolverQrHouseCol_D64;
+import org.ejml.alg.dense.linsol.qr.LinearSolverQrHouse_D64;
+import org.ejml.alg.dense.linsol.svd.SolvePseudoInverseSvd;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class BenchmarkSolveEq {
+    private static final long SEED = 6;
+    private static final Random rand = new Random();
+    private static DenseMatrix64F A;
+    private static DenseMatrix64F B;
+
+    private static boolean includeSet = false;
+
+    public static long solveBenchmark( LinearSolver<DenseMatrix64F> solver , int numTrials ) {
+        rand.setSeed(SEED);
+        DenseMatrix64F X = new DenseMatrix64F(B.numRows,B.numCols);
+        RandomMatrices.setRandom(A,rand);
+        RandomMatrices.setRandom(B,rand);
+
+        if( !includeSet ) solver.setA(A);
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            if(includeSet) solver.setA(A);
+
+            solver.solve(B,X);
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    private static void runAlgorithms( int numTrials )
+    {
+        System.out.println("solve LU A            = "+ solveBenchmark(
+                new LinearSolverLu_D64(new LUDecompositionAlt_D64()),numTrials));
+        System.out.println("solve LU B            = "+ solveBenchmark(
+                new LinearSolverLuKJI_D64(new LUDecompositionAlt_D64()),numTrials));
+        System.out.println("solve QR house        = "+ solveBenchmark(
+                new LinearSolverQrHouse_D64(),numTrials));
+        System.out.println("solve QR house Col    = "+ solveBenchmark(
+                new LinearSolverQrHouseCol_D64(),numTrials));
+        System.out.println("solve PInv            = "+ solveBenchmark(
+                new SolvePseudoInverseSvd(),numTrials));
+//        System.out.println("solve SVD             = "+ solveBenchmark(
+//                new LinearSolverSvd(new SvdNumericalRecipes(A.numRows,A.numCols)),numTrials/8));
+    }
+
+    public static void main( String args [] ) {
+        int size[] = new int[]{2,4,10,100,1000,2000};
+        int trials[] = new int[]{(int)1e7,(int)5e6,(int)1e6,2000,8,3};
+        int trialsX[] = new int[]{(int)5e5,(int)4e5,(int)2e5,(int)7e4,4000,2000};
+
+        System.out.println("Increasing matrix A size");
+        for( int i = 0; i < size.length; i++ ) {
+            int w = size[i];
+
+            System.out.printf("Solving A size %3d for %12d trials\n",w,trials[i]);
+            A = RandomMatrices.createRandom(w,w,rand);
+            B = new DenseMatrix64F(w,2);
+
+            runAlgorithms(trials[i]);
+        }
+
+        System.out.println("Increasing matrix B size");
+        for( int i = 0; i < size.length; i++ ) {
+            int w = size[i];
+
+            System.out.printf("Solving B size %3d for %12d trials\n",w,trialsX[i]);
+            A = RandomMatrices.createRandom(100,100,rand);
+            B = new DenseMatrix64F(100,w);
+
+            runAlgorithms(trialsX[i]/80);
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/linsol/BenchmarkSolveOver.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/linsol/BenchmarkSolveOver.java
new file mode 100644
index 0000000..5bf25f4
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/linsol/BenchmarkSolveOver.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol;
+
+import org.ejml.alg.dense.linsol.qr.LinearSolverQrBlock64_D64;
+import org.ejml.alg.dense.linsol.qr.LinearSolverQrHouseCol_D64;
+import org.ejml.alg.dense.linsol.qr.LinearSolverQrHouseTran_D64;
+import org.ejml.alg.dense.linsol.qr.LinearSolverQrHouse_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.LinearSolverFactory;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class BenchmarkSolveOver {
+    private static final long SEED = 6;
+    private static Random rand = new Random();
+    private static DenseMatrix64F A;
+    private static DenseMatrix64F B;
+
+    private static boolean includeSet = false;
+
+    public static long solveBenchmark( LinearSolver<DenseMatrix64F> solver , int numTrials ) {
+        rand.setSeed(SEED);
+        DenseMatrix64F X = new DenseMatrix64F(A.numCols,B.numCols);
+        RandomMatrices.setRandom(A,rand);
+        RandomMatrices.setRandom(B,rand);
+
+        DenseMatrix64F B_tmp = new DenseMatrix64F(B.numRows,B.numCols);
+
+        if( !includeSet ) solver.setA(A);
+
+        DenseMatrix64F A_copy = solver.modifiesA() ? A.copy() : A;
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+
+            if( solver.modifiesA() ) {
+                A_copy.set(A);
+            }
+
+            if(includeSet) solver.setA(A_copy);
+
+            if( solver.modifiesB() ) {
+                B_tmp.set(B);
+                solver.solve(B_tmp,X);
+            } else {
+                solver.solve(B,X);
+            }
+
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    private static void runAlgorithms( int numTrials )
+    {
+        System.out.println("  solve QR house        = "+ solveBenchmark(
+                new LinearSolverQrHouse_D64(),numTrials));
+        System.out.println("  solve QR house Col    = "+ solveBenchmark(
+                new LinearSolverQrHouseCol_D64(),numTrials));
+        System.out.println("  solve QR tran        = "+ solveBenchmark(
+                new LinearSolverQrHouseTran_D64(),numTrials));
+        System.out.println("  solve QR Block64      = "+ solveBenchmark(
+                new LinearSolverQrBlock64_D64(),numTrials));
+        System.out.println("  Selected              = "+ solveBenchmark(
+                LinearSolverFactory.leastSquares(A.numRows, A.numCols),numTrials));
+//        System.out.println("  solve PInv            = "+ solveBenchmark(
+//                new SolvePseudoInverse(),numTrials));
+    }
+
+    public static void main( String args [] ) {
+        int trialsWith[] = new int[]{3000000,1000000,200000,400,3,1,1,1,1,1};
+
+        int width[] = new int[]{2,4,10,100,500,1000,2000,5000,10000};
+
+        includeSet = true;
+        System.out.println("Solving for least squares fitting type problems with set");
+        for( int i = 0; i < width.length; i++ ) {
+            int N = width[i]*3;
+
+            System.out.printf("height %d Width = %d   trials = %d\n",N,width[i],trialsWith[i]);
+            A = new DenseMatrix64F(N,width[i]);
+            B = new DenseMatrix64F(N,1);
+
+            runAlgorithms(trialsWith[i]);
+        }
+
+        System.out.println();
+        includeSet = false;
+        System.out.println("Solving for least squares fitting type problems without set");
+        for( int i = 0; i < width.length; i++ ) {
+            int N = width[i]*3;
+
+            System.out.printf("height %d Width = %d   trials = %d\n",N,width[i],trialsWith[i]);
+            A = new DenseMatrix64F(N,width[i]);
+            B = new DenseMatrix64F(N,1);
+
+            runAlgorithms(trialsWith[i]);
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/linsol/BenchmarkSolvePseudoInverse.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/linsol/BenchmarkSolvePseudoInverse.java
new file mode 100644
index 0000000..a721ce9
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/linsol/BenchmarkSolvePseudoInverse.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol;
+
+import org.ejml.alg.dense.decomposition.qr.QRColPivDecompositionHouseholderColumn_D64;
+import org.ejml.alg.dense.linsol.qr.LinearSolverQrpHouseCol_D64;
+import org.ejml.alg.dense.linsol.qr.SolvePseudoInverseQrp_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.LinearSolverFactory;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class BenchmarkSolvePseudoInverse {
+    private static final long SEED = 6;
+    private static final Random rand = new Random();
+    private static DenseMatrix64F A;
+    private static DenseMatrix64F B;
+
+    private static boolean includeSet = true;
+
+    public static long solveBenchmark( LinearSolver<DenseMatrix64F> solver , int numTrials ) {
+        rand.setSeed(SEED);
+        DenseMatrix64F X = new DenseMatrix64F(B.numRows,B.numCols);
+
+        solver = new LinearSolverSafe<DenseMatrix64F>(solver);
+
+        if( !includeSet ) solver.setA(A);
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            if(includeSet) solver.setA(A);
+
+            solver.solve(B,X);
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    private static void runAlgorithms( int numTrials )
+    {
+//        System.out.println("solve SVD            = "+ solveBenchmark(
+//                new SolvePseudoInverseSvd(),numTrials));
+        System.out.println("solve Gen QRP Basic  = "+ solveBenchmark(
+                new SolvePseudoInverseQrp_D64(new QRColPivDecompositionHouseholderColumn_D64(),false),numTrials));
+        System.out.println("solve Gen QRP        = "+ solveBenchmark(
+                new SolvePseudoInverseQrp_D64(new QRColPivDecompositionHouseholderColumn_D64(),true),numTrials));
+        System.out.println("solve QRP Col Basic  = "+ solveBenchmark(
+                new LinearSolverQrpHouseCol_D64(new QRColPivDecompositionHouseholderColumn_D64(),false),numTrials));
+        System.out.println("solve QRP Col        = "+ solveBenchmark(
+                new LinearSolverQrpHouseCol_D64(new QRColPivDecompositionHouseholderColumn_D64(),true),numTrials));
+        System.out.println("solve QRP Col        = "+ solveBenchmark(
+                LinearSolverFactory.leastSquaresQrPivot(true,false),numTrials));
+    }
+
+    public static void main( String args [] ) {
+        int size[] = new int[]{2,4,10,100,1000,2000};
+        int trials[] = new int[]{(int)1e6,(int)5e5,(int)1e5,500,2,1};
+        int trialsX[] = new int[]{(int)5e5,(int)4e5,(int)2e5,(int)7e4,4000,2000};
+
+        System.out.println("Increasing matrix A size");
+        for( int i = 3; i < size.length; i++ ) {
+            int w = size[i];
+
+            // create a singular matrix
+            double singularValues[] = new double[w];
+            for( int j = 0; j < w-1; j++ )
+                singularValues[j] = 10+w-j;
+
+            System.out.printf("Solving A size %3d for %12d trials\n",w,trials[i]);
+            A = RandomMatrices.createSingularValues(w, w, rand, singularValues);
+            B = new DenseMatrix64F(w,2);
+
+            runAlgorithms(trials[i]);
+        }
+
+        System.out.println("Increasing matrix B size");
+        for( int i = 0; i < size.length; i++ ) {
+            int w = size[i];
+
+            System.out.printf("Solving B size %3d for %12d trials\n",w,trialsX[i]);
+            A = RandomMatrices.createRandom(100,100,rand);
+            B = new DenseMatrix64F(100,w);
+
+            runAlgorithms(trialsX[i]/80);
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/linsol/BenchmarkSolveSymPosDef.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/linsol/BenchmarkSolveSymPosDef.java
new file mode 100644
index 0000000..accec07
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/linsol/BenchmarkSolveSymPosDef.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.linsol;
+
+import org.ejml.alg.dense.decomposition.chol.CholeskyDecompositionInner_D64;
+import org.ejml.alg.dense.decomposition.chol.CholeskyDecompositionLDL_D64;
+import org.ejml.alg.dense.linsol.chol.LinearSolverCholLDL_D64;
+import org.ejml.alg.dense.linsol.chol.LinearSolverChol_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.linsol.LinearSolver;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * Compare the speed of various algorithms at inverting square matrices
+ *
+ * @author Peter Abeles
+ */
+public class BenchmarkSolveSymPosDef {
+
+
+    public static long solve( LinearSolver solver , DenseMatrix64F A, DenseMatrix64F b , int numTrials ) {
+
+        DenseMatrix64F x = new DenseMatrix64F(A.numCols,b.numCols);
+
+        if( !solver.setA(A) ) {
+            throw new RuntimeException("Bad matrix");
+        }
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            solver.solve(b,x);
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    private static void runAlgorithms( DenseMatrix64F A , DenseMatrix64F b ,int numTrials )
+    {
+        System.out.println("Solve Cholesky         = "+solve(
+                new LinearSolverChol_D64(new CholeskyDecompositionInner_D64(true)),
+                A,b,numTrials));
+        System.out.println("Solve Cholesky LDL     = "+solve(
+                new LinearSolverCholLDL_D64(new CholeskyDecompositionLDL_D64()),
+                A,b,numTrials));
+    }
+
+    public static void main( String args [] ) {
+        Random rand = new Random(23423);
+
+        int size[] = new int[]{2,4,10,100,1000};
+        int trials[] = new int[]{(int)6e6,(int)1e6,(int)2e5,500,1};
+
+        for( int i = 0; i < size.length; i++ ) {
+            int w = size[i];
+
+            System.out.printf("Matrix A size %3d for %12d trials\n",w,trials[i]);
+
+            while( true ) {
+                DenseMatrix64F mat = RandomMatrices.createRandom(w,w,rand);
+                DenseMatrix64F symMat = new DenseMatrix64F(w,w);
+                CommonOps.multTransA(mat,mat,symMat);
+                DenseMatrix64F b = RandomMatrices.createRandom(w,w*2,rand);
+
+                if(CommonOps.det(symMat) > 0 ) {
+                    runAlgorithms(symMat,b,trials[i]);
+                    break;
+                }
+            }
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/misc/BenchmarkDeterminant.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/misc/BenchmarkDeterminant.java
new file mode 100644
index 0000000..598ad07
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/misc/BenchmarkDeterminant.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.misc;
+
+import org.ejml.alg.dense.decomposition.lu.LUDecompositionAlt_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class BenchmarkDeterminant {
+
+    static int TOTAL_TRIALS = 50000000;
+
+
+    public static long computeAuto( DenseMatrix64F mat ,int numTrials )
+    {
+        long before = System.currentTimeMillis();
+
+        double total = 0;
+
+        for( int i = 0; i < numTrials; i++ ) {
+            total += UnrolledDeterminantFromMinor.det(mat);
+        }
+
+        long after = System.currentTimeMillis();
+
+        if( total == 100 )
+            System.out.println(total);
+
+        return after-before;
+    }
+
+    public static long computeFixed4x4( DenseMatrix64F mat )
+    {
+        long before = System.currentTimeMillis();
+
+        double total = 0;
+
+        for( int i = 0; i < TOTAL_TRIALS; i++ ) {
+            total += UnrolledDeterminantFromMinor.det4(mat);
+        }
+
+        long after = System.currentTimeMillis();
+
+        if( total == 100 )
+            System.out.println(total);
+
+        return after-before;
+    }
+
+    public static long computeMinor4x4( DenseMatrix64F mat )
+    {
+        long before = System.currentTimeMillis();
+
+        DeterminantFromMinor minor = new DeterminantFromMinor(4,5);
+
+        double total = 0;
+
+        for( int i = 0; i < TOTAL_TRIALS; i++ ) {
+            total += minor.compute(mat);
+        }
+
+        long after = System.currentTimeMillis();
+
+        if( total == 100 )
+            System.out.println(total);
+        
+        return after-before;
+    }
+
+    public static long computeLU( DenseMatrix64F mat , int numTrials )
+    {
+        long before = System.currentTimeMillis();
+
+        LUDecompositionAlt_D64 alg = new LUDecompositionAlt_D64();
+
+        double total = 0;
+
+        for( int i = 0; i < numTrials; i++ ) {
+            alg.decompose(mat);
+            total += alg.computeDeterminant().real;
+        }
+
+//        System.out.println("   total = "+total);
+        long after = System.currentTimeMillis();
+
+        if( total == 100 )
+            System.out.println(total);
+
+        return after-before;
+    }
+
+    public static long computeMinor( DenseMatrix64F mat , int numTrials )
+    {
+        long before = System.currentTimeMillis();
+
+        DeterminantFromMinor minor = new DeterminantFromMinor(mat.numRows,5);
+
+        double total = 0;
+
+        for( int i = 0; i < numTrials; i++ ) {
+            total += minor.compute(mat);
+        }
+
+//        System.out.println("   total = "+total);
+        long after = System.currentTimeMillis();
+
+        if( total == 100 )
+            System.out.println(total);
+
+        return after-before;
+    }
+
+    public static long computeLeibniz( DenseMatrix64F mat , int numTrials )
+    {
+        long before = System.currentTimeMillis();
+
+        double total = 0;
+
+        for( int i = 0; i < numTrials; i++ ) {
+            total += NaiveDeterminant.leibniz(mat);
+        }
+
+//        System.out.println("   total = "+total);
+        long after = System.currentTimeMillis();
+
+        if( total == 100 )
+            System.out.println(total);
+
+        return after-before;
+    }
+
+
+    public static void main( String args[] ) {
+        double[] d = new double[]{5 ,-2 ,-4 ,0.5, 0.1, 91, 8, 66, 1, -2, 10, -4, -0.2, 7, -4, 0.8};
+
+        DenseMatrix64F mat = new DenseMatrix64F(4,4, true, d);
+
+//        System.out.println("Fixed 4x4       = "+computeFixed4x4(mat));
+//        System.out.println("Auto 4x4       = "+computeAuto(mat,TOTAL_TRIALS));
+////        System.out.println("Minor 4x4       = "+computeMinor4x4(mat));
+//        System.out.println("LU alg NR 4x4   = "+computeLU(mat,TOTAL_TRIALS));
+////        System.out.println("Leibniz  4x4    = "+computeLeibniz(mat,TOTAL_TRIALS));
+
+        Random rand = new Random(4344535);
+
+        for( int i = 2; i <= 25; i+= 1) {
+            int numTrials = TOTAL_TRIALS/(i*i);
+
+            System.out.println("Dimension = "+i+"  trials = "+numTrials);
+
+            mat = RandomMatrices.createRandom(i,i,rand);
+
+            System.out.println("  Auto         = "+computeAuto(mat,numTrials));
+//            System.out.println("  Minor         = "+computeMinor(mat,numTrials));
+//            System.out.println("  Leibniz         = "+computeLeibniz(mat,numTrials));
+            System.out.println("  LU alg NR     = "+ computeLU(mat,numTrials));
+        }
+
+//        System.out.println("Recursive 4x4   = "+computeRecursive4x4(mat));
+    }
+}
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/misc/BenchmarkImplCommonOps.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/misc/BenchmarkImplCommonOps.java
new file mode 100644
index 0000000..03cd7fa
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/misc/BenchmarkImplCommonOps.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.misc;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.data.RealMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+/**
+ * @author Peter Abeles
+ */
+public class BenchmarkImplCommonOps {
+
+    public static long extract_DenseMatrix64F( DenseMatrix64F src ,  DenseMatrix64F dst ,
+                                               int numTrials) {
+
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            ImplCommonOps_DenseMatrix64F.extract(src, 0, 0, dst, 0, 0, src.numRows, src.numCols);
+        }
+        long curr = System.currentTimeMillis();
+
+        return curr-prev;
+    }
+
+    public static long extract_Matrix64F( RealMatrix64F src ,  RealMatrix64F dst , int numTrials) {
+
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            ImplCommonOps_Matrix64F.extract(src,0,0,dst,0,0, src.getNumRows(), src.getNumCols());
+        }
+        long curr = System.currentTimeMillis();
+
+        return curr-prev;
+    }
+
+    public static long extract_Common( DenseMatrix64F src ,  DenseMatrix64F dst , int numTrials) {
+
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CommonOps.extract(src, 0, src.numRows, 0 , src.numCols, dst, 0, 0 );
+        }
+        long curr = System.currentTimeMillis();
+
+        return curr-prev;
+    }
+
+    public static void benchmark( int N , int trials ) {
+        Random rand = new Random(234);
+
+        DenseMatrix64F src = new DenseMatrix64F(N,N);
+        DenseMatrix64F dst = new DenseMatrix64F(N,N);
+
+        RandomMatrices.addRandom(src,0,100,rand);
+
+        System.out.println("N = "+N);
+        System.out.println("extract DenseMatrix64F = "+extract_DenseMatrix64F(src,dst,trials));
+        System.out.println("extract Matrix64F      = "+extract_Matrix64F(src,dst,trials));
+        System.out.println("extract Common         = "+extract_Common(src, dst, trials));
+    }
+
+    public static void main( String args[] ) {
+        benchmark(5,10000000);
+        benchmark(100,100000);
+        benchmark(1000,500);
+        benchmark(2000,75);
+    }
+}
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/misc/BenchmarkTranspose.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/misc/BenchmarkTranspose.java
new file mode 100644
index 0000000..c823b5d
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/misc/BenchmarkTranspose.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.misc;
+
+import org.ejml.EjmlParameters;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class BenchmarkTranspose {
+    static Random rand = new Random(234);
+
+    public static long square( DenseMatrix64F mat , int numTrials) {
+
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            TransposeAlgs.square(mat);
+        }
+        long curr = System.currentTimeMillis();
+
+        return curr-prev;
+    }
+
+    public static long block( DenseMatrix64F mat , int numTrials , int blockLength ) {
+        DenseMatrix64F tran = new DenseMatrix64F(mat.numCols,mat.numRows);
+
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            TransposeAlgs.block(mat,tran,blockLength);
+        }
+        long curr = System.currentTimeMillis();
+
+        return curr-prev;
+    }
+
+    public static long standard( DenseMatrix64F mat , int numTrials) {
+        DenseMatrix64F tran = new DenseMatrix64F(mat.numCols,mat.numRows);
+
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            TransposeAlgs.standard(mat,tran);
+        }
+        long curr = System.currentTimeMillis();
+
+        return curr-prev;
+    }
+
+    public static long common( DenseMatrix64F mat , int numTrials) {
+        DenseMatrix64F tran = new DenseMatrix64F(mat.numCols,mat.numRows);
+
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CommonOps.transpose(mat,tran);
+        }
+        long curr = System.currentTimeMillis();
+
+        return curr-prev;
+    }
+
+
+    public static void main( String args[] ) {
+
+//        evaluateMatrix(3, 50000000);
+//        evaluateMatrix(20, 1000000);
+//        evaluateMatrix(120, 50000);
+//        evaluateMatrix(EjmlParameters.TRANSPOSE_SWITCH+1, 4000);
+        evaluateMatrix(2000, 80);
+//        evaluateMatrix(10000, 1);
+    }
+
+    private static void evaluateMatrix( int length , int n) {
+        System.out.println("*** Size "+length);
+        DenseMatrix64F A = RandomMatrices.createRandom(length,length,rand);
+
+        System.out.println("---------- Square ----------------");
+        System.out.println("In place  : "+square(A, n));
+        System.out.println("Block     : "+block(A, n, EjmlParameters.BLOCK_WIDTH));
+        System.out.println("Block 15  : "+block(A, n, 15));
+        System.out.println("Block 20  : "+block(A, n, 20));
+        System.out.println("Block 30  : "+block(A, n, 30));
+        System.out.println("Block 50  : "+block(A, n, 50));
+        System.out.println("Standard  : "+standard(A, n));
+        System.out.println("Common    : "+common(A, n));
+        System.out.println();
+        System.out.println("---------- Tall ----------------");
+        A = RandomMatrices.createRandom(2*length,length,rand);
+        System.out.println("Block     : "+block(A, n,EjmlParameters.BLOCK_WIDTH));
+        System.out.println("Block 20  : "+block(A, n, 20));
+        System.out.println("Block 30  : "+block(A, n, 30));
+        System.out.println("Block 50  : "+block(A, n, 50));
+        System.out.println("Standard  : "+standard(A, n));
+        System.out.println("Common    : "+common(A, n));
+        System.out.println("---------- Wide ----------------");
+        A = RandomMatrices.createRandom(length,2*length,rand);
+        System.out.println("Block     : "+block(A, n, EjmlParameters.BLOCK_WIDTH));
+        System.out.println("Block 20  : "+block(A, n, 20));
+        System.out.println("Block 30  : "+block(A, n, 30));
+        System.out.println("Block 50  : "+block(A, n, 50));
+        System.out.println("Standard  : "+standard(A, n));
+        System.out.println("Common    : "+common(A, n));
+    }
+}
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/mult/BenchmarkMatrixMatrixMult.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/mult/BenchmarkMatrixMatrixMult.java
new file mode 100644
index 0000000..c03c627
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/mult/BenchmarkMatrixMatrixMult.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.mult;
+
+import org.ejml.alg.block.BlockMatrixOps;
+import org.ejml.alg.blockd3.BlockD3MatrixOps;
+import org.ejml.data.BlockD3Matrix64F;
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ *
+ * Some notes:
+ *
+ * Other libraries implement there multiplication the same as my aux implementation, but theirs run faster.
+ * That is because they use 2D arrays, this allows them to only increment one variable in their inner
+ * most loop.  While in mine I have to increment two.  Thus there is an additional N^3 addition operations.
+ *
+ * @author Peter Abeles
+ */
+public class BenchmarkMatrixMatrixMult {
+
+    static Random rand = new Random(234234);
+
+    static int TRIALS_MULT = 10000000;
+
+    public static long mult( DenseMatrix64F matA , DenseMatrix64F matB ,
+                             DenseMatrix64F matResult , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CommonOps.mult(matA,matB,matResult);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long multSmall( DenseMatrix64F matA , DenseMatrix64F matB ,
+                             DenseMatrix64F matResult , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            MatrixMatrixMult.mult_small(matA,matB,matResult);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long multAux( DenseMatrix64F matA , DenseMatrix64F matB ,
+                             DenseMatrix64F matResult , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            MatrixMatrixMult.mult_aux(matA,matB,matResult,null);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long multReorder( DenseMatrix64F matA , DenseMatrix64F matB ,
+                             DenseMatrix64F matResult , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            MatrixMatrixMult.mult_reorder(matA,matB,matResult);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long multBlockNative( DenseMatrix64F matA , DenseMatrix64F matB ,
+                                        DenseMatrix64F matResult , int numTrials) {
+        BlockMatrix64F blockA = BlockMatrixOps.convert(matA);
+        BlockMatrix64F blockB = BlockMatrixOps.convert(matB);
+        BlockMatrix64F blockC = new BlockMatrix64F(matResult.numRows,matResult.numCols);
+
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            BlockMatrixOps.mult(blockA,blockB,blockC);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long multBlockD3Native( DenseMatrix64F matA , DenseMatrix64F matB ,
+                                          DenseMatrix64F matResult , int numTrials) {
+        BlockD3Matrix64F blockA = BlockD3MatrixOps.convert(matA);
+        BlockD3Matrix64F blockB = BlockD3MatrixOps.convert(matB);
+        BlockD3Matrix64F blockC = new BlockD3Matrix64F(matResult.numRows,matResult.numCols);
+
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            BlockD3MatrixOps.mult(blockA,blockB,blockC);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+
+    public static void performTests( int numRows , int numCols , int numK,
+                                     int numTrials )
+    {
+        System.out.println("M = "+numRows+" N = "+numCols+" K = "+numK);
+        DenseMatrix64F matA = RandomMatrices.createRandom(numRows,numCols,rand);
+        DenseMatrix64F matB = RandomMatrices.createRandom(numCols,numK,rand);
+        DenseMatrix64F matResult = RandomMatrices.createRandom(numRows,numK,rand);
+
+        System.out.printf("Mult: %7d  Small %7d  Aux %7d  Reord %7d  Block %7d  BlockD3 %7d\n",
+                0,//mult(matA,matB,matResult,numTrials),
+                multSmall(matA,matB,matResult,numTrials),
+                0,//multAux(matA,matB,matResult,numTrials),
+                multReorder(matA,matB,matResult,numTrials),
+                0,//multBlockNative(matA,matB,matResult,numTrials),
+                0);//multBlockD3Native(matA,matB,matResult,numTrials));
+        System.gc();
+    }
+
+    public static void main( String args[] ) {
+        int size[] = new int[]{2,4,10,15,20,50,100,200,500,1000,2000,4000,10000};
+        int count[] = new int[]{40000000,10000000,1000000,300000,100000,10000,1000,100,8,2,1,1,1};
+
+        int sizeTall[] = new int[]{1,2,4,10,20,50,100,200,500,1000,2000,5000,10000};
+        int countTall[] = new int[]{3000,2400,1500,1000,200,200,100,50,10,5,2,1,1};
+
+        int N = size.length;
+
+        System.out.println("******* Square:\n");
+        for( int i = 5; i < N; i++ ) {
+            System.out.println();
+
+            performTests(size[i],size[i],size[i],count[i]);
+        }
+
+        N = sizeTall.length;
+        System.out.println("\n******* Wide A:");
+        for( int i = 0; i < N; i++ ) {
+            System.out.println();
+
+            performTests(sizeTall[i],1500,100,countTall[i]);
+        }
+
+        System.out.println("\n******* Tall A:");
+        for( int i = 7; i < N; i++ ) {
+            System.out.println();
+
+            performTests(1500,sizeTall[i],100,countTall[i]);
+        }
+
+        System.out.println("\n******* Wide B:");
+        for( int i = 7; i < N; i++ ) {
+            System.out.println();
+
+            performTests(100,sizeTall[i],1500,countTall[i]);
+        }
+
+        System.out.println("\n******* Tall B:");
+        for( int i = 7; i < N; i++ ) {
+            System.out.println();
+
+            performTests(100,1500,sizeTall[i],countTall[i]);
+        }
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/mult/BenchmarkMatrixMatrixMultAdd.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/mult/BenchmarkMatrixMatrixMultAdd.java
new file mode 100644
index 0000000..f8e39fb
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/mult/BenchmarkMatrixMatrixMultAdd.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.mult;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ *
+ * Some notes:
+ *
+ * Other libraries implement there multiplication the same as my aux implementation, but theirs run faster.
+ * That is because they use 2D arrays, this allows them to only increment one variable in their inner
+ * most loop.  While in mine I have to increment two.  Thus there is an additional N^3 addition operations.
+ *
+ * @author Peter Abeles
+ */
+public class BenchmarkMatrixMatrixMultAdd {
+
+    static Random rand = new Random(234234);
+
+    static int TRIALS_MULT = 10000000;
+
+    public static long mult( DenseMatrix64F matA , DenseMatrix64F matB ,
+                             DenseMatrix64F matResult , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CommonOps.multAdd(matA,matB,matResult);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long multSmall( DenseMatrix64F matA , DenseMatrix64F matB ,
+                             DenseMatrix64F matResult , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            MatrixMatrixMult.multAdd_small(matA,matB,matResult);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long multAux( DenseMatrix64F matA , DenseMatrix64F matB ,
+                             DenseMatrix64F matResult , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            MatrixMatrixMult.multAdd_aux(matA,matB,matResult,null);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long multReorder( DenseMatrix64F matA , DenseMatrix64F matB ,
+                             DenseMatrix64F matResult , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            MatrixMatrixMult.multAdd_reorder(matA,matB,matResult);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+
+    public static void performTests( int numRows , int numCols , int numK,
+                                     int numTrials )
+    {
+        DenseMatrix64F matA = RandomMatrices.createRandom(numRows,numCols,rand);
+        DenseMatrix64F matB = RandomMatrices.createRandom(numCols,numK,rand);
+        DenseMatrix64F matResult = RandomMatrices.createRandom(numRows,numK,rand);
+
+        System.out.printf("Mult: %7d  Small %7d  Aux %7d  Reord %7d\n",
+                mult(matA,matB,matResult,numTrials),
+                multSmall(matA,matB,matResult,numTrials),
+                multAux(matA,matB,matResult,numTrials),
+                multReorder(matA,matB,matResult,numTrials));
+
+        System.gc();
+    }
+
+    public static void main( String args[] ) {
+        int size[] = new int[]{2,4,10,};//20,50,100,200,500,1000};//,2000,4000};
+        int count[] = new int[]{40000000,10000000,1000000,100000,10000,1000,100,8,2,1,1};
+
+        int sizeTall[] = new int[]{1,2,4,10,};//20,50,100,200,500,1000};
+        int countTall[] = new int[]{3000,2400,1500,1000,200,200,100,50,10,5};
+
+        System.out.println("******* Square:\n");
+        for( int i = 0; i < size.length; i++ ) {
+            System.out.println("\nWidth = "+size[i]);
+
+            performTests(size[i],size[i],size[i],count[i]);
+        }
+
+        System.out.println("\n******* Wide A:");
+        for( int i = 0; i < sizeTall.length; i++ ) {
+            System.out.println("\nHeight = "+sizeTall[i]);
+
+            performTests(sizeTall[i],1500,100,countTall[i]);
+        }
+
+        System.out.println("\n******* Tall A:");
+        for( int i = 0; i < sizeTall.length; i++ ) {
+            System.out.println("\nWidth = "+sizeTall[i]);
+
+            performTests(1500,sizeTall[i],100,countTall[i]);
+        }
+
+        System.out.println("\n******* Wide B:");
+        for( int i = 0; i < sizeTall.length; i++ ) {
+            System.out.println("\nHeight = "+sizeTall[i]);
+
+            performTests(100,sizeTall[i],1500,countTall[i]);
+        }
+
+        System.out.println("\n******* Tall B:");
+        for( int i = 0; i < sizeTall.length; i++ ) {
+            System.out.println("\nWidth = "+sizeTall[i]);
+
+            performTests(100,1500,sizeTall[i],countTall[i]);
+        }
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/mult/BenchmarkMatrixMatrixMultQuad.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/mult/BenchmarkMatrixMatrixMultQuad.java
new file mode 100644
index 0000000..ef76241
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/mult/BenchmarkMatrixMatrixMultQuad.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.mult;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ *
+ * @author Peter Abeles
+ */
+public class BenchmarkMatrixMatrixMultQuad {
+
+    static Random rand = new Random(234234);
+
+    static int TRIALS_MULT = 10000000;
+
+    public static long mult1( DenseMatrix64F A , DenseMatrix64F B , DenseMatrix64F tmp,
+                              DenseMatrix64F expected , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            MatrixMatrixMult.mult_small(A,B,tmp);
+            MatrixMatrixMult.multTransB(tmp, A, expected);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long quad1( DenseMatrix64F A , DenseMatrix64F B ,
+                              DenseMatrix64F expected , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            MatrixMultQuad.multQuad1(A, B, expected);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static void performTests( int numRows , int numCols ,
+                                     int numTrials ) {
+        DenseMatrix64F A = RandomMatrices.createRandom(numRows,numCols,rand);
+        DenseMatrix64F B = RandomMatrices.createRandom(numCols,numCols,rand);
+        DenseMatrix64F out = RandomMatrices.createRandom(numRows, numRows, rand);
+        DenseMatrix64F tmp = new DenseMatrix64F(numRows,numCols);
+
+        System.out.printf(numRows+"  "+numCols+"     Mult1: %7d  Quad1 %7d\n",
+                mult1(A,B,tmp,out,numTrials),
+                quad1(A,B,out,numTrials));
+    }
+
+    public static void main( String args[] ) {
+        int size[] = new int[]{2,4,10,15,20,50,100,200,500,1000,2000,4000,10000};
+        int count[] = new int[]{12000000,2000000,200000,60000,30000,1000,300,25,1,1,1,1,1};
+
+        int N = size.length;
+
+        for( int i = 0; i < N; i++ ) {
+            System.out.println();
+
+            performTests(size[i],size[i]*2,count[i]);
+        }
+
+
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/mult/BenchmarkMatrixMatrixMultTransA.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/mult/BenchmarkMatrixMatrixMultTransA.java
new file mode 100644
index 0000000..3d7dec3
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/mult/BenchmarkMatrixMatrixMultTransA.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.mult;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ *
+ * Some notes:
+ *
+ * Other libraries implement there multiplication the same as my aux implementation, but theirs run faster.
+ * That is because they use 2D arrays, this allows them to only increment one variable in their inner
+ * most loop.  While in mine I have to increment two.  Thus there is an additional N^3 addition operations.
+ *
+ * @author Peter Abeles
+ */
+public class BenchmarkMatrixMatrixMultTransA {
+
+    static Random rand = new Random(234234);
+
+    static int TRIALS_MULT = 10000000;
+
+    public static long mult( DenseMatrix64F matA , DenseMatrix64F matB ,
+                             DenseMatrix64F matResult , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CommonOps.multTransA(matA,matB,matResult);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long multSmall( DenseMatrix64F matA , DenseMatrix64F matB ,
+                             DenseMatrix64F matResult , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            MatrixMatrixMult.multTransA_small(matA,matB,matResult);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long multReorder( DenseMatrix64F matA , DenseMatrix64F matB ,
+                             DenseMatrix64F matResult , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            MatrixMatrixMult.multTransA_reorder(matA,matB,matResult);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static void performTests( int numRows , int numCols , int numK,
+                                     int numTrials )
+    {
+        DenseMatrix64F matA = RandomMatrices.createRandom(numRows,numCols,rand);
+        DenseMatrix64F matB = RandomMatrices.createRandom(numCols,numK,rand);
+        DenseMatrix64F matResult = RandomMatrices.createRandom(numRows,numK,rand);
+
+        System.out.printf("Mult: %7d  Small %7d  Reord %7d\n",
+                mult(matA,matB,matResult,numTrials),
+                multSmall(matA,matB,matResult,numTrials),
+                multReorder(matA,matB,matResult,numTrials));
+
+        System.gc();
+    }
+
+    public static void main( String args[] ) {
+        int size[] = new int[]{2,4,10,20,50,100,200,500,1000};//,2000,4000};
+        int count[] = new int[]{40000000,10000000,1000000,100000,10000,1000,100,8,2,1,1};
+
+        int sizeTall[] = new int[]{1,2,4,10,20,50,100,200,500,1000};
+        int countTall[] = new int[]{3000,2400,1500,1000,200,200,100,50,10,5};
+
+        System.out.println("******* Square:\n");
+        for( int i = 0; i < size.length; i++ ) {
+            System.out.println("\nWidth = "+size[i]);
+
+            performTests(size[i],size[i],size[i],count[i]);
+        }
+
+        System.out.println("\n******* Wide A:");
+        for( int i = 0; i < sizeTall.length; i++ ) {
+            System.out.println("\nHeight = "+sizeTall[i]);
+
+            performTests(sizeTall[i],1500,100,countTall[i]);
+        }
+
+        System.out.println("\n******* Tall A:");
+        for( int i = 0; i < sizeTall.length; i++ ) {
+            System.out.println("\nWidth = "+sizeTall[i]);
+
+            performTests(1500,sizeTall[i],100,countTall[i]);
+        }
+
+        System.out.println("\n******* Wide B:");
+        for( int i = 0; i < sizeTall.length; i++ ) {
+            System.out.println("\nHeight = "+sizeTall[i]);
+
+            performTests(100,sizeTall[i],1500,countTall[i]);
+        }
+
+        System.out.println("\n******* Tall B:");
+        for( int i = 0; i < sizeTall.length; i++ ) {
+            System.out.println("\nWidth = "+sizeTall[i]);
+
+            performTests(100,1500,sizeTall[i],countTall[i]);
+        }
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/mult/BenchmarkMatrixMatrixMultTransAB.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/mult/BenchmarkMatrixMatrixMultTransAB.java
new file mode 100644
index 0000000..90be5b9
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/mult/BenchmarkMatrixMatrixMultTransAB.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.mult;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ *
+ * Some notes:
+ *
+ * Other libraries implement there multiplication the same as my aux implementation, but theirs run faster.
+ * That is because they use 2D arrays, this allows them to only increment one variable in their inner
+ * most loop.  While in mine I have to increment two.  Thus there is an additional N^3 addition operations.
+ *
+ * @author Peter Abeles
+ */
+public class BenchmarkMatrixMatrixMultTransAB {
+
+    static Random rand = new Random(234234);
+
+    static int TRIALS_MULT = 10000000;
+
+    public static long mult( DenseMatrix64F matA , DenseMatrix64F matB ,
+                             DenseMatrix64F matResult , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CommonOps.multTransAB(matA,matB,matResult);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long multSmall( DenseMatrix64F matA , DenseMatrix64F matB ,
+                             DenseMatrix64F matResult , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            MatrixMatrixMult.multTransAB(matA,matB,matResult);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long multAux( DenseMatrix64F matA , DenseMatrix64F matB ,
+                             DenseMatrix64F matResult , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            MatrixMatrixMult.multTransAB_aux(matA,matB,matResult,null);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static void performTests( int numRows , int numCols , int numK,
+                                     int numTrials )
+    {
+        DenseMatrix64F matA = RandomMatrices.createRandom(numRows,numCols,rand);
+        DenseMatrix64F matB = RandomMatrices.createRandom(numCols,numK,rand);
+        DenseMatrix64F matResult = RandomMatrices.createRandom(numRows,numK,rand);
+
+        System.out.printf("Mult: %7d  Small %7d  Aux %7d\n",
+                mult(matA,matB,matResult,numTrials),
+                multSmall(matA,matB,matResult,numTrials),
+                multAux(matA,matB,matResult,numTrials));
+
+        System.gc();
+    }
+
+    public static void main( String args[] ) {
+        int size[] = new int[]{2,4,10,20,50,100,200,500,1000};//,2000,4000};
+        int count[] = new int[]{40000000,10000000,1000000,100000,10000,1000,100,8,2,1,1};
+
+        int sizeTall[] = new int[]{1,2,4,10,20,50,100,200,500,1000};
+        int countTall[] = new int[]{3000,2400,1500,1000,200,200,100,50,10,5};
+
+        System.out.println("******* Square:\n");
+        for( int i = 0; i < size.length; i++ ) {
+            System.out.println("\nWidth = "+size[i]);
+
+            performTests(size[i],size[i],size[i],count[i]);
+        }
+
+        System.out.println("\n******* Wide A:");
+        for( int i = 0; i < sizeTall.length; i++ ) {
+            System.out.println("\nHeight = "+sizeTall[i]);
+
+            performTests(sizeTall[i],1500,100,countTall[i]);
+        }
+
+        System.out.println("\n******* Tall A:");
+        for( int i = 0; i < sizeTall.length; i++ ) {
+            System.out.println("\nWidth = "+sizeTall[i]);
+
+            performTests(1500,sizeTall[i],100,countTall[i]);
+        }
+
+        System.out.println("\n******* Wide B:");
+        for( int i = 0; i < sizeTall.length; i++ ) {
+            System.out.println("\nHeight = "+sizeTall[i]);
+
+            performTests(100,sizeTall[i],1500,countTall[i]);
+        }
+
+        System.out.println("\n******* Tall B:");
+        for( int i = 0; i < sizeTall.length; i++ ) {
+            System.out.println("\nWidth = "+sizeTall[i]);
+
+            performTests(100,1500,sizeTall[i],countTall[i]);
+        }
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/mult/BenchmarkMatrixMultAccessors.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/mult/BenchmarkMatrixMultAccessors.java
new file mode 100644
index 0000000..025fe48
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/mult/BenchmarkMatrixMultAccessors.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.mult;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+/**
+ * Compares different implementations of a reordered matrix multiplication that use different accessor functions.
+ * but identical algorithms.
+ *
+ * @author Peter Abeles
+ */
+public class BenchmarkMatrixMultAccessors {
+
+    /**
+     * All reads/writes have been inline by hand
+     */
+    public static long inlined( DenseMatrix64F a , DenseMatrix64F b , DenseMatrix64F c )
+    {
+        long timeBefore = System.currentTimeMillis();
+
+        double dataA[] = a.data;
+        double dataB[] = b.data;
+        double dataC[] = c.data;
+
+        double valA;
+        int indexCbase= 0;
+        int endOfKLoop = b.numRows*b.numCols;
+
+        for( int i = 0; i < a.numRows; i++ ) {
+            int indexA = i*a.numCols;
+
+            // need to assign dataC to a value initially
+            int indexB = 0;
+            int indexC = indexCbase;
+            int end = indexB + b.numCols;
+
+            valA = dataA[indexA++];
+
+            while( indexB < end ) {
+                dataC[indexC++] = valA*dataB[indexB++];
+            }
+
+            // now add to it
+            while( indexB != endOfKLoop ) { // k loop
+                indexC = indexCbase;
+                end = indexB + b.numCols;
+
+                valA = dataA[indexA++];
+
+                while( indexB < end ) { // j loop
+                    dataC[indexC++] += valA*dataB[indexB++];
+                }
+            }
+            indexCbase += c.numCols;
+        }
+
+        return System.currentTimeMillis() - timeBefore;
+    }
+
+    /**
+     * Wrapper functions with no bounds checking are used to access matrix internals
+     */
+    public static long wrapped( DenseMatrix64F a , DenseMatrix64F b , DenseMatrix64F c )
+    {
+        long timeBefore = System.currentTimeMillis();
+        double valA;
+        int indexCbase= 0;
+        int endOfKLoop = b.numRows*b.numCols;
+
+        for( int i = 0; i < a.numRows; i++ ) {
+            int indexA = i*a.numCols;
+
+            // need to assign dataC to a value initially
+            int indexB = 0;
+            int indexC = indexCbase;
+            int end = indexB + b.numCols;
+
+            valA = a.get(indexA++);
+
+            while( indexB < end ) {
+                c.set( indexC++ , valA*b.get(indexB++));
+            }
+
+            // now add to it
+            while( indexB != endOfKLoop ) { // k loop
+                indexC = indexCbase;
+                end = indexB + b.numCols;
+
+                valA = a.get(indexA++);
+
+                while( indexB < end ) { // j loop
+                    c.plus( indexC++ , valA*b.get(indexB++));
+                }
+            }
+            indexCbase += c.numCols;
+        }
+
+        return System.currentTimeMillis() - timeBefore;
+    }
+
+    /**
+     * Only sets and gets that are by row and column are used.
+     */
+    public static long access2d( DenseMatrix64F a , DenseMatrix64F b , DenseMatrix64F c )
+    {
+        long timeBefore = System.currentTimeMillis();
+
+        for( int i = 0; i < a.numRows; i++ ) {
+
+            for( int j = 0; j < b.numCols; j++ ) {
+                c.set(i,j,a.get(i,0)*b.get(0,j));
+            }
+
+            for( int k = 1; k < b.numRows; k++ ) {
+                for( int j = 0; j < b.numCols; j++ ) {
+//                    c.set(i,j, c.get(i,j) + a.get(i,k)*b.get(k,j));
+                    c.data[i*b.numCols+j] +=a.get(i,k)*b.get(k,j);
+                }
+            }
+        }
+
+        return System.currentTimeMillis() - timeBefore;
+    }
+
+    public static void main( String args[] ) {
+        Random rand = new Random(9234243);
+
+        int N = 1000;
+
+        DenseMatrix64F A = RandomMatrices.createRandom(N,N,rand);
+        DenseMatrix64F B = RandomMatrices.createRandom(N,N,rand);
+        DenseMatrix64F C = new DenseMatrix64F(N,N);
+
+        long timeInlined = inlined(A,B,C);
+        long timeWrapped = wrapped(A,B,C);
+        long time2D = access2d(A,B,C);
+
+        System.out.println("inlined "+timeInlined+" wrapped "+timeWrapped+" access2d "+time2D);
+    }
+}
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/mult/BenchmarkMatrixMultProduct.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/mult/BenchmarkMatrixMultProduct.java
new file mode 100644
index 0000000..28988d9
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/mult/BenchmarkMatrixMultProduct.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.mult;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+/**
+ * @author Peter Abeles
+ */
+public class BenchmarkMatrixMultProduct {
+    static Random rand = new Random(234234);
+
+    static int TRIALS_MULT = 10000000;
+
+    public static long multTransA( DenseMatrix64F matA ,
+                             DenseMatrix64F matResult , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CommonOps.multTransA(matA, matA, matResult);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long innerProd_small( DenseMatrix64F matA ,
+                                        DenseMatrix64F matResult , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            MatrixMultProduct.inner_small(matA, matResult);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long innerProd_reorder( DenseMatrix64F matA ,
+                                        DenseMatrix64F matResult , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            MatrixMultProduct.inner_reorder(matA, matResult);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static void performTests( int numRows , int numCols ,
+                                     int numTrials )
+    {
+        System.out.println("M = "+numRows+" N = "+numCols+" trials "+numTrials);
+        DenseMatrix64F matA = RandomMatrices.createRandom(numRows, numCols, rand);
+        DenseMatrix64F matResult = RandomMatrices.createRandom(numCols,numCols,rand);
+
+        System.out.printf("Mult: %7d  Small %7d  Reord %7d\n",
+                0,//multTransA(matA,matResult,numTrials),
+                innerProd_small(matA,matResult,numTrials),
+                innerProd_reorder(matA,matResult,numTrials));
+        System.gc();
+    }
+
+    public static void main( String args[] ) {
+        int size[] = new int[]{2,4,10,15,20,50,100,200,500,1000,2000,4000,10000};
+        int count[] = new int[]{20000000,5000000,500000,150000,100000,5000,500,50,4,1,1,1,1};
+
+
+        int N = size.length;
+
+        for( int i = 0; i < N; i++ ) {
+            System.out.println();
+
+            performTests(2*size[i],size[i],count[i]);
+        }
+    }
+}
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/dense/mult/BenchmarkMatrixVectorOps.java b/main/experimental/benchmarks/src/org/ejml/alg/dense/mult/BenchmarkMatrixVectorOps.java
new file mode 100644
index 0000000..4caefd2
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/dense/mult/BenchmarkMatrixVectorOps.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.mult;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class BenchmarkMatrixVectorOps {
+
+    static Random rand = new Random(234234);
+
+    static int TRIALS_MULT = 40000000;//40000000;
+
+    public static long mm_mult_small( DenseMatrix64F matA , DenseMatrix64F matB ,
+                             DenseMatrix64F matResult , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            MatrixMatrixMult.mult_small(matA,matB,matResult);
+//            MatrixMatrixMult.mult_aux(matA,matB,matResult,null);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long mm_multTranA_small( DenseMatrix64F matA , DenseMatrix64F matB ,
+                                  DenseMatrix64F matResult , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            MatrixMatrixMult.multTransA_small(matA,matB,matResult);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long mm_multTranA_large( DenseMatrix64F matA , DenseMatrix64F matB ,
+                                  DenseMatrix64F matResult , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            MatrixMatrixMult.multTransA_reorder(matA,matB,matResult);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long mv_mult( DenseMatrix64F matA , DenseMatrix64F matB ,
+                             DenseMatrix64F matResult , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            MatrixVectorMult.mult(matA,matB,matResult);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long mv_multTranA_small( DenseMatrix64F matA , DenseMatrix64F matB ,
+                             DenseMatrix64F matResult , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            MatrixVectorMult.multTransA_small(matA,matB,matResult);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long mv_multTranA_large( DenseMatrix64F matA , DenseMatrix64F matB ,
+                                        DenseMatrix64F matResult , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            MatrixVectorMult.multTransA_reorder(matA,matB,matResult);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static void performTests( int numRows , int numCols ,
+                                     int numTrials )
+    {
+        DenseMatrix64F matA = RandomMatrices.createRandom(numRows,numCols,rand);
+        DenseMatrix64F matA_tran = RandomMatrices.createRandom(numCols,numRows,rand);
+        DenseMatrix64F matB = RandomMatrices.createRandom(numCols,1,rand);
+        DenseMatrix64F matResult = RandomMatrices.createRandom(numRows,1,rand);
+
+
+        System.out.printf("Mult Vec:              %10d\n",
+                mv_mult(matA,matB,matResult,numTrials));
+        System.out.printf("Mult Tran A Small Vec: %10d\n",
+                mv_multTranA_small(matA_tran,matB,matResult,numTrials));
+        System.out.printf("Mult Tran A Large Vec: %10d\n",
+                mv_multTranA_large(matA_tran,matB,matResult,numTrials));
+        System.out.printf("Mult small:            %10d\n",
+                mm_mult_small(matA,matB,matResult,numTrials));
+        System.out.printf("Mult Tran A small:     %10d\n",
+                mm_multTranA_small(matA_tran,matB,matResult,numTrials));
+        System.out.printf("Mult Tran A large:     %10d\n",
+                mm_multTranA_large(matA_tran,matB,matResult,numTrials));
+
+        System.gc();
+    }
+
+    public static void main( String args[] ) {
+        System.out.println("Small Matrix Results:") ;
+        performTests(4,4,TRIALS_MULT);
+
+        System.out.println();
+        System.out.println("Large Matrix Results:") ;
+
+        performTests(1000,1000,2000);
+
+        System.out.println();
+        System.out.println("Large Not Square Matrix Results:") ;
+
+        performTests(20,1000,10000);
+
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/fixed/BenchmarkInverseFixed.java b/main/experimental/benchmarks/src/org/ejml/alg/fixed/BenchmarkInverseFixed.java
new file mode 100644
index 0000000..87a1a74
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/fixed/BenchmarkInverseFixed.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.fixed;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.data.FixedMatrix3x3_64F;
+import org.ejml.data.FixedMatrix4x4_64F;
+import org.ejml.data.FixedMatrix6x6_64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.ConvertMatrixType;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * Feeds the algorithms matrices that are closer and closer to being singular
+ * and sees at which point they break.
+ *
+ * @author Peter Abeles
+ */
+public class BenchmarkInverseFixed {
+    private static Random rand = new Random(234);
+
+    private static DenseMatrix64F dm3x3_a = new DenseMatrix64F(3,3);
+    private static DenseMatrix64F dm3x3_b = new DenseMatrix64F(3,3);
+
+    private static DenseMatrix64F dm4x4_a = new DenseMatrix64F(4,4);
+    private static DenseMatrix64F dm4x4_b = new DenseMatrix64F(4,4);
+
+    private static DenseMatrix64F dm6x6_a = new DenseMatrix64F(6,6);
+    private static DenseMatrix64F dm6x6_b = new DenseMatrix64F(6,6);
+
+    private static FixedMatrix3x3_64F fixed3x3_a = new FixedMatrix3x3_64F();
+    private static FixedMatrix3x3_64F fixed3x3_b = new FixedMatrix3x3_64F();
+
+    private static FixedMatrix4x4_64F fixed4x4_a = new FixedMatrix4x4_64F();
+    private static FixedMatrix4x4_64F fixed4x4_b = new FixedMatrix4x4_64F();
+
+    private static FixedMatrix6x6_64F fixed6x6_a = new FixedMatrix6x6_64F();
+    private static FixedMatrix6x6_64F fixed6x6_b = new FixedMatrix6x6_64F();
+
+    public static long benchmark(DenseMatrix64F a, DenseMatrix64F b , int numTrials ) {
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            CommonOps.invert(a,b);
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    public static long benchmark(FixedMatrix3x3_64F a, FixedMatrix3x3_64F b , int numTrials ) {
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            FixedOps3.invert(a,b);
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    public static long benchmark(FixedMatrix4x4_64F a, FixedMatrix4x4_64F b , int numTrials ) {
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            FixedOps4.invert(a,b);
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    public static void main( String arg[] ) {
+        RandomMatrices.setRandom(dm3x3_a,rand);
+        RandomMatrices.setRandom(dm3x3_b,rand);
+
+        RandomMatrices.setRandom(dm4x4_a,rand);
+        RandomMatrices.setRandom(dm4x4_b,rand);
+
+        RandomMatrices.setRandom(dm6x6_a,rand);
+        RandomMatrices.setRandom(dm6x6_b,rand);
+
+        ConvertMatrixType.convert(dm3x3_a,fixed3x3_a);
+        ConvertMatrixType.convert(dm3x3_b,fixed3x3_b);
+
+        ConvertMatrixType.convert(dm4x4_a,fixed4x4_a);
+        ConvertMatrixType.convert(dm4x4_b,fixed4x4_b);
+
+        ConvertMatrixType.convert(dm6x6_a,fixed6x6_a);
+        ConvertMatrixType.convert(dm6x6_b,fixed6x6_b);
+
+        int numTrials = 100000;
+
+        System.out.println("Dense 3x3 = "+benchmark(dm3x3_a,dm3x3_b,numTrials));
+        System.out.println("Fixed 3x3 = "+benchmark(fixed3x3_a,fixed3x3_b,numTrials));
+
+        System.out.println("Dense 4x4 = "+benchmark(dm4x4_a,dm4x4_b,numTrials));
+        System.out.println("Fixed 4x4 = "+benchmark(fixed4x4_a,fixed4x4_b,numTrials));
+
+    }
+}
diff --git a/main/experimental/benchmarks/src/org/ejml/alg/fixed/BenchmarkMultiplicationFixed.java b/main/experimental/benchmarks/src/org/ejml/alg/fixed/BenchmarkMultiplicationFixed.java
new file mode 100644
index 0000000..a9a62ea
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/alg/fixed/BenchmarkMultiplicationFixed.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.fixed;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.data.FixedMatrix3x3_64F;
+import org.ejml.data.FixedMatrix4x4_64F;
+import org.ejml.data.FixedMatrix6x6_64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.ConvertMatrixType;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * Feeds the algorithms matrices that are closer and closer to being singular
+ * and sees at which point they break.
+ *
+ * @author Peter Abeles
+ */
+public class BenchmarkMultiplicationFixed {
+    private static Random rand = new Random(234);
+
+    private static DenseMatrix64F dm3x3_a = new DenseMatrix64F(3,3);
+    private static DenseMatrix64F dm3x3_b = new DenseMatrix64F(3,3);
+    private static DenseMatrix64F dm3x3_c = new DenseMatrix64F(3,3);
+
+    private static DenseMatrix64F dm4x4_a = new DenseMatrix64F(4,4);
+    private static DenseMatrix64F dm4x4_b = new DenseMatrix64F(4,4);
+    private static DenseMatrix64F dm4x4_c = new DenseMatrix64F(4,4);
+
+    private static DenseMatrix64F dm6x6_a = new DenseMatrix64F(6,6);
+    private static DenseMatrix64F dm6x6_b = new DenseMatrix64F(6,6);
+    private static DenseMatrix64F dm6x6_c = new DenseMatrix64F(6,6);
+
+
+    private static FixedMatrix3x3_64F fixed3x3_a = new FixedMatrix3x3_64F();
+    private static FixedMatrix3x3_64F fixed3x3_b = new FixedMatrix3x3_64F();
+    private static FixedMatrix3x3_64F fixed3x3_c = new FixedMatrix3x3_64F();
+
+    private static FixedMatrix4x4_64F fixed4x4_a = new FixedMatrix4x4_64F();
+    private static FixedMatrix4x4_64F fixed4x4_b = new FixedMatrix4x4_64F();
+    private static FixedMatrix4x4_64F fixed4x4_c = new FixedMatrix4x4_64F();
+
+    private static FixedMatrix6x6_64F fixed6x6_a = new FixedMatrix6x6_64F();
+    private static FixedMatrix6x6_64F fixed6x6_b = new FixedMatrix6x6_64F();
+    private static FixedMatrix6x6_64F fixed6x6_c = new FixedMatrix6x6_64F();
+
+    public static long benchmark(DenseMatrix64F a, DenseMatrix64F b , DenseMatrix64F c , int numTrials ) {
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            CommonOps.mult(a,b,c);
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    public static long benchmark(FixedMatrix3x3_64F a, FixedMatrix3x3_64F b , FixedMatrix3x3_64F c , int numTrials ) {
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            FixedOps3.mult(a,b,c);
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    public static long benchmark(FixedMatrix4x4_64F a, FixedMatrix4x4_64F b , FixedMatrix4x4_64F c , int numTrials ) {
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            FixedOps4.mult(a,b,c);
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    public static long benchmark(FixedMatrix6x6_64F a, FixedMatrix6x6_64F b , FixedMatrix6x6_64F c , int numTrials ) {
+
+        long prev = System.currentTimeMillis();
+
+        for( long i = 0; i < numTrials; i++ ) {
+            FixedOps6.mult(a,b,c);
+        }
+
+        return System.currentTimeMillis() - prev;
+    }
+
+    public static void main( String arg[] ) {
+        RandomMatrices.setRandom(dm3x3_a,rand);
+        RandomMatrices.setRandom(dm3x3_b,rand);
+        RandomMatrices.setRandom(dm3x3_c,rand);
+
+        RandomMatrices.setRandom(dm4x4_a,rand);
+        RandomMatrices.setRandom(dm4x4_b,rand);
+        RandomMatrices.setRandom(dm4x4_c,rand);
+
+        RandomMatrices.setRandom(dm6x6_a,rand);
+        RandomMatrices.setRandom(dm6x6_b,rand);
+        RandomMatrices.setRandom(dm6x6_c,rand);
+
+        ConvertMatrixType.convert(dm3x3_a,fixed3x3_a);
+        ConvertMatrixType.convert(dm3x3_b,fixed3x3_b);
+        ConvertMatrixType.convert(dm3x3_c,fixed3x3_c);
+
+        ConvertMatrixType.convert(dm4x4_a,fixed4x4_a);
+        ConvertMatrixType.convert(dm4x4_b,fixed4x4_b);
+        ConvertMatrixType.convert(dm4x4_c,fixed4x4_c);
+
+        ConvertMatrixType.convert(dm6x6_a,fixed6x6_a);
+        ConvertMatrixType.convert(dm6x6_b,fixed6x6_b);
+        ConvertMatrixType.convert(dm6x6_c,fixed6x6_c);
+
+        int numTrials = 30000000;
+
+        System.out.println("Dense 3x3 = "+benchmark(dm3x3_a,dm3x3_b,dm3x3_c,numTrials));
+        System.out.println("Fixed 3x3 = "+benchmark(fixed3x3_a,fixed3x3_b,fixed3x3_c,numTrials));
+
+        System.out.println("Dense 4x4 = "+benchmark(dm4x4_a,dm4x4_b,dm4x4_c,numTrials));
+        System.out.println("Fixed 4x4 = "+benchmark(fixed4x4_a,fixed4x4_b,fixed4x4_c,numTrials));
+
+        numTrials = 10000000;
+
+        System.out.println("Dense 6x6 = "+benchmark(dm6x6_a,dm6x6_b,dm6x6_c,numTrials));
+        System.out.println("Fixed 6x6 = "+benchmark(fixed6x6_a,fixed6x6_b,fixed6x6_c,numTrials));
+    }
+}
diff --git a/main/experimental/benchmarks/src/org/ejml/data/BenchmarkFunctionReturn.java b/main/experimental/benchmarks/src/org/ejml/data/BenchmarkFunctionReturn.java
new file mode 100644
index 0000000..956f356
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/data/BenchmarkFunctionReturn.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+/**
+ * Checks to see if having a return statement that isn't used makes any different or not
+ *
+ * @author Peter Abeles
+ */
+public class BenchmarkFunctionReturn {
+
+    double data[] = new double[ 1000 ];
+
+    private void resetData() {
+        for( int i = 0; i < data.length; i++ ) {
+            data[i] = i + 1;
+        }
+    }
+
+
+    public double funcA( int i , double b) {
+        return data[i] *= b;
+    }
+
+    public void funcB( int i , double b) {
+        data[i] *= b;
+    }
+
+    public long benchmarkA( int numTrials ) {
+
+        resetData();
+
+        long timeBefore = System.currentTimeMillis();
+        for( int i = 0; i < numTrials; i++ ) {
+            for( int j = 0; j < 50; j++ ) {
+                for( int k = j; k < data.length; k++ ) {
+                    funcA(k,1.1);
+                }
+            }
+        }
+        long timeAfter = System.currentTimeMillis();
+
+        return timeAfter-timeBefore;
+    }
+
+    public long benchmarkB( int numTrials ) {
+
+        resetData();
+
+        long timeBefore = System.currentTimeMillis();
+        for( int i = 0; i < numTrials; i++ ) {
+            for( int j = 0; j < 50; j++ ) {
+                for( int k = j; k < data.length; k++ ) {
+                    funcB(k,1.1);
+                }
+            }
+        }
+        long timeAfter = System.currentTimeMillis();
+
+        return timeAfter-timeBefore;
+    }
+
+    public static void main( String args[] ) {
+        BenchmarkFunctionReturn app = new BenchmarkFunctionReturn();
+        int N = 100000;
+
+        System.out.println("With return = "+app.benchmarkA(N));
+        System.out.println("No return   = "+app.benchmarkB(N));
+    }
+}
diff --git a/main/experimental/benchmarks/src/org/ejml/ops/BenchmarkEquality.java b/main/experimental/benchmarks/src/org/ejml/ops/BenchmarkEquality.java
new file mode 100644
index 0000000..c166b42
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/ops/BenchmarkEquality.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.data.DenseMatrix64F;
+
+import java.util.Random;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class BenchmarkEquality {
+
+    public static long equals( DenseMatrix64F matA ,
+                               DenseMatrix64F matB ,
+                               int numTrials) {
+        boolean args = false;
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            args = MatrixFeatures.isEquals(matA,matB,1e-8);
+        }
+
+        long curr = System.currentTimeMillis();
+        if( !args )
+            throw new RuntimeException("don't optimize me away!");
+        return curr-prev;
+    }
+
+    public static long identical( DenseMatrix64F matA ,
+                                  DenseMatrix64F matB ,
+                                  int numTrials) {
+
+        boolean args = false;
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            args = MatrixFeatures.isIdentical(matA,matB,1e-8);
+        }
+
+        long curr = System.currentTimeMillis();
+        if( !args )
+            throw new RuntimeException("don't optimize me away!");
+        return curr-prev;
+    }
+
+    public static void main( String args[] ) {
+        Random rand = new Random(234234);
+
+        DenseMatrix64F A = RandomMatrices.createRandom(1000,2000,rand);
+        DenseMatrix64F B = A.copy();
+
+        int N = 1000;
+
+        System.out.println("Equals:    "+equals(A,B,N));
+        System.out.println("Identical: "+identical(A,B,N));
+    }
+
+}
diff --git a/main/experimental/benchmarks/src/org/ejml/ops/BenchmarkMultAndAddOps.java b/main/experimental/benchmarks/src/org/ejml/ops/BenchmarkMultAndAddOps.java
new file mode 100644
index 0000000..d8a7bb6
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/ops/BenchmarkMultAndAddOps.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.alg.dense.mult.MatrixMatrixMult;
+import org.ejml.data.DenseMatrix64F;
+
+import java.util.Random;
+
+
+
+/**
+ * @author Peter Abeles
+ */
+public class BenchmarkMultAndAddOps {
+
+    static Random rand = new Random(234234);
+
+    static int TRIALS_MULT = 4000000;
+    static int TRIALS_ADD = 100000000;
+
+    public static long mult( DenseMatrix64F matA , DenseMatrix64F matB , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        DenseMatrix64F results = new DenseMatrix64F(matA.numRows,matB.numCols);
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CommonOps.mult(matA,matB,results);
+//            MatrixMatrixMult.mult_small(matA,matB,results);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long mult_alpha( DenseMatrix64F matA , DenseMatrix64F matB , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        DenseMatrix64F results = new DenseMatrix64F(matA.numRows,matB.numCols);
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CommonOps.mult(2.0,matA,matB,results);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long mult_alt( DenseMatrix64F matA , DenseMatrix64F matB , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        DenseMatrix64F results = new DenseMatrix64F(matA.numRows,matB.numCols);
+
+        for( int i = 0; i < numTrials; i++ ) {
+            MatrixMatrixMult.mult_reorder(matA,matB,results);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long multTranA( DenseMatrix64F matA , DenseMatrix64F matB , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        DenseMatrix64F results = new DenseMatrix64F(matA.numCols,matB.numCols);
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CommonOps.multTransA(matA,matB,results);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long multTranA_alpha( DenseMatrix64F matA , DenseMatrix64F matB , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        DenseMatrix64F results = new DenseMatrix64F(matA.numCols,matB.numCols);
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CommonOps.multTransA(2.0,matA,matB,results);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long multTranB( DenseMatrix64F matA , DenseMatrix64F matB , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        DenseMatrix64F results = new DenseMatrix64F(matA.numRows,matB.numRows);
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CommonOps.multTransB(matA,matB,results);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long multTranAB( DenseMatrix64F matA , DenseMatrix64F matB , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        DenseMatrix64F results = new DenseMatrix64F(matA.numCols,matB.numRows);
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CommonOps.multTransAB(matA,matB,results);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long multAdd( DenseMatrix64F matA , DenseMatrix64F matB , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        DenseMatrix64F results = new DenseMatrix64F(matA.numRows,matB.numCols);
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CommonOps.multAdd(matA,matB,results);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long multAddTranA( DenseMatrix64F matA , DenseMatrix64F matB , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        DenseMatrix64F results = new DenseMatrix64F(matA.numCols,matB.numCols);
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CommonOps.multAddTransA(matA,matB,results);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long multAddTranB( DenseMatrix64F matA , DenseMatrix64F matB , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        DenseMatrix64F results = new DenseMatrix64F(matA.numRows,matB.numRows);
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CommonOps.multAddTransB(matA,matB,results);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long multAddTranAB( DenseMatrix64F matA , DenseMatrix64F matB , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        DenseMatrix64F results = new DenseMatrix64F(matA.numCols,matB.numRows);
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CommonOps.multAddTransAB(matA,matB,results);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long addEquals( DenseMatrix64F matA , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        DenseMatrix64F results = new DenseMatrix64F(matA.numRows,matA.numCols);
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CommonOps.addEquals(results,matA);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long add( DenseMatrix64F matA , DenseMatrix64F matB , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        DenseMatrix64F results = new DenseMatrix64F(matA.numRows,matA.numCols);
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CommonOps.add(matA,matB,results);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long add_a_b( DenseMatrix64F matA , DenseMatrix64F matB , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        DenseMatrix64F results = new DenseMatrix64F(matA.numRows,matA.numCols);
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CommonOps.add(1.5,matA,3.4,matB,results);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long addEqualBeta( DenseMatrix64F matA , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        DenseMatrix64F results = new DenseMatrix64F(matA.numRows,matA.numCols);
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CommonOps.addEquals(results,2.5,matA);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long minusEquals( DenseMatrix64F matA , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        DenseMatrix64F results = new DenseMatrix64F(matA.numRows,matA.numCols);
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CommonOps.subtractEquals(results, matA);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long minus( DenseMatrix64F matA , DenseMatrix64F matB , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        DenseMatrix64F results = new DenseMatrix64F(matA.numRows,matA.numCols);
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CommonOps.subtract(matA, matB, results);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static void performMultTests( DenseMatrix64F matA , DenseMatrix64F matB ,
+                                         DenseMatrix64F matC , DenseMatrix64F matD ,
+                                         int numTrials )
+    {
+        System.out.printf("Mult:                  = %10d\n",
+                mult(matA,matB,numTrials));
+        System.out.printf("Mult Alpha:            = %10d\n",
+                mult_alpha(matA,matB,numTrials));
+        System.out.printf("Tran A Mult:           = %10d\n",
+                multTranA(matD,matB,numTrials));
+        System.out.printf("Tran A Mult Alpha:     = %10d\n",
+                multTranA_alpha(matD,matB,numTrials));
+        System.out.printf("Tran B Mult:           = %10d\n",
+                multTranB(matA,matC,numTrials));
+        System.out.printf("Tran AB Mult:          = %10d\n",
+                multTranAB(matD,matC,numTrials));
+        System.out.printf("Mult Add:              = %10d\n",
+                multAdd(matA,matB,numTrials));
+        System.out.printf("Tran A Mult Add:       = %10d\n",
+                multAddTranA(matD,matB,numTrials));
+        System.out.printf("Tran B Mult Add:       = %10d\n",
+                multAddTranB(matA,matC,numTrials));
+        System.out.printf("Tran AB Mult Add:      = %10d\n",
+                multAddTranAB(matD,matC,numTrials));
+    }
+
+    public static void performAddTests( DenseMatrix64F matA , DenseMatrix64F matB ,
+                                         DenseMatrix64F matC , DenseMatrix64F matD ,
+                                         int numTrials )
+    {
+        System.out.printf("Add Equal:             = %10d\n",
+                addEquals(matA,numTrials));
+        System.out.printf("Add Equals b:          = %10d\n",
+                addEqualBeta(matA,numTrials));
+        System.out.printf("Add:                   = %10d\n",
+                add(matA,matC,numTrials));
+        System.out.printf("Add a b:               = %10d\n",
+                add(matA,matC,numTrials));
+        System.out.printf("Minus Equal:           = %10d\n",
+                minusEquals(matA,numTrials));
+        System.out.printf("Minus:                 = %10d\n",
+                minus(matA,matC,numTrials));
+    }
+
+    public static void main( String args[] ) {
+        System.out.println("Small Matrix Results:") ;
+        int N = 2;
+        DenseMatrix64F matA = RandomMatrices.createRandom(N,N,rand);
+        DenseMatrix64F matB = RandomMatrices.createRandom(N,N,rand);
+        DenseMatrix64F matC,matD;
+
+        performMultTests(matA,matB,matB,matA,TRIALS_MULT*10);
+        performAddTests(matA,matB,matB,matA,TRIALS_ADD);
+
+
+        System.out.println();
+        System.out.println("Large Matrix Results:") ;
+        matA = RandomMatrices.createRandom(1000,1000,rand);
+        matB = RandomMatrices.createRandom(1000,1000,rand);
+
+        performMultTests(matA,matB,matB,matA,1);
+        performAddTests(matA,matB,matB,matA,500);
+
+        System.out.println();
+        System.out.println("Large Not Square Matrix Results:") ;
+        matA = RandomMatrices.createRandom(600,1000,rand);
+        matB = RandomMatrices.createRandom(1000,600,rand);
+        matC = RandomMatrices.createRandom(600,1000,rand);
+        matD = RandomMatrices.createRandom(1000,600,rand);
+
+        performMultTests(matA,matB,matC,matD,1);
+        performAddTests(matA,matB,matC,matD,1000);
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/benchmarks/src/org/ejml/ops/BenchmarkVariousOps.java b/main/experimental/benchmarks/src/org/ejml/ops/BenchmarkVariousOps.java
new file mode 100644
index 0000000..cce83ce
--- /dev/null
+++ b/main/experimental/benchmarks/src/org/ejml/ops/BenchmarkVariousOps.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.ops;
+
+import org.ejml.data.DenseMatrix64F;
+
+import java.util.Arrays;
+import java.util.Random;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class BenchmarkVariousOps {
+
+    static Random rand = new Random(0xffff);
+
+    static int TRIALS_TRANSPOSE = 20000000;
+    static int TRIALS_SCALE = 30000000;
+    static int TRIALS_NORM = 10000000;
+    static int TRIALS_DETERMINANT = 20000000;
+
+    public static long transposeEml( DenseMatrix64F mat , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        DenseMatrix64F tran = new DenseMatrix64F(mat.numCols,mat.numRows);
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CommonOps.transpose(mat,tran);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+//    public static long transposeMtj( DenseMatrix64F orig , int numTrials) {
+//        DenseMatrix mat = UtilMatrixToolkitsJava.convertToMtj(orig);
+//
+//        long prev = System.currentTimeMillis();
+//
+//        DenseMatrix tran = new DenseMatrix(mat.numColumns(),mat.numRows());
+//
+//        for( int i = 0; i < numTrials; i++ ) {
+//            mat.transpose(tran);
+//        }
+//
+//        long curr = System.currentTimeMillis();
+//        return curr-prev;
+//    }
+
+    public static long scale( DenseMatrix64F mat , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CommonOps.scale(10,mat);
+            CommonOps.scale(0.1,mat);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long scale2( DenseMatrix64F mat , int numTrials) {
+        DenseMatrix64F result = mat.copy();
+
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CommonOps.scale(10,mat,result);
+            CommonOps.scale(0.1,mat,result);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+//    public static long scaleMtj( DenseMatrix64F orig , int numTrials) {
+//        DenseMatrix mat = UtilMatrixToolkitsJava.convertToMtj(orig);
+//
+//        long prev = System.currentTimeMillis();
+//
+//        for( int i = 0; i < numTrials; i++ ) {
+//            mat.scale(10);
+//            mat.scale(0.1);
+//        }
+//
+//        long curr = System.currentTimeMillis();
+//        return curr-prev;
+//    }
+
+    public static long normEml( DenseMatrix64F mat , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            NormOps.normF(mat);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long determinant( DenseMatrix64F mat , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            CommonOps.det(mat);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long fillManual( DenseMatrix64F mat , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            final int size = mat.getNumElements();
+
+            for( int j = 0; j < size; j++ ) {
+                mat.set( j , 2 );
+            }
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+    public static long fillArrays( DenseMatrix64F mat , int numTrials) {
+        long prev = System.currentTimeMillis();
+
+        for( int i = 0; i < numTrials; i++ ) {
+            Arrays.fill(mat.data,0,mat.getNumElements(),2);
+        }
+
+        long curr = System.currentTimeMillis();
+        return curr-prev;
+    }
+
+//    public static long normMtj( DenseMatrix64F orig , int numTrials) {
+//        DenseMatrix mat = UtilMatrixToolkitsJava.convertToMtj(orig);
+//
+//        long prev = System.currentTimeMillis();
+//
+//        for( int i = 0; i < numTrials; i++ ) {
+//            mat.norm(Matrix.Norm.Frobenius);
+//        }
+//
+//        long curr = System.currentTimeMillis();
+//        return curr-prev;
+//    }
+
+    public static void main( String args[] ) {
+        System.out.println("Small Matrix Results:") ;
+        DenseMatrix64F mat = RandomMatrices.createRandom(4,4,rand);
+
+//        System.out.printf("Transpose:         eml = %10d\n",
+//                transposeEml(mat,TRIALS_TRANSPOSE));
+//        System.out.printf("Scale:             eml = %10d\n",
+//                scale(mat,TRIALS_SCALE));
+//        System.out.printf("Scale2:            eml = %10d\n",
+//                scale2(mat,TRIALS_SCALE));
+//        System.out.printf("Norm:              eml = %10d\n",
+//                normEml(mat,TRIALS_NORM));
+//        System.out.printf("Determinant:       eml = %10d\n",
+//                determinant(mat,TRIALS_DETERMINANT));
+        System.out.printf("FillManual:        eml = %10d\n",
+                fillManual(mat,TRIALS_SCALE));
+        System.out.printf("FillArrays:        eml = %10d\n",
+                fillArrays(mat,TRIALS_SCALE));
+
+        System.out.println();
+        System.out.println("Large Matrix Results:") ;
+        mat = RandomMatrices.createRandom(2000,2000,rand);
+//        System.out.printf("Transpose:         eml = %10d\n",
+//                transposeEml(mat,100));
+//        System.out.printf("Scale:             eml = %10d\n",
+//                scaleEml(mat,100));
+//        System.out.printf("Norm:              eml = %10d\n",
+//                normEml(mat,100));
+//        System.out.printf("Determinant:       eml = %10d\n",
+//                determinant(mat,1));
+        System.out.printf("FillManual:        eml = %10d\n",
+                fillManual(mat,1000));
+        System.out.printf("FillArrays:        eml = %10d\n",
+                fillArrays(mat,1000));
+    }
+}
diff --git a/main/experimental/benchmarks/test/org/ejml/alg/dense/decompose/eig/RealEigenvalueHessenbergStressTest.java b/main/experimental/benchmarks/test/org/ejml/alg/dense/decompose/eig/RealEigenvalueHessenbergStressTest.java
new file mode 100644
index 0000000..e27207d
--- /dev/null
+++ b/main/experimental/benchmarks/test/org/ejml/alg/dense/decompose/eig/RealEigenvalueHessenbergStressTest.java
@@ -0,0 +1,361 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decompose.eig;
+
+import org.ejml.alg.dense.decomposition.eig.EigenvalueExtractor;
+import org.ejml.alg.dense.decomposition.eig.watched.WatchedDoubleStepQREigenvalue;
+import org.ejml.data.Complex64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.*;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+
+
+/**
+ * A stress test that sees how well eigenvalues of Hessenberg matrices can be computed.
+ *
+ * @author Peter Abeles
+ */
+public class RealEigenvalueHessenbergStressTest {
+
+    double tol = 1e-10;
+
+    Random rand = new Random(0x3434);
+
+    EigenvalueExtractor extractor;
+
+//    public RealEigenvalueStressTest(EigenvalueExtractor extractor) {
+//        this.extractor = extractor;
+//    }
+
+    int numCantFindEigenvector;
+
+    public RealEigenvalueHessenbergStressTest() {
+        extractor = new WatchedDoubleStepQREigenvalue();
+    }
+
+    public void evaluateRandom() {
+
+        int sizes[] = new int[]{3,5,10,20,50,100,200};
+
+        int totalFailed = 0;
+        numCantFindEigenvector = 0;
+        for( int sizeIndex = 0; sizeIndex < sizes.length; sizeIndex++ ) {
+
+            int n = sizes[sizeIndex];
+            System.out.println("Matrix size = "+n);
+
+            long startTime = System.currentTimeMillis();
+            for( int i = 0; i < 100; i++ ) {
+                DenseMatrix64F A = RandomMatrices.createUpperTriangle(n,1,-1,1,rand);
+
+                extractor.process(A);
+
+                if( isAllComplex() )
+                    continue;
+
+                totalFailed += checkEigenvalues(A);
+
+                if( System.currentTimeMillis() - startTime > 10000 ) {
+                    System.out.println("i = "+i);
+                    startTime = System.currentTimeMillis();
+                }
+            }
+        }
+
+        System.out.println("Num failed = "+totalFailed);
+        System.out.println("Num couldn't find eigenvector = "+numCantFindEigenvector);
+    }
+
+    private int checkEigenvalues(DenseMatrix64F a ) {
+        Complex64F[]ev = extractor.getEigenvalues();
+
+//        a.print("%14.5e");
+
+        int numFailed = 0;
+
+        double totalError = 0;
+
+        for( int j = 0; j < extractor.getNumberOfEigenvalues(); j++ ) {
+            if( ev[j].imaginary != 0 )
+                continue;
+
+            DenseMatrix64F v = EigenOps.computeEigenVector(a,ev[j].real).vector;
+            Complex64F c = ev[j];
+
+            if( v == null || MatrixFeatures.hasUncountable(v)) {
+                a.print("%f");
+                System.out.println("Can't find eigen vector?!?!");
+                numCantFindEigenvector++;
+                EigenOps.computeEigenVector(a,ev[j].real);
+                continue;
+            }
+
+            double error = computeError(a,v,c.getReal());
+
+            if( error > tol ) {
+//                System.out.println("Failed on this matrix:");
+//                a.print("%f");
+                numFailed++;
+                EigenOps.computeEigenVector(a,ev[j].real);
+            }
+
+            totalError += error;
+        }
+
+        totalError /= extractor.getNumberOfEigenvalues();
+        System.out.println("Mean error = "+(totalError));
+
+        return numFailed;
+    }
+
+    private double computeError( DenseMatrix64F A, DenseMatrix64F v , double eigenvalue ) {
+
+        if( v == null ) {   
+            throw new RuntimeException("WTF crappy tool");
+        }
+
+//        A.print();
+//        System.out.println("Eigen value = "+eigenvalue);
+//        NormOps.normalizeF(v);
+//        v.print();
+        DenseMatrix64F l = new DenseMatrix64F(A.numRows,1);
+
+        CommonOps.mult(A,v,l);
+        CommonOps.scale(eigenvalue,v);
+
+        double top = SpecializedOps.diffNormF(l,v);
+        double bottom = NormOps.normF(v);
+
+        if( Double.isNaN(top) || Double.isInfinite(top) || Double.isNaN(bottom) || Double.isInfinite(bottom) )
+            System.out.println("bad stuff");
+
+        double result = top/bottom;
+
+//        if( result >= tol || Double.isNaN(result) || Double.isInfinite(result))
+//            System.out.println("Crap");
+
+        return result;
+    }
+
+    public void evaluateScalingUp() {
+        double []scales = new double[]{1.0,10.0,1e100,1e200,1e306};
+
+        evaluateScaling(scales);
+    }
+
+    public void evaluateScalingDown() {
+        double []scales = new double[]{1.0,1e-1,1e-100,1e-200,1e-290};
+
+        evaluateScaling(scales);
+    }
+
+    private void evaluateScaling(double[] scales) {
+        int totalFailures[] = new int[scales.length];
+
+        for( int n = 3; n < 100; n++ ) {
+            System.out.println("Matrix size = "+n);
+
+            DenseMatrix64F A = RandomMatrices.createUpperTriangle(n,1,-1,1,rand);
+            if( !extractor.process(A) ){
+                throw new RuntimeException("Failed!");
+            }
+            while( isAllComplex()) {
+                System.out.println("Trying to find a matrix that isn't all complex number");
+                A = RandomMatrices.createUpperTriangle(n,1,-1,1,rand);
+                extractor.process(A);
+            }
+
+            for( int indexScale = 0; indexScale < scales.length; indexScale++ ) {
+                double s = scales[indexScale];
+
+                System.out.println("Scale = "+s);
+                DenseMatrix64F B = A.copy();
+
+                CommonOps.scale(s,B);
+
+                if( !extractor.process(B) ) {
+                    System.out.println("  Failed to converge.");
+                    continue;
+                }
+
+//                B.print("%15.6e");
+                totalFailures[indexScale] += checkEigenvalues(B);
+            }
+        }
+
+        System.out.println("------------ results --------------");
+        for( int i = 0; i < totalFailures.length; i++ ) {
+            System.out.printf("  %7.2e fail =  %d\n",scales[i],totalFailures[i]);
+        }
+    }
+
+    /**
+     * See if a totally zero matrix messes it up
+     */
+    public void testMatrix0() {
+        DenseMatrix64F A = new DenseMatrix64F(5,5);
+
+        if( !extractor.process(A) ){
+            throw new RuntimeException("Failed!");
+        }
+
+        assertEquals(5,extractor.getNumberOfEigenvalues());
+
+        for( int i = 0 ; i < 5; i++ ) {
+            Complex64F c = extractor.getEigenvalues()[i];
+
+            assertEquals(0,c.imaginary,1e-12);
+            assertEquals(0,c.getReal(),1e-12);
+        }
+    }
+
+    public void testMatrixNegHessenberg() {
+        DenseMatrix64F A = new DenseMatrix64F(5,5, true, 0, 1, 2, 3, 5, 0, 0, 4, 9, 3, 0, 0, 0, -1, 3, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0);
+
+        if( !extractor.process(A) ){
+            throw new RuntimeException("Failed!");
+        }
+
+        assertEquals(5,extractor.getNumberOfEigenvalues());
+
+        for( int i = 0 ; i < 5; i++ ) {
+            Complex64F c = extractor.getEigenvalues()[i];
+
+            assertEquals(0,c.imaginary,1e-12);
+            assertEquals(0,c.getReal(),1e-12);
+        }
+    }
+
+    /**
+     * Special case that requires exceptional shifts to work
+     */
+    public void testMatrixExceptional() {
+        DenseMatrix64F A = new DenseMatrix64F(5,5, true, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0);
+        if( !extractor.process(A) ){
+            throw new RuntimeException("Failed!");
+        }
+
+        assertEquals(5,extractor.getNumberOfEigenvalues());
+        // TODO compare to expected eigenvalues
+    }
+
+    public void testMatrixZeroButUpperDiag() {
+        DenseMatrix64F A = new DenseMatrix64F(5,5, true, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 4, 0);
+        if( !extractor.process(A) ){
+            throw new RuntimeException("Failed!");
+        }
+
+        assertEquals(5,extractor.getNumberOfEigenvalues());
+        for( int i = 0 ; i < 5; i++ ) {
+            Complex64F c = extractor.getEigenvalues()[i];
+
+            assertEquals(0,c.imaginary,1e-12);
+            assertEquals(0,c.getReal(),1e-12);
+        }
+    }
+
+    public void testMatrixVerySmallButUpperDiag() {
+
+        DenseMatrix64F A = new DenseMatrix64F(5,5);
+
+        for( int i = 0; i < 5; i++ ) {
+            int start = i < 2 ? 0 : i-1;
+
+            for( int j = start; j < 5; j++ ) {
+                A.set(i,j,1e-32);
+            }
+        }
+
+        for( int i = 0; i < 4; i++) {
+            A.set(i+1,i,i+1);
+        }
+
+        if( !extractor.process(A) ){
+            throw new RuntimeException("Failed!");
+        }
+
+        assertEquals(5,extractor.getNumberOfEigenvalues());
+        for( int i = 0 ; i < 5; i++ ) {
+            Complex64F c = extractor.getEigenvalues()[i];
+
+            assertEquals(0,c.imaginary,1e-12);
+            assertEquals(0,c.getReal(),1e-12);
+        }
+    }
+
+    public void testMatrixAlmostAllOnes() {
+
+        DenseMatrix64F A = new DenseMatrix64F(5,5);
+
+        for( int i = 0; i < 5; i++ ) {
+            int start = i < 2 ? 0 : i-1;
+
+            for( int j = start; j < 5; j++ ) {
+                A.set(i,j,1);
+            }
+        }
+
+        A.set(2,2,1e-32);
+        A.set(3,2,1e-32);
+
+        if( !extractor.process(A) ){
+            throw new RuntimeException("Failed!");
+        }
+
+//        A.print("%15.5e");
+
+        assertEquals(5,extractor.getNumberOfEigenvalues());
+        // TODO add expected list of eigenvalues
+    }
+
+    private boolean hasComplex() {
+        Complex64F[]ev = extractor.getEigenvalues();
+
+        for( int j = 0; j < extractor.getNumberOfEigenvalues(); j++ ) {
+            if( ev[j].getImaginary() != 0 )
+                return true;
+        }
+
+        return false;
+    }
+
+    private boolean isAllComplex() {
+        Complex64F[]ev = extractor.getEigenvalues();
+
+        for( int j = 0; j < extractor.getNumberOfEigenvalues(); j++ ) {
+            if( ev[j].getImaginary() == 0 )
+                return false;
+        }
+
+        return true;
+    }
+
+//    public static void main( String []args ) {
+//        EigenvalueExtractor extractor = new PrintDoubleStepQREigenvalue();
+//
+//        RealEigenvalueStressTest test = new RealEigenvalueStressTest(extractor);
+//
+////        test.evaluateRandom();
+////        test.evaluateScalingUp();
+//        test.evaluateScalingDown();
+//    }
+}
diff --git a/main/experimental/build.gradle b/main/experimental/build.gradle
new file mode 100644
index 0000000..3ea40b2
--- /dev/null
+++ b/main/experimental/build.gradle
@@ -0,0 +1,17 @@
+dependencies {
+    compile project(':main:core')
+    compile project(':main:dense64')
+    compile project(':main:denseC64')
+    compile project(':main:simple')
+    compile project(':main:equation')
+
+    testCompile project(':main:core').sourceSets.test.output
+    testCompile project(':main:dense64').sourceSets.test.output
+    testCompile project(':main:denseC64').sourceSets.test.output
+}
+
+idea {
+    module {
+        name = "EJML Experimental"
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/src/org/ejml/alg/blockd3/BlockD3MatrixOps.java b/main/experimental/src/org/ejml/alg/blockd3/BlockD3MatrixOps.java
new file mode 100644
index 0000000..7012335
--- /dev/null
+++ b/main/experimental/src/org/ejml/alg/blockd3/BlockD3MatrixOps.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.blockd3;
+
+import org.ejml.alg.generic.GenericMatrixOps;
+import org.ejml.data.BlockD3Matrix64F;
+import org.ejml.data.DenseMatrix64F;
+
+import java.util.Random;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class BlockD3MatrixOps {
+
+    public static BlockD3Matrix64F convert( DenseMatrix64F src , int blockLength )
+    {
+        BlockD3Matrix64F ret = new BlockD3Matrix64F(src.numRows,src.numCols,blockLength);
+
+        convert(src,ret);
+
+        return ret;
+    }
+
+    public static BlockD3Matrix64F convert( DenseMatrix64F src )
+    {
+        BlockD3Matrix64F ret = new BlockD3Matrix64F(src.numRows,src.numCols);
+
+        convert(src,ret);
+
+        return ret;
+    }
+
+    public static void convert( DenseMatrix64F src , BlockD3Matrix64F dst )
+    {
+        if( src.numRows != dst.numRows || src.numCols != dst.numCols )
+            throw new IllegalArgumentException("Must be the same size.");
+
+        for( int i = 0; i < dst.numRows; i += dst.blockLength ) {
+            int blockHeight = Math.min( dst.blockLength , dst.numRows - i);
+
+            for( int j = 0; j < dst.numCols; j += dst.blockLength ) {
+                int blockWidth = Math.min( dst.blockLength , dst.numCols - j);
+
+                int indexSrcRow = i*dst.numCols + j;
+
+                double block[] = dst.blocks[ i / dst.blockLength][ j/dst.blockLength];
+                int indexDstRow = 0;
+
+                for( int k = 0; k < blockHeight; k++ ) {
+                    int indexSrc = indexSrcRow;
+                    int end = indexSrc + blockWidth;
+                    int indexDst = indexDstRow;
+                    for( ;indexSrc != end; ) {
+                        block[indexDst++] = src.data[indexSrc++];
+                    }
+                    indexSrcRow += dst.numCols;
+                    indexDstRow += dst.blockLength;
+                }
+            }
+        }
+    }
+
+    public static void convert( BlockD3Matrix64F src , DenseMatrix64F dst )
+    {
+        if( dst.numRows != src.numRows || dst.numCols != src.numCols )
+            throw new IllegalArgumentException("Must be the same size.");
+
+        for( int i = 0; i < src.numRows; i += src.blockLength ) {
+            int blockHeight = Math.min( src.blockLength , src.numRows - i);
+
+            for( int j = 0; j < src.numCols; j += src.blockLength ) {
+                int blockWidth = Math.min( src.blockLength , src.numCols - j);
+
+                int indexSrcRow = 0;
+                int indexDstRow = i*dst.numCols + j;
+
+                double block[] = src.blocks[ i / src.blockLength][ j/src.blockLength];
+
+                for( int k = 0; k < blockHeight; k++ ) {
+                    int indexDst = indexDstRow;
+                    int indexSrc = indexSrcRow;
+                    int end = indexSrc + blockWidth;
+                    for( ;indexSrc != end; ) {
+                        dst.data[indexDst++] = block[indexSrc++];
+                    }
+                    indexDstRow += dst.numCols;
+                    indexSrcRow += src.blockLength;
+                }
+            }
+        }
+    }
+
+    public static BlockD3Matrix64F random( int numRows , int numCols ,
+                                           double min , double max ,
+                                           Random rand , int blockLength )
+    {
+        BlockD3Matrix64F ret = new BlockD3Matrix64F(numRows,numCols,blockLength);
+
+        GenericMatrixOps.setRandom(ret,min,max,rand);
+
+        return ret;
+    }
+
+
+    public static void mult( BlockD3Matrix64F A , BlockD3Matrix64F B , BlockD3Matrix64F C )
+    {
+        if( A.numCols != B.numRows )
+            throw new IllegalArgumentException("Rows in A are incompatible with columns in B");
+        if( A.numRows != C.numRows )
+            throw new IllegalArgumentException("Rows in A are incompatible with rows in C");
+        if( B.numCols != C.numCols )
+            throw new IllegalArgumentException("Columns in B are incompatible with columns in C");
+        if( A.blockLength != B.blockLength || A.blockLength != C.blockLength )
+            throw new IllegalArgumentException("Block lengths are not all the same.");
+
+        final int blockLength = A.blockLength;
+
+        for( int i = 0; i < A.numRows; i += blockLength ) {
+            int heightA = Math.min( blockLength , A.numRows - i);
+            int blockI = i/blockLength;
+
+            for( int j = 0; j < B.numCols; j += blockLength ) {
+                int widthB = Math.min( blockLength , B.numCols-j);
+                int blockJ = j/blockLength;
+
+                double blockC[] = C.blocks[ blockI ][ blockJ ];
+
+                for( int k = 0; k < A.numCols; k += blockLength ) {
+                    int widthA = Math.min( blockLength , A.numCols - k);
+                    int blockK = k/blockLength;
+
+                    double blockA[] = A.blocks[ blockI ][ blockK ];
+                    double blockB[] = B.blocks[ blockK ][ blockJ ];
+
+                    // by inlining these functions it can be made to be comparable (but slightly
+                    // slow than) mult_reorder().  Now its about 1.5x slower
+                    if( k == 0 )
+                        multBlockSet(blockA,blockB,blockC,heightA,widthA,widthB,blockLength);
+                    else
+                        multBlockAdd(blockA,blockB,blockC,heightA,widthA,widthB,blockLength);
+                }
+            }
+        }
+    }
+
+    /**
+     * Performs a matrix multiplication between inner block matrices.
+     *
+     * (m , o) += (m , n) * (n , o)
+     */
+    private static void multBlockAdd( double []blockA, double []blockB, double []blockC,
+                                     final int m, final int n, final int o,
+                                     final int blockLength ) {
+//        for( int i = 0; i < m; i++ ) {
+//            for( int j = 0; j < o; j++ ) {
+//                double val = 0;
+//                for( int k = 0; k < n; k++ ) {
+//                    val += blockA[ i*blockLength + k]*blockB[ k*blockLength + j];
+//                }
+//
+//                blockC[ i*blockLength + j] += val;
+//            }
+//        }
+
+//        int rowA = 0;
+//        for( int i = 0; i < m; i++ , rowA += blockLength) {
+//            for( int j = 0; j < o; j++ ) {
+//                double val = 0;
+//                int indexB = j;
+//                int indexA = rowA;
+//                int end = indexA + n;
+//                for( ; indexA != end; indexA++ , indexB += blockLength ) {
+//                    val += blockA[ indexA ]*blockB[ indexB ];
+//                }
+//
+//                blockC[ rowA + j] += val;
+//            }
+//        }
+
+//        for( int k = 0; k < n; k++ ) {
+//            for( int i = 0; i < m; i++ ) {
+//                for( int j = 0; j < o; j++ ) {
+//                    blockC[ i*blockLength + j] += blockA[ i*blockLength + k]*blockB[ k*blockLength + j];
+//                }
+//            }
+//        }
+
+        for( int k = 0; k < n; k++ ) {
+            int rowB = k*blockLength;
+            int endB = rowB+o;
+            for( int i = 0; i < m; i++ ) {
+                int indexC = i*blockLength;
+                double valA = blockA[ indexC + k];
+                int indexB = rowB;
+                
+                while( indexB != endB ) {
+                    blockC[ indexC++ ] += valA*blockB[ indexB++];
+                }
+            }
+        }
+    }
+
+    private static void multBlockSet( double []blockA, double []blockB, double []blockC,
+                                     final int m, final int n, final int o,
+                                     final int blockLength ) {
+        int rowA = 0;
+        for( int i = 0; i < m; i++ , rowA += blockLength) {
+            for( int j = 0; j < o; j++ ) {
+                double val = 0;
+                int indexB = j;
+                int indexA = rowA;
+                int end = indexA + n;
+                for( ; indexA != end; indexA++ , indexB += blockLength ) {
+                    val += blockA[ indexA ]*blockB[ indexB ];
+                }
+
+                blockC[ rowA + j] = val;
+            }
+        }
+    }
+}
diff --git a/main/experimental/src/org/ejml/alg/dense/decomposition/bidiagonal/BidiagonalDecompositionNaive_D64.java b/main/experimental/src/org/ejml/alg/dense/decomposition/bidiagonal/BidiagonalDecompositionNaive_D64.java
new file mode 100644
index 0000000..2f74929
--- /dev/null
+++ b/main/experimental/src/org/ejml/alg/dense/decomposition/bidiagonal/BidiagonalDecompositionNaive_D64.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.bidiagonal;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.SpecializedOps;
+import org.ejml.simple.SimpleMatrix;
+
+
+/**
+ * A slower but much simpler version of {@link BidiagonalDecompositionRow_D64} that internally uses
+ * SimpleMatrix and explicitly computes the householder matrices.  This was easier to code up and is
+ * used to validate other implementations.
+ *
+ * @author Peter Abeles
+ */
+public class BidiagonalDecompositionNaive_D64 {
+    private SimpleMatrix U;
+    private SimpleMatrix B;
+    private SimpleMatrix V;
+
+    // number of rows
+    private int m;
+    // number of columns
+    private int n;
+    // smallest of m and n
+    private int min;
+
+
+    DenseMatrix64F u;
+
+    public SimpleMatrix getU() {
+        return U;
+    }
+
+    public SimpleMatrix getB() {
+        return B;
+    }
+
+    public SimpleMatrix getV() {
+        return V;
+    }
+
+    /**
+     * Computes the decomposition of the provided matrix.  If no errors are detected then true is returned,
+     * false otherwise.
+     * @param A  The matrix that is being decomposed.  Not modified.
+     * @return If it detects any errors or not.
+     */
+    public boolean decompose( DenseMatrix64F A )
+    {
+        init(A);
+        return _decompose();
+    }
+
+    protected void init(DenseMatrix64F A) {
+        m = A.numRows;
+        n = A.numCols;
+
+        min = Math.min(m,n);
+
+        U = SimpleMatrix.identity(m);
+        B = new SimpleMatrix(A);
+        V = SimpleMatrix.identity(n);
+
+        int max = Math.max(m,n);
+        u = new DenseMatrix64F(max,1);
+    }
+
+
+
+    /**
+     * Internal function for computing the decomposition.
+     */
+    private boolean _decompose() {
+        for( int k = 0; k < min; k++ ) {
+            computeU(k);
+            computeV(k);
+        }
+
+        return true;
+    }
+
+    protected void computeU( int k) {
+        u.reshape(m,1, false);
+        double u[] = this.u.data;
+
+        // find the largest value in this column
+        // this is used to normalize the column and mitigate overflow/underflow
+        double max = 0;
+
+        for( int i = k; i < m; i++ ) {
+            // copy the householder vector to vector outside of the matrix to reduce caching issues
+            // big improvement on larger matrices and a relatively small performance hit on small matrices.
+            double val = u[i] = B.get(i,k);
+            val = Math.abs(val);
+            if( val > max )
+                max = val;
+        }
+
+        if( max > 0 ) {
+            // -------- set up the reflector Q_k
+
+            double tau = 0;
+            // normalize to reduce overflow/underflow
+            // and compute tau for the reflector
+            for( int i = k; i < m; i++ ) {
+                double val = u[i] /= max;
+                tau += val*val;
+            }
+
+            tau = Math.sqrt(tau);
+
+            if( u[k] < 0 )
+                tau = -tau;
+
+            // write the reflector into the lower left column of the matrix
+            double nu = u[k] + tau;
+            u[k] = 1.0;
+
+            for( int i = k+1; i < m; i++ ) {
+                u[i] /= nu;
+            }
+
+            SimpleMatrix Q_k = SimpleMatrix.wrap(SpecializedOps.createReflector(this.u,nu/tau));
+            U = U.mult(Q_k);
+            B = Q_k.mult(B);
+        }
+    }
+
+    protected void computeV(int k) {
+        u.reshape(n,1, false);
+        u.zero();
+        double u[] = this.u.data;
+
+
+        // find the largest value in this column
+        // this is used to normalize the column and mitigate overflow/underflow
+        double max = 0;
+        
+        for( int i = k+1; i < n; i++ ) {
+            // copy the householder vector to vector outside of the matrix to reduce caching issues
+            // big improvement on larger matrices and a relatively small performance hit on small matrices.
+            double val = u[i] = B.get(k,i);
+            val = Math.abs(val);
+            if( val > max )
+                max = val;
+        }
+
+        if( max > 0 ) {
+            // -------- set up the reflector Q_k
+
+            double tau = 0;
+            // normalize to reduce overflow/underflow
+            // and compute tau for the reflector
+            for( int i = k+1; i < n; i++ ) {
+                double val = u[i] /= max;
+                tau += val*val;
+            }
+
+            tau = Math.sqrt(tau);
+
+            if( u[k+1] < 0 )
+                tau = -tau;
+
+            // write the reflector into the lower left column of the matrix
+            double nu = u[k+1] + tau;
+            u[k+1] = 1.0;
+
+            for( int i = k+2; i < n; i++ ) {
+                u[i] /= nu;
+            }
+
+            // ---------- multiply on the left by Q_k
+            SimpleMatrix Q_k = SimpleMatrix.wrap(SpecializedOps.createReflector(this.u,nu/tau));
+
+            V = V.mult(Q_k);
+            B = B.mult(Q_k);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/main/experimental/src/org/ejml/alg/dense/decomposition/lu/LUDecompositionNR_CD64.java b/main/experimental/src/org/ejml/alg/dense/decomposition/lu/LUDecompositionNR_CD64.java
new file mode 100644
index 0000000..52c6ba7
--- /dev/null
+++ b/main/experimental/src/org/ejml/alg/dense/decomposition/lu/LUDecompositionNR_CD64.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.lu;
+
+import org.ejml.alg.dense.decompose.lu.LUDecompositionBase_CD64;
+import org.ejml.data.CDenseMatrix64F;
+
+
+/**
+ * This code is inspired from what's in numerical recipes.
+ *
+ * @author Peter Abeles
+ */
+public class LUDecompositionNR_CD64 extends LUDecompositionBase_CD64 {
+
+    private static final double TINY = 1.0e-40;
+
+
+    /**
+     * <p>
+     * This implementation of LU Decomposition uses the algorithm specified below:
+     *
+     * "Numerical Recipes The Art of Scientific Computing", Third Edition, Pages 48-55<br>
+     * </p>
+     *
+     * @param orig The matrix that is to be decomposed.  Not modified.
+     * @return true If the matrix can be decomposed and false if it can not.  It can
+     * return true and still be singular.
+     */
+    @Override
+    public boolean decompose( CDenseMatrix64F orig ) {
+//        if( orig.numCols != orig.numRows )
+//            throw new RuntimeException("Must be square");
+        decomposeCommonInit(orig);
+
+        // loop over the rows to get implicit scaling information
+        for( int i = 0; i < m; i++ ) {
+            double big = 0;
+            double bigReal = 0.0;
+            double bigImg = 0.0;
+            for( int j = 0; j < n; j++ ) {
+                double real = dataLU[i*stride +j*2];
+                double img =  dataLU[i*stride +j*2+1];
+
+                double temp = real*real + img*img;
+                if( big < temp ) {
+                    big = temp;
+                    bigReal = real;
+                    bigImg = img;
+                }
+            }
+            // see if it is singular
+            if( big == 0.0 ) {
+                bigReal = 1.0;
+                bigImg = 0.0;
+            }
+
+            // vv = 1.0/big
+            vv[i*2] = bigReal/(big*big);
+            vv[i*2+1] = -bigImg/(big*big);
+        }
+
+        // outermost kij loop
+        for( int k = 0; k < n; k++ ) {
+            int imax=-1;
+
+            // start search by row for largest pivot element
+            double big = 0.0;
+            for( int i=k; i< m; i++ ) {
+                double luReal = dataLU[i*stride + k*2];
+                double luImg  = dataLU[i*stride + k*2 + 1];
+
+                double vvReal = vv[i*2];
+                double vvImg  = vv[i*2+1];
+
+                // double temp = vv[i*2]* dataLU[i* n +k ];
+                double tmpReal = luReal*vvReal - luImg*vvImg;
+                double tmpImg  = luImg*vvReal + luReal*vvReal;
+
+                double tmpMag = tmpReal*tmpReal + tmpImg*tmpImg;
+
+                if( tmpMag > big ) {
+                    big = tmpMag;
+                    imax=i;
+                }
+            }
+
+            // see if it is singular
+            if( imax < 0 ) {
+                indx[k] = -1;
+                return true;
+            } else {
+                // check to see if rows need to be interchanged
+                if( k != imax ) {
+                    int imax_n = imax*stride;
+                    int k_n = k*stride;
+                    int end = k_n+n*2;
+                    // j=0:n-1
+                    for( ; k_n < end; imax_n+=2,k_n+=2) {
+                        double tempReal = dataLU[imax_n];
+                        double tempImg  = dataLU[imax_n+1];
+                        dataLU[imax_n]   = dataLU[k_n];
+                        dataLU[imax_n+1] = dataLU[k_n+1];
+                        dataLU[k_n]   = tempReal;
+                        dataLU[k_n+1] = tempImg;
+                    }
+                    pivsign = -pivsign;
+                    vv[imax*2] = vv[k*2];
+                    vv[imax*2+1] = vv[k*2+1];
+
+                    int z = pivot[imax]; pivot[imax] = pivot[k]; pivot[k] = z;
+                }
+
+                indx[k] = imax;
+                // for some applications it is better to have this set to tiny even though
+                // it is singular.  see the book
+                double element_kk_real = dataLU[k*stride +k*2];
+                double element_kk_img  = dataLU[k*stride +k*2+1];
+                double mag = element_kk_real*element_kk_real + element_kk_img*element_kk_img;
+                if( mag == 0.0) {
+                    dataLU[k*stride +k*2]   = element_kk_real = TINY;
+                    dataLU[k*stride +k*2+1] = element_kk_img  = 0;
+                }
+
+                double element_kk_norm2 = element_kk_real*element_kk_real + element_kk_img*element_kk_img;
+
+                // the large majority of the processing time is spent in the code below
+                for( int i =k+1; i < m; i++ ) {
+                    int i_n=i*stride;
+
+                    // divide the pivot element
+                    double luReal = dataLU[i_n + k*2];
+                    double luImg = dataLU[i_n + k*2+1];
+
+                    double tmpReal = (luReal*element_kk_real + luImg*element_kk_img)/element_kk_norm2;
+                    double tmpImg  = (luImg*element_kk_real - luReal*element_kk_img)/element_kk_norm2;
+
+                    dataLU[i_n + k*2] = tmpReal;
+                    dataLU[i_n + k*2+1] = tmpImg;
+
+                    int k_n = k*stride + k*2+2;
+                    int end = i_n+n*2;
+                    i_n += k*2+2;
+                    // reduce remaining submatrix
+                    // j = k+1:n-1
+                    for( ; i_n<end; k_n+=2,i_n+=2) {
+                        // dataLU[i*n +j] -= temp* dataLU[k* n +j];
+                        luReal = dataLU[k_n];
+                        luImg = dataLU[k_n+1];
+
+                        dataLU[i_n]   -= tmpReal*luReal - tmpImg*luImg;
+                        dataLU[i_n+1] -= tmpReal*luImg + tmpImg*luReal;
+                    }
+                }
+            }
+        }
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/src/org/ejml/alg/dense/decomposition/lu/LUDecompositionNR_D64.java b/main/experimental/src/org/ejml/alg/dense/decomposition/lu/LUDecompositionNR_D64.java
new file mode 100644
index 0000000..237b9e5
--- /dev/null
+++ b/main/experimental/src/org/ejml/alg/dense/decomposition/lu/LUDecompositionNR_D64.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.lu;
+
+import org.ejml.data.DenseMatrix64F;
+
+
+/**
+ * This code is inspired from what's in numerical recipes.
+ *
+ * @author Peter Abeles
+ */
+public class LUDecompositionNR_D64 extends LUDecompositionBase_D64 {
+
+    private static final double TINY = 1.0e-40;
+
+
+    /**
+     * <p>
+     * This implementation of LU Decomposition uses the algorithm specified below:
+     *
+     * "Numerical Recipes The Art of Scientific Computing", Third Edition, Pages 48-55<br>
+     * </p>
+     *
+     * @param orig The matrix that is to be decomposed.  Not modified.
+     * @return true If the matrix can be decomposed and false if it can not.  It can
+     * return true and still be singular.
+     */
+    @Override
+    public boolean decompose( DenseMatrix64F orig ) {
+//        if( orig.numCols != orig.numRows )
+//            throw new RuntimeException("Must be square");
+        decomposeCommonInit(orig);
+
+        // loop over the rows to get implicit scaling information
+        for( int i = 0; i < m; i++ ) {
+            double big = 0.0;
+            for( int j = 0; j < n; j++ ) {
+                double temp = Math.abs(dataLU[i* n +j]);
+                if( big < temp ) big = temp;
+            }
+            // see if it is singular
+            if( big == 0.0 ) big = 1.0;
+            vv[i] = 1.0/big;
+        }
+
+        // outermost kij loop
+        for( int k = 0; k < n; k++ ) {
+            int imax=-1;
+
+            // start search by row for largest pivot element
+            double big = 0.0;
+            for( int i=k; i< m; i++ ) {
+                double temp = vv[i]* dataLU[i* n +k];
+                if( temp < 0 ) temp = -temp;
+                if( temp > big ) {
+                    big = temp;
+                    imax=i;
+                }
+            }
+
+            // see if it is singular
+            if( imax < 0 ) {
+                indx[k] = -1;
+                return true;
+            } else {
+                // check to see if rows need to be interchanged
+                if( k != imax ) {
+                    int imax_n = imax*n;
+                    int k_n = k*n;
+                    int end = k_n+n;
+                    // j=0:n-1
+                    for( ; k_n < end; imax_n++,k_n++) {
+                        double temp = dataLU[imax_n];
+                        dataLU[imax_n] = dataLU[k_n];
+                        dataLU[k_n] = temp;
+                    }
+                    pivsign = -pivsign;
+                    vv[imax] = vv[k];
+
+                    int z = pivot[imax]; pivot[imax] = pivot[k]; pivot[k] = z;
+                }
+
+                indx[k] = imax;
+                // for some applications it is better to have this set to tiny even though
+                // it is singular.  see the book
+                double element_kk = dataLU[k* n +k];
+                if( element_kk == 0.0) {
+                    dataLU[k* n +k] = TINY;
+                    element_kk = TINY;
+                }
+
+                // the large majority of the processing time is spent in the code below
+                for( int i =k+1; i < m; i++ ) {
+                    int i_n=i*n;
+
+                    // divide the pivot element
+                    double temp = dataLU[i_n +k] /= element_kk;
+
+                    int k_n = k*n + k+1;
+                    int end = i_n+n;
+                    i_n += k+1;
+                    // reduce remaining submatrix
+                    // j = k+1:n-1
+                    for( ; i_n<end; k_n++,i_n++) {
+                        // dataLU[i*n +j] -= temp* dataLU[k* n +j];
+                        dataLU[i_n] -= temp* dataLU[k_n];
+                    }
+                }
+            }
+        }
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/src/org/ejml/alg/dense/decomposition/svd/SmartRotatorUpdate.java b/main/experimental/src/org/ejml/alg/dense/decomposition/svd/SmartRotatorUpdate.java
new file mode 100644
index 0000000..1742fcd
--- /dev/null
+++ b/main/experimental/src/org/ejml/alg/dense/decomposition/svd/SmartRotatorUpdate.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.svd;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class SmartRotatorUpdate {
+
+    DenseMatrix64F R;
+    int mod[] = new int[ 1 ];
+
+    public SmartRotatorUpdate() {
+         
+    }
+
+    public DenseMatrix64F getR() {
+        return R;
+    }
+
+    public void init( DenseMatrix64F R ) {
+        this.R = R;
+        CommonOps.setIdentity(R);
+
+        int a = Math.min(R.numRows,R.numCols);
+
+        if( mod.length < a ) {
+            mod = new int[ a ];
+        }
+
+        for( int i = 0; i < a; i++ ) {
+            mod[i] = i;
+        }
+    }
+
+    public void update( int rowA , int rowB , double c , double s )
+    {
+        int l = Math.max( mod[rowA] , mod[rowB] );
+        mod[rowA] = l;
+        mod[rowB] = l;
+
+        int indexA = rowA*R.numCols;
+        int indexB = rowB*R.numCols;
+
+        for( int i = 0; i < l; i++ , indexA++,indexB++) {
+            double a = R.data[indexA];
+            double b = R.data[indexB];
+            R.data[indexA] = c*a + s*b;
+            R.data[indexB] = -s*a + c*b;
+        }
+    }
+}
diff --git a/main/experimental/src/org/ejml/alg/dense/decomposition/svd/SvdImplicitQrAlgorithmSmart.java b/main/experimental/src/org/ejml/alg/dense/decomposition/svd/SvdImplicitQrAlgorithmSmart.java
new file mode 100644
index 0000000..7b38bc0
--- /dev/null
+++ b/main/experimental/src/org/ejml/alg/dense/decomposition/svd/SvdImplicitQrAlgorithmSmart.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.svd;
+
+import org.ejml.alg.dense.decomposition.svd.implicitqr.SvdImplicitQrAlgorithm;
+import org.ejml.data.DenseMatrix64F;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class SvdImplicitQrAlgorithmSmart extends SvdImplicitQrAlgorithm {
+
+    SmartRotatorUpdate smartU = new SmartRotatorUpdate();
+    SmartRotatorUpdate smartV = new SmartRotatorUpdate();
+
+    @Override
+    public void setUt(DenseMatrix64F ut) {
+        super.setUt(ut);
+        if(Ut != null )
+            smartU.init(Ut);
+    }
+
+    @Override
+    public void setVt(DenseMatrix64F vt) {
+        super.setVt(vt);
+        if(Vt != null )
+            smartV.init(Vt);
+    }
+
+
+    @Override
+    protected void updateRotator( DenseMatrix64F Q , int m, int n, double c, double s) {
+        if( Q == smartU.getR() ) {
+            smartU.update(m,n,c,s);
+        } else if( Q == smartV.getR() ) {
+            smartV.update(m,n,c,s);
+        } else {
+            throw new RuntimeException("Unknown");
+        }
+    }
+}
diff --git a/main/experimental/src/org/ejml/alg/dense/decomposition/svd/SvdImplicitQrDecompose_Ultimate.java b/main/experimental/src/org/ejml/alg/dense/decomposition/svd/SvdImplicitQrDecompose_Ultimate.java
new file mode 100644
index 0000000..0b78788
--- /dev/null
+++ b/main/experimental/src/org/ejml/alg/dense/decomposition/svd/SvdImplicitQrDecompose_Ultimate.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.svd;
+
+import org.ejml.alg.dense.decomposition.bidiagonal.BidiagonalDecompositionRow_D64;
+import org.ejml.alg.dense.decomposition.svd.implicitqr.SvdImplicitQrAlgorithm;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.SingularValueDecomposition;
+import org.ejml.ops.CommonOps;
+
+
+/**
+ * <p>
+ * Similar to {@link SvdImplicitQrDecompose_D64} but it employs the
+ * ultimate shift strategy.  Ultimate shift involves first computing singular values then uses those
+ * to quickly compute the U and W matrices.  For EVD this strategy seems to work very well, but for
+ * this problem it needs to have little benefit and makes the code more complex.
+ * </p>
+ *
+ * NOTE: This code is much faster for 2x2 matrices since  it computes the eigenvalues in one step.
+ * 
+ * @author Peter Abeles
+ */
+public class SvdImplicitQrDecompose_Ultimate
+        implements SingularValueDecomposition<DenseMatrix64F> {
+
+    private int numRows;
+    private int numCols;
+    private int smallSide;
+
+    private BidiagonalDecompositionRow_D64 bidiag = new BidiagonalDecompositionRow_D64();
+    private SvdImplicitQrAlgorithm qralg = new SvdImplicitQrAlgorithm();
+
+    private double diag[];
+    private double off[];
+
+    private DenseMatrix64F Ut;
+    private DenseMatrix64F Vt;
+
+    private double singularValues[];
+    private int numSingular;
+
+    // compute a compact SVD
+    private boolean compact;
+    // What is actually computed
+    private boolean computeU;
+    private boolean computeV;
+
+    // What the user requested to be computed
+    // If the transpose is computed instead then what is actually computed is swapped
+    private boolean prefComputeU;
+    private boolean prefComputeV;
+
+    // stores the results of bidiagonalization
+    private double diagOld[];
+    private double offOld[];
+
+    // Either a copy of the input matrix or a copy of it transposed
+    private DenseMatrix64F A_mod = new DenseMatrix64F(1,1);
+
+    public SvdImplicitQrDecompose_Ultimate( boolean compact , boolean computeU , boolean computeV  ) {
+        this.compact = compact;
+        this.prefComputeU = computeU;
+        this.prefComputeV = computeV;
+    }
+
+    @Override
+    public double[] getSingularValues() {
+        return singularValues;
+    }
+
+    @Override
+    public int numberOfSingularValues() {
+        return numSingular;
+    }
+
+    @Override
+    public boolean isCompact() {
+        return compact;
+    }
+
+    @Override
+    public DenseMatrix64F getU(DenseMatrix64F U , boolean transpose) {
+        if( !prefComputeU )
+            throw new IllegalArgumentException("As requested U was not computed.");
+        if( transpose )
+            return Ut;
+
+        U = new DenseMatrix64F(Ut.numCols,Ut.numRows);
+        CommonOps.transpose(Ut,U);
+
+        return U;
+    }
+
+    @Override
+    public DenseMatrix64F getV( DenseMatrix64F V , boolean transpose ) {
+        if( !prefComputeV )
+            throw new IllegalArgumentException("As requested V was not computed.");
+        if( transpose )
+            return Vt;
+
+        V = new DenseMatrix64F(Vt.numCols,Vt.numRows);
+        CommonOps.transpose(Vt,V);
+
+        return V;
+    }
+
+    @Override
+    public DenseMatrix64F getW( DenseMatrix64F W ) {
+        int m = compact ? numSingular : numRows;
+        int n = compact ? numSingular : numCols;
+
+        if( W == null )
+            W = new DenseMatrix64F(m,n);
+        else {
+            W.reshape(m,n, false);
+            W.zero();
+        }
+
+        for( int i = 0; i < numSingular; i++ ) {
+            W.data[i*W.numCols+i] = singularValues[i];
+        }
+
+        return W;
+    }
+
+    @Override
+    public boolean decompose(DenseMatrix64F orig) {
+        boolean transposed = orig.numCols > orig.numRows;
+
+        init(orig, transposed);
+
+        if (computeSingularValues(orig, transposed))
+            return false;
+
+
+        if( computeU || computeV ) {
+            if (computeUandV(transposed))
+                return false;
+        }
+
+        // make sure all the singular values or positive
+        makeSingularPositive();
+
+        return true;
+    }
+
+    @Override
+    public boolean inputModified() {
+        return false;
+    }
+
+    private void init(DenseMatrix64F orig, boolean transposed) {
+        if( transposed ) {
+            computeU = prefComputeV;
+            computeV = prefComputeU;
+        } else {
+            computeU = prefComputeU;
+            computeV = prefComputeV;
+        }
+
+        numRows = orig.numRows;
+        numCols = orig.numCols;
+
+        smallSide = Math.min(numRows,numCols);
+        if( diagOld == null || diagOld.length < smallSide ) {
+            diagOld = new double[ smallSide ];
+            offOld = new double[ smallSide -1];
+            diag = new double[ smallSide ];
+            off = new double[ smallSide -1];
+        }
+    }
+
+    private boolean computeUandV(boolean transposed) {
+//        System.out.println("-------------- Computing U and V --------------------");
+
+//        long pointA = System.currentTimeMillis();
+
+        // compute U and V matrices
+        if( computeU )
+            Ut = bidiag.getU(Ut,true,compact);
+        if( computeV )
+            Vt = bidiag.getV(Vt,true,compact);
+
+        // set up the qr algorithm, reusing the previous extraction
+        if( transposed )
+            qralg.initParam(numCols,numRows);
+        else
+            qralg.initParam(numRows,numCols);
+        diagOld = qralg.swapDiag(diagOld);
+        offOld = qralg.swapOff(offOld);
+        // set it up to compute both U and V matrices
+        qralg.setFastValues(false);
+        if( computeU )
+            qralg.setUt(Ut);
+        if( computeV )
+            qralg.setVt(Vt);
+
+//        long pointB = System.currentTimeMillis();
+
+        if( !qralg.process(diagOld) )
+            return true;
+
+//        long pointC = System.currentTimeMillis();
+
+//        System.out.println("  bidiag UV "+(pointB-pointA)+" qr UV "+(pointC-pointB));
+
+        if( transposed ) {
+            DenseMatrix64F temp = Vt;
+            Vt = Ut;
+            Ut = temp;
+        }
+        return false;
+    }
+
+    private boolean computeSingularValues(DenseMatrix64F orig, boolean transposed) {
+//        long pointA = System.currentTimeMillis();
+
+        // change the matrix to bidiagonal form
+        if (bidiagonalization(orig, transposed))
+            return false;
+
+//        long pointB = System.currentTimeMillis();
+
+        // compute singular values
+        bidiag.getDiagonal(diag,off);
+        qralg.setMatrix(numRows,numCols,diag,off);
+
+        // copy the diagonal elements
+        // this way it doesn't need to be copied twice and will slightly speed it up
+        System.arraycopy(diag,0, diagOld,0,smallSide);
+        System.arraycopy(off,0, offOld,0,smallSide-1);
+
+        qralg.setFastValues(true);
+        qralg.setUt(null);
+        qralg.setVt(null);
+
+        boolean ret = !qralg.process();
+
+//        long pointC = System.currentTimeMillis();
+//        System.out.println("  bidiag "+(pointB-pointA)+" qr W "+(pointC-pointB));
+
+        return ret;
+    }
+
+    private boolean bidiagonalization(DenseMatrix64F orig, boolean transposed) {
+        if( transposed ) {
+            A_mod.reshape(orig.numCols,orig.numRows,false);
+            CommonOps.transpose(orig,A_mod);
+        } else {
+            A_mod.reshape(orig.numRows,orig.numCols,false);
+            A_mod.set(orig);
+        }
+        if( !bidiag.decompose(A_mod) )
+            return true;
+        return false;
+    }
+
+    /**
+     * With the QR algorithm it is possible for the found singular values to be native.  This
+     * makes them all positive by multiplying it by a diagonal matrix that has
+     */
+    private void makeSingularPositive() {
+        numSingular = qralg.getNumberOfSingularValues();
+        singularValues = qralg.getSingularValues();
+
+        for( int i = 0; i < numSingular; i++ ) {
+            double val = singularValues[i];
+
+            if( val < 0 ) {
+                singularValues[i] = -val;
+
+                if( computeU ) {
+                    // compute the results of multiplying it by an element of -1 at this location in
+                    // a diagonal matrix.
+                    int start = i* Ut.numCols;
+                    int stop = start+ Ut.numCols;
+
+                    for( int j = start; j < stop; j++ ) {
+                        Ut.data[j] = -Ut.data[j];
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public int numRows() {
+        return numRows;
+    }
+
+    @Override
+    public int numCols() {
+        return numCols;
+    }
+}
diff --git a/main/experimental/src/org/ejml/alg/dense/decomposition/svd/SvdImplicitQrDecompose_UltimateS.java b/main/experimental/src/org/ejml/alg/dense/decomposition/svd/SvdImplicitQrDecompose_UltimateS.java
new file mode 100644
index 0000000..4bdb012
--- /dev/null
+++ b/main/experimental/src/org/ejml/alg/dense/decomposition/svd/SvdImplicitQrDecompose_UltimateS.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decomposition.svd;
+
+import org.ejml.alg.dense.decomposition.bidiagonal.BidiagonalDecompositionRow_D64;
+import org.ejml.alg.dense.decomposition.svd.implicitqr.SvdImplicitQrAlgorithm;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.interfaces.decomposition.SingularValueDecomposition;
+import org.ejml.ops.CommonOps;
+
+
+/**
+ * <p>
+ * Similar to {@link SvdImplicitQrDecompose_D64} but it employs the
+ * ultimate shift strategy.  Ultimate shift involves first computing singular values then uses those
+ * to quickly compute the U and W matrices.  For EVD this strategy seems to work very well, but for
+ * this problem it needs to have little benefit and makes the code more complex.
+ * </p>
+ *
+ * NOTE: This code is much faster for 2x2 matrices since  it computes the eigenvalues in one step.
+ *
+ * @author Peter Abeles
+ */
+public class SvdImplicitQrDecompose_UltimateS
+        implements SingularValueDecomposition<DenseMatrix64F> {
+
+    private int numRows;
+    private int numCols;
+    private int smallSide;
+
+    private BidiagonalDecompositionRow_D64 bidiag = new BidiagonalDecompositionRow_D64();
+    private SvdImplicitQrAlgorithm qralg = new SvdImplicitQrAlgorithmSmart();
+
+    private double diag[];
+    private double off[];
+
+    private DenseMatrix64F Ut;
+    private DenseMatrix64F Vt;
+
+    private double singularValues[];
+    private int numSingular;
+
+    // compute a compact SVD
+    private boolean compact;
+    // What is actually computed
+    private boolean computeU;
+    private boolean computeV;
+
+    // What the user requested to be computed
+    // If the transpose is computed instead then what is actually computed is swapped
+    private boolean prefComputeU;
+    private boolean prefComputeV;
+
+    // stores the results of bidiagonalization
+    private double diagOld[];
+    private double offOld[];
+
+    // Either a copy of the input matrix or a copy of it transposed
+    private DenseMatrix64F A_mod = new DenseMatrix64F(1,1);
+
+    public SvdImplicitQrDecompose_UltimateS( boolean compact , boolean computeU , boolean computeV  ) {
+        this.compact = compact;
+        this.prefComputeU = computeU;
+        this.prefComputeV = computeV;
+    }
+
+    @Override
+    public double[] getSingularValues() {
+        return singularValues;
+    }
+
+    @Override
+    public int numberOfSingularValues() {
+        return numSingular;
+    }
+
+    @Override
+    public boolean isCompact() {
+        return compact;
+    }
+
+    @Override
+    public DenseMatrix64F getU( DenseMatrix64F U ,boolean transpose) {
+        if( !prefComputeU )
+            throw new IllegalArgumentException("As requested U was not computed.");
+        if( transpose )
+            return Ut;
+
+        U = new DenseMatrix64F(Ut.numCols,Ut.numRows);
+        CommonOps.transpose(Ut,U);
+
+        return U;
+    }
+
+    @Override
+    public DenseMatrix64F getV( DenseMatrix64F V , boolean transpose ) {
+        if( !prefComputeV )
+            throw new IllegalArgumentException("As requested V was not computed.");
+        if( transpose )
+            return Vt;
+
+        V = new DenseMatrix64F(Vt.numCols,Vt.numRows);
+        CommonOps.transpose(Vt,V);
+
+        return V;
+    }
+
+    @Override
+    public DenseMatrix64F getW( DenseMatrix64F W ) {
+        int m = compact ? numSingular : numRows;
+        int n = compact ? numSingular : numCols;
+
+        if( W == null )
+            W = new DenseMatrix64F(m,n);
+        else {
+            W.reshape(m,n, false);
+            W.zero();
+        }
+
+        for( int i = 0; i < numSingular; i++ ) {
+            W.data[i*W.numCols+i] = singularValues[i];
+        }
+
+        return W;
+    }
+
+    @Override
+    public boolean decompose(DenseMatrix64F orig) {
+        boolean transposed = orig.numCols > orig.numRows;
+
+        init(orig, transposed);
+
+        if (computeSingularValues(orig, transposed))
+            return false;
+
+
+        if( computeU || computeV ) {
+            if (computeUandV(transposed))
+                return false;
+        }
+
+        // make sure all the singular values or positive
+        makeSingularPositive();
+
+        return true;
+    }
+
+    @Override
+    public boolean inputModified() {
+        return false;
+    }
+
+    private void init(DenseMatrix64F orig, boolean transposed) {
+        if( transposed ) {
+            computeU = prefComputeV;
+            computeV = prefComputeU;
+        } else {
+            computeU = prefComputeU;
+            computeV = prefComputeV;
+        }
+
+        numRows = orig.numRows;
+        numCols = orig.numCols;
+
+        smallSide = Math.min(numRows,numCols);
+        if( diagOld == null || diagOld.length < smallSide ) {
+            diagOld = new double[ smallSide ];
+            offOld = new double[ smallSide -1];
+            diag = new double[ smallSide ];
+            off = new double[ smallSide -1];
+        }
+    }
+
+    private boolean computeUandV(boolean transposed) {
+//        System.out.println("-------------- Computing U and V --------------------");
+
+        long pointA = System.currentTimeMillis();
+
+        // compute U and V matrices
+        if( computeU )
+            Ut = bidiag.getU(Ut,true,compact);
+        if( computeV )
+            Vt = bidiag.getV(Vt,true,compact);
+
+        // set up the qr algorithm, reusing the previous extraction
+        if( transposed )
+            qralg.initParam(numCols,numRows);
+        else
+            qralg.initParam(numRows,numCols);
+        diagOld = qralg.swapDiag(diagOld);
+        offOld = qralg.swapOff(offOld);
+        // set it up to compute both U and V matrices
+        qralg.setFastValues(false);
+        if( computeU )
+            qralg.setUt(Ut);
+        if( computeV )
+            qralg.setVt(Vt);
+
+        CommonOps.setIdentity(Ut);
+        CommonOps.setIdentity(Vt);
+
+        long pointB = System.currentTimeMillis();
+
+        if( !qralg.process(diagOld) )
+            return true;
+
+        long pointC = System.currentTimeMillis();
+
+        System.out.println("  bidiag UV "+(pointB-pointA)+" qr UV "+(pointC-pointB));
+
+        if( transposed ) {
+            DenseMatrix64F temp = Vt;
+            Vt = Ut;
+            Ut = temp;
+        }
+        return false;
+    }
+
+    private boolean computeSingularValues(DenseMatrix64F orig, boolean transposed) {
+        long pointA = System.currentTimeMillis();
+
+        // change the matrix to bidiagonal form
+        if (bidiagonalization(orig, transposed))
+            return false;
+
+        long pointB = System.currentTimeMillis();
+
+        // compute singular values
+        bidiag.getDiagonal(diag,off);
+        qralg.setMatrix(numRows,numCols,diag,off);
+
+        // copy the diagonal elements
+        // this way it doesn't need to be copied twice and will slightly speed it up
+        System.arraycopy(diag,0, diagOld,0,smallSide);
+        System.arraycopy(off,0, offOld,0,smallSide-1);
+
+        qralg.setFastValues(true);
+        qralg.setUt(null);
+        qralg.setVt(null);
+
+        boolean ret = !qralg.process();
+
+        long pointC = System.currentTimeMillis();
+        System.out.println("  bidiag "+(pointB-pointA)+" qr W "+(pointC-pointB));
+
+        return ret;
+    }
+
+    private boolean bidiagonalization(DenseMatrix64F orig, boolean transposed) {
+        if( transposed ) {
+            A_mod.reshape(orig.numCols,orig.numRows,false);
+            CommonOps.transpose(orig,A_mod);
+        } else {
+            A_mod.reshape(orig.numRows,orig.numCols,false);
+            A_mod.set(orig);
+        }
+        if( !bidiag.decompose(A_mod) )
+            return true;
+        return false;
+    }
+
+    /**
+     * With the QR algorithm it is possible for the found singular values to be native.  This
+     * makes them all positive by multiplying it by a diagonal matrix that has
+     */
+    private void makeSingularPositive() {
+        numSingular = qralg.getNumberOfSingularValues();
+        singularValues = qralg.getSingularValues();
+
+        for( int i = 0; i < numSingular; i++ ) {
+            double val = singularValues[i];
+
+            if( val < 0 ) {
+                singularValues[i] = -val;
+
+                if( computeU ) {
+                    // compute the results of multiplying it by an element of -1 at this location in
+                    // a diagonal matrix.
+                    int start = i* Ut.numCols;
+                    int stop = start+ Ut.numCols;
+
+                    for( int j = start; j < stop; j++ ) {
+                        Ut.data[j] = -Ut.data[j];
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public int numRows() {
+        return numRows;
+    }
+
+    @Override
+    public int numCols() {
+        return numCols;
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/src/org/ejml/alg/dense/misc/NaiveDeterminant.java b/main/experimental/src/org/ejml/alg/dense/misc/NaiveDeterminant.java
new file mode 100644
index 0000000..6199b7f
--- /dev/null
+++ b/main/experimental/src/org/ejml/alg/dense/misc/NaiveDeterminant.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.misc;
+
+import org.ejml.data.DenseMatrix64F;
+
+
+/**
+ * Computes the determinant using different very simple and computationally expensive algorithms.
+ *
+ * @author Peter Abeles
+ */
+public class NaiveDeterminant {
+
+    /**
+     * <p>
+     * Computes the determinant of the matrix using Leibniz's formula
+     * </p>
+     *
+     * <p>
+     * A direct implementation of Leibniz determinant equation.  This is of little practical use
+     * because of its slow runtime of O(n!) where n is the width of the matrix. LU decomposition
+     * should be used instead.  One advantage of Leibniz's equation is how simplistic it is.
+     * </p>
+     * <p>
+     * det(A) = Sum( σ in S<sub>n</sub> ; sgn(σ) Prod( i = 1 to n ; a<sub>i,σ(i)</sub>) )<br>
+     * </p>
+     * <ul>
+     * <li>sgn is the sign function of permutations. +1 or -1 for even and odd permutations</li>
+     * <li>a set of permutations. if n=3 then the possible permutations are (1,2,3) (1,3,2), (3,2,1), ... etc</li>
+     * </ul>
+
+     *
+     * @param mat The matrix whose determinant is computed.
+     * @return The value of the determinant
+     */
+    public static double leibniz( DenseMatrix64F mat )
+    {
+        PermuteArray perm = new PermuteArray( mat.numCols );
+
+        double total = 0;
+
+        int p[] = perm.next();
+
+        while( p != null ) {
+
+            double prod = 1;
+
+            for( int i = 0; i < mat.numRows; i++ ) {
+                prod *= mat.get(i,p[i]);
+            }
+
+            total += perm.sgn()*prod;
+            p = perm.next();
+        }
+
+        return total;
+    }
+
+    /**
+     * <p>
+     * A simple and inefficient algorithm for computing the determinant. This should never be used.
+     * It is at least two orders of magnitude slower than {@link DeterminantFromMinor}. This is included
+     * to provide a point of comparison for other algorithms.
+     * </p>
+     * @param mat The matrix that the determinant is to be computed from
+     * @return The determinant.
+     */
+    public static double recursive( DenseMatrix64F mat )
+    {
+        if(mat.numRows == 1) {
+            return mat.get(0);
+        } else if(mat.numRows == 2) {
+            return mat.get(0) * mat.get(3) - mat.get(1) * mat.get(2);
+        } else if( mat.numRows == 3 ) {
+            return UnrolledDeterminantFromMinor.det3(mat);
+        }
+
+        double result = 0;
+
+        for(int i = 0; i < mat.numRows; i++) {
+            DenseMatrix64F minorMat = new DenseMatrix64F(mat.numRows-1,mat.numRows-1);
+
+            for(int j = 1; j < mat.numRows; j++) {
+                for(int k = 0; k < mat.numRows; k++) {
+
+                    if(k < i) {
+                        minorMat.set(j-1,k,mat.get(j,k));
+                    } else if(k > i) {
+                        minorMat.set(j-1,k-1,mat.get(j,k));
+                    }
+                }
+            }
+
+            if( i % 2 == 0 )
+                result += mat.get(0,i) * recursive(minorMat);
+            else
+                result -= mat.get(0,i) * recursive(minorMat);
+
+        }
+
+        return result;
+    }
+}
diff --git a/main/experimental/src/org/ejml/alg/dense/misc/PermuteArray.java b/main/experimental/src/org/ejml/alg/dense/misc/PermuteArray.java
new file mode 100644
index 0000000..3db44d2
--- /dev/null
+++ b/main/experimental/src/org/ejml/alg/dense/misc/PermuteArray.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.misc;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * Generates a permutations of an integer set from 0 to N-1.  This can either be generated
+ * all at once as a list of one at a time.
+ *
+ * @author Peter Abeles
+ */
+public class PermuteArray {
+
+    // used by next
+    private int level;
+
+    private int data[];
+    private int iter[];
+    private int valk[];
+    private int ret[];
+
+    public PermuteArray( int N ) {
+        level = 0;
+        iter = new int[ N ];
+        valk = new int[ N ];
+        data = new int[ N ];
+        ret = new int[ N ];
+        for( int i = 0; i < data.length; i++ ) {
+            data[i] = -1;
+        }
+    }
+
+    /**
+     * <p>
+     * Returns signature of the permutation.  This is the sgn() operator and returns
+     * -1 or 1 depending on it being odd or even.
+     * <br>
+     * sgn(σ) = ( − 1)<sup>m</sup><br>
+     * <br>
+     * where m is the number of inversions.
+     * </p>
+     * <p>
+     * NOTE: This implementation is painfully slow O(N!).  There is probably another algorithm out there
+     * which is much faster.
+     * </p>
+     *
+     * @return -1 or 1 for odd or even permutations.
+     */
+    public int sgn() {
+        // Is there a way to compute the parity while performing the permutations
+        // making this much less expensive
+        int total = 0;
+
+        for( int i = 0; i < ret.length; i++ ) {
+            int val = ret[i];
+
+            for( int j = i+1; j < ret.length; j++ ) {
+                if( val > ret[j] ) {
+                    total++;
+                }
+            }
+        }
+
+        if( total % 2 == 1 )
+            return -1;
+        return 1;
+    }
+
+    /**
+     * Computes N factorial
+     */
+    public static int fact( int N ) {
+        int ret = 1;
+
+        while( N > 0 ) {
+            ret *= N--;
+        }
+
+        return ret;
+    }
+
+    /**
+     * Creates a list of all permutations for a set with N elements.
+     *
+     * @param N Number of elements in the list being permuted.
+     * @return A list containing all the permutations.
+     */
+    public static List<int[]> createList( int N )
+    {
+        int data[] = new int[ N ];
+        for( int i = 0; i < data.length; i++ ) {
+            data[i] = -1;
+        }
+
+        List<int[]> ret = new ArrayList<int[]>();
+
+        createList(data,0,-1,ret);
+
+        return ret;
+    }
+
+
+    /**
+     * Internal function that uses recursion to create the list
+     */
+    private static void createList( int data[], int k , int level , List<int[]> ret )
+    {
+        data[k] = level;
+
+        if( level < data.length-1 ) {
+            for( int i = 0; i < data.length; i++ ) {
+                if( data[i] == -1 ) {
+                    createList(data,i,level+1,ret);
+                }
+            }
+        } else {
+            int []copy = new int[data.length];
+            System.arraycopy(data,0,copy,0,data.length);
+            ret.add(copy);
+        }
+        data[k] = -1;
+    }
+
+    /**
+     * Creates the next permutation in the sequence.
+     *
+     * @return An array containing the permutation.  The returned array is modified each time this function is called.
+     */
+    public int[] next()
+    {
+        boolean hasNewPerm = false;
+
+        escape:while( level >= 0) {
+//            boolean foundZero = false;
+            for( int i = iter[level]; i < data.length; i = iter[level] ) {
+                iter[level]++;
+
+                if( data[i] == -1 ) {
+                    level++;
+                    data[i] = level-1;
+
+                    if( level >= data.length ) {
+                        // a new permutation has been created return the results.
+                        hasNewPerm = true;
+                        System.arraycopy(data,0,ret,0,ret.length);
+                        level = level-1;
+                        data[i] = -1;
+                        break escape;
+                    } else {
+                        valk[level] = i;
+                    }
+                }
+            }
+
+            data[valk[level]] = -1;
+            iter[level] = 0;
+            level = level-1;
+
+        }
+
+        if( hasNewPerm )
+            return ret;
+        return null;
+    }
+}
diff --git a/main/experimental/src/org/ejml/alg/dense/mult/MatrixMultQuad.java b/main/experimental/src/org/ejml/alg/dense/mult/MatrixMultQuad.java
new file mode 100644
index 0000000..b3ea3c9
--- /dev/null
+++ b/main/experimental/src/org/ejml/alg/dense/mult/MatrixMultQuad.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.mult;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+
+/**
+ * @author Peter Abeles
+ */
+public class MatrixMultQuad {
+    /**
+     * <p>
+     * Performs matrix multiplication on an equation in quadratic form with a transpose on the second A:<br>
+     * <br>
+     * out = A*B*A<sup>T</sup>
+     * </p>
+     * @param A Left and right matrix.
+     * @param B Middle square matrix.  Size = (A.numCols,A.numCols)
+     * @param out Output matrix.  Size = (A.numRows,A.numRows);
+     */
+    public static void multQuad1( DenseMatrix64F A , DenseMatrix64F B , DenseMatrix64F out ) {
+
+        if( A.numCols != B.numCols || A.numCols != B.numRows
+                || A.numRows != out.numRows || A.numRows != out.numCols )
+            throw new IllegalArgumentException("Incompatible matrix shapes");
+
+        CommonOps.fill(out, 0);
+
+        for (int i = 0; i < A.numRows; i++) {
+            for (int j = 0; j < A.numCols; j++) {
+                double total = 0;
+
+                int indexA = i*A.numCols;
+                int indexB = j;
+                int end = indexA + A.numCols;
+                for (; indexA < end; indexA++ , indexB += B.numCols ) {
+//                for (int k = 0; k < A.numCols; k++) {
+//                    total += A.get(i,k)*B.get(k,j);
+                    total += A.data[indexA] * B.data[indexB];
+                }
+
+                int indexOut = i*out.numCols;
+                indexA = j;
+                end = indexOut + A.numRows;
+                for (; indexOut < end; indexOut++, indexA += A.numCols) {
+//                for (int l = 0; l < A.numRows; l++) {
+//                    out.data[ out.getIndex(i,l) ] += total*A.get(l,j);
+                    out.data[ indexOut] += total*A.data[indexA];
+                }
+            }
+        }
+    }
+}
diff --git a/main/experimental/src/org/ejml/alg/densed2/mult/MatrixMatrixMult_D2.java b/main/experimental/src/org/ejml/alg/densed2/mult/MatrixMatrixMult_D2.java
new file mode 100644
index 0000000..1a0695f
--- /dev/null
+++ b/main/experimental/src/org/ejml/alg/densed2/mult/MatrixMatrixMult_D2.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.densed2.mult;
+
+import org.ejml.data.DenseD2Matrix64F;
+import org.ejml.ops.MatrixDimensionException;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class MatrixMatrixMult_D2 {
+
+    /**
+     * @see org.ejml.ops.CommonOps#mult(org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void mult_small( DenseD2Matrix64F a , DenseD2Matrix64F b , DenseD2Matrix64F c )
+    {
+        if( a.numCols != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numRows != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        double dataA[][] = a.data;
+        double dataB[][] = b.data;
+        double dataR[][] = c.data;
+
+
+        for( int i = 0; i < a.numRows; i++ ) {
+            double dataAi[] = dataA[i];
+            double dataRi[] = dataR[i];
+
+            for( int j = 0; j < b.numCols; j++ ) {
+                double total = 0;
+
+                for( int k = 0; k < a.numCols; k++ ) {
+                    total += dataAi[k] * dataB[k][j];
+                }
+
+                dataRi[j] = total;
+            }
+        }
+    }
+
+    /**
+     * @see org.ejml.ops.CommonOps#mult(org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F, org.ejml.data.RowD1Matrix64F)
+     */
+    public static void mult_aux( DenseD2Matrix64F a , DenseD2Matrix64F b , DenseD2Matrix64F c , double []aux )
+    {
+        if( a.numCols != b.numRows ) {
+            throw new MatrixDimensionException("The 'a' and 'b' matrices do not have compatible dimensions");
+        } else if( a.numRows != c.numRows || b.numCols != c.numCols ) {
+            throw new MatrixDimensionException("The results matrix does not have the desired dimensions");
+        }
+
+        if( aux == null ) aux = new double[ b.numRows ];
+
+        double dataA[][] = a.data;
+        double dataB[][] = b.data;
+        double dataR[][] = c.data;
+
+        for( int j = 0; j < b.numCols; j++ ) {
+            // create a copy of the column in B to avoid cache issues
+            for( int k = 0; k < b.numRows; k++ ) {
+                aux[k] = dataB[k][j];
+            }
+
+            for( int i = 0; i < a.numRows; i++ ) {
+                double dataAi[] = dataA[i];
+
+                double total = 0;
+                for( int k = 0; k < b.numRows; ) {
+                    total += dataAi[k]*aux[k++];
+                }
+                dataR[i][j] = total;
+            }
+        }
+    }
+}
diff --git a/main/experimental/src/org/ejml/data/BlockD3Matrix64F.java b/main/experimental/src/org/ejml/data/BlockD3Matrix64F.java
new file mode 100644
index 0000000..a2dcc01
--- /dev/null
+++ b/main/experimental/src/org/ejml/data/BlockD3Matrix64F.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import org.ejml.EjmlParameters;
+import org.ejml.ops.MatrixIO;
+
+
+/**
+ * Row-major block matrix declared using 3D array.
+ *
+ * @author Peter Abeles
+ */
+public class BlockD3Matrix64F implements ReshapeMatrix, RealMatrix64F {
+    public int blockLength;
+    public double[][][] blocks;
+
+    /**
+     * Number of rows in the matrix.
+     */
+    public int numRows;
+    /**
+     * Number of columns in the matrix.
+     */
+    public int numCols;
+
+    public BlockD3Matrix64F( int numRows , int numCols , int blockLength)
+    {
+        this.blockLength = blockLength;
+
+        reshape(numRows,numCols);
+    }
+
+    public BlockD3Matrix64F( int numRows , int numCols )
+    {
+        this(numRows,numCols, EjmlParameters.BLOCK_WIDTH);
+    }
+
+
+    public double[][][] getData() {
+        return blocks;
+    }
+
+    @Override
+    public void reshape(int numRows, int numCols )
+    {
+        this.numRows = numRows;
+        this.numCols = numCols;
+
+
+        int blockM = numRows / blockLength;
+        int blockN = numCols / blockLength;
+
+        if( numRows % blockLength >  0) blockM++;
+        if( numCols % blockLength >  0) blockN++;
+
+        this.blocks = new double[blockM][blockN][];
+
+        for( int i = 0; i < numRows; i += blockLength ) {
+            int ii = i/blockLength;
+
+            for( int j = 0; j < numCols; j += blockLength ) {
+                int jj = j/blockLength;
+
+                blocks[ii][jj] = new double[ blockLength*blockLength ];
+            }
+        }
+    }
+
+    @Override
+    public double get( int row, int col) {
+        int blockM = row / blockLength;
+        int blockN = col / blockLength;
+
+        int m = row % blockLength;
+        int n = col % blockLength;
+
+        int index = m*blockLength + n;
+
+        return blocks[blockM][blockN][index];
+    }
+
+    @Override
+    public void set( int row, int col, double val) {
+        int blockM = row / blockLength;
+        int blockN = col / blockLength;
+
+        int m = row % blockLength;
+        int n = col % blockLength;
+
+        int index = m*blockLength + n;
+
+        blocks[blockM][blockN][index] = val;
+    }
+
+    @Override
+    public double unsafe_get( int row, int col) {
+        return get(row,col);
+    }
+
+    @Override
+    public void unsafe_set( int row, int col, double val) {
+        set(row,col,val);
+    }
+
+    @Override
+    public int getNumRows() {
+        return numRows;
+    }
+
+    @Override
+    public int getNumCols() {
+        return numCols;
+    }
+
+    @Override
+    public int getNumElements() {
+        return numRows*numCols;
+    }
+
+    @Override
+    public void print() {
+        MatrixIO.print(System.out,this);
+    }
+
+    @Override
+    public <T extends Matrix> T copy() {
+        return null;
+    }
+
+    @Override
+    public void set(Matrix original) {
+        throw new RuntimeException("Not supported yet");
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/src/org/ejml/data/DenseD2Matrix64F.java b/main/experimental/src/org/ejml/data/DenseD2Matrix64F.java
new file mode 100644
index 0000000..2b4cb62
--- /dev/null
+++ b/main/experimental/src/org/ejml/data/DenseD2Matrix64F.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import java.io.Serializable;
+
+
+/**
+ * A dense matrix where the array is stored as a 2D array.
+ *
+ * @author Peter Abeles
+ */
+public class DenseD2Matrix64F implements Serializable, ReshapeMatrix, RealMatrix64F {
+
+    /**
+     * Where the raw data for the matrix is stored.  The format is type dependent.
+     */
+    public double[][] data;
+
+    /**
+     * Number of rows in the matrix.
+     */
+    public int numRows;
+    /**
+     * Number of columns in the matrix.
+     */
+    public int numCols;
+
+    public DenseD2Matrix64F( int numRows , int numCols ) {
+        data = new double[ numRows ][numCols];
+        this.numRows = numRows;
+        this.numCols = numCols;
+    }
+
+//    public double[][] getData() {
+//        return data;
+//    }
+
+    @Override
+    public void reshape(int numRows, int numCols ) {
+        if( numRows <= data.length ) {
+            this.numRows = numRows;
+        } else {
+            throw new IllegalArgumentException("Requested number of rows is too great.");
+        }
+
+        if( numCols <= data[0].length ) {
+            this.numCols = numCols;
+        } else {
+            throw new IllegalArgumentException("Requested number of columns is too great.");
+        }
+    }
+
+    @Override
+    public double get(int row, int col) {
+        return data[row][col];
+    }
+
+    @Override
+    public void set(int row, int col, double val) {
+        data[row][col] = val;
+    }
+
+    @Override
+    public double unsafe_get( int row, int col) {
+        return get(row,col);
+    }
+
+    @Override
+    public void unsafe_set( int row, int col, double val) {
+        set(row,col,val);
+    }
+
+    @Override
+    public int getNumElements() {
+        return numRows*numCols;
+    }
+
+    @Override
+    public int getNumRows() {
+        return numRows;
+    }
+
+    @Override
+    public int getNumCols() {
+        return numCols;
+    }
+
+    @Override
+    public void print() {
+    }
+
+    @Override
+    public <T extends Matrix> T copy() {
+        return null;
+    }
+
+    @Override
+    public void set(Matrix original) {
+        throw new RuntimeException("Not yet supported");
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/test/org/ejml/alg/blockd3/TestBlockD3MatrixOps.java b/main/experimental/test/org/ejml/alg/blockd3/TestBlockD3MatrixOps.java
new file mode 100644
index 0000000..197c770
--- /dev/null
+++ b/main/experimental/test/org/ejml/alg/blockd3/TestBlockD3MatrixOps.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.blockd3;
+
+import org.ejml.alg.generic.GenericMatrixOps;
+import org.ejml.data.BlockD3Matrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestBlockD3MatrixOps {
+    final static int BLOCK_LENGTH = 10;
+
+    Random rand = new Random(234);
+
+    @Test
+    public void convert_dense_to_block() {
+        checkConvert_dense_to_block(10,10);
+        checkConvert_dense_to_block(5,8);
+        checkConvert_dense_to_block(12,16);
+        checkConvert_dense_to_block(16,12);
+        checkConvert_dense_to_block(21,27);
+        checkConvert_dense_to_block(28,5);
+        checkConvert_dense_to_block(5,28);
+        checkConvert_dense_to_block(20,20);
+    }
+
+    private void checkConvert_dense_to_block( int m , int n ) {
+        DenseMatrix64F A = RandomMatrices.createRandom(m,n,rand);
+        BlockD3Matrix64F B = new BlockD3Matrix64F(A.numRows,A.numCols,BLOCK_LENGTH);
+
+        BlockD3MatrixOps.convert(A,B);
+
+        assertTrue( GenericMatrixOps.isEquivalent(A,B,1e-8));
+    }
+
+    @Test
+    public void convert_block_to_dense() {
+        checkBlockToDense(10,10);
+        checkBlockToDense(5,8);
+        checkBlockToDense(12,16);
+        checkBlockToDense(16,12);
+        checkBlockToDense(21,27);
+        checkBlockToDense(28,5);
+        checkBlockToDense(5,28);
+        checkBlockToDense(20,20);
+    }
+
+    private void checkBlockToDense( int m , int n ) {
+        DenseMatrix64F A = new DenseMatrix64F(m,n);
+        BlockD3Matrix64F B = BlockD3MatrixOps.random(m,n,-1,1,rand,BLOCK_LENGTH);
+
+        BlockD3MatrixOps.convert(B,A);
+
+        assertTrue( GenericMatrixOps.isEquivalent(A,B,1e-8));
+    }
+
+        @Test
+    public void mult() {
+        // trivial case
+        checkMult(BLOCK_LENGTH, BLOCK_LENGTH, BLOCK_LENGTH);
+
+        // stuff larger than the block size
+        checkMult(BLOCK_LENGTH+1, BLOCK_LENGTH, BLOCK_LENGTH);
+        checkMult(BLOCK_LENGTH, BLOCK_LENGTH+1, BLOCK_LENGTH);
+        checkMult(BLOCK_LENGTH, BLOCK_LENGTH, BLOCK_LENGTH+1);
+        checkMult(BLOCK_LENGTH+1, BLOCK_LENGTH+1, BLOCK_LENGTH+1);
+
+        // stuff smaller than the block size
+        checkMult(BLOCK_LENGTH-1, BLOCK_LENGTH, BLOCK_LENGTH);
+        checkMult(BLOCK_LENGTH, BLOCK_LENGTH-1, BLOCK_LENGTH);
+        checkMult(BLOCK_LENGTH, BLOCK_LENGTH, BLOCK_LENGTH-1);
+        checkMult(BLOCK_LENGTH-1, BLOCK_LENGTH-1, BLOCK_LENGTH-1);
+
+        // stuff multiple blocks
+        checkMult(BLOCK_LENGTH*2, BLOCK_LENGTH, BLOCK_LENGTH);
+        checkMult(BLOCK_LENGTH, BLOCK_LENGTH*2, BLOCK_LENGTH);
+        checkMult(BLOCK_LENGTH, BLOCK_LENGTH, BLOCK_LENGTH*2);
+        checkMult(BLOCK_LENGTH*2, BLOCK_LENGTH*2, BLOCK_LENGTH*2);
+        checkMult(BLOCK_LENGTH*2+4, BLOCK_LENGTH*2+3, BLOCK_LENGTH*2+2);
+
+    }
+
+    private void checkMult(int m, int n, int o) {
+        DenseMatrix64F A_d = RandomMatrices.createRandom(m, n,rand);
+        DenseMatrix64F B_d = RandomMatrices.createRandom(n, o,rand);
+        DenseMatrix64F C_d = new DenseMatrix64F(m, o);
+
+        BlockD3Matrix64F A_b = BlockD3MatrixOps.convert(A_d,BLOCK_LENGTH);
+        BlockD3Matrix64F B_b = BlockD3MatrixOps.convert(B_d,BLOCK_LENGTH);
+        BlockD3Matrix64F C_b = BlockD3MatrixOps.random(m, o, -1 , 1 , rand , BLOCK_LENGTH);
+
+        CommonOps.mult(A_d,B_d,C_d);
+        BlockD3MatrixOps.mult(A_b,B_b,C_b);
+
+        assertTrue( GenericMatrixOps.isEquivalent(C_d,C_b,1e-8));
+    }
+}
diff --git a/main/experimental/test/org/ejml/alg/dense/decompose/bidiagonal/TestBidiagonalDecompositionNaive_D64.java b/main/experimental/test/org/ejml/alg/dense/decompose/bidiagonal/TestBidiagonalDecompositionNaive_D64.java
new file mode 100644
index 0000000..d67d6e0
--- /dev/null
+++ b/main/experimental/test/org/ejml/alg/dense/decompose/bidiagonal/TestBidiagonalDecompositionNaive_D64.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decompose.bidiagonal;
+
+import org.ejml.alg.dense.decomposition.bidiagonal.BidiagonalDecompositionNaive_D64;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.MatrixFeatures;
+import org.ejml.ops.RandomMatrices;
+import org.ejml.simple.SimpleMatrix;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestBidiagonalDecompositionNaive_D64 {
+
+    Random rand = new Random(6455);
+
+    @Test
+    public void testItAll() {
+
+        checkAgainstRandom(7, 5);
+        checkAgainstRandom(5, 7);
+        checkAgainstRandom(2, 3);
+    }
+
+    private void checkAgainstRandom(int m, int n) {
+        SimpleMatrix A = SimpleMatrix.wrap(RandomMatrices.createRandom(m,n,rand));
+
+        BidiagonalDecompositionNaive_D64 decomp = new BidiagonalDecompositionNaive_D64();
+
+        assertTrue(decomp.decompose(A.getMatrix()));
+
+        SimpleMatrix U = decomp.getU();
+        SimpleMatrix B = decomp.getB();
+        SimpleMatrix V = decomp.getV();
+
+//        U.print();
+//        B.print();
+//        V.print();
+
+//        U.mult(A).mult(V).print();
+
+        // check the decomposition
+        DenseMatrix64F foundA = U.mult(B).mult(V.transpose()).getMatrix();
+
+//        A.print();
+//        foundA.print();
+
+        assertTrue(MatrixFeatures.isIdentical(A.getMatrix(), foundA, 1e-8));
+    }
+
+}
\ No newline at end of file
diff --git a/main/experimental/test/org/ejml/alg/dense/decompose/lu/TestLUDecompositionNR_CD64.java b/main/experimental/test/org/ejml/alg/dense/decompose/lu/TestLUDecompositionNR_CD64.java
new file mode 100644
index 0000000..b0c447c
--- /dev/null
+++ b/main/experimental/test/org/ejml/alg/dense/decompose/lu/TestLUDecompositionNR_CD64.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2009-2015, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decompose.lu;
+
+import org.ejml.alg.dense.decomposition.lu.LUDecompositionNR_CD64;
+
+/**
+ * @author Peter Abeles
+ */
+public class TestLUDecompositionNR_CD64 extends GeneralLuDecompositionChecks_CD64 {
+    @Override
+    public LUDecompositionBase_CD64 create(int numRows, int numCols) {
+        return new LUDecompositionNR_CD64();
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/test/org/ejml/alg/dense/decompose/lu/TestLUDecompositionNR_D64.java b/main/experimental/test/org/ejml/alg/dense/decompose/lu/TestLUDecompositionNR_D64.java
new file mode 100644
index 0000000..b45a349
--- /dev/null
+++ b/main/experimental/test/org/ejml/alg/dense/decompose/lu/TestLUDecompositionNR_D64.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.decompose.lu;
+
+import org.ejml.alg.dense.decomposition.lu.GeneralLuDecompositionChecks;
+import org.ejml.alg.dense.decomposition.lu.LUDecompositionBase_D64;
+import org.ejml.alg.dense.decomposition.lu.LUDecompositionNR_D64;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestLUDecompositionNR_D64 extends GeneralLuDecompositionChecks {
+    @Override
+    public LUDecompositionBase_D64 create(int numRows, int numCols) {
+        return new LUDecompositionNR_D64();
+    }
+}
\ No newline at end of file
diff --git a/main/experimental/test/org/ejml/alg/dense/misc/TestNaiveDeterminant.java b/main/experimental/test/org/ejml/alg/dense/misc/TestNaiveDeterminant.java
new file mode 100644
index 0000000..4974160
--- /dev/null
+++ b/main/experimental/test/org/ejml/alg/dense/misc/TestNaiveDeterminant.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.misc;
+
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestNaiveDeterminant {
+
+
+    @Test
+    public void detRecursive() {
+        double[] d = new double[]{5 ,-2 ,-4 ,0.5, 0.1, 91, 8, 66, 1, -2, 10, -4, -0.2, 7, -4, 0.8};
+
+        DenseMatrix64F mat = new DenseMatrix64F(4,4, true, d);
+
+        double val = NaiveDeterminant.recursive(mat);
+
+        assertEquals(-27288.86,val,1e-6);
+    }
+
+    /**
+     * Compares this formuation to the naive recursive formulation
+     */
+    @Test
+    public void det() {
+        Random rand = new Random(0xff);
+
+        for( int i = 1; i <= 5; i++ ) {
+            DenseMatrix64F A = RandomMatrices.createRandom(i,i,rand);
+
+            double expected = NaiveDeterminant.recursive(A);
+            double found = NaiveDeterminant.leibniz(A);
+
+            assertEquals(expected,found,1e-8);
+        }
+    }
+}
diff --git a/main/experimental/test/org/ejml/alg/dense/misc/TestPermuteArray.java b/main/experimental/test/org/ejml/alg/dense/misc/TestPermuteArray.java
new file mode 100644
index 0000000..56aa437
--- /dev/null
+++ b/main/experimental/test/org/ejml/alg/dense/misc/TestPermuteArray.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.alg.dense.misc;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestPermuteArray {
+
+    int fact( int N ) {
+        int ret = 1;
+
+        while( N > 0 ) {
+            ret *= N--;
+        }
+
+        return ret;
+    }
+
+    /**
+     * Sees if the expected number of permutations are created and that they are all
+     * unique
+     */
+    @Test
+    public void permuteList() {
+        int N = 4;
+
+        List<int[]> perms = PermuteArray.createList(N);
+
+        checkPermutationList(N, perms);
+    }
+
+    private void checkPermutationList(int n, List<int[]> perms) {
+        assertEquals(PermuteArray.fact(n),perms.size());
+
+        // make sure each permutation in the list is unique
+        for( int i = 0; i < perms.size(); i++ ) {
+            int a[] = perms.get(i);
+
+            assertEquals(4,a.length);
+
+            for( int j = i+1; j < perms.size(); j++ ) {
+                int b[] = perms.get(j);
+
+                boolean identical = true;
+                for( int k = 0; k < n; k++ ) {
+                    if( a[k] != b[k] ) {
+                        identical = false;
+                        break;
+                    }
+                }
+                assertFalse(identical);
+            }
+        }
+    }
+
+
+    @Test
+    public void next() {
+        // create a list of all the permutations
+        PermuteArray alg = new PermuteArray(4);
+
+        List<int[]> perms = new ArrayList<int[]>();
+        for(;;) {
+            int d[] = alg.next();
+            if( d == null )
+                break;
+
+            perms.add(d.clone());
+        }
+
+        // see if the list is correct
+        checkPermutationList(4, perms);
+    }
+}
diff --git a/main/experimental/test/org/ejml/data/TestBlockD3Matrix64F.java b/main/experimental/test/org/ejml/data/TestBlockD3Matrix64F.java
new file mode 100644
index 0000000..b573e22
--- /dev/null
+++ b/main/experimental/test/org/ejml/data/TestBlockD3Matrix64F.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import org.junit.Test;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestBlockD3Matrix64F {
+
+    @Test
+    public void testGeneric() {
+        GenericTestsMatrix64F g;
+        g = new GenericTestsMatrix64F() {
+            protected RealMatrix64F createMatrix(int numRows, int numCols) {
+                return new BlockD3Matrix64F(numRows,numCols,10);
+            }
+        };
+
+        g.allTests();
+    }
+
+}
\ No newline at end of file
diff --git a/main/experimental/test/org/ejml/data/TestDenseD2Matrix64F.java b/main/experimental/test/org/ejml/data/TestDenseD2Matrix64F.java
new file mode 100644
index 0000000..c20da19
--- /dev/null
+++ b/main/experimental/test/org/ejml/data/TestDenseD2Matrix64F.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.data;
+
+import org.junit.Test;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestDenseD2Matrix64F {
+
+    @Test
+    public void testGeneric() {
+        GenericTestsMatrix64F g;
+        g = new GenericTestsMatrix64F() {
+            protected RealMatrix64F createMatrix(int numRows, int numCols) {
+                return new DenseD2Matrix64F(numRows,numCols);
+            }
+        };
+
+        g.allTests();
+    }
+
+
+
+}
\ No newline at end of file
diff --git a/main/simple/EJML Simple.iml b/main/simple/EJML Simple.iml
new file mode 100644
index 0000000..c5f7973
--- /dev/null
+++ b/main/simple/EJML Simple.iml	
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module external.linked.project.id=":main:simple" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="org.ejml" external.system.module.version="0.27" type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="false">
+    <output url="file://$MODULE_DIR$/build/classes/main" />
+    <output-test url="file://$MODULE_DIR$/build/classes/test" />
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/benchmarks/src" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/benchmarks/test" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/generate" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/resources/src" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/resources/test" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
+      <excludeFolder url="file://$MODULE_DIR$/.gradle" />
+      <excludeFolder url="file://$MODULE_DIR$/build" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="module" module-name="EJML Core" exported="" />
+    <orderEntry type="module" module-name="EJML Dense 64" exported="" />
+    <orderEntry type="module" module-name="EJML Dense C64" exported="" />
+    <orderEntry type="module" module-name="EJML Experimental" scope="TEST" />
+    <orderEntry type="library" scope="TEST" name="Gradle: junit:junit:4.11" level="project" />
+    <orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-core:1.3" level="project" />
+  </component>
+</module>
\ No newline at end of file
diff --git a/main/simple/build.gradle b/main/simple/build.gradle
new file mode 100644
index 0000000..cfc2196
--- /dev/null
+++ b/main/simple/build.gradle
@@ -0,0 +1,11 @@
+dependencies {
+    compile project(':main:core')
+    compile project(':main:dense64')
+    testCompile project(':main:experimental')
+}
+
+idea {
+    module {
+        name = "EJML Simple"
+    }
+}
\ No newline at end of file
diff --git a/main/simple/src/org/ejml/simple/SimpleBase.java b/main/simple/src/org/ejml/simple/SimpleBase.java
new file mode 100644
index 0000000..b141b28
--- /dev/null
+++ b/main/simple/src/org/ejml/simple/SimpleBase.java
@@ -0,0 +1,1125 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.simple;
+
+import org.ejml.UtilEjml;
+import org.ejml.alg.dense.mult.VectorVectorMult;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.data.MatrixIterator64F;
+import org.ejml.data.RealMatrix64F;
+import org.ejml.factory.SingularMatrixException;
+import org.ejml.ops.*;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.Serializable;
+
+
+/**
+ * Parent of {@link SimpleMatrix} implements all the standard matrix operations and uses
+ * generics to allow the returned matrix type to be changed.  This class should be extended
+ * instead of SimpleMatrix.
+ *
+ * @author Peter Abeles
+ */
+ at SuppressWarnings({"unchecked"})
+public abstract class SimpleBase <T extends SimpleBase> implements Serializable {
+
+    /**
+     * Internal matrix which this is a wrapper around.
+     */
+    protected DenseMatrix64F mat;
+
+    public SimpleBase( int numRows , int numCols ) {
+        mat = new DenseMatrix64F(numRows, numCols);
+    }
+
+    protected SimpleBase() {
+    }
+
+    /**
+     * Used internally for creating new instances of SimpleMatrix.  If SimpleMatrix is extended
+     * by another class this function should be overridden so that the returned matrices are
+     * of the correct type.
+     *
+     * @param numRows number of rows in the new matrix.
+     * @param numCols number of columns in the new matrix.
+     * @return A new matrix.
+     */
+    protected abstract T createMatrix( int numRows , int numCols );
+
+    /**
+     * <p>
+     * Returns a reference to the matrix that it uses internally.  This is useful
+     * when an operation is needed that is not provided by this class.
+     * </p>
+     *
+     * @return Reference to the internal DenseMatrix64F.
+     */
+    public DenseMatrix64F getMatrix() {
+        return mat;
+    }
+
+    /**
+     * <p>
+     * Returns the transpose of this matrix.<br>
+     * a<sup>T</sup>
+     * </p>
+     *
+     * @see org.ejml.ops.CommonOps#transpose(DenseMatrix64F,DenseMatrix64F)
+     *
+     * @return A matrix that is n by m.
+     */
+    public T transpose() {
+        T ret = createMatrix(mat.numCols,mat.numRows);
+
+        CommonOps.transpose(mat,ret.getMatrix());
+
+        return ret;
+    }
+
+    /**
+     * <p>
+     * Returns a matrix which is the result of matrix multiplication:<br>
+     * <br>
+     * c = a * b <br>
+     * <br>
+     * where c is the returned matrix, a is this matrix, and b is the passed in matrix.
+     * </p>
+     *
+     * @see CommonOps#mult(org.ejml.data.RowD1Matrix64F , org.ejml.data.RowD1Matrix64F , org.ejml.data.RowD1Matrix64F)
+     *
+     * @param b A matrix that is n by bn. Not modified.
+     *
+     * @return The results of this operation.
+     */
+    public T mult( T b ) {
+        T ret = createMatrix(mat.numRows,b.getMatrix().numCols);
+
+        CommonOps.mult(mat,b.getMatrix(),ret.getMatrix());
+
+        return ret;
+    }
+
+    /**
+     * <p>
+     * Computes the Kronecker product between this matrix and the provided B matrix:<br>
+     * <br>
+     * C = kron(A,B)
+     * </p>
+
+     * @see CommonOps#kron(DenseMatrix64F, DenseMatrix64F, DenseMatrix64F)
+     *
+     * @param B The right matrix in the operation. Not modified.
+     * @return Kronecker product between this matrix and B.
+     */
+    public T kron( T B ) {
+        T ret = createMatrix(mat.numRows*B.numRows(),mat.numCols*B.numCols());
+        CommonOps.kron(mat,B.getMatrix(),ret.getMatrix());
+
+        return ret;
+    }
+
+    /**
+     * <p>
+     * Returns the result of matrix addition:<br>
+     * <br>
+     * c = a + b <br>
+     * <br>
+     * where c is the returned matrix, a is this matrix, and b is the passed in matrix.
+     * </p>
+     *
+     * @see CommonOps#mult(org.ejml.data.RowD1Matrix64F , org.ejml.data.RowD1Matrix64F , org.ejml.data.RowD1Matrix64F)
+     *
+     * @param b m by n matrix. Not modified.
+     *
+     * @return The results of this operation.
+     */
+    public T plus( T b ) {
+        T ret = copy();
+
+        CommonOps.addEquals(ret.getMatrix(),b.getMatrix());
+
+        return ret;
+    }
+
+    /**
+     * <p>
+     * Returns the result of matrix subtraction:<br>
+     * <br>
+     * c = a - b <br>
+     * <br>
+     * where c is the returned matrix, a is this matrix, and b is the passed in matrix.
+     * </p>
+     *
+     * @see CommonOps#subtract(org.ejml.data.D1Matrix64F , org.ejml.data.D1Matrix64F , org.ejml.data.D1Matrix64F)
+     *
+     * @param b m by n matrix. Not modified.
+     *
+     * @return The results of this operation.
+     */
+    public T minus( T b ) {
+        T ret = copy();
+
+        CommonOps.subtract(getMatrix(), b.getMatrix(), ret.getMatrix());
+
+        return ret;
+    }
+
+    /**
+     * <p>
+     * Returns the result of matrix-double subtraction:<br>
+     * <br>
+     * c = a - b <br>
+     * <br>
+     * where c is the returned matrix, a is this matrix, and b is the passed in double.
+     * </p>
+     *
+     * @see CommonOps#subtract(org.ejml.data.D1Matrix64F , double , org.ejml.data.D1Matrix64F)
+     *
+     * @param b Value subtracted from each element
+     *
+     * @return The results of this operation.
+     */
+    public T minus( double b ) {
+        T ret = copy();
+
+        CommonOps.subtract(getMatrix(), b, ret.getMatrix());
+
+        return ret;
+    }
+
+    /**
+     * <p>
+     * Performs a element-wise scale operation.<br>
+     * <br>
+     * c = β*a <br>
+     * <br>
+     * where c is the returned matrix, a is this matrix.
+     * </p>
+     *
+     * @see CommonOps#add( org.ejml.data.D1Matrix64F , double , org.ejml.data.D1Matrix64F)
+     *
+     * @param beta Double value
+     *
+     * @return A matrix that contains the results.
+     */
+    public T plus( double beta ) {
+        T ret = createMatrix(numRows(),numCols());
+
+        CommonOps.add(getMatrix(), beta, ret.getMatrix());
+
+        return ret;
+    }
+
+    /**
+     * <p>
+     * Performs a matrix addition and scale operation.<br>
+     * <br>
+     * c = a + β*b <br>
+     * <br>
+     * where c is the returned matrix, a is this matrix, and b is the passed in matrix.
+     * </p>
+     *
+     * @see CommonOps#add( org.ejml.data.D1Matrix64F , double , org.ejml.data.D1Matrix64F , org.ejml.data.D1Matrix64F)
+     *
+     * @param b m by n matrix. Not modified.
+     *
+     * @return A matrix that contains the results.
+     */
+    public T plus( double beta , T b ) {
+        T ret = copy();
+
+        CommonOps.addEquals(ret.getMatrix(),beta,b.getMatrix());
+
+        return ret;
+    }
+
+    /**
+     * Computes the dot product (a.k.a. inner product) between this vector and vector 'v'.
+     *
+     * @param v The second vector in the dot product.  Not modified.
+     * @return dot product
+     */
+    public double dot( T v ) {
+        if( !isVector() ) {
+            throw new IllegalArgumentException("'this' matrix is not a vector.");
+        } else if( !v.isVector() ) {
+            throw new IllegalArgumentException("'v' matrix is not a vector.");
+        }
+
+        return VectorVectorMult.innerProd(mat,v.getMatrix());
+    }
+
+    /**
+     * Returns true if this matrix is a vector.  A vector is defined as a matrix
+     * that has either one row or column.
+     *
+     * @return Returns true for vectors and false otherwise.
+     */
+    public boolean isVector() {
+        return mat.numRows == 1 || mat.numCols == 1;
+    }
+
+    /**
+     * <p>
+     * Returns the result of scaling each element by 'val':<br>
+     * b<sub>i,j</sub> = val*a<sub>i,j</sub>
+     * </p>
+     *
+     * @see CommonOps#scale(double, org.ejml.data.D1Matrix64F)
+     *
+     * @param val The multiplication factor.
+     * @return The scaled matrix.
+     */
+    public T scale( double val ) {
+        T ret = copy();
+
+        CommonOps.scale(val,ret.getMatrix());
+
+        return ret;
+    }
+
+    /**
+     * <p>
+     * Returns the result of dividing each element by 'val':
+     * b<sub>i,j</sub> = a<sub>i,j</sub>/val
+     * </p>
+     *
+     * @see CommonOps#divide(org.ejml.data.D1Matrix64F,double)
+     *
+     * @param val Divisor.
+     * @return Matrix with its elements divided by the specified value.
+     */
+    public T divide( double val ) {
+        T ret = copy();
+
+        CommonOps.divide(ret.getMatrix(),val);
+
+        return ret;
+    }
+
+    /**
+     * <p>
+     * Returns the inverse of this matrix.<br>
+     * <br>
+     * b = a<sup>-1<sup><br>
+     * </p>
+     *
+     * <p>
+     * If the matrix could not be inverted then SingularMatrixException is thrown.  Even
+     * if no exception is thrown the matrix could still be singular or nearly singular.
+     * </p>
+     *
+     * @see CommonOps#invert(DenseMatrix64F, DenseMatrix64F)
+     *
+     * @throws org.ejml.factory.SingularMatrixException
+     *
+     * @return The inverse of this matrix.
+     */
+    public T invert() {
+        T ret = createMatrix(mat.numRows,mat.numCols);
+        if( !CommonOps.invert(mat,ret.getMatrix()) ) {
+            throw new SingularMatrixException();
+        }
+        if( MatrixFeatures.hasUncountable(ret.getMatrix()))
+            throw new SingularMatrixException("Solution has uncountable numbers");
+        return ret;
+    }
+
+    /**
+     * <p>
+     * Computes the Moore-Penrose pseudo-inverse
+     * </p>
+     *
+     * @return inverse computed using the pseudo inverse.
+     */
+    public T pseudoInverse() {
+        T ret = createMatrix(mat.numCols,mat.numRows);
+        CommonOps.pinv(mat,ret.getMatrix());
+        return ret;
+    }
+
+    /**
+     * <p>
+     * Solves for X in the following equation:<br>
+     * <br>
+     * x = a<sup>-1</sup>b<br>
+     * <br>
+     * where 'a' is this matrix and 'b' is an n by p matrix.
+     * </p>
+     *
+     * <p>
+     * If the system could not be solved then SingularMatrixException is thrown.  Even
+     * if no exception is thrown 'a' could still be singular or nearly singular.
+     * </p>
+     *
+     * @see CommonOps#solve(DenseMatrix64F, DenseMatrix64F, DenseMatrix64F)
+     *
+     * @throws SingularMatrixException
+     *
+     * @param b n by p matrix. Not modified.
+     * @return The solution for 'x' that is n by p.
+     */
+    public T solve( T b )
+    {
+        T x = createMatrix(mat.numCols,b.getMatrix().numCols);
+
+        if( !CommonOps.solve(mat,b.getMatrix(),x.getMatrix()) )
+            throw new SingularMatrixException();
+
+        if( MatrixFeatures.hasUncountable(x.getMatrix()) )
+            throw new SingularMatrixException("Solution contains uncountable numbers");
+
+        return x;
+    }
+
+
+    /**
+     * Sets the elements in this matrix to be equal to the elements in the passed in matrix.
+     * Both matrix must have the same dimension.
+     *
+     * @param a The matrix whose value this matrix is being set to.
+     */
+    public void set( T a ) {
+        mat.set(a.getMatrix());
+    }
+
+
+    /**
+     * <p>
+     * Sets all the elements in this matrix equal to the specified value.<br>
+     * <br>
+     * a<sub>ij</sub> = val<br>
+     * </p>
+     *
+     * @see CommonOps#fill(org.ejml.data.D1Matrix64F , double)
+     *
+     * @param val The value each element is set to.
+     */
+    public void set( double val ) {
+        CommonOps.fill(mat, val);
+    }
+
+    /**
+     * Sets all the elements in the matrix equal to zero.
+     *
+     * @see CommonOps#fill(org.ejml.data.D1Matrix64F , double)
+     */
+    public void zero() {
+        mat.zero();
+    }
+
+    /**
+     * <p>
+     * Computes the Frobenius normal of the matrix:<br>
+     * <br>
+     * normF = Sqrt{  ∑<sub>i=1:m</sub> ∑<sub>j=1:n</sub> { a<sub>ij</sub><sup>2</sup>}   }
+     * </p>
+     *
+     * @see org.ejml.ops.NormOps#normF(org.ejml.data.D1Matrix64F)
+     *
+     * @return The matrix's Frobenius normal.
+     */
+    public double normF() {
+        return NormOps.normF(mat);
+    }
+
+    /**
+     * <p>
+     * The condition p = 2 number of a matrix is used to measure the sensitivity of the linear
+     * system <b>Ax=b</b>.  A value near one indicates that it is a well conditioned matrix.
+     * </p>
+     *
+     * @see NormOps#conditionP2(DenseMatrix64F)
+     *
+     * @return The condition number.
+     */
+    public double conditionP2() {
+        return NormOps.conditionP2(mat);
+    }
+
+    /**
+     * Computes the determinant of the matrix.
+     *
+     * @see CommonOps#det(DenseMatrix64F)
+     *
+     * @return The determinant.
+     */
+    public double determinant() {
+        double ret = CommonOps.det(mat);
+        // if the decomposition silently failed then the matrix is most likely singular
+        if(UtilEjml.isUncountable(ret))
+            return 0;
+        return ret;
+    }
+
+    /**
+     * <p>
+     * Computes the trace of the matrix.
+     * </p>
+     *
+     * @see CommonOps#trace(org.ejml.data.RowD1Matrix64F)
+     *
+     * @return The trace of the matrix.
+     */
+    public double trace() {
+        return CommonOps.trace(mat);
+    }
+
+    /**
+     * <p>
+     * Reshapes the matrix to the specified number of rows and columns.  If the total number of elements
+     * is <= number of elements it had before the data is saved.  Otherwise a new internal array is
+     * declared and the old data lost.
+     * </p>
+     *
+     * <p>
+     * This is equivalent to calling A.getMatrix().reshape(numRows,numCols,false).
+     * </p>
+     *
+     * @see org.ejml.data.DenseMatrix64F#reshape(int,int,boolean)
+     *
+     * @param numRows The new number of rows in the matrix.
+     * @param numCols The new number of columns in the matrix.
+     */
+    public void reshape( int numRows , int numCols ) {
+        mat.reshape(numRows,numCols, false);
+    }
+
+    /**
+     * Assigns the element in the Matrix to the specified value.  Performs a bounds check to make sure
+     * the requested element is part of the matrix.
+     *
+     * @param row The row of the element.
+     * @param col The column of the element.
+     * @param value The element's new value.
+     */
+    public void set( int row , int col , double value ) {
+        mat.set(row,col,value);
+    }
+
+    /**
+     * Assigns an element a value based on its index in the internal array..
+     *
+     * @param index The matrix element that is being assigned a value.
+     * @param value The element's new value.
+     */
+    public void set( int index , double value ) {
+        mat.set(index,value);
+    }
+
+    /**
+     * <p>
+     * Assigns consecutive elements inside a row to the provided array.<br>
+     * <br>
+     * A(row,offset:(offset + values.length)) = values
+     * </p>
+     *
+     * @param row The row that the array is to be written to.
+     * @param offset The initial column that the array is written to.
+     * @param values Values which are to be written to the row in a matrix.
+     */
+    public void setRow( int row , int offset , double ...values ) {
+        for( int i = 0; i < values.length; i++ ) {
+            mat.set(row,offset+i,values[i]);
+        }
+    }
+
+    /**
+     * <p>
+     * Assigns consecutive elements inside a column to the provided array.<br>
+     * <br>
+     * A(offset:(offset + values.length),column) = values
+     * </p>
+     *
+     * @param column The column that the array is to be written to.
+     * @param offset The initial column that the array is written to.
+     * @param values Values which are to be written to the row in a matrix.
+     */
+    public void setColumn( int column , int offset , double ...values ) {
+        for( int i = 0; i < values.length; i++ ) {
+            mat.set(offset+i,column,values[i]);
+        }
+    }
+
+    /**
+     * Returns the value of the specified matrix element.  Performs a bounds check to make sure
+     * the requested element is part of the matrix.
+     *
+     * @param row The row of the element.
+     * @param col The column of the element.
+     * @return The value of the element.
+     */
+    public double get( int row , int col ) {
+        return mat.get(row,col);
+    }
+
+    /**
+     * Returns the value of the matrix at the specified index of the 1D row major array.
+     *
+     * @see org.ejml.data.DenseMatrix64F#get(int)
+     *
+     * @param index The element's index whose value is to be returned
+     * @return The value of the specified element.
+     */
+    public double get( int index ) {
+        return mat.data[ index ];
+    }
+
+    /**
+     * Returns the index in the matrix's array.
+     *
+     * @see org.ejml.data.DenseMatrix64F#getIndex(int, int)
+     *
+     * @param row The row number.
+     * @param col The column number.
+     * @return The index of the specified element.
+     */
+    public int getIndex( int row , int col ) {
+        return row * mat.numCols + col;
+    }
+
+    /**
+     * Creates a new iterator for traversing through a submatrix inside this matrix.  It can be traversed
+     * by row or by column.  Range of elements is inclusive, e.g. minRow = 0 and maxRow = 1 will include rows
+     * 0 and 1.  The iteration starts at (minRow,minCol) and ends at (maxRow,maxCol)
+     *
+     * @param rowMajor true means it will traverse through the submatrix by row first, false by columns.
+     * @param minRow first row it will start at.
+     * @param minCol first column it will start at.
+     * @param maxRow last row it will stop at.
+     * @param maxCol last column it will stop at.
+     * @return A new MatrixIterator
+     */
+    public MatrixIterator64F iterator(boolean rowMajor, int minRow, int minCol, int maxRow, int maxCol)
+    {
+        return new MatrixIterator64F(mat,rowMajor, minRow, minCol, maxRow, maxCol);
+    }
+
+    /**
+     * Creates and returns a matrix which is idential to this one.
+     *
+     * @return A new identical matrix.
+     */
+    public T copy() {
+        T ret = createMatrix(mat.numRows,mat.numCols);
+        ret.getMatrix().set(this.getMatrix());
+        return ret;
+    }
+
+    /**
+     * Returns the number of rows in this matrix.
+     *
+     * @return number of rows.
+     */
+    public int numRows() {
+        return mat.numRows;
+    }
+
+    /**
+     * Returns the number of columns in this matrix.
+     *
+     * @return number of columns.
+     */
+    public int numCols() {
+        return mat.numCols;
+    }
+
+    /**
+     * Returns the number of elements in this matrix, which is equal to
+     * the number of rows times the number of columns.
+     *
+     * @return The number of elements in the matrix.
+     */
+    public int getNumElements() {
+        return mat.getNumElements();
+    }
+
+
+    /**
+     * Prints the matrix to standard out.
+     */
+    public void print() {
+        MatrixIO.print(System.out,mat);
+    }
+
+    /**
+     * Prints the matrix to standard out with the specified precision.
+     */
+    public void print(int numChar , int precision) {
+        MatrixIO.print(System.out,mat,numChar,precision);
+    }
+
+    /**
+     * <p>
+     * Prints the matrix to standard out given a {@link java.io.PrintStream#printf) style floating point format,
+     * e.g. print("%f").
+     * </p>
+     */
+    public void print( String format ) {
+        MatrixIO.print(System.out,mat,format);
+    }
+
+    /**
+     * <p>
+     * Converts the array into a string format for display purposes.
+     * The conversion is done using {@link MatrixIO#print(java.io.PrintStream, org.ejml.data.RealMatrix64F)}.
+     * </p>
+     *
+     * @return String representation of the matrix.
+     */
+    public String toString() {
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+        MatrixIO.print(new PrintStream(stream),mat);
+
+        return stream.toString();
+    }
+
+    /**
+     * <p>
+     * Creates a new SimpleMatrix which is a submatrix of this matrix.
+     * </p>
+     * <p>
+     * s<sub>i-y0 , j-x0</sub> = o<sub>ij</sub> for all y0 ≤ i < y1 and x0 ≤ j < x1<br>
+     * <br>
+     * where 's<sub>ij</sub>' is an element in the submatrix and 'o<sub>ij</sub>' is an element in the
+     * original matrix.
+     * </p>
+     *
+     * <p>
+     * If any of the inputs are set to T.END then it will be set to the last row
+     * or column in the matrix.
+     * </p>
+     *
+     * @param y0 Start row.
+     * @param y1 Stop row + 1.
+     * @param x0 Start column.
+     * @param x1 Stop column + 1.
+     * @return The submatrix.
+     */
+    public T extractMatrix(int y0 , int y1, int x0 , int x1 ) {
+        if( y0 == SimpleMatrix.END ) y0 = mat.numRows;
+        if( y1 == SimpleMatrix.END ) y1 = mat.numRows;
+        if( x0 == SimpleMatrix.END ) x0 = mat.numCols;
+        if( x1 == SimpleMatrix.END ) x1 = mat.numCols;
+
+        T ret = createMatrix(y1-y0,x1-x0);
+
+        CommonOps.extract(mat,y0,y1,x0,x1,ret.getMatrix(),0,0);
+
+        return ret;
+    }
+
+    /**
+     * <p>
+     * Extracts a row or column from this matrix. The returned vector will either be a row
+     * or column vector depending on the input type.
+     * </p>
+     *
+     * @param extractRow If true a row will be extracted.
+     * @param element The row or column the vector is contained in.
+     * @return Extracted vector.
+     */
+    public T extractVector( boolean extractRow , int element )
+    {
+        int length = extractRow ? mat.numCols : mat.numRows;
+
+        T ret = extractRow ? createMatrix(1,length) : createMatrix(length,1);
+
+        if( extractRow ) {
+            SpecializedOps.subvector(mat,element,0,length,true,0,ret.getMatrix());
+        } else {
+            SpecializedOps.subvector(mat,0,element,length,false,0,ret.getMatrix());
+        }
+
+        return ret;
+    }
+
+    /**
+     * <p>
+     * Extracts the diagonal from this matrix and returns them inside a column vector.
+     * </p>
+     *
+     * @see org.ejml.ops.CommonOps#extractDiag(DenseMatrix64F, DenseMatrix64F)
+     * @return Diagonal elements inside a column vector.
+     */
+    public T extractDiag()
+    {
+        int N = Math.min(mat.numCols,mat.numRows);
+
+        T diag = createMatrix(N,1);
+
+        CommonOps.extractDiag(mat,diag.getMatrix());
+
+        return diag;
+    }
+
+    /**
+     * Checks to see if matrix 'a' is the same as this matrix within the specified
+     * tolerance.
+     *
+     * @param a The matrix it is being compared against.
+     * @param tol How similar they must be to be equals.
+     * @return If they are equal within tolerance of each other.
+     */
+    public boolean isIdentical(T a, double tol) {
+        return MatrixFeatures.isIdentical(mat,a.getMatrix(),tol);
+    }
+
+    /**
+     * Checks to see if any of the elements in this matrix are either NaN or infinite.
+     *
+     * @return True of an element is NaN or infinite.  False otherwise.
+     */
+    public boolean hasUncountable() {
+        return MatrixFeatures.hasUncountable(mat);
+    }
+
+    /**
+     * Computes a full Singular Value Decomposition (SVD) of this matrix with the
+     * eigenvalues ordered from largest to smallest.
+     *
+     * @return SVD
+     */
+    public SimpleSVD svd() {
+        return new SimpleSVD(mat,false);
+    }
+
+    /**
+     * Computes the SVD in either  compact format or full format.
+     *
+     * @return SVD of this matrix.
+     */
+    public SimpleSVD svd( boolean compact ) {
+        return new SimpleSVD(mat,compact);
+    }
+
+    /**
+     * Returns the Eigen Value Decomposition (EVD) of this matrix.
+     */
+    public SimpleEVD eig() {
+        return new SimpleEVD(mat);
+    }
+
+    /**
+     * Copy matrix B into this matrix at location (insertRow, insertCol).
+     *
+     * @param insertRow First row the matrix is to be inserted into.
+     * @param insertCol First column the matrix is to be inserted into.
+     * @param B The matrix that is being inserted.
+     */
+    public void insertIntoThis(int insertRow, int insertCol, T B) {
+        CommonOps.insert(B.getMatrix(), mat, insertRow,insertCol);
+    }
+
+    /**
+     * <p>
+     * Creates a new matrix that is a combination of this matrix and matrix B.  B is
+     * written into A at the specified location if needed the size of A is increased by
+     * growing it.  A is grown by padding the new area with zeros.
+     * </p>
+     *
+     * <p>
+     * While useful when adding data to a matrix which will be solved for it is also much
+     * less efficient than predeclaring a matrix and inserting data into it.
+     * </p>
+     *
+     * <p>
+     * If insertRow or insertCol is set to SimpleMatrix.END then it will be combined
+     * at the last row or column respectively.
+     * <p>
+     *
+     * @param insertRow Row where matrix B is written in to.
+     * @param insertCol Column where matrix B is written in to.
+     * @param B The matrix that is written into A.
+     * @return A new combined matrix.
+     */
+    public T combine( int insertRow, int insertCol, T B) {
+
+        if( insertRow == SimpleMatrix.END ) {
+            insertRow = mat.numRows;
+        }
+
+        if( insertCol == SimpleMatrix.END ) {
+            insertCol = mat.numCols;
+        }
+
+        int maxRow = insertRow + B.numRows();
+        int maxCol = insertCol + B.numCols();
+
+        T ret;
+
+        if( maxRow > mat.numRows || maxCol > mat.numCols) {
+            int M = Math.max(maxRow,mat.numRows);
+            int N = Math.max(maxCol,mat.numCols);
+
+            ret = createMatrix(M,N);
+            ret.insertIntoThis(0,0,this);
+        } else {
+            ret = copy();
+        }
+
+        ret.insertIntoThis(insertRow,insertCol,B);
+
+        return ret;
+    }
+
+    /**
+     * Returns the maximum absolute value of all the elements in this matrix.  This is
+     * equivalent the the infinite p-norm of the matrix.
+     *
+     * @return Largest absolute value of any element.
+     */
+    public double elementMaxAbs() {
+        return CommonOps.elementMaxAbs(mat);
+    }
+
+    /**
+     * Computes the sum of all the elements in the matrix.
+     *
+     * @return Sum of all the elements.
+     */
+    public double elementSum() {
+        return CommonOps.elementSum(mat);
+    }
+
+    /**
+     * <p>
+     * Returns a matrix which is the result of an element by element multiplication of 'this' and 'b':
+     * c<sub>i,j</sub> = a<sub>i,j</sub>*b<sub>i,j</sub>
+     * </p>
+     *
+     * @param b A simple matrix.
+     * @return The element by element multiplication of 'this' and 'b'.
+     */
+    public T elementMult( T b )
+    {
+        T c = createMatrix(mat.numRows,mat.numCols);
+
+        CommonOps.elementMult(mat,b.getMatrix(),c.getMatrix());
+
+        return c;
+    }
+
+    /**
+     * <p>
+     * Returns a matrix which is the result of an element by element division of 'this' and 'b':
+     * c<sub>i,j</sub> = a<sub>i,j</sub>/b<sub>i,j</sub>
+     * </p>
+     *
+     * @param b A simple matrix.
+     * @return The element by element division of 'this' and 'b'.
+     */
+    public T elementDiv( T b )
+    {
+        T c = createMatrix(mat.numRows,mat.numCols);
+
+        CommonOps.elementDiv(mat, b.getMatrix(), c.getMatrix());
+
+        return c;
+    }
+
+    /**
+     * <p>
+     * Returns a matrix which is the result of an element by element power of 'this' and 'b':
+     * c<sub>i,j</sub> = a<sub>i,j</sub> ^ b<sub>i,j</sub>
+     * </p>
+     *
+     * @param b A simple matrix.
+     * @return The element by element power of 'this' and 'b'.
+     */
+    public T elementPower( T b )
+    {
+        T c = createMatrix(mat.numRows,mat.numCols);
+
+        CommonOps.elementPower(mat, b.getMatrix(), c.getMatrix());
+
+        return c;
+    }
+
+    /**
+     * <p>
+     * Returns a matrix which is the result of an element by element power of 'this' and 'b':
+     * c<sub>i,j</sub> = a<sub>i,j</sub> ^ b
+     * </p>
+     *
+     * @param b Scalar
+     * @return The element by element power of 'this' and 'b'.
+     */
+    public T elementPower( double b )
+    {
+        T c = createMatrix(mat.numRows,mat.numCols);
+
+        CommonOps.elementPower(mat, b, c.getMatrix());
+
+        return c;
+    }
+
+    /**
+     * <p>
+     * Returns a matrix which is the result of an element by element exp of 'this'
+     * c<sub>i,j</sub> = Math.exp(a<sub>i,j</sub>)
+     * </p>
+     *
+     * @return The element by element power of 'this' and 'b'.
+     */
+    public T elementExp()
+    {
+        T c = createMatrix(mat.numRows,mat.numCols);
+
+        CommonOps.elementExp(mat, c.getMatrix());
+
+        return c;
+    }
+
+    /**
+     * <p>
+     * Returns a matrix which is the result of an element by element exp of 'this'
+     * c<sub>i,j</sub> = Math.log(a<sub>i,j</sub>)
+     * </p>
+     *
+     * @return The element by element power of 'this' and 'b'.
+     */
+    public T elementLog()
+    {
+        T c = createMatrix(mat.numRows,mat.numCols);
+
+        CommonOps.elementLog(mat, c.getMatrix());
+
+        return c;
+    }
+
+    /**
+     * <p>
+     * Returns a new matrix whose elements are the negative of 'this' matrix's elements.<br>
+     * <br>
+     * b<sub>ij</sub> = -a<sub>ij</sub>
+     * </p>
+     *
+     * @return A matrix that is the negative of the original.
+     */
+    public T negative() {
+        T A = copy();
+        CommonOps.changeSign(A.getMatrix());
+        return A;
+    }
+
+    /**
+     * <p>
+     * Saves this matrix to a file as a serialized binary object.
+     * </p>
+     *
+     * @see MatrixIO#saveBin( org.ejml.data.RealMatrix64F, String)
+     *
+     * @param fileName
+     * @throws java.io.IOException
+     */
+    public void saveToFileBinary( String fileName )
+        throws IOException
+    {
+        MatrixIO.saveBin(mat, fileName);
+    }
+
+    /**
+     * <p>
+     * Loads a new matrix from a serialized binary file.
+     * </p>
+     *
+     * @see MatrixIO#loadBin(String)
+     *
+     * @param fileName File which is to be loaded.
+     * @return The matrix.
+     * @throws IOException
+     */
+    public static SimpleMatrix loadBinary( String fileName )
+            throws IOException {
+        RealMatrix64F mat = MatrixIO.loadBin(fileName);
+
+        // see if its a DenseMatrix64F
+        if( mat instanceof DenseMatrix64F ) {
+            return SimpleMatrix.wrap((DenseMatrix64F)mat);
+        } else {
+            // if not convert it into one and wrap it
+            return SimpleMatrix.wrap( new DenseMatrix64F(mat));
+        }
+    }
+
+    /**
+     * <p>
+     * Saves this matrix to a file in a CSV format.  For the file format see {@link MatrixIO}.
+     * </p>
+     *
+     * @see MatrixIO#saveBin( org.ejml.data.RealMatrix64F, String)
+     *
+     * @param fileName
+     * @throws java.io.IOException
+     */
+    public void saveToFileCSV( String fileName )
+            throws IOException
+    {
+        MatrixIO.saveCSV(mat, fileName);
+    }
+
+    /**
+     * <p>
+     * Loads a new matrix from a CSV file.  For the file format see {@link MatrixIO}.
+     * </p>
+     *
+     * @see MatrixIO#loadCSV(String)
+     *
+     * @param fileName File which is to be loaded.
+     * @return The matrix.
+     * @throws IOException
+     */
+    public T loadCSV( String fileName )
+            throws IOException {
+        RealMatrix64F mat = MatrixIO.loadCSV(fileName);
+
+        T ret = createMatrix(1,1);
+
+        // see if its a DenseMatrix64F
+        if( mat instanceof DenseMatrix64F ) {
+            ret.mat = (DenseMatrix64F)mat;
+        } else {
+            // if not convert it into one and wrap it
+            ret.mat = new DenseMatrix64F(mat);
+        }
+        return ret;
+    }
+
+    /**
+     * Returns true of the specified matrix element is valid element inside this matrix.
+     * 
+     * @param row Row index.
+     * @param col Column index.
+     * @return true if it is a valid element in the matrix.
+     */
+    public boolean isInBounds(int row, int col) {
+        return row >= 0 && col >= 0 && row < mat.numRows && col < mat.numCols;
+    }
+
+    /**
+     * Prints the number of rows and column in this matrix.
+     */
+    public void printDimensions() {
+        System.out.println("[rows = "+numRows()+" , cols = "+numCols()+" ]");
+    }
+}
diff --git a/main/simple/src/org/ejml/simple/SimpleEVD.java b/main/simple/src/org/ejml/simple/SimpleEVD.java
new file mode 100644
index 0000000..a03b0d9
--- /dev/null
+++ b/main/simple/src/org/ejml/simple/SimpleEVD.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.simple;
+
+import org.ejml.data.Complex64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.DecompositionFactory;
+import org.ejml.interfaces.decomposition.EigenDecomposition;
+
+
+/**
+ * Wrapper around EigenDecomposition for SimpleMatrix
+ *
+ * @author Peter Abeles
+ */
+ at SuppressWarnings({"unchecked"})
+public class SimpleEVD <T extends SimpleMatrix>
+{
+    private EigenDecomposition<DenseMatrix64F> eig;
+
+    DenseMatrix64F mat;
+
+    public SimpleEVD( DenseMatrix64F mat )
+    {
+        this.mat = mat;
+        eig = DecompositionFactory.eig(mat.numCols,true);
+        if( !eig.decompose(mat))
+            throw new RuntimeException("Eigenvalue Decomposition failed");
+    }
+
+    /**
+     * Returns the number of eigenvalues/eigenvectors.  This is the matrix's dimension.
+     *
+     * @return number of eigenvalues/eigenvectors.
+     */
+    public int getNumberOfEigenvalues() {
+        return eig.getNumberOfEigenvalues();
+    }
+
+    /**
+     * <p>
+     * Returns an eigenvalue as a complex number.  For symmetric matrices the returned eigenvalue will always be a real
+     * number, which means the imaginary component will be equal to zero.
+     * </p>
+     *
+     * <p>
+     * NOTE: The order of the eigenvalues is dependent upon the decomposition algorithm used.  This means that they may
+     * or may not be ordered by magnitude.  For example the QR algorithm will returns results that are partially
+     * ordered by magnitude, but this behavior should not be relied upon.
+     * </p>
+     *
+     * @param index Index of the eigenvalue eigenvector pair.
+     * @return An eigenvalue.
+     */
+    public Complex64F getEigenvalue( int index ) {
+        return eig.getEigenvalue(index);
+    }
+
+    /**
+     * <p>
+     * Used to retrieve real valued eigenvectors.  If an eigenvector is associated with a complex eigenvalue
+     * then null is returned instead.
+     * </p>
+     *
+     * @param index Index of the eigenvalue eigenvector pair.
+     * @return If the associated eigenvalue is real then an eigenvector is returned, null otherwise.
+     */
+    public T getEigenVector( int index ) {
+        return (T)SimpleMatrix.wrap(eig.getEigenVector(index));
+    }
+
+    /**
+     * <p>
+     * Computes the quality of the computed decomposition.  A value close to or less than 1e-15
+     * is considered to be within machine precision.
+     * </p>
+     *
+     * <p>
+     * This function must be called before the original matrix has been modified or else it will
+     * produce meaningless results.
+     * </p>
+     *
+     * @return Quality of the decomposition.
+     */
+    public double quality() {
+        return DecompositionFactory.quality(mat,eig);
+    }
+
+    /**
+     * Returns the underlying decomposition that this is a wrapper around.
+     *
+     * @return EigenDecomposition
+     */
+    public EigenDecomposition getEVD() {
+        return eig;
+    }
+
+    /**
+     * Returns the index of the eigenvalue which has the largest magnitude.
+     *
+     * @return index of the largest magnitude eigen value.
+     */
+    public int getIndexMax() {
+        int indexMax = 0;
+        double max = getEigenvalue(0).getMagnitude2();
+
+        final int N = getNumberOfEigenvalues();
+        for( int i = 1; i < N; i++ ) {
+            double m = getEigenvalue(i).getMagnitude2();
+            if( m > max ) {
+                max = m;
+                indexMax = i;
+            }
+        }
+
+        return indexMax;
+    }
+
+    /**
+     * Returns the index of the eigenvalue which has the smallest magnitude.
+     *
+     * @return index of the smallest magnitude eigen value.
+     */
+    public int getIndexMin() {
+        int indexMin = 0;
+        double min = getEigenvalue(0).getMagnitude2();
+
+        final int N = getNumberOfEigenvalues();
+        for( int i = 1; i < N; i++ ) {
+            double m = getEigenvalue(i).getMagnitude2();
+            if( m < min ) {
+                min = m;
+                indexMin = i;
+            }
+        }
+
+        return indexMin;
+    }
+}
\ No newline at end of file
diff --git a/main/simple/src/org/ejml/simple/SimpleMatrix.java b/main/simple/src/org/ejml/simple/SimpleMatrix.java
new file mode 100644
index 0000000..6c7b74a
--- /dev/null
+++ b/main/simple/src/org/ejml/simple/SimpleMatrix.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.simple;
+
+import org.ejml.alg.generic.GenericMatrixOps;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.data.RealMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.CovarianceRandomDraw;
+import org.ejml.ops.RandomMatrices;
+
+import java.util.Random;
+
+
+/**
+ * <p>
+ * {@link SimpleMatrix} is a wrapper around {@link org.ejml.data.DenseMatrix64F} that provides an
+ * easy to use object oriented interface for performing matrix operations.  It is designed to be
+ * more accessible to novice programmers and provide a way to rapidly code up solutions by simplifying
+ * memory management and providing easy to use functions.
+ * </p>
+ *
+ * <p>
+ * Most functions in SimpleMatrix do not modify the original matrix.  Instead they
+ * create a new SimpleMatrix instance which is modified and returned.  This greatly simplifies memory
+ * management and writing of code in general. It also allows operations to be chained, as is shown
+ * below:<br>
+ * <br>
+ * SimpleMatrix K = P.mult(H.transpose().mult(S.invert()));
+ * </p>
+ *
+ * <p>
+ * Working with both {@link org.ejml.data.DenseMatrix64F} and SimpleMatrix in the same code base is easy.
+ * To access the internal DenseMatrix64F in a SimpleMatrix simply call {@link SimpleMatrix#getMatrix()}.
+ * To turn a DenseMatrix64F into a SimpleMatrix use {@link SimpleMatrix#wrap(org.ejml.data.DenseMatrix64F)}.  Not
+ * all operations in EJML are provided for SimpleMatrix, but can be accessed by extracting the internal
+ * DenseMatrix64F.
+ * </p>
+ *
+ * <p>
+ * EXTENDING: SimpleMatrix contains a list of narrowly focused functions for linear algebra.  To harness
+ * the functionality for another application and to the number of functions it supports it is recommended
+ * that one extends {@link SimpleBase} instead.  This way the returned matrix type's of SimpleMatrix functions
+ * will be of the appropriate types.  See StatisticsMatrix inside of the examples directory.
+ * </p>
+ *
+ * <p>
+ * PERFORMANCE: The disadvantage of using this class is that it is more resource intensive, since
+ * it creates a new matrix each time an operation is performed.  This makes the JavaVM work harder and
+ * Java automatically initializes the matrix to be all zeros.  Typically operations on small matrices
+ * or operations that have a runtime linear with the number of elements are the most affected.  More
+ * computationally intensive operations have only a slight unnoticeable performance loss.  MOST PEOPLE
+ * SHOULD NOT WORRY ABOUT THE SLIGHT LOSS IN PERFORMANCE.
+ * </p>
+ *
+ * <p>
+ * It is hard to judge how significant the performance hit will be in general.  Often the performance
+ * hit is insignificant since other parts of the application are more processor intensive or the bottle
+ * neck is a more computationally complex operation.  The best approach is benchmark and then optimize the code.
+ * </p>
+ *
+ * <p>
+ * If SimpleMatrix is extended then the protected function {link #createMatrix} should be extended and return
+ * the child class.  The results of SimpleMatrix operations will then be of the correct matrix type. 
+ * </p>
+ *
+ * <p>
+ * The object oriented approach used in SimpleMatrix was originally inspired by Jama.
+ * http://math.nist.gov/javanumerics/jama/
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+public class SimpleMatrix extends SimpleBase<SimpleMatrix> {
+
+    /**
+     * A simplified way to reference the last row or column in the matrix for some functions.
+     */
+    public static final int END = Integer.MAX_VALUE;
+
+    /**
+     * <p>
+     * Creates a new matrix which has the same value as the matrix encoded in the
+     * provided array.  The input matrix's format can either be row-major or
+     * column-major.
+     * </p>
+     *
+     * <p>
+     * Note that 'data' is a variable argument type, so either 1D arrays or a set of numbers can be
+     * passed in:<br>
+     * SimpleMatrix a = new SimpleMatrix(2,2,true,new double[]{1,2,3,4});<br>
+     * SimpleMatrix b = new SimpleMatrix(2,2,true,1,2,3,4);<br>
+     * <br>
+     * Both are equivalent.
+     * </p>
+     *
+     * @see DenseMatrix64F#DenseMatrix64F(int, int, boolean, double...)
+     *
+     * @param numRows The number of rows.
+     * @param numCols The number of columns.
+     * @param rowMajor If the array is encoded in a row-major or a column-major format.
+     * @param data The formatted 1D array. Not modified.
+     */
+    public SimpleMatrix(int numRows, int numCols, boolean rowMajor, double ...data) {
+        mat = new DenseMatrix64F(numRows,numCols, rowMajor, data);
+    }
+
+    /**
+     * <p>
+     * Creates a matrix with the values and shape defined by the 2D array 'data'.
+     * It is assumed that 'data' has a row-major formatting:<br>
+     * <br>
+     * data[ row ][ column ]
+     * </p>
+     *
+     * @see org.ejml.data.DenseMatrix64F#DenseMatrix64F(double[][])
+     *
+     * @param data 2D array representation of the matrix. Not modified.
+     */
+    public SimpleMatrix(double data[][]) {
+        mat = new DenseMatrix64F(data);
+    }
+
+    /**
+     * Creates a new matrix that is initially set to zero with the specified dimensions.
+     *
+     * @see org.ejml.data.DenseMatrix64F#DenseMatrix64F(int, int) 
+     *
+     * @param numRows The number of rows in the matrix.
+     * @param numCols The number of columns in the matrix.
+     */
+    public SimpleMatrix(int numRows, int numCols) {
+        mat = new DenseMatrix64F(numRows, numCols);
+    }
+
+    /**
+     * Creats a new SimpleMatrix which is identical to the original.
+     *
+     * @param orig The matrix which is to be copied. Not modified.
+     */
+    public SimpleMatrix( SimpleMatrix orig ) {
+        this.mat = orig.mat.copy();
+    }
+
+    /**
+     * Creates a new SimpleMatrix which is a copy of the DenseMatrix64F.
+     *
+     * @param orig The original matrix whose value is copied.  Not modified.
+     */
+    public SimpleMatrix( DenseMatrix64F orig ) {
+        this.mat = orig.copy();
+    }
+
+    /**
+     * Creates a new SimpleMatrix which is a copy of the Matrix64F.
+     *
+     * @param orig The original matrix whose value is copied.  Not modified.
+     */
+    public SimpleMatrix( RealMatrix64F orig ) {
+        this.mat = new DenseMatrix64F(orig.getNumRows(),orig.getNumCols());
+
+        GenericMatrixOps.copy(orig,mat);
+    }
+
+    /**
+     * Constructor for internal library use only.  Nothing is configured and is intended for serialization.
+     */
+    public SimpleMatrix(){}
+
+    /**
+     * Creates a new SimpleMatrix with the specified DenseMatrix64F used as its internal matrix.  This means
+     * that the reference is saved and calls made to the returned SimpleMatrix will modify the passed in DenseMatrix64F.
+     *
+     * @param internalMat The internal DenseMatrix64F of the returned SimpleMatrix. Will be modified.
+     */
+    public static SimpleMatrix wrap( DenseMatrix64F internalMat ) {
+        SimpleMatrix ret = new SimpleMatrix();
+        ret.mat = internalMat;
+        return ret;
+    }
+
+    /**
+     * Creates a new identity matrix with the specified size.
+     *
+     * @see org.ejml.ops.CommonOps#identity(int)
+     *
+     * @param width The width and height of the matrix.
+     * @return An identity matrix.
+     */
+    public static SimpleMatrix identity( int width ) {
+        SimpleMatrix ret = new SimpleMatrix(width,width);
+
+        CommonOps.setIdentity(ret.mat);
+
+        return ret;
+    }
+
+    /**
+     * <p>
+     * Creates a matrix where all but the diagonal elements are zero.  The values
+     * of the diagonal elements are specified by the parameter 'vals'.
+     * </p>
+     *
+     * <p>
+     * To extract the diagonal elements from a matrix see {@link #extractDiag()}.
+     * </p>
+     *
+     * @see org.ejml.ops.CommonOps#diag(double...)
+     *
+     * @param vals The values of the diagonal elements.
+     * @return A diagonal matrix.
+     */
+    public static SimpleMatrix diag( double ...vals ) {
+        DenseMatrix64F m = CommonOps.diag(vals);
+        SimpleMatrix ret = wrap(m);
+        return ret;
+    }
+
+    /**
+     * <p>
+     * Creates a new SimpleMatrix with random elements drawn from a uniform distribution from minValue to maxValue.
+     * </p>
+     *
+     * @see org.ejml.ops.RandomMatrices#setRandom(DenseMatrix64F,java.util.Random)
+     *
+     * @param numRows The number of rows in the new matrix
+     * @param numCols The number of columns in the new matrix
+     * @param minValue Lower bound
+     * @param maxValue Upper bound
+     * @param rand The random number generator that's used to fill the matrix.  @return The new random matrix.
+     */
+    public static SimpleMatrix random(int numRows, int numCols, double minValue, double maxValue, Random rand) {
+        SimpleMatrix ret = new SimpleMatrix(numRows,numCols);
+        RandomMatrices.setRandom(ret.mat,minValue,maxValue,rand);
+        return ret;
+    }
+
+    /**
+     * <p>
+     * Creates a new vector which is drawn from a multivariate normal distribution with zero mean
+     * and the provided covariance.
+     * </p>
+     *
+     * @see CovarianceRandomDraw
+     *
+     * @param covariance Covariance of the multivariate normal distribution
+     * @return Vector randomly drawn from the distribution
+     */
+    public static SimpleMatrix randomNormal( SimpleMatrix covariance , Random random ) {
+        CovarianceRandomDraw draw = new CovarianceRandomDraw(random,covariance.getMatrix());
+
+        SimpleMatrix found = new SimpleMatrix(covariance.numRows(),1);
+        draw.next(found.getMatrix());
+
+        return found;
+    }
+
+    /**
+     * @inheritdoc
+     */
+    @Override
+    protected SimpleMatrix createMatrix( int numRows , int numCols ) {
+        return new SimpleMatrix(numRows,numCols);
+    }
+
+    // TODO should this function be added back?  It makes the code hard to read when its used
+//    /**
+//     * <p>
+//     * Performs one of the following matrix multiplication operations:<br>
+//     * <br>
+//     * c = a * b <br>
+//     * c = a<sup>T</sup> * b <br>
+//     * c = a * b <sup>T</sup><br>
+//     * c = a<sup>T</sup> * b <sup>T</sup><br>
+//     * <br>
+//     * where c is the returned matrix, a is this matrix, and b is the passed in matrix.
+//     * </p>
+//     *
+//     * @see CommonOps#mult(DenseMatrix64F, DenseMatrix64F, DenseMatrix64F)
+//     * @see CommonOps#multTransA(DenseMatrix64F, DenseMatrix64F, DenseMatrix64F)
+//     * @see CommonOps#multTransB(DenseMatrix64F, DenseMatrix64F, DenseMatrix64F)
+//     * @see CommonOps#multTransAB(DenseMatrix64F, DenseMatrix64F, DenseMatrix64F)
+//     *
+//     * @param tranA If true matrix A is transposed.
+//     * @param tranB If true matrix B is transposed.
+//     * @param b A matrix that is n by bn. Not modified.
+//     *
+//     * @return The results of this operation.
+//     */
+//    public SimpleMatrix mult( boolean tranA , boolean tranB , SimpleMatrix b) {
+//        SimpleMatrix ret;
+//
+//        if( tranA && tranB ) {
+//            ret = createMatrix(mat.numCols,b.mat.numRows);
+//            CommonOps.multTransAB(mat,b.mat,ret.mat);
+//        } else if( tranA ) {
+//            ret = createMatrix(mat.numCols,b.mat.numCols);
+//            CommonOps.multTransA(mat,b.mat,ret.mat);
+//        } else if( tranB ) {
+//            ret = createMatrix(mat.numRows,b.mat.numRows);
+//            CommonOps.multTransB(mat,b.mat,ret.mat);
+//        }  else  {
+//            ret = createMatrix(mat.numRows,b.mat.numCols);
+//            CommonOps.mult(mat,b.mat,ret.mat);
+//        }
+//
+//        return ret;
+//    }
+
+}
diff --git a/main/simple/src/org/ejml/simple/SimpleSVD.java b/main/simple/src/org/ejml/simple/SimpleSVD.java
new file mode 100644
index 0000000..8fd52ce
--- /dev/null
+++ b/main/simple/src/org/ejml/simple/SimpleSVD.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.simple;
+
+import org.ejml.UtilEjml;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.factory.DecompositionFactory;
+import org.ejml.interfaces.decomposition.SingularValueDecomposition;
+import org.ejml.ops.SingularOps;
+
+
+/**
+ * <p>
+ * Wrapper around SVD for simple matrix.  See {@link SingularValueDecomposition} for more details.
+ * </p>
+ * <p>
+ * SVD is defined as the following decomposition:<br>
+ * <div align=center> A = U * W * V <sup>T</sup> </div><br>
+ * where A is m by n, and U and V are orthogonal matrices, and  W is a diagonal matrix
+ * </p>
+ *
+ * <p>
+ * Tolerance for singular values is
+ * Math.max(mat.numRows,mat.numCols) * W.get(0,0) * UtilEjml.EPS;
+ * where W.get(0,0) is the largest singular value.
+ * </p>
+ *
+ * @author Peter Abeles
+ */
+ at SuppressWarnings({"unchecked"})
+public class SimpleSVD<T extends SimpleMatrix> {
+
+    private SingularValueDecomposition<DenseMatrix64F> svd;
+    private T U;
+    private T W;
+    private T V;
+
+    private DenseMatrix64F mat;
+
+    // tolerance for singular values
+    double tol;
+
+    public SimpleSVD( DenseMatrix64F mat , boolean compact ) {
+        this.mat = mat;
+        svd = DecompositionFactory.svd(mat.numRows,mat.numCols,true,true,compact);
+        if( !svd.decompose(mat) )
+            throw new RuntimeException("Decomposition failed");
+        U = (T)SimpleMatrix.wrap(svd.getU(null,false));
+        W = (T)SimpleMatrix.wrap(svd.getW(null));
+        V = (T)SimpleMatrix.wrap(svd.getV(null,false));
+
+        // order singular values from largest to smallest
+        SingularOps.descendingOrder(U.getMatrix(),false,W.getMatrix(),V.getMatrix(),false);
+
+        tol = SingularOps.singularThreshold(svd);
+
+    }
+
+    /**
+     * <p>
+     * Returns the orthogonal 'U' matrix.
+     * </p>
+     *
+     * @return An orthogonal m by m matrix.
+     */
+    public T getU() {
+        return U;
+    }
+
+    /**
+     * Returns a diagonal matrix with the singular values.  The singular values are ordered
+     * from largest to smallest.
+     *
+     * @return Diagonal matrix with singular values along the diagonal.
+     */
+    public T getW() {
+        return W;
+    }
+
+    /**
+     * <p>
+     * Returns the orthogonal 'V' matrix.
+     * </p>
+     *
+     * @return An orthogonal n by n matrix.
+     */
+    public T getV() {
+        return V;
+    }
+
+    /**
+     * <p>
+     * Computes the quality of the computed decomposition.  A value close to or less than 1e-15
+     * is considered to be within machine precision.
+     * </p>
+     *
+     * <p>
+     * This function must be called before the original matrix has been modified or else it will
+     * produce meaningless results.
+     * </p>
+     *
+     * @return Quality of the decomposition.
+     */
+    public double quality() {
+        return DecompositionFactory.quality(mat,U.getMatrix(),W.getMatrix(),V.transpose().getMatrix());
+    }
+
+    /**
+     * Computes the null space from an SVD.  For more information see {@link SingularOps#nullSpace}.
+     * @return Null space vector.
+     */
+    public SimpleMatrix nullSpace() {
+        // TODO take advantage of the singular values being ordered already
+        return SimpleMatrix.wrap(SingularOps.nullSpace(svd,null,tol));
+    }
+
+    /**
+     * Returns the specified singular value.
+     *
+     * @param index Which singular value is to be returned.
+     * @return A singular value.
+     */
+    public double getSingleValue( int index ) {
+        return W.get(index,index);
+    }
+
+    /**
+     * Returns the rank of the decomposed matrix.
+     *
+     * @see SingularOps#rank(org.ejml.interfaces.decomposition.SingularValueDecomposition, double)
+     *
+     * @return The matrix's rank
+     */
+    public int rank() {
+        return SingularOps.rank(svd,tol);
+    }
+
+    /**
+     * The nullity of the decomposed matrix.
+     *
+     * @see SingularOps#nullity(org.ejml.interfaces.decomposition.SingularValueDecomposition, double)
+     *
+     * @return The matrix's nullity
+     */
+    public int nullity() {
+        return SingularOps.nullity(svd,10.0*UtilEjml.EPS);
+    }
+
+    /**
+     * Returns the underlying decomposition that this is a wrapper around.
+     *
+     * @return SingularValueDecomposition
+     */
+    public SingularValueDecomposition getSVD() {
+        return svd;
+    }
+}
diff --git a/main/simple/src/org/ejml/simple/SimpleUnitTests.java b/main/simple/src/org/ejml/simple/SimpleUnitTests.java
new file mode 100644
index 0000000..2a8be4e
--- /dev/null
+++ b/main/simple/src/org/ejml/simple/SimpleUnitTests.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.simple;
+
+import org.ejml.ops.EjmlUnitTests;
+
+/**
+ * Unit testing functions for {@link SimpleMatrix}
+ *
+ * @author Peter Abeles
+ */
+public class SimpleUnitTests {
+    /**
+     * Checks to see if every element in A is countable.  A doesn't have any element with
+     * a value of NaN or infinite.
+     *
+     * @param A Matrix
+     */
+    public static void assertCountable( SimpleMatrix A ) {
+        EjmlUnitTests.assertCountable(A.getMatrix());
+    }
+
+    /**
+     * <p>
+     * Checks to see if A and B have the same shape.
+     * </p>
+     *
+     * @param A Matrix
+     * @param B Matrix
+     */
+    public static void assertShape( SimpleMatrix A , SimpleMatrix B ) {
+        EjmlUnitTests.assertShape(A.getMatrix(), B.getMatrix());
+    }
+
+    /**
+     * <p>
+     * Checks to see if each element in the matrix is within tolerance of each other:
+     * </p>
+     *
+     * <p>
+     * The two matrices are identical with in tolerance if:<br>
+     * |a<sub>ij</sub> - b<sub>ij</sub>| ≤ tol
+     * </p>
+     *
+     * <p>
+     * In addition if an element is NaN or infinite in one matrix it must be the same in the other.
+     * </p>
+     *
+     * @param A Matrix A
+     * @param B Matrix B
+     * @param tol Tolerance
+     */
+    public static void assertEqualsUncountable( SimpleMatrix A , SimpleMatrix B , double tol ) {
+        EjmlUnitTests.assertEqualsUncountable(A.getMatrix(), B.getMatrix(), tol);
+    }
+
+    /**
+     * <p>
+     * Checks to see if each element in the matrices are within tolerance of each other and countable:
+     * </p>
+     *
+     * <p>
+     * The two matrices are identical with in tolerance if:<br>
+     * |a<sub>ij</sub> - b<sub>ij</sub>| ≤ tol
+     * </p>
+     *
+     * <p>
+     * The test will fail if any element in either matrix is NaN or infinite.
+     * </p>
+     *
+     * @param A Matrix A
+     * @param B Matrix B
+     * @param tol Tolerance
+     */
+    public static void assertEquals( SimpleMatrix A , SimpleMatrix B , double tol ) {
+        EjmlUnitTests.assertEquals(A.getMatrix(), B.getMatrix(), tol);
+    }
+}
diff --git a/main/simple/src/org/ejml/simple/UtilSimpleMatrix.java b/main/simple/src/org/ejml/simple/UtilSimpleMatrix.java
new file mode 100644
index 0000000..bef7b88
--- /dev/null
+++ b/main/simple/src/org/ejml/simple/UtilSimpleMatrix.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.simple;
+
+import org.ejml.data.BlockMatrix64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.ConvertMatrixType;
+
+/**
+ * @author Peter Abeles
+ */
+public class UtilSimpleMatrix {
+    /**
+     * <p>Converts the block matrix into a SimpleMatrix.</p>
+     *
+     * @param A Block matrix that is being converted.  Not modified.
+     * @return Equivalent SimpleMatrix.
+     */
+    public static SimpleMatrix convertSimple( BlockMatrix64F A ) {
+        DenseMatrix64F B = ConvertMatrixType.convert(A, null);
+
+        return SimpleMatrix.wrap(B);
+    }
+}
diff --git a/main/simple/test/org/ejml/simple/TestSimpleMatrix.java b/main/simple/test/org/ejml/simple/TestSimpleMatrix.java
new file mode 100644
index 0000000..5f4750d
--- /dev/null
+++ b/main/simple/test/org/ejml/simple/TestSimpleMatrix.java
@@ -0,0 +1,767 @@
+/*
+ * Copyright (c) 2009-2014, Peter Abeles. All Rights Reserved.
+ *
+ * This file is part of Efficient Java Matrix Library (EJML).
+ *
+ * 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.
+ */
+
+package org.ejml.simple;
+
+import org.ejml.data.Complex64F;
+import org.ejml.data.DenseMatrix64F;
+import org.ejml.ops.CommonOps;
+import org.ejml.ops.EjmlUnitTests;
+import org.ejml.ops.NormOps;
+import org.ejml.ops.RandomMatrices;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * @author Peter Abeles
+ */
+public class TestSimpleMatrix {
+
+    Random rand = new Random(76343);
+
+    @Test
+    public void randomNormal() {
+        SimpleMatrix Q = SimpleMatrix.diag(5,3,12);
+        Q.set(0,1,0.5);
+        Q.set(1,0,0.5);
+
+        int N = 200;
+        double sum[] = new double[3];
+        for (int i = 0; i < N; i++) {
+            SimpleMatrix x = SimpleMatrix.randomNormal(Q,rand);
+
+            for (int j = 0; j < x.getNumElements(); j++) {
+                sum[j] += x.get(j);
+            }
+        }
+        for (int i = 0; i < sum.length; i++) {
+            sum[i] /= N;
+            assertTrue(sum[i]!=0);
+            assertEquals(0,sum[i],0.3);
+        }
+    }
+
+    @Test
+    public void constructor_1d_array() {
+        double d[] = new double[]{2,5,3,9,-2,6,7,4};
+        SimpleMatrix s = new SimpleMatrix(3,2, true, d);
+        DenseMatrix64F m = new DenseMatrix64F(3,2, true, d);
+
+        EjmlUnitTests.assertEquals(m,s.getMatrix(),1e-8);
+    }
+
+    @Test
+    public void constructor_2d_array() {
+        double d[][] = new double[][]{{1,2},{3,4},{5,6}};
+
+        SimpleMatrix s = new SimpleMatrix(d);
+        DenseMatrix64F mat = new DenseMatrix64F(d);
+
+        EjmlUnitTests.assertEquals(mat,s.getMatrix(),1e-8);
+    }
+
+    @Test
+    public void constructor_dense() {
+        DenseMatrix64F mat = RandomMatrices.createRandom(3,2,rand);
+        SimpleMatrix s = new SimpleMatrix(mat);
+
+        assertTrue( mat != s.getMatrix() );
+        EjmlUnitTests.assertEquals(mat,s.getMatrix(),1e-8);
+    }
+
+    @Test
+    public void constructor_simple() {
+        SimpleMatrix orig = SimpleMatrix.random(3,2, 0, 1, rand);
+        SimpleMatrix copy = new SimpleMatrix(orig);
+
+        assertTrue(orig.mat != copy.mat);
+        EjmlUnitTests.assertEquals(orig.mat,copy.mat,1e-8);
+    }
+
+    @Test
+    public void wrap() {
+        DenseMatrix64F mat = RandomMatrices.createRandom(3,2,rand);
+
+        SimpleMatrix s = SimpleMatrix.wrap(mat);
+
+        assertTrue(s.mat == mat);
+    }
+
+    @Test
+    public void identity() {
+        SimpleMatrix s = SimpleMatrix.identity(3);
+
+        DenseMatrix64F d = CommonOps.identity(3);
+
+        EjmlUnitTests.assertEquals(d,s.mat,1e-8);
+    }
+
+    @Test
+    public void getMatrix() {
+        SimpleMatrix s = new SimpleMatrix(3,2);
+
+        // make sure a new instance isn't returned
+        assertTrue(s.mat == s.getMatrix());
+    }
+
+    @Test
+    public void transpose() {
+        SimpleMatrix orig = SimpleMatrix.random(3,2, 0, 1, rand);
+        SimpleMatrix tran = orig.transpose();
+
+        DenseMatrix64F dTran = new DenseMatrix64F(2,3);
+        CommonOps.transpose(orig.mat,dTran);
+
+        EjmlUnitTests.assertEquals(dTran,tran.mat,1e-8);
+    }
+
+    @Test
+    public void mult() {
+        SimpleMatrix a = SimpleMatrix.random(3,2, 0, 1, rand);
+        SimpleMatrix b = SimpleMatrix.random(2,3, 0, 1, rand);
+        SimpleMatrix c = a.mult(b);
+
+        DenseMatrix64F c_dense = new DenseMatrix64F(3,3);
+        CommonOps.mult(a.mat,b.mat,c_dense);
+
+        EjmlUnitTests.assertEquals(c_dense,c.mat,1e-8);
+    }
+
+    @Test
+    public void kron() {
+        SimpleMatrix a = SimpleMatrix.random(3,2, 0, 1, rand);
+        SimpleMatrix b = SimpleMatrix.random(2,3, 0, 1, rand);
+        SimpleMatrix c = a.kron(b);
+
+        DenseMatrix64F c_dense = new DenseMatrix64F(6,6);
+        CommonOps.kron(a.getMatrix(),b.getMatrix(),c_dense);
+
+        EjmlUnitTests.assertEquals(c_dense,c.mat,1e-8);
+    }
+
+//    @Test
+//    public void mult_trans() {
+//        SimpleMatrix a = SimpleMatrix.random(3,2,rand);
+//        SimpleMatrix b = SimpleMatrix.random(2,3,rand);
+//        SimpleMatrix c;
+//
+//        DenseMatrix64F c_dense = new DenseMatrix64F(3,3);
+//        CommonOps.mult(a.mat,b.mat,c_dense);
+//
+//        c = a.mult(false,false,b);
+//        EjmlUnitTests.assertEquals(c_dense,c.mat);
+//        c = a.transpose().mult(true,false,b);
+//        EjmlUnitTests.assertEquals(c_dense,c.mat);
+//        c = a.mult(false,true,b.transpose());
+//        EjmlUnitTests.assertEquals(c_dense,c.mat);
+//        c = a.transpose().mult(true,true,b.transpose());
+//        EjmlUnitTests.assertEquals(c_dense,c.mat);
+//    }
+
+    @Test
+    public void plus() {
+        SimpleMatrix a = SimpleMatrix.random(3,2, 0, 1, rand);
+        SimpleMatrix b = SimpleMatrix.random(3,2, 0, 1, rand);
+        SimpleMatrix c = a.plus(b);
+
+        DenseMatrix64F c_dense = new DenseMatrix64F(3,2);
+        CommonOps.add(a.mat,b.mat,c_dense);
+
+        EjmlUnitTests.assertEquals(c_dense,c.mat,1e-8);
+    }
+
+    @Test
+    public void plus_scalar() {
+        SimpleMatrix a = SimpleMatrix.random(3,2, 0, 1, rand);
+        double b = 2.5;
+        SimpleMatrix c = a.plus(b);
+
+        DenseMatrix64F c_dense = new DenseMatrix64F(3,2);
+        CommonOps.add(a.mat,b,c_dense);
+
+        EjmlUnitTests.assertEquals(c_dense,c.mat,1e-8);
+    }
+
+    @Test
+    public void dot() {
+        SimpleMatrix a = SimpleMatrix.random(10,1,-1,1,rand);
+        SimpleMatrix b = SimpleMatrix.random(10,1,-1,1,rand);
+
+        double expected = 0;
+        for( int i = 0; i < 10; i++ )
+            expected += a.get(i)*b.get(i);
+
+        double found = a.dot(b);
+
+        assertEquals(expected,found,1e-8);
+    }
+
+    @Test
+    public void isVector() {
+        assertTrue(new SimpleMatrix(1,1).isVector());
+        assertTrue(new SimpleMatrix(1,10).isVector());
+        assertTrue(new SimpleMatrix(10,1).isVector());
+        assertFalse(new SimpleMatrix(6,5).isVector());
+    }
+
+    @Test
+    public void minus_matrix_matrix() {
+        SimpleMatrix a = SimpleMatrix.random(3,2, 0, 1, rand);
+        SimpleMatrix b = SimpleMatrix.random(3,2, 0, 1, rand);
+        SimpleMatrix c = a.minus(b);
+
+        DenseMatrix64F c_dense = new DenseMatrix64F(3,2);
+        CommonOps.subtract(a.mat, b.mat, c_dense);
+
+        EjmlUnitTests.assertEquals(c_dense,c.mat,1e-8);
+    }
+
+    @Test
+    public void minus_matrix_scalar() {
+        SimpleMatrix a = SimpleMatrix.random(3,2, 0, 1, rand);
+        double b = 0.14;
+        SimpleMatrix c = a.minus(b);
+
+        DenseMatrix64F c_dense = new DenseMatrix64F(3,2);
+        CommonOps.subtract(a.mat, b, c_dense);
+
+        EjmlUnitTests.assertEquals(c_dense,c.mat,1e-8);
+    }
+
+    @Test
+    public void plus_beta() {
+        SimpleMatrix a = SimpleMatrix.random(3,2, 0, 1, rand);
+        SimpleMatrix b = SimpleMatrix.random(3,2, 0, 1, rand);
+        SimpleMatrix c = a.plus(2.5, b);
+
+        DenseMatrix64F c_dense = new DenseMatrix64F(3,2);
+        CommonOps.add(a.mat, 2.5, b.mat, c_dense);
+
+        EjmlUnitTests.assertEquals(c_dense,c.mat,1e-8);
+    }
+
+    @Test
+    public void invert() {
+        SimpleMatrix a = SimpleMatrix.random(3,3, 0, 1, rand);
+        SimpleMatrix inv = a.invert();
+
+        DenseMatrix64F d_inv = new DenseMatrix64F(3,3);
+        CommonOps.invert(a.mat,d_inv);
+
+        EjmlUnitTests.assertEquals(d_inv,inv.mat,1e-8);
+    }
+
+    @Test
+    public void invert_NaN_INFINITY() {
+        SimpleMatrix a = new SimpleMatrix(3,3);
+        try {
+            a.set(Double.NaN);
+            a.invert();
+            fail("Should have thrown an exception");
+        } catch( RuntimeException ignore ) {}
+
+        try {
+            a.set(Double.POSITIVE_INFINITY);
+            a.invert();
+            fail("Should have thrown an exception");
+        } catch( RuntimeException ignore ) {}
+    }
+
+    @Test
+    public void pseudoInverse() {
+        // first test it against a non-square zero matrix
+        SimpleMatrix inv = new SimpleMatrix(3,4).pseudoInverse();
+        assertEquals(0,inv.normF(),1e-8);
+
+        // now try it against a more standard matrix
+        SimpleMatrix a = SimpleMatrix.random(3,3, 0, 1, rand);
+        inv = a.pseudoInverse();
+
+        DenseMatrix64F d_inv = new DenseMatrix64F(3,3);
+        CommonOps.invert(a.mat,d_inv);
+
+        EjmlUnitTests.assertEquals(d_inv,inv.mat,1e-8);
+    }
+
+    @Test
+    public void solve() {
+        SimpleMatrix a = SimpleMatrix.random(3,3, 0, 1, rand);
+        SimpleMatrix b = SimpleMatrix.random(3,2, 0, 1, rand);
+        SimpleMatrix c = a.solve(b);
+
+        DenseMatrix64F c_dense = new DenseMatrix64F(3,2);
+        CommonOps.solve(a.mat,b.mat,c_dense);
+
+        EjmlUnitTests.assertEquals(c_dense,c.mat,1e-8);
+    }
+
+    @Test
+    public void solve_NaN_INFINITY() {
+        SimpleMatrix a = new SimpleMatrix(3,3);
+        SimpleMatrix b = SimpleMatrix.random(3,2, 0, 1, rand);
+        try {
+            a.set(Double.NaN);
+            a.solve(b);
+            fail("Should have thrown an exception");
+        } catch( RuntimeException ignore ) {}
+
+        try {
+            a.set(Double.POSITIVE_INFINITY);
+            a.solve(b);
+            fail("Should have thrown an exception");
+        } catch( RuntimeException ignore ) {}
+    }
+
+    /**
+     * See if it solves an over determined system correctly
+     */
+    @Test
+    public void solve_notsquare() {
+        SimpleMatrix a = SimpleMatrix.random(6,3, 0, 1, rand);
+        SimpleMatrix b = SimpleMatrix.random(6,2, 0, 1, rand);
+        SimpleMatrix c = a.solve(b);
+
+        DenseMatrix64F c_dense = new DenseMatrix64F(3,2);
+        CommonOps.solve(a.mat,b.mat,c_dense);
+
+        EjmlUnitTests.assertEquals(c_dense,c.mat,1e-8);
+    }
+
+    @Test
+    public void set_double() {
+        SimpleMatrix a = new SimpleMatrix(3,3);
+        a.set(16.0);
+
+        DenseMatrix64F d = new DenseMatrix64F(3,3);
+        CommonOps.fill(d, 16.0);
+
+        EjmlUnitTests.assertEquals(d,a.mat,1e-8);
+    }
+
+    @Test
+    public void zero() {
+        SimpleMatrix a = SimpleMatrix.random(3,3, 0, 1, rand);
+        a.zero();
+
+        DenseMatrix64F d = new DenseMatrix64F(3,3);
+
+        EjmlUnitTests.assertEquals(d,a.mat,1e-8);
+    }
+
+    @Test
+    public void normF() {
+        SimpleMatrix a = SimpleMatrix.random(3,3, 0, 1, rand);
+
+        double norm = a.normF();
+        double dnorm = NormOps.fastNormF(a.mat);
+
+        assertEquals(dnorm,norm,1e-10);
+    }
+
+    @Test
+    public void conditionP2() {
+        SimpleMatrix a = SimpleMatrix.random(3,3, 0, 1, rand);
+
+        double cond = NormOps.conditionP2(a.getMatrix());
+        double found = a.conditionP2();
+
+        assertTrue(cond == found);
+    }
+
+    @Test
+    public void determinant() {
+        SimpleMatrix a = SimpleMatrix.random(3,3, 0, 1, rand);
+
+        double det = a.determinant();
+        double ddet = CommonOps.det(a.mat);
+
+        assertEquals(ddet,det,1e-10);
+    }
+
+    @Test
+    public void trace() {
+        SimpleMatrix a = SimpleMatrix.random(3,3, 0, 1, rand);
+
+        double trace = a.trace();
+        double dtrace = CommonOps.trace(a.mat);
+
+        assertEquals(dtrace,trace,1e-10);
+    }
+
+    @Test
+    public void reshape() {
+        SimpleMatrix a = SimpleMatrix.random(3,3, 0, 1, rand);
+        DenseMatrix64F b = a.mat.copy();
+
+        a.reshape(2,3);
+        b.reshape(2,3, false);
+
+        EjmlUnitTests.assertEquals(b,a.mat,1e-8);
+    }
+
+    @Test
+    public void set_element() {
+        SimpleMatrix a = SimpleMatrix.random(3,3, 0, 1, rand);
+        a.set(0,1,10.3);
+
+        assertEquals(10.3,a.get(0,1),1e-6);
+    }
+
+    @Test
+    public void setRow() {
+        SimpleMatrix a = SimpleMatrix.random(3,3, 0, 1, rand);
+        a.setRow(2,1,2,3);
+
+        assertEquals(2,a.get(2,1),1e-6);
+        assertEquals(3,a.get(2,2),1e-6);
+    }
+
+    @Test
+    public void setColumn() {
+        SimpleMatrix a = SimpleMatrix.random(3,3, 0, 1, rand);
+        a.setColumn(2,1,2,3);
+
+        assertEquals(2,a.get(1,2),1e-6);
+        assertEquals(3,a.get(2,2),1e-6);
+    }
+
+    @Test
+    public void get_2d() {
+        SimpleMatrix a = SimpleMatrix.random(3,3, 0, 1, rand);
+
+        assertEquals(a.mat.get(0,1),a.get(0,1),1e-6);
+    }
+
+    @Test
+    public void get_1d() {
+        SimpleMatrix a = SimpleMatrix.random(3,3, 0, 1, rand);
+
+        assertEquals(a.mat.get(3),a.get(3),1e-6);
+    }
+
+    @Test
+    public void getIndex() {
+        SimpleMatrix a = SimpleMatrix.random(3,3, 0, 1, rand);
+
+        assertEquals(a.mat.getIndex(0,2),a.getIndex(0,2),1e-6);
+    }
+
+    @Test
+    public void copy() {
+        SimpleMatrix a = SimpleMatrix.random(3,3, 0, 1, rand);
+        SimpleMatrix b = a.copy();
+
+        assertTrue(a.mat!=b.mat);
+        EjmlUnitTests.assertEquals(b.mat,a.mat,1e-8);
+    }
+
+    @Test
+    public void svd() {
+        SimpleMatrix a = SimpleMatrix.random(3,4, 0, 1, rand);
+
+        SimpleSVD svd = a.svd();
+
+        SimpleMatrix U = svd.getU();
+        SimpleMatrix W = svd.getW();
+        SimpleMatrix V = svd.getV();
+
+        SimpleMatrix a_found = U.mult(W).mult(V.transpose());
+
+        EjmlUnitTests.assertEquals(a.mat,a_found.mat,1e-8);
+    }
+
+    @Test
+    public void eig() {
+        SimpleMatrix a = SimpleMatrix.random(4,4, 0, 1, rand);
+
+        SimpleEVD evd = a.eig();
+
+        assertEquals(4,evd.getNumberOfEigenvalues());
+
+        for( int i = 0; i < 4; i++ ) {
+            Complex64F c = evd.getEigenvalue(i);
+            assertTrue(c != null );
+            evd.getEigenVector(i);
+        }
+    }
+
+    @Test
+    public void insertIntoThis() {
+        SimpleMatrix A = new SimpleMatrix(6,4);
+        SimpleMatrix B = SimpleMatrix.random(3,2, 0, 1, rand);
+
+        DenseMatrix64F A_ = A.getMatrix().copy();
+
+        A.insertIntoThis(1,2,B);
+
+        CommonOps.insert(B.getMatrix(), A_, 1,2);
+
+        EjmlUnitTests.assertEquals(A_,A.getMatrix(),1e-8);
+    }
+
+    @Test
+    public void combine() {
+        SimpleMatrix A = new SimpleMatrix(6,4);
+        SimpleMatrix B = SimpleMatrix.random(3,4, 0, 1, rand);
+
+        SimpleMatrix C = A.combine(2,2,B);
+
+        assertEquals(6,C.numRows());
+        assertEquals(6,C.numCols());
+
+        for( int i = 0; i < 6; i++ ) {
+            for( int j = 0; j < 6; j++ ) {
+                if( i >= 2 && i < 5 && j >= 2 && j < 6 ) {
+                    // check to see if B was overlayed
+                    assertTrue( B.get(i-2,j-2) == C.get(i,j));
+                } else if( i >= 5 || j >= 4 ) {
+                    // check zero padding
+                    assertTrue( C.get(i,j) == 0 );
+                } else {
+                    // see if the parts of A remain there
+                    assertTrue( A.get(i,j) == C.get(i,j));
+                }
+            }
+        }
+    }
+
+    @Test
+    public void scale() {
+        SimpleMatrix a = SimpleMatrix.random(3,2, 0, 1, rand);
+        SimpleMatrix b = a.scale(1.5);
+
+        for( int i = 0; i < a.numRows(); i++ ) {
+            for( int j = 0; j < a.numCols(); j++ ) {
+                assertEquals( a.get(i,j)*1.5 , b.get(i,j),1e-10);
+            }
+        }
+    }
+
+    @Test
+    public void div_scalar() {
+        SimpleMatrix a = SimpleMatrix.random(3,2, 0, 1, rand);
+        SimpleMatrix b = a.divide(1.5);
+
+        for( int i = 0; i < a.numRows(); i++ ) {
+            for( int j = 0; j < a.numCols(); j++ ) {
+                assertEquals( a.get(i,j)/1.5 , b.get(i,j),1e-10);
+            }
+        }
+    }
+
+    @Test
+    public void elementSum() {
+        SimpleMatrix a = new SimpleMatrix(7,4);
+
+        double expectedSum = 0;
+
+        int index = 0;
+        for( int i = 0; i < a.numRows(); i++ ) {
+            for( int j = 0; j < a.numCols(); j++ , index++ ) {
+                expectedSum += index;
+                a.set(i,j,index);
+            }
+        }
+
+        assertEquals( expectedSum , a.elementSum() , 1e-8);
+    }
+
+    @Test
+    public void elementMaxAbs() {
+        SimpleMatrix a = SimpleMatrix.random(7,5, 0, 1, rand);
+
+        a.set(3,4,-5);
+        a.set(4,4,4);
+
+        assertTrue(5 == a.elementMaxAbs());
+    }
+
+    @Test
+    public void elementMult() {
+        SimpleMatrix A = SimpleMatrix.random(4,5,-1,1,rand);
+        SimpleMatrix B = SimpleMatrix.random(4,5,-1,1,rand);
+
+        SimpleMatrix C = A.elementMult(B);
+
+        for( int i = 0; i < A.numRows(); i++ ) {
+            for( int j = 0; j < A.numCols(); j++ ) {
+                double expected = A.get(i,j)*B.get(i,j);
+
+                assertTrue(expected == C.get(i,j));
+            }
+        }
+    }
+
+    @Test
+    public void elementDiv() {
+        SimpleMatrix A = SimpleMatrix.random(4,5,-1,1,rand);
+        SimpleMatrix B = SimpleMatrix.random(4,5,-1,1,rand);
+
+        SimpleMatrix C = A.elementDiv(B);
+
+        for( int i = 0; i < A.numRows(); i++ ) {
+            for( int j = 0; j < A.numCols(); j++ ) {
+                double expected = A.get(i,j)/B.get(i,j);
+
+                assertTrue(expected == C.get(i,j));
+            }
+        }
+    }
+
+    @Test
+    public void elementPower_m() {
+        SimpleMatrix A = SimpleMatrix.random(4,5,0,1,rand);
+        SimpleMatrix B = SimpleMatrix.random(4,5,0,1,rand);
+
+        SimpleMatrix C = A.elementPower(B);
+
+        for( int i = 0; i < A.numRows(); i++ ) {
+            for( int j = 0; j < A.numCols(); j++ ) {
+                double expected = Math.pow(A.get(i,j),B.get(i,j));
+
+                assertTrue(expected == C.get(i,j));
+            }
+        }
+    }
+
+    @Test
+    public void elementPower_s() {
+        SimpleMatrix A = SimpleMatrix.random(4,5,0,1,rand);
+        double b = 1.1;
+
+        SimpleMatrix C = A.elementPower(b);
+
+        for( int i = 0; i < A.numRows(); i++ ) {
+            for( int j = 0; j < A.numCols(); j++ ) {
+                double expected = Math.pow(A.get(i,j),b);
+
+                assertTrue(expected == C.get(i,j));
+            }
+        }
+    }
+
+    @Test
+    public void elementLog() {
+        SimpleMatrix A = SimpleMatrix.random(4,5,0,1,rand);
+
+        SimpleMatrix C = A.elementLog();
+
+        for( int i = 0; i < A.numRows(); i++ ) {
+            for( int j = 0; j < A.numCols(); j++ ) {
+                double expected = Math.log(A.get(i, j));
+
+                assertTrue(expected == C.get(i,j));
+            }
+        }
+    }
+
+    @Test
+    public void elementExp() {
+        SimpleMatrix A = SimpleMatrix.random(4,5,0,1,rand);
+
+        SimpleMatrix C = A.elementExp();
+
+        for( int i = 0; i < A.numRows(); i++ ) {
+            for( int j = 0; j < A.numCols(); j++ ) {
+                double expected = Math.exp(A.get(i, j));
+
+                assertTrue(expected == C.get(i,j));
+            }
+        }
+    }
+
+    @Test
+    public void extractMatrix() {
+        SimpleMatrix a = SimpleMatrix.random(7,5, 0, 1, rand);
+
+        SimpleMatrix b = a.extractMatrix(2,5,3,5);
+
+        for( int i = 2; i <= 4; i++ ) {
+            for( int j = 3; j <= 4; j++ ) {
+                double expected = a.get(i,j);
+                double found = b.get(i-2,j-3);
+
+                assertTrue( expected == found );
+            }
+        }
+    }
+
+    @Test
+    public void extractDiag() {
+        SimpleMatrix a = SimpleMatrix.random(3,4, 0, 1, rand);
+
+        DenseMatrix64F found = a.extractDiag().getMatrix();
+        DenseMatrix64F expected = new DenseMatrix64F(3,1);
+
+        CommonOps.extractDiag(a.getMatrix(),expected);
+
+        EjmlUnitTests.assertEquals(found,expected,1e-8);
+    }
+
+    @Test
+    public void extractVector() {
+        SimpleMatrix A = SimpleMatrix.random(10,7, 0, 1, rand);
+
+        SimpleMatrix c = A.extractVector(false,2);
+        SimpleMatrix r = A.extractVector(true,2);
+
+        assertEquals(A.numCols(),r.numCols());
+        assertEquals(1,r.numRows());
+        assertEquals(A.numRows(),c.numRows());
+        assertEquals(1,c.numCols());
+
+        for( int i = 0; i < A.numCols(); i++ ) {
+            assertEquals(A.get(2,i),r.get(i),1e-10);
+        }
+
+        for( int i = 0; i < A.numRows(); i++ ) {
+            assertEquals(A.get(i,2),c.get(i),1e-10);
+        }
+    }
+
+    @Test
+    public void negative() {
+        SimpleMatrix A = SimpleMatrix.random(5,7,-1,1,rand);
+
+        SimpleMatrix A_neg = A.negative();
+
+        double value = A.plus(A_neg).normF();
+
+        assertEquals(0,value,1e-8);
+    }
+
+    @Test
+    public void isInBounds() {
+        SimpleMatrix A = new SimpleMatrix(10,15);
+
+        assertTrue(A.isInBounds(0,0));
+        assertTrue(A.isInBounds(9,0));
+        assertTrue(A.isInBounds(0,14));
+        assertTrue(A.isInBounds(3,3));
+
+        assertFalse(A.isInBounds(-1,0));
+        assertFalse(A.isInBounds(0,-1));
+        assertFalse(A.isInBounds(10,0));
+        assertFalse(A.isInBounds(0,15));
+        assertFalse(A.isInBounds(3,1000));
+    }
+}
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..7b8f89f
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,2 @@
+include 'main:equation','main:core','main:experimental','main:dense64','main:denseC64',
+        'main:simple','examples',"main:all"

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/libejml-java.git



More information about the pkg-java-commits mailing list