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

    #include <sstream>
    #include <string>
    #include <ctime>
    #include <cstdlib>

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

    namespace
    {

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

        logger dlog("test.sliding_buffer");

        template <
            typename buf
            >
        void sliding_buffer_kernel_test (
        )
        /*!
            requires
                - buf is an implementation of sliding_buffer/sliding_buffer_kernel_abstract.h
                - buf is instantiated with T=unsigned char
            ensures
                - runs tests on buf for compliance with the specs
        !*/
        {

            print_spinner();

            buf test;

            DLIB_TEST(test.size() == 0);

            test.set_size(3);
            buf test2;

            DLIB_TEST(test.size() == 8);

            for (int g = 0; g < 2; ++g)
            {

                test.clear();

                DLIB_TEST(test.size() == 0);
                test.set_size(2);

                DLIB_TEST(test.size() == 4);

                test[0] = 'a';
                test[1] = 's';
                test[2] = 'd';
                test[3] = 'f';

                unsigned long id = test.get_element_id(2);
                DLIB_TEST(test[test.get_element_index(id)] == 'd');

                DLIB_TEST(test[0] == 'a');
                DLIB_TEST(test[1] == 's');
                DLIB_TEST(test[2] == 'd');
                DLIB_TEST(test[3] == 'f');

                DLIB_TEST(test2.size() == 0);
                swap(test,test2);
                DLIB_TEST(test2.size() == 4);

                DLIB_TEST(test2[0] == 'a');
                DLIB_TEST(test2[1] == 's');
                DLIB_TEST(test2[2] == 'd');
                DLIB_TEST(test2[3] == 'f');

                swap(test,test2);

                test.rotate_left(4);

                DLIB_TEST(test[test.get_element_index(id)] == 'd');

                DLIB_TEST(test[0] == 'a');
                DLIB_TEST(test[1] == 's');
                DLIB_TEST(test[2] == 'd');
                DLIB_TEST(test[3] == 'f');

                test.rotate_right(1);

                DLIB_TEST(test[test.get_element_index(id)] == 'd');

                DLIB_TEST(test[0] == 's');
                DLIB_TEST(test[1] == 'd');
                DLIB_TEST(test[2] == 'f');
                DLIB_TEST(test[3] == 'a');

                test.rotate_left(1);

                DLIB_TEST(test[test.get_element_index(id)] == 'd');
                DLIB_TEST(test[0] == 'a');
                DLIB_TEST(test[1] == 's');
                DLIB_TEST(test[2] == 'd');
                DLIB_TEST(test[3] == 'f');

                test.rotate_left(16);

                DLIB_TEST(test[test.get_element_index(id)] == 'd');
                DLIB_TEST(test[0] == 'a');
                DLIB_TEST(test[1] == 's');
                DLIB_TEST(test[2] == 'd');
                DLIB_TEST(test[3] == 'f');

                test.rotate_left(2);

                DLIB_TEST(test[test.get_element_index(id)] == 'd');

                DLIB_TEST(test[0] == 'd');
                DLIB_TEST(test[1] == 'f');
                DLIB_TEST(test[2] == 'a');
                DLIB_TEST(test[3] == 's');

                test.rotate_left(1);

                DLIB_TEST(test[test.get_element_index(id)] == 'd');
                DLIB_TEST(test[0] == 's');
                DLIB_TEST(test[1] == 'd');
                DLIB_TEST(test[2] == 'f');
                DLIB_TEST(test[3] == 'a');

                test.rotate_left(1);

                DLIB_TEST(test[test.get_element_index(id)] == 'd');
                DLIB_TEST(test[0] == 'a');
                DLIB_TEST(test[1] == 's');
                DLIB_TEST(test[2] == 'd');
                DLIB_TEST(test[3] == 'f');

                DLIB_TEST(test.size() == 4);

                test[0] = 'x';

                DLIB_TEST(test[0] == 'x');
                DLIB_TEST(test[1] == 's');
                DLIB_TEST(test[2] == 'd');
                DLIB_TEST(test[3] == 'f');

                test.rotate_left(1);

                DLIB_TEST_MSG(test[0] == 'f',test[0]);
                DLIB_TEST(test[1] == 'x');
                DLIB_TEST(test[2] == 's');
                DLIB_TEST(test[3] == 'd');

                test[0] = 'x';

                DLIB_TEST(test[0] == 'x');
                DLIB_TEST(test[1] == 'x');
                DLIB_TEST(test[2] == 's');
                DLIB_TEST(test[3] == 'd');

                test.rotate_left(1);

                DLIB_TEST(test[0] == 'd');
                DLIB_TEST(test[1] == 'x');
                DLIB_TEST(test[2] == 'x');
                DLIB_TEST(test[3] == 's');

                DLIB_TEST(test.at_start() == true);
                DLIB_TEST(test.current_element_valid() == false);
                DLIB_TEST(test.move_next() == true);
                DLIB_TEST(test.at_start() == false);
                DLIB_TEST(test.current_element_valid() == true);

                test.clear();
                test2.clear();

                DLIB_TEST(test.size() == 0);
                DLIB_TEST(test.at_start() == true);
                DLIB_TEST(test.current_element_valid() == false);
                DLIB_TEST(test.move_next() == false);
                DLIB_TEST(test.at_start() == false);
                DLIB_TEST(test.current_element_valid() == false);

                swap(test,test2);

                DLIB_TEST(test2.at_start() == false);
                DLIB_TEST(test2.current_element_valid() == false);
                DLIB_TEST(test2.move_next() == false);
                DLIB_TEST(test2.at_start() == false);
                DLIB_TEST(test2.current_element_valid() == false);

                DLIB_TEST(test.at_start() == true);
                DLIB_TEST(test.current_element_valid() == false);
                DLIB_TEST(test.move_next() == false);
                DLIB_TEST(test.at_start() == false);

                test.set_size(3);
                DLIB_TEST(test.size() == 8);

                DLIB_TEST(test.at_start() == true);
                DLIB_TEST(test.current_element_valid() == false);
                DLIB_TEST(test.move_next() == true);
                DLIB_TEST(test.at_start() == false);
                DLIB_TEST(test.current_element_valid() == true);
                test.reset();
                DLIB_TEST(test.size() == 8);
                DLIB_TEST(test.at_start() == true);
                DLIB_TEST(test.current_element_valid() == false);
                DLIB_TEST(test.move_next() == true);
                DLIB_TEST(test.at_start() == false);
                DLIB_TEST(test.current_element_valid() == true);

                test.rotate_right(1);
                DLIB_TEST(test.size() == 8);
                DLIB_TEST(test.at_start() == true);
                DLIB_TEST(test.current_element_valid() == false);
                DLIB_TEST(test.move_next() == true);
                DLIB_TEST(test.at_start() == false);
                DLIB_TEST(test.current_element_valid() == true);

                test.rotate_left(1);
                DLIB_TEST(test.size() == 8);
                DLIB_TEST(test.at_start() == true);
                DLIB_TEST(test.current_element_valid() == false);
                DLIB_TEST(test.move_next() == true);
                DLIB_TEST(test.at_start() == false);
                DLIB_TEST(test.current_element_valid() == true);
                test.reset();

                for (unsigned long i = 0; i < test.size(); ++i)
                {
                    test[i] = static_cast<unsigned char>(i);
                }

                unsigned long count = 0;
                while (test.move_next())
                {
                    DLIB_TEST(test.element() == count);
                    ++count;
                }

                DLIB_TEST(count == test.size());

                test2.clear();
                ostringstream sout;
                istringstream sin;

                serialize(test,sout);
                sin.str(sout.str());
                deserialize(test2,sin);

                char ch;
                sin >> ch;
                DLIB_TEST( !sin);

                DLIB_TEST(test2.size() == test.size());

                for (unsigned long i = 0; i < test.size(); ++i)
                {
                    DLIB_TEST_MSG(test[i] == test2[i],
                                 "\ni:        " << i <<
                                 "\ntest[i]:  " << test[i] <<
                                 "\ntest2[i]: " << test2[i]);
                }

                count = 0;
                while (test.move_next() && test2.move_next())
                {
                    DLIB_TEST(test.element() == count);
                    DLIB_TEST(test2.element() == count);
                    ++count;
                }

                DLIB_TEST(test2.size() == count);
                DLIB_TEST(test.size() == count);

                test2.clear();

            } // for (int g = 0; g < 2; ++g)

        }

        void test_circular_buffer()
        {
            circular_buffer<int> buf;

            DLIB_TEST(buf.size() == 0);

            buf.assign(4, 0);
            DLIB_TEST(buf.size() == 4);

            DLIB_TEST(buf[0] == 0);
            DLIB_TEST(buf[1] == 0);
            DLIB_TEST(buf[2] == 0);
            DLIB_TEST(buf[3] == 0);
            buf.push_back(1);
            DLIB_TEST(buf[0] == 0);
            DLIB_TEST(buf[1] == 0);
            DLIB_TEST(buf[2] == 0);
            DLIB_TEST(buf[3] == 1);
            buf.push_back(2);
            DLIB_TEST(buf[0] == 0);
            DLIB_TEST(buf[1] == 0);
            DLIB_TEST(buf[2] == 1);
            DLIB_TEST(buf[3] == 2);
            buf.push_front(3);
            DLIB_TEST(buf[0] == 3);
            DLIB_TEST(buf[1] == 0);
            DLIB_TEST(buf[2] == 0);
            DLIB_TEST(buf[3] == 1);
            buf.push_front(4);
            DLIB_TEST(buf.front() == 4);
            DLIB_TEST(buf[0] == 4);
            DLIB_TEST(buf[1] == 3);
            DLIB_TEST(buf[2] == 0);
            DLIB_TEST(buf[3] == 0);

            buf.assign(4, 5);
            DLIB_TEST(buf[0] == 5);
            DLIB_TEST(buf[1] == 5);
            DLIB_TEST(buf[2] == 5);
            DLIB_TEST(buf[3] == 5);

            buf.push_back(3);
            DLIB_TEST(buf[0] == 5);
            DLIB_TEST(buf[1] == 5);
            DLIB_TEST(buf[2] == 5);
            DLIB_TEST(buf[3] == 3);
            buf.push_back(2);
            DLIB_TEST(buf[0] == 5);
            DLIB_TEST(buf[1] == 5);
            DLIB_TEST(buf[2] == 3);
            DLIB_TEST(buf[3] == 2);
            buf.push_back(1);
            DLIB_TEST(buf[0] == 5);
            DLIB_TEST(buf[1] == 3);
            DLIB_TEST(buf[2] == 2);
            DLIB_TEST(buf[3] == 1);
            buf.push_back(0);
            DLIB_TEST(buf[0] == 3);
            DLIB_TEST(buf[1] == 2);
            DLIB_TEST(buf[2] == 1);
            DLIB_TEST(buf[3] == 0);
            buf.push_back(-1);
            DLIB_TEST(buf.back() == -1);
            DLIB_TEST(buf[0] == 2);
            DLIB_TEST(buf[1] == 1);
            DLIB_TEST(buf[2] == 0);
            DLIB_TEST(buf[3] == -1);

            buf.resize(1);
            buf[0] = 9;
            DLIB_TEST(buf.size() == 1);
            DLIB_TEST(buf[0] == 9);
            buf.push_back(1);
            DLIB_TEST(buf[0] == 1);
            buf.push_back(4);
            DLIB_TEST(buf[0] == 4);
            buf.push_front(3);
            DLIB_TEST(buf[0] == 3);

            buf.clear();
            DLIB_TEST(buf.size() == 0);

            buf.assign(3, 0);

            circular_buffer<int> buf2, buf3;

            buf.push_back(1);
            buf.push_back(2);

            ostringstream sout;
            serialize(buf, sout);
            istringstream sin(sout.str());
            deserialize(buf2, sin);

            DLIB_TEST(buf.size() == buf2.size());
            for (unsigned long i = 0; i < buf.size(); ++i)
                DLIB_TEST(buf[i] == buf2[i]);

            buf.swap(buf3);
            DLIB_TEST(buf.size() == 0);
            DLIB_TEST(buf3.size() == buf2.size());
            for (unsigned long i = 0; i < buf3.size(); ++i)
                DLIB_TEST(buf3[i] == buf2[i]);

        }

        class sliding_buffer_tester : public tester
        {
        public:
            sliding_buffer_tester (
            ) :
                tester ("test_sliding_buffer",
                        "Runs tests on the sliding_buffer component.")
            {}

            void perform_test (
            )
            {
                dlog << LINFO << "testing kernel_1a";
                sliding_buffer_kernel_test<sliding_buffer<unsigned char>::kernel_1a>  ();
                dlog << LINFO << "testing kernel_1a_c";
                sliding_buffer_kernel_test<sliding_buffer<unsigned char>::kernel_1a_c>();

                test_circular_buffer();
            }
        } a;

    }