#!/usr/bin/env ruby
require("gsl")
require("./gsl_test.rb")
include GSL::Test

def test_eigen_results(n, m, eval, evec, desc, desc2)
  x = Vector.new(n)
  y = Vector.new(n)

# check eigenvalues
  for i in 0...n
    ei = eval[i]
    vi = evec.col(i)
    Vector.memcpy(x, vi)
    y = Blas.dgemv(Blas::NoTrans, 1.0, m, x, 0.0, y)
#    y = Blas.dgemv(Blas::NoTrans, 1.0, m, x)
    for j in 0...n
      xj = x[j]
      yj = y[j]
      desc3 = sprintf("%s, eigenvalue(%d,%d), %s", desc, i, j, desc2)
      GSL::Test::test_rel(yj, ei*xj, 1e8*DBL_EPSILON, desc3)
    end
  end
# check eigenvectors are orthonormal
  for i in 0...n
    vi = evec.col(i)
    nrm_v = Blas.dnrm2(vi)
    desc3 = sprintf("%s, normalized(%d), %s", desc, i, desc2)
    GSL::Test::test_rel(nrm_v, 1.0, n*DBL_EPSILON, desc3)
  end

  for i in 0...n
    vi = evec.col(i)
    for j in (i+1)...n
      vj = evec.col(j)
      vivj = Blas.ddot(vi, vj)
      desc3 = sprintf("%s, orthogonal(%d,%d), %s", desc, i, j, desc2)
      GSL::Test::test_abs(vivj, 0.0, n*DBL_EPSILON, desc3)
    end
  end
end

def test_eigen_complex_results(n, m, eval, evec, desc, desc2)
  x = Vector::Complex.new(n)
  y = Vector::Complex.new(n)

# check eigenvalues
  for i in 0...n
    ei = eval[i]
    vi = evec.col(i)
    Vector::Complex.memcpy(x, vi)
    c1 = GSL::Complex.new(1.0, 0.0)
    c0 = GSL::Complex.new(0.0, 0.0)
    y = Blas.zgemv(Blas::NoTrans, c1, m, x, c0, y)
    for j in 0...n
      xj = x[j]
      yj = y[j]
      desc3 = sprintf("%s, eigenvalue(%d,%d), real, %s", desc, i, j, desc2)
      GSL::Test::test_rel(yj.re, ei*xj.re, 1e8*DBL_EPSILON, desc3)
      desc3 = sprintf("%s, eigenvalue(%d,%d), imag, %s", desc, i, j, desc2)
      GSL::Test::test_rel(yj.im, ei*xj.im, 1e8*DBL_EPSILON, desc3)
    end
  end
# check eigenvectors are orthonormal
  for i in 0...n
    vi = evec.col(i)
    nrm_v = Blas.dznrm2(vi)
    desc3 = sprintf("%s, normalized(%d), %s", desc, i, desc2)
    GSL::Test::test_rel(nrm_v, 1.0, n*DBL_EPSILON, desc3)
  end

  for i in 0...n
    vi = evec.col(i)
    for j in (i+1)...n
      vj = evec.col(j)
      vivj = Blas.zdotc(vi, vj)
      desc3 = sprintf("%s, orthogonal(%d,%d), %s", desc, i, j, desc2)
      GSL::Test::test_abs(vivj.abs, 0.0, n*DBL_EPSILON, desc3)
    end
  end
end

def test_eigenvalues(n, eval, eval2, desc, desc2)
  for i in 0...n
    ei = eval[i]
    e2i = eval2[i]
    desc3 = sprintf("%s, direct eigenvalue(%d), %s", desc, i, desc2)
    GSL::Test::test_rel(ei, e2i, GSL::DBL_EPSILON, desc3)
  end
end

def test_eigen_symm(desc, m)
  n = m.size1
  a = Matrix.new(n, n)

  w1 = Eigen::Symm::Workspace.new(n)
  w2 = Eigen::Symmv::Workspace.new(n)

  Matrix.memcpy(a, m)
  eval, evec = a.eigen_symmv(w2)
  test_eigen_results(n, m, eval, evec, desc, "unsorted")

  Matrix.memcpy(a, m)
  eval2 = a.eigen_symm(w1)
  test_eigenvalues(n, eval, eval2, desc, "unsorted")

  Eigen::Symmv::sort(eval, evec, Eigen::SORT_VAL_ASC)
  test_eigen_results(n, m, eval, evec, desc, "val/asc")
  Eigen::Symmv::sort(eval, evec, Eigen::SORT_VAL_DESC)
  test_eigen_results(n, m, eval, evec, desc, "val/desc")
  Eigen::Symmv::sort(eval, evec, Eigen::SORT_ABS_ASC)
  test_eigen_results(n, m, eval, evec, desc, "abs/asc")
  Eigen::Symmv::sort(eval, evec, Eigen::SORT_ABS_DESC)
  test_eigen_results(n, m, eval, evec, desc, "abs/desc")
end

def test_eigen_herm(desc, m)
  n = m.size1
  a = Matrix::Complex.new(n, n)

  w1 = Eigen::Herm::Workspace.new(n)
  w2 = Eigen::Hermv::Workspace.new(n)

  Matrix::Complex.memcpy(a, m)
  eval, evec = a.eigen_hermv(w2)
  test_eigen_complex_results(n, m, eval, evec, desc, "unsorted")

  Matrix::Complex.memcpy(a, m)
  eval2 = a.eigen_herm(w1)
  test_eigenvalues(n, eval, eval2, desc, "unsorted")

  Eigen::Hermv::sort(eval, evec, Eigen::SORT_VAL_ASC)
  test_eigen_complex_results(n, m, eval, evec, desc, "val/asc")
  Eigen::Hermv::sort(eval, evec, Eigen::SORT_VAL_DESC)
  test_eigen_complex_results(n, m, eval, evec, desc, "val/desc")
  Eigen::Hermv::sort(eval, evec, Eigen::SORT_ABS_ASC)
  test_eigen_complex_results(n, m, eval, evec, desc, "abs/asc")
  Eigen::Hermv::sort(eval, evec, Eigen::SORT_ABS_DESC)
  test_eigen_complex_results(n, m, eval, evec, desc, "abs/desc")
end



r = Matrix.new([0, 0, -1, 0], [0, 1, 0, 1], [-1, 0, 0, 0], [0, 1, 0, 0])
test_eigen_symm("symm(4)", r)

c = r.to_complex
test_eigen_herm("herm(4)", c)

r = Matrix.new(4, 4)
r.set_diagonal([1, 2, 3, 4])
test_eigen_symm("symm(4) diag", r)

c = r.to_complex
test_eigen_herm("herm(4) diag", c)



