#include <gtest/gtest.h>

#include <climits>
#include <cstdlib>
#include <iostream>
#include <list>
#include <vector>


#include "base/consts.h"
#include "base/datatypes.h"
#include "tbpm/propagation.h"


namespace {

TEST(Bessel, positve)
{
    double epsilon = 1e-14; // 
    
    std::vector<double> Jn;
    double tol = 1e-10;
    double x = 35000.;
    double max_order = static_cast<int>(std::abs(x)) + 1000;
    
    tbplas::tbpm::calculate_bessel_j_series(x, max_order, epsilon, Jn);
    ASSERT_LT(std::abs(Jn[max_order]),epsilon);
    for (int n = 0; n < max_order; ++n){
        if(Jn[n]>epsilon){
            ASSERT_NEAR(Jn[n], bessel::cyl_j(n , x),tol);
        }  
    }

    tol = 1e-14;
    x = 100.;
    max_order = static_cast<int>(std::abs(x)) + 1000;
    tbplas::tbpm::calculate_bessel_j_series(x, max_order, epsilon, Jn);
    ASSERT_LT(std::abs(Jn[max_order]),epsilon);
    for (int n = 0; n < max_order; ++n){
        if(Jn[n]>epsilon){
            ASSERT_NEAR(Jn[n], bessel::cyl_j(n , x),tol);
        }  
    }
}

TEST(Bessel, zero)
{
    double epsilon = 1e-14; // 
    
    std::vector<double> Jn;
    double tol = 1e-15;
    double x = 1e-10;
    double max_order = static_cast<int>(std::abs(x)) + 1000;
    
    tbplas::tbpm::calculate_bessel_j_series(x, max_order, epsilon, Jn);
    ASSERT_LT(std::abs(Jn[max_order]),epsilon);
    for (int n = 0; n < max_order; ++n){
        if(Jn[n]>epsilon){
            ASSERT_NEAR(Jn[n], bessel::cyl_j(n , x),tol);
        }  
    }

    tol = 1e-15;
    x = 0.;
    max_order = static_cast<int>(std::abs(x)) + 1000;
    tbplas::tbpm::calculate_bessel_j_series(x, max_order, epsilon, Jn);
    ASSERT_EQ(Jn.size(),1001);
    for (int n = 0; n < max_order; ++n){
        if(Jn[n]>epsilon){
            ASSERT_NEAR(Jn[n], bessel::cyl_j(n , x),tol);
        }  
    }
}


TEST(Bessel, negtive)
{
    double epsilon = 1e-14; // 
    
    std::vector<double> Jn;
    double tol = 1e-10;
    double x = -35000.;
    double max_order = static_cast<int>(std::abs(x)) + 1000;
    
    tbplas::tbpm::calculate_bessel_j_series(x, max_order, epsilon, Jn);
    ASSERT_LT(std::abs(Jn[max_order]),epsilon);
    ASSERT_LT(std::abs(Jn[max_order]),epsilon);
    for (int n = 0; n < max_order; ++n){
        if(Jn[n]>epsilon){
            ASSERT_NEAR(Jn[n], bessel::cyl_j(n , x),tol);
        }  
    }

    tol = 1e-14;
    x = -100.;
    max_order = static_cast<int>(std::abs(x)) + 1000;
    tbplas::tbpm::calculate_bessel_j_series(x, max_order, epsilon, Jn);
    ASSERT_LT(std::abs(Jn[max_order]),epsilon);
    for (int n = 0; n < max_order; ++n){
        if(Jn[n]>epsilon){
            ASSERT_NEAR(Jn[n], bessel::cyl_j(n , x),tol);
        }  
    }
}

TEST(Bessel_vector, all)
{
    double epsilon = 1e-16; // 
    
    Eigen::VectorXcd bes_vec, bes_vec1;
    double tol = 1e-10;
    double x = 35000.;
    tbplas::tbpm::build_bessel_vector_new(x,epsilon,bes_vec);
    tbplas::tbpm::build_bessel_vector(x,epsilon,bes_vec1);
    ASSERT_EQ(bes_vec1.size(),bes_vec.size());
    for (int n = 0; n < bes_vec1.size(); ++n){
        ASSERT_NEAR(bes_vec1[n].real(), bes_vec[n].real(), tol)<< "with index " << n;
        ASSERT_NEAR(bes_vec1[n].imag(), bes_vec[n].imag(), tol);
    }

    tol = 1e-10;
    x = 0.;
    tbplas::tbpm::build_bessel_vector_new(x,epsilon,bes_vec);
    tbplas::tbpm::build_bessel_vector(x,epsilon,bes_vec1);
    ASSERT_EQ(bes_vec1.size(),bes_vec.size());
    for (int n = 0; n < bes_vec1.size(); ++n){
        ASSERT_NEAR(bes_vec1[n].real(), bes_vec[n].real(), tol)<< "with index " << n;
        ASSERT_NEAR(bes_vec1[n].imag(), bes_vec[n].imag(), tol);
    }

    tol = 1e-10;
    x = -35000.;
    tbplas::tbpm::build_bessel_vector_new(x,epsilon,bes_vec);
    tbplas::tbpm::build_bessel_vector(x,epsilon,bes_vec1);
    ASSERT_EQ(bes_vec1.size(),bes_vec.size());
    for (int n = 0; n < bes_vec1.size(); ++n){
        ASSERT_NEAR(bes_vec1[n].real(), bes_vec[n].real(), tol)<< "with index " << n;
        ASSERT_NEAR(bes_vec1[n].imag(), bes_vec[n].imag(), tol);
    }
}

}