/** Checks to see if all the real eigenvectors are linearly independent of each other. */
  public void testVectorsLinearlyIndependent(SymmetricQRAlgorithmDecomposition alg) {
    int N = alg.getNumberOfEigenvalues();

    // create a matrix out of the eigenvectors
    Matrix A = Matrix.create(N, N);

    int off = 0;
    for (int i = 0; i < N; i++) {
      AVector 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);
    A = A.reshape(N - off, N);

    AltLU lu = new AltLU();
    lu._decompose(A);
    assertFalse(lu.isSingular());
    //        assertTrue(MatrixFeatures.isRowsLinearIndependent(A));
  }
  /**
   * Preforms standard tests that can be performed on any decomposition without prior knowledge of
   * what the results should be.
   */
  public void performStandardTests(SymmetricQRAlgorithmDecomposition alg, Matrix A, int numReal) {

    // basic sanity tests
    assertEquals(A.rowCount(), alg.getNumberOfEigenvalues());

    if (numReal >= 0) {
      for (int i = 0; i < A.rowCount(); i++) {
        Vector2 v = alg.getEigenvalue(i);

        assertFalse(Double.isNaN(v.x));
        if (v.y == 0) numReal--;
        else if (Math.abs(v.y) < 10 * 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);
    }
  }
  /** If the eigenvalues are all known, real, and the same this can be used to check them. */
  public void testEigenvalues(SymmetricQRAlgorithmDecomposition alg, double expected) {

    for (int i = 0; i < alg.getNumberOfEigenvalues(); i++) {
      Vector2 c = alg.getEigenvalue(i);

      assertTrue(c.y == 0);

      assertEquals(expected, c.x, 1e-8);
    }
  }
  public void checkAllZeros() {
    Matrix A = Matrix.create(5, 5);

    SymmetricQRAlgorithmDecomposition alg = createDecomposition();

    assertNotNull(alg.decompose(A));

    performStandardTests(alg, A, 5);
    testEigenvalues(alg, 0);
  }
  /**
   * 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() {
    Matrix A =
        Matrix.create(
            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}
            });

    SymmetricQRAlgorithmDecomposition alg = createDecomposition();

    assertNotNull(alg.decompose(A));

    performStandardTests(alg, A, 1);
  }
  public void checkLargeValue(boolean symmetric) {
    for (int i = 0; i < 20; i++) {
      SymmetricQRAlgorithmDecomposition alg = createDecomposition();
      Matrix z = Matrix.createRandom(3, 3);
      Matrix A = z.innerProduct(z.getTranspose()); // symmetric

      //            CommonOps.scale(1e-200,A);
      //            A.scale(1e100);

      assertNotNull(alg.decompose(A));

      performStandardTests(alg, A, -1);
    }
  }
  public void checkIdentity() {
    Matrix I = Matrix.createIdentity(4);

    SymmetricQRAlgorithmDecomposition alg = createDecomposition();

    assertNotNull(alg.decompose(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);
  }
  /** 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);
        AMatrix z = Matrixx.createRandomMatrix(3, 3);
        AMatrix A = z.innerProduct(z.getTranspose());

        SymmetricQRAlgorithmDecomposition alg = createDecomposition();

        assertNotNull(alg.decompose(A));

        performStandardTests(alg, A.toMatrix(), -1);
      }
    }
  }
  /** Sees if it correctly computed the eigenvalues. Does not check eigenvectors. */
  public void checkKnownSymmetric_JustValue() {
    Matrix A =
        Matrix.create(
            new double[][] {
              {0.98139, 0.78650, 0.78564},
              {0.78650, 1.03207, 0.29794},
              {0.78564, 0.29794, 0.91926}
            });
    SymmetricQRAlgorithmDecomposition alg = createDecomposition();

    assertNotNull(alg.decompose(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() {
    Matrix A =
        Matrix.create(
            new double[][] {
              {-0.418284, 0.279875, 0.452912},
              {-0.093748, -0.045179, 0.310949},
              {0.250513, -0.304077, -0.031414}
            });

    SymmetricQRAlgorithmDecomposition alg = createDecomposition();

    assertNotNull(alg.decompose(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);
  }
  public void testForEigenvalue(
      SymmetricQRAlgorithmDecomposition alg,
      Matrix A,
      double valueReal,
      double valueImg,
      int numMatched) {
    int N = alg.getNumberOfEigenvalues();

    int numFound = 0;
    for (int i = 0; i < N; i++) {
      Vector2 c = alg.getEigenvalue(i);

      if (Math.abs(c.x - valueReal) < 1e-4 && Math.abs(c.y - valueImg) < 1e-4) {
        numFound++;
      }
    }

    assertEquals(numMatched, numFound);
  }
  /**
   * 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};

    SymmetricQRAlgorithmDecomposition 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++) {
        Matrix A = Matrix.createRandom(N, N);
        A.multiply(2);
        A.sub(1);

        assertNotNull(alg.decompose(A));

        performStandardTests(alg, A, -1);
      }
    }
  }
  /** See if eigenvalues cause the characteristic equation to have a value of zero */
  public void checkCharacteristicEquation(SymmetricQRAlgorithmDecomposition alg, Matrix A) {
    int N = alg.getNumberOfEigenvalues();

    Matrix a = Matrix.create(A);

    for (int i = 0; i < N; i++) {
      Vector2 c = alg.getEigenvalue(i);

      if (Math.abs(c.y - 0) < 1e-8) {
        // test using the characteristic equation
        Matrix temp = Matrix.createIdentity(A.columnCount());
        temp.scale(c.x);
        temp.sub(a);
        double det = temp.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);
      }
    }
  }
  /** Sees if the pair of eigenvalue and eigenvector was found in the decomposition. */
  public void testForEigenpair(
      SymmetricQRAlgorithmDecomposition alg, double valueReal, double valueImg, double... vector) {
    int N = alg.getNumberOfEigenvalues();

    int numMatched = 0;
    for (int i = 0; i < N; i++) {
      Vector2 c = alg.getEigenvalue(i);

      if (Math.abs(c.x - valueReal) < 1e-4 && Math.abs(c.y - valueImg) < 1e-4) {

        //                if( c.isReal() ) {
        if (Math.abs(c.y - 0) < 1e-8)
          if (vector.length > 0) {
            AVector v = alg.getEigenVector(i);
            AMatrix e = Matrix.createFromRows(vector);
            e = e.getTranspose();

            Matrix t = Matrix.create(v.length(), 1);
            t.setColumn(0, v);
            double error = diffNormF(e, t);
            //                        CommonOps.changeSign(e);
            e.multiply(-1);
            double error2 = diffNormF(e, t);

            if (error < 1e-3 || error2 < 1e-3) numMatched++;
          } else {
            numMatched++;
          }
        else if (Math.abs(c.y - 0) > 1e-8) {
          numMatched++;
        }
      }
    }

    assertEquals(1, numMatched);
  }
  public SymmetricQRAlgorithmDecomposition createDecomposition() {
    SymmetricQRAlgorithmDecomposition alg = new SymmetricQRAlgorithmDecomposition(computeVectors);
    if (computeVectors) alg.setComputeVectorsWithValues(together);

    return alg;
  }
  /**
   * 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(SymmetricQRAlgorithmDecomposition alg, Matrix A) {
    //
    // System.out.println("-------------------------------------------------------------------------");
    int N = alg.getNumberOfEigenvalues();

    for (int i = 0; i < N; i++) {
      Vector2 c = alg.getEigenvalue(i);
      AVector v = alg.getEigenVector(i);

      if (Double.isInfinite(c.x)
          || Double.isNaN(c.x)
          || Double.isInfinite(c.y)
          || Double.isNaN(c.y)) fail("Uncountable eigenvalue");

      if (Math.abs(c.y) > 1e-20) {
        assertTrue(v == null);
      } else {
        assertTrue(v != null);
        //                if( MatrixFeatures.hasUncountable(v)) {
        //                    throw new RuntimeException("Egads");
        //                }
        assertFalse(v.hasUncountable());

        //                CommonOps.mult(A,v,tempA);
        Matrix ta = Matrix.create(A.rowCount(), 1);
        ta.setColumn(0, (v));
        AMatrix tempA = Multiplications.multiply(A, ta);
        //                CommonOps.scale(c.real,v,tempB);
        Matrix tb = Matrix.create(v.length(), 1);
        tb.setColumn(0, v);
        AMatrix tempB = tb.multiplyCopy(c.x);
        //                double max = NormOps.normPInf(A);
        double max = normPInf(A);
        if (max == 0) max = 1;

        double error = diffNormF(tempA, tempB) / max;

        if (error > 1e-12) {
          //                    System.out.println("Original matrix:");
          //                    System.out.println(A);
          //                    A.print();
          //                    System.out.println("Eigenvalue = "+c.x);
          //                    Eigenpair 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);
      }
    }
  }