/* -*- 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 <mia/internal/plugintester.hh>
#include <mia/core/fastica/deflationnonlinearity.hh>

using namespace fastica_deflnonlin;
using namespace mia;
using namespace gsl;
using namespace std;

struct DeflationNonlinearityFixture {

       DeflationNonlinearityFixture();
       Matrix signal;
       Vector w;

};

struct NonlinearitySaddlevalueFixture {

       NonlinearitySaddlevalueFixture();
       Vector u;
};

struct NonlinearitySaddleMatrixFixture {

       NonlinearitySaddleMatrixFixture();
       Matrix U;
};


BOOST_FIXTURE_TEST_CASE( test_pow3_nonlinearity, DeflationNonlinearityFixture )
{
       auto plugin = BOOST_TEST_create_from_plugin<CFastICADeflPow3Plugin>("pow3");
       plugin->set_signal(&signal);
       plugin->apply(w);
       BOOST_CHECK_CLOSE(w[0],  0.67748, 0.01);
       BOOST_CHECK_CLOSE(w[1], -0.48505, 0.01);
       BOOST_CHECK_CLOSE(w[2], -1.35082, 0.01);
       BOOST_CHECK_CLOSE(w[3], -0.35110, 0.01);
}


BOOST_FIXTURE_TEST_CASE( test_pow3_SIR,  NonlinearitySaddlevalueFixture )
{
       auto plugin = BOOST_TEST_create_from_plugin<CFastICADeflPow3Plugin>("pow3");
       BOOST_CHECK_CLOSE(plugin->get_saddle_test_value(u), 8.9148, 0.1);
}

BOOST_FIXTURE_TEST_CASE( test_pow3_SIR_Matrix,  NonlinearitySaddleMatrixFixture )
{
       auto plugin = BOOST_TEST_create_from_plugin<CFastICADeflPow3Plugin>("pow3");
       auto values = plugin->get_saddle_test_table(U);
       BOOST_CHECK_CLOSE(values[0], 8.9148, 0.1);
       BOOST_CHECK_CLOSE(values[1], 8.1902, 0.1);
}



BOOST_FIXTURE_TEST_CASE( test_pow3_nonlinearity_stabelized, DeflationNonlinearityFixture )
{
       auto plugin = BOOST_TEST_create_from_plugin<CFastICADeflPow3Plugin>("pow3");
       plugin->set_signal(&signal);
       plugin->set_mu(0.1);
       plugin->apply(w);
       BOOST_CHECK_CLOSE(w[0], -0.22633, 0.01);
       BOOST_CHECK_CLOSE(w[1], 0.16169, 0.01);
       BOOST_CHECK_CLOSE(w[2], 0.45097, 0.01);
       BOOST_CHECK_CLOSE(w[3], 0.11686, 0.01);
}

BOOST_FIXTURE_TEST_CASE( test_tanh_nonlinearity, DeflationNonlinearityFixture )
{
       auto plugin = BOOST_TEST_create_from_plugin<CFastICADeflTanhPlugin>("tanh:a=1.5");
       plugin->set_signal(&signal);
       plugin->apply(w);
       BOOST_CHECK_CLOSE(w[0],  0.26363, 0.01);
       BOOST_CHECK_CLOSE(w[1], -0.22528, 0.01);
       BOOST_CHECK_CLOSE(w[2], -0.57095, 0.01);
       BOOST_CHECK_CLOSE(w[3], -0.17719, 0.01);
}


BOOST_FIXTURE_TEST_CASE( test_tanh_1_SIR,  NonlinearitySaddlevalueFixture )
{
       auto plugin = BOOST_TEST_create_from_plugin<CFastICADeflTanhPlugin>("tanh");
       BOOST_CHECK_CLOSE(plugin->get_saddle_test_value(u), 0.11077, 0.1);
}

BOOST_FIXTURE_TEST_CASE( test_tanh_SIR_Matrix,  NonlinearitySaddleMatrixFixture )
{
       auto plugin = BOOST_TEST_create_from_plugin<CFastICADeflTanhPlugin>("tanh");
       auto values = plugin->get_saddle_test_table(U);
       BOOST_CHECK_CLOSE(values[0], 0.11077, 0.1);
       BOOST_CHECK_CLOSE(values[1], 0.070633, 0.1);
}



BOOST_FIXTURE_TEST_CASE( test_tanh_nonlinearity_stabelized, DeflationNonlinearityFixture )
{
       auto plugin = BOOST_TEST_create_from_plugin<CFastICADeflTanhPlugin>("tanh:a=1.5");
       plugin->set_signal(&signal);
       plugin->set_mu(0.1);
       plugin->apply(w);
       BOOST_CHECK_CLOSE(w[0], -0.22299, 0.01);
       BOOST_CHECK_CLOSE(w[1], 0.16196, 0.01);
       BOOST_CHECK_CLOSE(w[2], 0.44761, 0.01);
       BOOST_CHECK_CLOSE(w[3], 0.11809, 0.01);
}


BOOST_FIXTURE_TEST_CASE( test_gauss_nonlinearity, DeflationNonlinearityFixture )
{
       auto plugin = BOOST_TEST_create_from_plugin<CFastICADeflGaussPlugin>("gauss:a=1.1");
       plugin->set_signal(&signal);
       plugin->apply(w);
       BOOST_CHECK_CLOSE(w[0],  0.17860, 0.01);
       BOOST_CHECK_CLOSE(w[1], -0.15242, 0.01);
       BOOST_CHECK_CLOSE(w[2], -0.38649, 0.01);
       BOOST_CHECK_CLOSE(w[3], -0.11984, 0.01);
}

BOOST_FIXTURE_TEST_CASE( test_gauss_SIR_matrix,  NonlinearitySaddleMatrixFixture )
{
       auto plugin = BOOST_TEST_create_from_plugin<CFastICADeflGaussPlugin>("gauss");
       auto values = plugin->get_saddle_test_table(U);
       BOOST_CHECK_CLOSE(values[0], 0.063381, 0.1);
       BOOST_CHECK_CLOSE(values[1], 0.036016, 0.1);
}

BOOST_FIXTURE_TEST_CASE( test_gauss_SIR,  NonlinearitySaddlevalueFixture )
{
       auto plugin = BOOST_TEST_create_from_plugin<CFastICADeflGaussPlugin>("gauss");
       BOOST_CHECK_CLOSE(plugin->get_saddle_test_value(u), 0.063381, 0.1);
}


BOOST_FIXTURE_TEST_CASE( test_gauss_nonlinearity_stabilized, DeflationNonlinearityFixture )
{
       auto plugin = BOOST_TEST_create_from_plugin<CFastICADeflGaussPlugin>("gauss:a=1.1");
       plugin->set_signal(&signal);
       plugin->set_mu(0.1);
       plugin->apply(w);
       BOOST_CHECK_CLOSE(w[0],  -0.22302, 0.01);
       BOOST_CHECK_CLOSE(w[1],  0.16196, 0.01);
       BOOST_CHECK_CLOSE(w[2],  0.44763, 0.01);
       BOOST_CHECK_CLOSE(w[3],  0.11809, 0.01);
}

const double init_signal[] = {
       -0.150788,  0.185673,  0.253528, -0.192714,  0.309594,  0.384079,  0.117799,  0.200267, -0.212151, -0.257026,
       0.221388, -0.166861,  0.128731, -0.207766, -0.041186, -0.089463, -0.151564,  0.249002, -0.060958,  0.223747,
       0.170328, -0.071203, -0.435380,  0.093816, -0.206290, -0.408690, -0.212328, -0.211139,  0.499941, -0.015164,
       -0.240928,  0.052391,  0.053121,  0.306664, -0.062118,  0.114074,  0.246093, -0.238130, -0.226832,  0.048444
};

DeflationNonlinearityFixture::DeflationNonlinearityFixture()
{
       double init_w[] = { -0.22638, 0.16168, 0.45102, 0.11684};
       signal = Matrix(4, 10, init_signal);
       w = Vector(4, false);
       std::copy(init_w, init_w + 4, w.begin());
}

struct SymmetryNonlinearityFixture {

       SymmetryNonlinearityFixture();
       Matrix signal;
       Matrix W;

};


BOOST_FIXTURE_TEST_CASE( test_pow_nonlinearity_stabilized_Symm, SymmetryNonlinearityFixture )
{
       auto plugin = BOOST_TEST_create_from_plugin<CFastICADeflPow3Plugin>("pow3");
       plugin->set_signal(&signal);
       plugin->apply(W);
       BOOST_CHECK_CLOSE(W(0, 0), 1.24114, 0.01);
       BOOST_CHECK_CLOSE(W(0, 1), -2.34633, 0.01);
       BOOST_CHECK_CLOSE(W(1, 0), -0.89385, 0.01);
       BOOST_CHECK_CLOSE(W(1, 1), -1.22523, 0.01);
       BOOST_CHECK_CLOSE(W(2, 0), -2.47949, 0.01);
       BOOST_CHECK_CLOSE(W(2, 1), -0.39022, 0.01);
       BOOST_CHECK_CLOSE(W(3, 0), -0.64952, 0.01);
       BOOST_CHECK_CLOSE(W(3, 1), -1.35443, 0.01);
}


BOOST_FIXTURE_TEST_CASE( test_tanh_nonlinearity_Symm, SymmetryNonlinearityFixture )
{
       auto plugin = BOOST_TEST_create_from_plugin<CFastICADeflTanhPlugin>("tanh:a=1.5");
       plugin->set_signal(&signal);
       plugin->apply(W);
       BOOST_CHECK_CLOSE(W(0, 0), 0.42007, 0.01);
       BOOST_CHECK_CLOSE(W(0, 1), -1.05476, 0.01);
       BOOST_CHECK_CLOSE(W(1, 0),  -0.36337, 0.01);
       BOOST_CHECK_CLOSE(W(1, 1), -0.58123, 0.01);
       BOOST_CHECK_CLOSE(W(2, 0), -0.91679, 0.01);
       BOOST_CHECK_CLOSE(W(2, 1), -0.24641, 0.01);
       BOOST_CHECK_CLOSE(W(3, 0),  -0.28688, 0.01);
       BOOST_CHECK_CLOSE(W(3, 1), -0.62782, 0.01);
}



SymmetryNonlinearityFixture::SymmetryNonlinearityFixture()
{
       double init_W[] = { -0.41718, 0.78261,
                           0.29795, 0.40841,
                           0.83116, 0.12941,
                           0.21532, 0.45165
                         };
       signal = Matrix(4, 10, init_signal);
       W = Matrix(4, 2, init_W);
}

NonlinearitySaddlevalueFixture::NonlinearitySaddlevalueFixture():
       u(7, false)
{
       double init_u[] = { .1, .2, .3, .4, .5, .1, .2 };
       copy(init_u, init_u + 7, u.begin());
}


NonlinearitySaddleMatrixFixture::NonlinearitySaddleMatrixFixture()
{
       double init_U[] = { .1, .2, .3, .4, .5, .1, .2,
                           .3, .1, .5, .7, .1, .9, .0
                         };
       U = Matrix(2, 7, init_U);
}
