#include "qc/matrix.hpp"

#include <cmath>
#include <cstdio>
#include <stdexcept>

static void expect_near(const char* name, double a, double b, double tol) {
  if (std::abs(a - b) > tol) {
    std::fprintf(stderr, "FAIL %s: got %.12g expected %.12g\n", name, a, b);
    throw std::runtime_error(name);
  }
}

int main() {
  qc::Matrix A(2, 2);
  A(0, 0) = 4.0;
  A(0, 1) = 1.0;
  A(1, 0) = 1.0;
  A(1, 1) = 3.0;
  auto d = qc::symmetric_eigen_jacobi(std::move(A));
  const double rt5 = std::sqrt(5.0);
  expect_near("e0", d.eigenvalues[0], 0.5 * (7.0 - rt5), 1e-10);
  expect_near("e1", d.eigenvalues[1], 0.5 * (7.0 + rt5), 1e-10);

  qc::Matrix S(2, 2);
  S(0, 0) = 1.0;
  S(0, 1) = 0.5;
  S(1, 0) = 0.5;
  S(1, 1) = 1.0;
  qc::Matrix F(2, 2);
  F(0, 0) = 2.0;
  F(0, 1) = 0.25;
  F(1, 0) = 0.25;
  F(1, 1) = 3.0;
  qc::Vector w;
  qc::Matrix V;
  qc::generalized_symmetric_eigen(F, S, w, V);
  const double orth =
      V(0, 0) * (S(0, 0) * V(0, 0) + S(0, 1) * V(1, 0)) +
      V(1, 0) * (S(1, 0) * V(0, 0) + S(1, 1) * V(1, 0));
  expect_near("V^T S V", orth, 1.0, 1e-9);

  qc::Matrix h(2, 2);
  h(0, 0) = -1.12006617;
  h(0, 1) = -0.95775913;
  h(1, 0) = -0.95775913;
  h(1, 1) = -1.12006617;
  qc::Matrix S2(2, 2);
  S2(0, 0) = 1.00000026;
  S2(0, 1) = 0.65897215;
  S2(1, 0) = 0.65897215;
  S2(1, 1) = 1.00000026;
  qc::Vector ew;
  qc::Matrix ev;
  qc::generalized_symmetric_eigen(h, S2, ew, ev);
  expect_near("h2 core e0", ew[0], -1.25247731, 1e-5);
  expect_near("h2 core e1", ew[1], -0.47593449, 1e-5);

  std::puts("ok");
  return 0;
}
