/**
   * Tridiagonalize a real symmetric matrix A, i.e. A = Q * T * Q' such that Q is an orthogonal
   * matrix and T is a tridiagonal matrix.
   *
   * <p>A = QTQ' <=> Q'AQ = T
   *
   * @param A a real symmetric matrix
   * @param computeV if V is to be computed
   * @return a {@code Matrix} array [Q, T]
   */
  private static Matrix[] tridiagonalize(Matrix A, boolean computeV) {
    A = full(A).copy();
    int m = A.getRowDimension();
    int n = A.getColumnDimension();
    Matrix[] QT = new Matrix[2];
    double[] a = ArrayOperator.allocateVector(n, 0);
    double[] b = ArrayOperator.allocateVector(n, 0);
    double[][] AData = ((DenseMatrix) A).getData();
    double c = 0;
    double s = 0;
    double r = 0;
    for (int j = 0; j < n - 2; j++) {
      a[j] = AData[j][j];
      // Householder transformation on columns of A(j+1:m, j+1:n)
      // Compute the norm of A(j+1:m, j)
      c = 0;
      for (int i = j + 1; i < m; i++) {
        c += Math.pow(AData[i][j], 2);
      }
      if (c == 0) continue;
      s = Math.sqrt(c);
      b[j] = AData[j + 1][j] > 0 ? -s : s;
      r = Math.sqrt(s * (s + abs(AData[j + 1][j])));

      /*double[] u1 = ArrayOperation.allocate1DArray(n, 0);
      for (int k = j + 1; k < m; k++) {
      	u1[k] = AData[k][j];
      }
      u1[j + 1] -= b[j];
      for (int k = j + 1; k < m; k++) {
      	u1[k] /= r;
      }
      Matrix H = eye(n).minus(new DenseMatrix(u1, 1).mtimes(new DenseMatrix(u1, 2)));
      disp(new DenseMatrix(u1, 1));
      disp(eye(n));
      disp(H);
      disp(A);
      disp(H.mtimes(A).mtimes(H));
      disp(A);*/

      AData[j + 1][j] -= b[j];
      for (int k = j + 1; k < m; k++) {
        AData[k][j] /= r;
      }

      double[] w = new double[n - j - 1];
      double[] u = new double[n - j - 1];
      double[] v = new double[n - j - 1];

      for (int i = j + 1, t = 0; i < m; i++, t++) {
        u[t] = AData[i][j];
      }

      // v = B33 * u
      for (int i = j + 1, t = 0; i < m; i++, t++) {
        double[] ARow_i = AData[i];
        s = 0;
        for (int k = j + 1, l = 0; k < n; k++, l++) {
          s += ARow_i[k] * u[l];
        }
        v[t] = s;
      }

      c = ArrayOperator.innerProduct(u, v) / 2;
      for (int i = j + 1, t = 0; i < m; i++, t++) {
        w[t] = v[t] - c * u[t];
      }

      /*disp(w);
      Matrix B33 = new DenseMatrix(n - j - 1, n - j - 1, 0);
      for (int i = j + 1, t = 0; i < m; i++, t++) {
      	double[] ARow_i = AData[i];
      	for (int k = j + 1, l = 0; k < n; k++, l++) {
      		B33.setEntry(t, l, ARow_i[k]);
      	}
      }
      disp(B33);
      Matrix U = new DenseMatrix(u, 1);
      disp(U.transpose().mtimes(U));
      disp(U);
      Matrix W = B33.mtimes(U).minus(U);
      disp(W);
      disp(B33.minus(plus(U.mtimes(W.transpose()), W.mtimes(U.transpose()))));
      Matrix Hk = eye(n - j - 1).minus(U.mtimes(U.transpose()));
      disp(Hk);
      disp(Hk.mtimes(B33).mtimes(Hk));*/
      for (int i = j + 1, t = 0; i < m; i++, t++) {
        double[] ARow_i = AData[i];
        for (int k = j + 1, l = 0; k < n; k++, l++) {
          ARow_i[k] = ARow_i[k] - (u[t] * w[l] + w[t] * u[l]);
        }
      }
      // disp(A);

      /*fprintf("Householder transformation on n - 1 columns:\n");
      disp(A);*/
      // disp(A);
      // Householder transformation on rows of A(j:m, j+1:n)

    }
    a[n - 2] = AData[n - 2][n - 2];
    a[n - 1] = AData[n - 1][n - 1];
    b[n - 2] = AData[n - 1][n - 2];
    QT = unpack(A, a, b, computeV);
    return QT;
  }