/**
   * Extracts the epipoles from the trifocal tensor. Extracted epipoles will have a norm of 1 as an
   * artifact of using SVD.
   *
   * @param tensor Input: Trifocal tensor. Not Modified
   * @param e2 Output: Epipole in image 2. Homogeneous coordinates. Modified
   * @param e3 Output: Epipole in image 3. Homogeneous coordinates. Modified
   */
  public void process(TrifocalTensor tensor, Point3D_F64 e2, Point3D_F64 e3) {
    svd.decompose(tensor.T1);
    SingularOps.nullVector(svd, true, v1);
    SingularOps.nullVector(svd, false, u1);

    svd.decompose(tensor.T2);
    SingularOps.nullVector(svd, true, v2);
    SingularOps.nullVector(svd, false, u2);

    svd.decompose(tensor.T3);
    SingularOps.nullVector(svd, true, v3);
    SingularOps.nullVector(svd, false, u3);

    for (int i = 0; i < 3; i++) {
      U.set(i, 0, u1.get(i));
      U.set(i, 1, u2.get(i));
      U.set(i, 2, u3.get(i));

      V.set(i, 0, v1.get(i));
      V.set(i, 1, v2.get(i));
      V.set(i, 2, v3.get(i));
    }

    svd.decompose(U);
    SingularOps.nullVector(svd, false, tempE);
    e2.set(tempE.get(0), tempE.get(1), tempE.get(2));

    svd.decompose(V);
    SingularOps.nullVector(svd, false, tempE);
    e3.set(tempE.get(0), tempE.get(1), tempE.get(2));
  }
  /**
   * Computes a basis (the principle 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);
  }