#!/usr/bin/env ruby
# Ruby/GSL implementation of GSL "eigen/test.c"
require("gsl")
require("../gsl_test.rb")
include GSL::Test

def create_random_symm_matrix(size1, size2, rng, lower, upper)
  m = GSL::Matrix.alloc(size1, size2)
  for i in 0...size1 do
    for j in i...size2 do
      x = rng.uniform()*(upper-lower) + lower
      m[i,j] = x
      m[j,i] = x
    end
  end
  m
end

def create_random_herm_matrix(size1, size2, rng, lower, upper)
  m = GSL::Matrix::Complex.alloc(size1, size2)
  for i in 0...size1 do
    for j in i...size2 do
      re = rng.uniform()* (upper - lower) + lower
      if i == j
      	im = 0.0
      else
        im = rng.uniform()* (upper - lower) + lower
      end
      z = GSL::Complex.alloc(re, im)
      m[i,j] = z
      m[j,i] = z.conjugate
    end
  end
  m
end

def create_random_posdef_matrix(size1, size2, rng)
  m = GSL::Matrix.alloc(size1, size2)
  x = rng.uniform()
  for i in 0...size1 do
    for j in i...size2 do
      a = pow(x, (j - i).to_f)
      m[i,j] = a
      m[j,i] = a
    end
  end
  m
end

def create_random_complex_posdef_matrix(size1, size2, rng)
  m = GSL::Matrix::Complex.calloc(size1, size2)
  n = size1
  
  for i in 0...n do
    x = rng.uniform()
    z = GSL::Complex.alloc(x, 0.0)
    m[i,i] = z
  end
  
  work = GSL::Vector::Complex.alloc(n)
  for i in 0...n do
    for j in 0...n do
      x = 2.0*rng.uniform() - 1.0
      y = 2.0*rng.uniform() - 1.0			
      z = GSL::Complex.alloc(x, y)
      work[j] 
    end
    tau = GSL::Linalg::Complex::householder_transform(work)
    GSL::Linalg::Complex::householder_hm(tau, work, m)
    GSL::Linalg::Complex::householder_mh(tau.conjugate, work, m)		
  end
  m
end

def create_random_nonsymm_matrix(size1, size2, rng, lower, upper)
  m = GSL::Matrix.alloc(size1, size2)
  for i in 0...size1 do
    for j in 0...size2 do
      m[i,j] = rng.uniform()*(upper - lower) + lower
    end
  end
  m
end

def test_eigen_results(n, m, eval, evec, desc, desc2)
  x = GSL::Vector.alloc(n)
  y = GSL::Vector.alloc(n)
  
  # check eigenvalues
  for i in 0...n
    ei = eval[i]
    vi = evec.col(i)
    GSL::Vector.memcpy(x, vi)
    y = GSL::Blas.dgemv(GSL::Blas::NoTrans, 1.0, m, x, 0.0, y)
#    y = GSL::Blas.dgemv(GSL::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 = GSL::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 = GSL::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 = GSL::Vector::Complex.alloc(n)
  y = GSL::Vector::Complex.alloc(n)

# check eigenvalues
  for i in 0...n
    ei = eval[i]
    vi = evec.col(i)
    GSL::Vector::Complex.memcpy(x, vi)
    c1 = GSL::Complex.alloc(1.0, 0.0)
    c0 = GSL::Complex.alloc(0.0, 0.0)
    y = GSL::Blas.zgemv(GSL::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 = GSL::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 = GSL::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 chop_subnormals(x)
  # Chop any subnormal values */
  return x.abs < GSL::DBL_MIN ? 0 : x
end

def test_eigenvalues_real(eval, eval2, desc, desc2)
  n = eval.size
  emax = 0
  # check eigenvalues 
  for i in 0...n do
    ei = eval[i]
    if ei.abs > emax
      emax = ei.abs
    end
  end
  
  for i in 0...n do
    ei = eval[i]
    e2i = chop_subnormals(eval2[i])
    GSL::Test::test_abs(ei, e2i, emax * 1e8 * GSL::DBL_EPSILON,  "#{desc}, direct eigenvalue(#{i}), #{desc2}")
  end
end

def test_eigenvalues_complex(eval, eval2, desc, desc2)
  n = eval.size
  for i in 0...n do
    ei = eval[i]
    e2i = eval2[i]
    GSL::Test::test_rel(ei.real, e2i.real, 10*n*GSL::DBL_EPSILON, 
                        "#{desc}, direct eigenvalue(#{i}) real, #{desc2}")
    
    GSL::Test::test_rel(ei.imag, e2i.imag, 10*n*GSL::DBL_EPSILON, 
                        "#{desc}, direct eigenvalue(#{i}) imag, #{desc2}")		
  end
end

def test_eigen_schur(a, s, q, z, count, desc, desc2)
  n = a.size1
  
  t1 = a*z
  t2 = q*s
  for i in 0...n do
    for j in 0...n do
      x = t1[i,j]
      y = t2[i,j]
      GSL::test_abs(x, y, 1.0e8 * GSL::DBL_EPSILON,
                    "#{desc}(N=#{n},cnt=#{count}), #{desc2}, schur(#{i},#{j})")
    end
  end
end
