/** {@inheritDoc} */
  public RealMatrix getV() throws InvalidMatrixException {

    if (cachedV == null) {

      if (m >= n) {
        // the tridiagonal matrix is Bt.B, where B is upper bidiagonal
        cachedV = transformer.getV().multiply(eigenDecomposition.getV());
      } else {
        // the tridiagonal matrix is B.Bt, where B is lower bidiagonal
        final double[][] eData = eigenDecomposition.getV().getData();
        final double[][] iData = new double[n][];
        double[] ei1 = eData[0];
        iData[0] = ei1;
        for (int i = 0; i < m - 1; ++i) {
          // compute Bt.E.S^(-1) where E is the eigenvectors matrix
          // we reuse the array from matrix E to store the result
          final double mi = mainBidiagonal[i];
          final double si = secondaryBidiagonal[i];
          final double[] ei0 = ei1;
          ei1 = eData[i + 1];
          iData[i + 1] = ei1;
          for (int j = 0; j < m; ++j) {
            ei0[j] = (mi * ei0[j] + si * ei1[j]) / singularValues[j];
          }
        }
        // last row
        final double lastMain = mainBidiagonal[m - 1];
        for (int j = 0; j < m; ++j) {
          ei1[j] *= lastMain / singularValues[j];
        }
        for (int i = m; i < n; ++i) {
          iData[i] = new double[m];
        }
        cachedV = transformer.getV().multiply(MatrixUtils.createRealMatrix(iData));
      }
    }

    // return the cached matrix
    return cachedV;
  }
  /**
   * Calculates the Singular Value Decomposition of the given matrix.
   *
   * @param matrix The matrix to decompose.
   * @exception InvalidMatrixException (wrapping a {@link
   *     org.apache.commons.math.ConvergenceException} if algorithm fails to converge
   */
  public SingularValueDecompositionImpl(RealMatrix matrix) throws InvalidMatrixException {

    m = matrix.getRowDimension();
    n = matrix.getColumnDimension();

    cachedU = null;
    cachedS = null;
    cachedV = null;
    cachedVt = null;

    // transform the matrix to bidiagonal
    transformer = new BiDiagonalTransformer(matrix);
    mainBidiagonal = transformer.getMainDiagonalRef();
    secondaryBidiagonal = transformer.getSecondaryDiagonalRef();

    // compute Bt.B (if upper diagonal) or B.Bt (if lower diagonal)
    mainTridiagonal = new double[mainBidiagonal.length];
    secondaryTridiagonal = new double[mainBidiagonal.length - 1];
    double a = mainBidiagonal[0];
    mainTridiagonal[0] = a * a;
    for (int i = 1; i < mainBidiagonal.length; ++i) {
      final double b = secondaryBidiagonal[i - 1];
      secondaryTridiagonal[i - 1] = a * b;
      a = mainBidiagonal[i];
      mainTridiagonal[i] = a * a + b * b;
    }

    // compute singular values
    eigenDecomposition =
        new EigenDecompositionImpl(mainTridiagonal, secondaryTridiagonal, MathUtils.SAFE_MIN);
    singularValues = eigenDecomposition.getRealEigenvalues();
    for (int i = 0; i < singularValues.length; ++i) {
      final double si = singularValues[i];
      singularValues[i] = (si < 0) ? 0.0 : Math.sqrt(si);
    }
  }