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

    #include <dlib/sparse_vector.h>
    #include "tester.h"
    #include <dlib/rand.h>
    #include <dlib/string.h>
    #include <vector>
    #include <sstream>
    #include <ctime>

    namespace
    {
        using namespace test;
        using namespace dlib;
        using namespace std;
        dlib::logger dlog("test.sparse_vector");

        void test_sparse_matrix_vector_multiplies()
        {
            dlib::rand rnd;

            const long size = 30;

            for (int iter = 0; iter < 10; ++iter)
            {
                print_spinner();

                std::vector<sample_pair> edges;
                std::vector<ordered_sample_pair> oedges;
                matrix<double> M(size,size);
                M = 0;
                for (long i = 0; i < M.size()/3; ++i)
                {
                    const long r = rnd.get_random_32bit_number()%M.nr();
                    const long c = rnd.get_random_32bit_number()%M.nc();
                    const double d = rnd.get_random_gaussian()*10;
                    M(r,c) += d;
                    oedges.push_back(ordered_sample_pair(r,c,d));
                }

                matrix<double> SM(size,size);
                SM = 0;
                for (long i = 0; i < SM.size()/3; ++i)
                {
                    const long r = rnd.get_random_32bit_number()%SM.nr();
                    const long c = rnd.get_random_32bit_number()%SM.nc();
                    const double d = rnd.get_random_gaussian()*10;
                    SM(r,c) += d;
                    if (r != c)
                        SM(c,r) += d;
                    edges.push_back(sample_pair(r,c,d));
                }

                const matrix<double> v = randm(size,1);

                matrix<double> result;

                sparse_matrix_vector_multiply(oedges, v, result);
                DLIB_TEST_MSG(length(M*v - result) < 1e-12, length(M*v - result));

                sparse_matrix_vector_multiply(edges, v, result);
                DLIB_TEST_MSG(length(SM*v - result) < 1e-12, length(SM*v - result));

            }
        }

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

        void test_sparse_matrix_vector_multiply1()
        {
            print_spinner();
            std::map<unsigned long,double> sv;
            sv[2] = 8;
            sv[6] = 2.3;

            matrix<double,10,1> v;
            v = 0;
            v(2) = 8;
            v(6) = 2.3;

            matrix<double,0,1> r1, r2;

            r1 = gaussian_randm(4,10)*v;
            r2 = sparse_matrix_vector_multiply(gaussian_randm(4,std::numeric_limits<long>::max()),sv);

            DLIB_TEST(max(abs(r1-r2)) < 1e-15);
        }

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

        void test_sparse_matrix_vector_multiply2()
        {
            std::vector<std::pair<unsigned long,double> > sv;
            sv.push_back(make_pair(6, 1.42));
            sv.push_back(make_pair(3, 5));

            matrix<double,9,1> v;
            v = 0;
            v(3) = 5;
            v(6) = 1.42;

            matrix<double,0,1> r1, r2;

            r1 = gaussian_randm(3,9)*v;
            r2 = sparse_matrix_vector_multiply(gaussian_randm(3,std::numeric_limits<long>::max()),sv);

            DLIB_TEST(max(abs(r1-r2)) < 1e-15);
        }

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

        void test_make_sparse_vector_inplace()
        {
            std::vector<std::pair<unsigned long,double> > vect;
            vect.push_back(make_pair(4,1));
            vect.push_back(make_pair(0,1));
            vect.push_back(make_pair(4,1));
            vect.push_back(make_pair(3,1));
            vect.push_back(make_pair(8,1));
            vect.push_back(make_pair(8,1));
            vect.push_back(make_pair(8,1));
            vect.push_back(make_pair(8,1));

            make_sparse_vector_inplace(vect);

            DLIB_TEST(vect.size() == 4);
            DLIB_TEST(vect[0].first == 0);
            DLIB_TEST(vect[1].first == 3);
            DLIB_TEST(vect[2].first == 4);
            DLIB_TEST(vect[3].first == 8);

            DLIB_TEST(vect[0].second == 1);
            DLIB_TEST(vect[1].second == 1);
            DLIB_TEST(vect[2].second == 2);
            DLIB_TEST(vect[3].second == 4);
        }

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

        class sparse_vector_tester : public tester
        {
        public:
            sparse_vector_tester (
            ) :
                tester (
                    "test_sparse_vector",       // the command line argument name for this test
                    "Run tests on the sparse_vector routines.", // the command line argument description
                    0                     // the number of command line arguments for this test
                )
            {
            }

            void perform_test (
            )
            {
                test_make_sparse_vector_inplace();

                std::map<unsigned int, double> v;
                v[4] = 8;
                v[2] = -4;
                v[9] = 10;

                DLIB_TEST(max(v) == 10);
                DLIB_TEST(min(v) == -4);

                v.clear();
                v[4] = 8;
                v[9] = 10;
                DLIB_TEST(max(v) == 10);
                DLIB_TEST(min(v) == 0);

                v.clear();
                v[4] = -9;
                v[9] = -4;
                DLIB_TEST(max(v) == 0);
                DLIB_TEST(min(v) == -9);

                {
                    matrix<double> a(2,2), b(2,2);
                    a = randm(2,2);
                    b = randm(2,2);

                    DLIB_TEST(equal(a-b, subtract(a,b)));
                    DLIB_TEST(equal(a+b, add(a,b)));
                    DLIB_TEST(equal(a-(b+b), subtract(a,b+b)));
                    DLIB_TEST(equal(a+b+b, add(a,b+b)));
                }

                {
                    std::map<unsigned long,double> a, b, c;
                    a[1] = 2;
                    a[3] = 5;

                    b[0] = 3;
                    b[1] = 1;

                    c = add(a,b);
                    DLIB_TEST(c.size() == 3);
                    DLIB_TEST(c[0] == 3);
                    DLIB_TEST(c[1] == 3);
                    DLIB_TEST(c[3] == 5);

                    c = subtract(a,b);
                    DLIB_TEST(c.size() == 3);
                    DLIB_TEST(c[0] == -3);
                    DLIB_TEST(c[1] == 1);
                    DLIB_TEST(c[3] == 5);

                    c = add(b,a);
                    DLIB_TEST(c.size() == 3);
                    DLIB_TEST(c[0] == 3);
                    DLIB_TEST(c[1] == 3);
                    DLIB_TEST(c[3] == 5);

                    c = subtract(b,a);
                    DLIB_TEST(c.size() == 3);
                    DLIB_TEST(c[0] == 3);
                    DLIB_TEST(c[1] == -1);
                    DLIB_TEST(c[3] == -5);

                    std::vector<std::pair<unsigned long,double> > aa, bb, cc;

                    aa.assign(a.begin(), a.end());
                    bb.assign(b.begin(), b.end());

                    cc = add(aa,bb);
                    DLIB_TEST(cc.size() == 3);
                    DLIB_TEST(cc[0].first == 0);
                    DLIB_TEST(cc[1].first == 1);
                    DLIB_TEST(cc[2].first == 3);
                    DLIB_TEST(cc[0].second == 3);
                    DLIB_TEST(cc[1].second == 3);
                    DLIB_TEST(cc[2].second == 5);

                    cc = subtract(aa,bb);
                    DLIB_TEST(cc.size() == 3);
                    DLIB_TEST(cc[0].first == 0);
                    DLIB_TEST(cc[1].first == 1);
                    DLIB_TEST(cc[2].first == 3);
                    DLIB_TEST(cc[0].second == -3);
                    DLIB_TEST(cc[1].second == 1);
                    DLIB_TEST(cc[2].second == 5);

                    cc = add(bb,aa);
                    DLIB_TEST(cc.size() == 3);
                    DLIB_TEST(cc[0].first == 0);
                    DLIB_TEST(cc[1].first == 1);
                    DLIB_TEST(cc[2].first == 3);
                    DLIB_TEST(cc[0].second == 3);
                    DLIB_TEST(cc[1].second == 3);
                    DLIB_TEST(cc[2].second == 5);

                    cc = subtract(bb,aa);
                    DLIB_TEST(cc.size() == 3);
                    DLIB_TEST(cc[0].first == 0);
                    DLIB_TEST(cc[1].first == 1);
                    DLIB_TEST(cc[2].first == 3);
                    DLIB_TEST(cc[0].second == 3);
                    DLIB_TEST(cc[1].second == -1);
                    DLIB_TEST(cc[2].second == -5);

                }

                test_sparse_matrix_vector_multiplies();
                test_sparse_matrix_vector_multiply1();
                test_sparse_matrix_vector_multiply2();

                {
                    matrix<double,0,1> a, b;
                    a = gaussian_randm(6,1, 0);
                    b = gaussian_randm(6,1, 1);

                    std::vector<std::pair<unsigned long,double> > aa, bb;

                    assign(aa, a);
                    assign(bb, b);

                    // dot() does something special when the sparse vectors have entries for
                    // each dimension, which is what happens when they are copied from dense
                    // vectors.  So the point of the tests in this block is to make sure dot()
                    // works right in this case.
                    DLIB_TEST(std::abs(dot(a,b) - dot(aa,bb)) < 1e-14);
                    a(3) = 0;
                    assign(aa, a);
                    DLIB_TEST(std::abs(dot(a,b) - dot(aa,bb)) < 1e-14);
                }
            }
        };

        sparse_vector_tester a;

    }