    // Copyright (C) 2013  Davis E. King (davis@dlib.net)
    // License: Boost Software License   See LICENSE.txt for the full license.

    #include <dlib/matrix.h>
    #include <sstream>
    #include <string>
    #include <ctime>
    #include <vector>
    #include <dlib/statistics.h>

    #include "tester.h"
    #include <dlib/svm.h>

    namespace
    {

        using namespace test;
        using namespace dlib;
        using namespace std;

        logger dlog("test.svr_linear_trainer");

        typedef matrix<double, 0, 1> sample_type;
        typedef std::vector<std::pair<unsigned int, double> > sparse_sample_type;

    // ----------------------------------------------------------------------------------------

        double sinc(double x)
        {
            if (x == 0)
                return 1;
            return sin(x)/x;
        }

        template <typename scalar_type>
        void test1()
        {
            typedef matrix<scalar_type,0,1> sample_type;

            typedef radial_basis_kernel<sample_type> kernel_type;

            print_spinner();

            std::vector<sample_type> samples;
            std::vector<scalar_type> targets;

            // The first thing we do is pick a few training points from the sinc() function.
            sample_type m(1);
            for (scalar_type x = -10; x <= 4; x += 1)
            {
                m(0) = x;

                samples.push_back(m);
                targets.push_back(sinc(x)+1.1);
            }

            randomize_samples(samples, targets);

            empirical_kernel_map<kernel_type> ekm;
            ekm.load(kernel_type(0.1), samples);

            for (unsigned long i = 0; i < samples.size(); ++i)
                samples[i] = ekm.project(samples[i]);

            svr_linear_trainer<linear_kernel<sample_type> > linear_trainer;
            linear_trainer.set_epsilon(0.0001);
            linear_trainer.set_c(30);
            linear_trainer.set_epsilon_insensitivity(0.001);

            matrix<double> res = cross_validate_regression_trainer(linear_trainer, samples, targets, 5);
            dlog << LINFO << "MSE and R-Squared: "<< res;
            DLIB_TEST(res(0) < 1e-4);
            DLIB_TEST(res(1) > 0.99);

            dlib::rand rnd;

            samples.clear();
            targets.clear();
            std::vector<scalar_type> noisefree_targets;
            for (scalar_type x = 0; x <= 5; x += 0.1)
            {
                m(0) = x;
                samples.push_back(matrix_cast<scalar_type>(linpiece(m, linspace(0,5,20))));
                targets.push_back(x*x + rnd.get_random_gaussian());
                noisefree_targets.push_back(x*x);
            }
            linear_trainer.set_learns_nonnegative_weights(true);
            linear_trainer.set_epsilon_insensitivity(1.0);
            decision_function<linear_kernel<sample_type> > df2 = linear_trainer.train(samples, targets);

            print_spinner();
            res = test_regression_function(df2, samples, noisefree_targets);
            dlog << LINFO << "MSE and R-Squared: "<< res;
            DLIB_TEST(res(0) < 0.15);
            DLIB_TEST(res(1) > 0.98);
            DLIB_TEST(df2.basis_vectors.size()==1);
            DLIB_TEST(max(df2.basis_vectors(0)) >= 0);

            linear_trainer.force_last_weight_to_1(true);
            df2 = linear_trainer.train(samples, targets);
            DLIB_TEST(std::abs(df2.basis_vectors(0)(samples[0].size()-1) - 1.0) < 1e-14);

            res = test_regression_function(df2, samples, noisefree_targets);
            dlog << LINFO << "MSE and R-Squared: "<< res;
            DLIB_TEST(res(0) < 0.20);
            DLIB_TEST(res(1) > 0.98);

            // convert into sparse vectors and try it out
            typedef std::vector<std::pair<unsigned long, scalar_type> > sparse_samp;
            std::vector<sparse_samp> ssamples;
            for (unsigned long i = 0; i < samples.size(); ++i)
            {
                sparse_samp s;
                for (long j = 0; j < samples[i].size(); ++j)
                    s.push_back(make_pair(j,samples[i](j)));
                ssamples.push_back(s);
            }

            svr_linear_trainer<sparse_linear_kernel<sparse_samp> > strainer;
            strainer.set_learns_nonnegative_weights(true);
            strainer.set_epsilon_insensitivity(1.0);
            strainer.set_c(30);
            decision_function<sparse_linear_kernel<sparse_samp> > df;
            df = strainer.train(ssamples, targets);
            res = test_regression_function(df, ssamples, noisefree_targets);
            dlog << LINFO << "MSE and R-Squared: "<< res;
            DLIB_TEST(res(0) < 0.15);
            DLIB_TEST(res(1) > 0.98);
            DLIB_TEST(df2.basis_vectors.size()==1);
            DLIB_TEST(max(sparse_to_dense(df2.basis_vectors(0))) >= 0);
        }

    // ----------------------------------------------------------------------------------------

        class tester_svr_linear_trainer : public tester
        {
        public:
            tester_svr_linear_trainer (
            ) :
                tester ("test_svr_linear_trainer",
                        "Runs tests on the svr_linear_trainer.")
            {}

            void perform_test (
            )
            {
                dlog << LINFO << "TEST double";
                test1<double>();
                dlog << LINFO << "TEST float";
                test1<float>();
            }
        } a;

    }