private void hessenbergReduction(final RealMatrix matrixToReduce) {
    final int n = matrixToReduce.getHeight();
    final double[][] V = matrixToReduce.blank().toDoubleArray();
    final double[][] H = matrixToReduce.toDoubleArray();
    final double[] ort = new double[matrixToReduce.getHeight()];

    //  This is derived from the Algol procedures hessenbergReduction and ortran,
    //  by Martin and Wilkinson, Handbook for Auto. Comp.,
    //  Vol.ii-Linear Algebra, and the corresponding
    //  Fortran subroutines in EISPACK.

    final int high = n - 1;

    for (int m = 1; m <= high - 1; m++) {
      // Scale column.
      double scale = 0.0;
      for (int i = m; i <= high; i++) scale = scale + Math.abs(H[i][m - 1]);
      if (scale != 0.0) {
        // Compute Householder transformation.
        double h = 0.0;
        for (int i = high; i >= m; i--) {
          ort[i] = H[i][m - 1] / scale;
          h += ort[i] * ort[i];
        }
        double g = Math.sqrt(h);
        if (ort[m] > 0) g = -g;
        h = h - ort[m] * g;
        ort[m] = ort[m] - g;

        // Apply Householder similarity transformation
        // hessenbergMatrixElements = (I-u*u'/h)*hessenbergMatrixElements*(I-u*u')/h)

        for (int j = m; j < n; j++) {
          double f = 0.0;
          for (int i = high; i >= m; i--) f += ort[i] * H[i][j];
          f = f / h;
          for (int i = m; i <= high; i++) H[i][j] -= f * ort[i];
        }

        for (int i = 0; i <= high; i++) {
          double f = 0.0;
          for (int j = high; j >= m; j--) f += ort[j] * H[i][j];
          f = f / h;
          for (int j = m; j <= high; j++) H[i][j] -= f * ort[j];
        }
        ort[m] = scale * ort[m];
        H[m][m - 1] = scale * g;
      }
    }

    // Accumulate transformations (Algol's ortran).
    for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) V[i][j] = (i == j ? 1.0 : 0.0);

    for (int m = high - 1; m >= 1; m--)
      if (H[m][m - 1] != 0.0) {
        for (int i = m + 1; i <= high; i++) ort[i] = H[i][m - 1];
        for (int j = m; j <= high; j++) {
          double g = 0.0;
          for (int i = m; i <= high; i++) g += ort[i] * V[i][j];
          // Double division avoids possible underflow
          g = (g / ort[m]) / H[m][m - 1];
          for (int i = m; i <= high; i++) V[i][j] += g * ort[i];
        }
      }

    this.matrix = new SimpleRealMatrix(V);
    this.hessenbergMatrix = new SimpleRealMatrix(H);
  }
  /**
   * Check for symmetry, then construct the eigenvalue decomposition. Gives access to D and
   * matrixElements.
   *
   * @param matrixToDecompose Elements Square matrix
   */
  public NonsymetricHessenbergReduction(final RealMatrix matrixToDecompose) {
    final int n = matrixToDecompose.getWidth();

    // Reduce to Hessenberg form.
    hessenbergReduction(matrixToDecompose.getSubmatrix(0, n, 0, n));
  }