/* -*- mia-c++  -*-
 *
 * This file is part of MIA - a toolbox for medical image analysis 
 * Copyright (c) Leipzig, Madrid 1999-2017 Gert Wollny
 *
 * MIA is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with MIA; if not, see <http://www.gnu.org/licenses/>.
 *
 */

#include <climits>

#define VSTREAM_DOMAIN "test-critical-points"
#include <mia/internal/autotest.hh>


#include <mia/3d/critical_point.hh>

using namespace mia; 

BOOST_AUTO_TEST_CASE( test_critical_point_simple )
{
        C3DCriticalPoint cp_0;

        BOOST_CHECK_EQUAL(cp_0.get_point(), C3DFVector::_0);
        BOOST_CHECK_EQUAL(cp_0.get_a(), C3DFMatrix::_0);
        BOOST_CHECK_EQUAL(cp_0.get_gamma(), 0.0f); 


        C3DFVector x(1,2,3); 
        C3DCriticalPoint cp_1(x);
        BOOST_CHECK_EQUAL(cp_1.get_point(), x);

        cp_1.set_a(C3DFMatrix::diagonal(C3DFVector(3.0f,2.0f,0.5f)));
        cp_1.set_gamma(0.1f);
        
        BOOST_CHECK_EQUAL(cp_1.get_gamma(), 0.1f);

        // check center 
        BOOST_CHECK_EQUAL(cp_1.at(x), C3DFVector::_0); 

        C3DFVector val_2_1_1 = cp_1.at(C3DFVector(2,1,1)); // delta = 1, -1, -2
        // C3DFVector test_2_1_1(0.1 * 3 / 6, 0.1f/3.0f,  -2 * 0.5 / 6 ); 

        BOOST_CHECK_CLOSE(val_2_1_1.x, 0.05f, 0.1);
        BOOST_CHECK_CLOSE(val_2_1_1.y, -0.1f/3.0f, 0.1);
        BOOST_CHECK_CLOSE(val_2_1_1.z, -0.1f/6.0f, 0.1);

	C3DFVector val_2_1_1_alt = cp_1.at_alt(C3DFVector(2,0,1)); //delta = 1, -2, -2
        // 3.9 
        // C3DFVector test_2_1_1(3 / 3.9    -4/ 3.9  -1/ 3.9  ); 

        BOOST_CHECK_CLOSE(val_2_1_1_alt.x,  3.0f / 3.9f, 0.1);
        BOOST_CHECK_CLOSE(val_2_1_1_alt.y, -4.0f / 3.9f, 0.1);
        BOOST_CHECK_CLOSE(val_2_1_1_alt.z, -1.0f / 3.9f, 0.1);
        
}

BOOST_AUTO_TEST_CASE( test_critical_point_eigen_zero )
{
        C3DCriticalPointEigen cp(C3DFVector::_1, C3DFMatrix::_0);

        BOOST_CHECK_EQUAL(cp.get_type(), C3DCriticalPointEigen::ev_zero); 
}

BOOST_AUTO_TEST_CASE( test_critical_point_eigen_real )
{
        C3DCriticalPointEigen cp(C3DFVector::_1,
                                 C3DFMatrix::diagonal(C3DFVector(1,2,3)));

        BOOST_CHECK_EQUAL(cp.get_type(), C3DCriticalPointEigen::ev_real);

        BOOST_CHECK_EQUAL(cp.get_eval1(), 3.0f);
        BOOST_CHECK_EQUAL(cp.get_real_eval2(), 2.0f);
        BOOST_CHECK_EQUAL(cp.get_real_eval3(), 1.0f);
        BOOST_CHECK_EQUAL(cp.get_eval2(), 2.0f);
        BOOST_CHECK_EQUAL(cp.get_eval3(), 1.0f); 

        BOOST_CHECK_EQUAL(cp.get_location(), C3DFVector::_1); 

        BOOST_CHECK_EQUAL(cp.get_evect1(), C3DFVector(0,0,1));
        BOOST_CHECK_EQUAL(cp.get_real_evect2(), C3DFVector(0,1,0));
        BOOST_CHECK_EQUAL(cp.get_real_evect3(), C3DFVector(1,0,0)); 

}

BOOST_AUTO_TEST_CASE( test_critical_point_eigen_real_two )
{
        C3DCriticalPointEigen cp(C3DFVector::_1,
                                 C3DFMatrix::diagonal(C3DFVector(2,2,3)));

        BOOST_CHECK_EQUAL(cp.get_type(), C3DCriticalPointEigen::ev_real_two_equal);

        BOOST_CHECK_EQUAL(cp.get_eval1(), 3.0f);
        BOOST_CHECK_EQUAL(cp.get_real_eval2(), 2.0f);
        BOOST_CHECK_EQUAL(cp.get_real_eval3(), 2.0f);
        BOOST_CHECK_EQUAL(cp.get_eval2(), 2.0f);
        BOOST_CHECK_EQUAL(cp.get_eval3(), 2.0f);
        
        BOOST_CHECK_EQUAL(cp.get_evect1(), C3DFVector(0,0,1));
        BOOST_CHECK_EQUAL(cp.get_real_evect2(), C3DFVector(1,0,0));
        BOOST_CHECK_EQUAL(cp.get_real_evect3(), C3DFVector(0,1,0)); 
        
}

BOOST_AUTO_TEST_CASE( test_critical_point_eigen_real_three )
{
        C3DCriticalPointEigen cp(C3DFVector::_1,
                                 C3DFMatrix::diagonal(C3DFVector(2,2,2)));

        BOOST_CHECK_EQUAL(cp.get_type(), C3DCriticalPointEigen::ev_real_three_equal);

        BOOST_CHECK_EQUAL(cp.get_eval1(), 2.0f);
        BOOST_CHECK_EQUAL(cp.get_real_eval2(), 2.0f);
        BOOST_CHECK_EQUAL(cp.get_real_eval3(), 2.0f);
        BOOST_CHECK_EQUAL(cp.get_eval2(), 2.0f);
        BOOST_CHECK_EQUAL(cp.get_eval3(), 2.0f);
        
        BOOST_CHECK_EQUAL(cp.get_evect1(), C3DFVector(0,0,1));
        BOOST_CHECK_EQUAL(cp.get_real_evect2(), C3DFVector(1,0,0));
        BOOST_CHECK_EQUAL(cp.get_real_evect3(), C3DFVector(0,1,0)); 
        
}

BOOST_AUTO_TEST_CASE( test_critical_point_eigen_two_complex )
{
        C3DCriticalPointEigen cp(C3DFVector::_1,
                                 C3DFMatrix(C3DFVector(1,0,0),
                                            C3DFVector(0,cos(M_PI/3),sin(M_PI/3)),
                                            C3DFVector(0,-sin(M_PI/3),cos(M_PI/3))));

        BOOST_CHECK_EQUAL(cp.get_type(), C3DCriticalPointEigen::ev_complex);

        BOOST_CHECK_EQUAL(cp.get_eval1(), 1.0f);
        auto cev2 = cp.get_complex_eval2(); 
        BOOST_CHECK_CLOSE(cev2.real(), 0.5f, 0.1);
        BOOST_CHECK_CLOSE(cev2.imag(), sqrt(3.0f)/2.0f, 0.1);

        auto cev3 = cp.get_complex_eval3(); 
        BOOST_CHECK_CLOSE(cev3.real(), 0.5f, 0.1);
        BOOST_CHECK_CLOSE(cev3.imag(), sqrt(3.0f)/2.0f, 0.1);

        // use directly what comes from the library eigen3
        
        BOOST_CHECK_EQUAL(cp.get_evect1(), C3DFVector(1,0,0));

        auto evect2 = cp.get_complex_evect2();
        BOOST_CHECK_SMALL(evect2.x.real(), 1e-5f);
        BOOST_CHECK_SMALL(evect2.x.imag(), 1e-5f);
        BOOST_CHECK_CLOSE(evect2.y.real(), 1.0f/sqrt(2.0f), 0.1);
        BOOST_CHECK_SMALL(evect2.y.imag(), 1e-5f);
        BOOST_CHECK_SMALL(evect2.z.real(), 1e-5f);
        BOOST_CHECK_CLOSE(evect2.z.imag(), 1.0f/sqrt(2.0f), 0.1);


        auto evect3 = cp.get_complex_evect3();
        BOOST_CHECK_SMALL(evect3.x.real(), 1e-5f);
        BOOST_CHECK_SMALL(evect3.x.imag(), 1e-5f);
        BOOST_CHECK_CLOSE(evect3.y.real(), 1.0f/sqrt(2.0f), 1e-5f);
        BOOST_CHECK_SMALL(evect3.y.imag(), 1e-5f);
        BOOST_CHECK_SMALL(evect3.z.real(), 1e-5f);
        BOOST_CHECK_CLOSE(evect3.z.imag(), 1.0f/sqrt(2.0f), 1e-5f);

        
        
}
                
