    // 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 <cstdlib>
    #include <ctime>

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

    namespace
    {

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

        logger dlog("test.sequence");

        template <
            typename seq
            >
        void sequence_sort_test (
        )
        /*!
            requires
                - seq is an implementation of sequence/sequence_sort_aseqract.h is instantiated
                  with int
            ensures
                - runs tests on seq for compliance with the specs
        !*/
        {

            srand(static_cast<unsigned int>(time(0)));

            print_spinner();

            {
                // this test is to make sure that jumping around via
                // operator[] doesn't corrupt the object

                seq a;

                for (int i = 0; i < 100; ++i)
                {
                    int x = i;
                    a.add(a.size(),x);
                }

                int x = 0;

                for (int i = 0; i < (int)a.size(); ++i)
                {
                    DLIB_TEST_MSG(a[i] >= i,"1");
                    // cout << a[i] << endl;
                }

                for (unsigned long i = 0; i < a.size(); ++i)
                {
                    for (unsigned long j = i+1; j < a.size(); ++j)
                    {
                        if ((a[j]+a[i])%3 ==0)
                        {
                            a.remove(j,x);
                            --j;
                        }
                    }
                }

                //cout << endl;

                for (int i = 0; i < (int)a.size(); ++i)
                {
                    //   cout << a[i] << endl;
                    DLIB_TEST_MSG(a[i] >= i,"2");
                }

            }

            seq test, test2;

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

            enumerable<int>& e = test;

            DLIB_TEST(e.at_start() == true);
            DLIB_TEST(e.current_element_valid() == false);

            for (int g = 0; g < 5; ++g)
            {
                test.clear();
                test2.clear();
                DLIB_TEST(test.size() == 0);
                DLIB_TEST(test.at_start() == true);
                DLIB_TEST(test.current_element_valid() == false);
                DLIB_TEST(e.at_start() == true);
                DLIB_TEST(e.current_element_valid() == false);

                DLIB_TEST(e.move_next() == false);
                DLIB_TEST(e.current_element_valid() == false);
                DLIB_TEST(e.at_start() == false);
                DLIB_TEST(test.at_start() == false);
                swap(test,test2);
                DLIB_TEST(test.at_start() == true);
                test.clear();
                test2.clear();

                int a;

                for (int i = 0; i < 100; ++i)
                {
                    a = i;
                    test.add(i,a);
                }

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

                for (int i = 0; i < static_cast<int>(test.size()); ++i)
                {
                    DLIB_TEST(test[i] == i);
                }

                swap(test,test2);

                a = 0;
                DLIB_TEST(test2.at_start() == true);
                while(test2.move_next())
                {
                    DLIB_TEST(test2.at_start() == false);
                    DLIB_TEST(test2.current_element_valid() == true);
                    DLIB_TEST(test2.element() == a);
                    ++a;
                }

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

                test2.reset();

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

                a = 0;
                while(test2.move_next())
                {
                    DLIB_TEST(test2.at_start() == false);
                    DLIB_TEST(test2.current_element_valid() == true);
                    DLIB_TEST(test2.element() == a);
                    ++a;
                }

                for (int i = 0; i < 1000; ++i)
                {
                    a = ::rand();
                    test.add(0,a);
                }
                DLIB_TEST(test.size() == 1000);

                test.sort();

                for (unsigned long i = 0; i < test.size()-1; ++i)
                {
                    DLIB_TEST(test[i] <= test[i+1]);
                }

                a = 0;
                while(test.move_next())
                {
                    DLIB_TEST(a <= test.element());
                    a = test.element();
                }

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

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

                for (int i = 0; i < 100; ++i)
                {
                    a = i;
                    test.add(i,a);
                }

                for (int i = 100; i < 200; ++i)
                {
                    a = i;
                    test.add(i,a);
                }

                test.cat(test2);
                DLIB_TEST(test.size() == 200);
                DLIB_TEST(test2.size() == 0);

                // serialize the state of test, then clear test, then
                // load the state back into test.
                ostringstream sout;
                serialize(test,sout);
                DLIB_TEST(test.at_start() == true);
                istringstream sin(sout.str());
                test.clear();
                deserialize(test,sin);

                for (int i = 0; i < 200; ++i)
                {
                    DLIB_TEST(test[i] == i);
                }

                a = 0;
                while (test.move_next())
                {
                    DLIB_TEST(test.element() == a);
                    DLIB_TEST(test[0]==0);
                    ++a;
                }

                DLIB_TEST(a == 200);

                DLIB_TEST(test[9] == 9);
                test.remove(9,a);
                DLIB_TEST(a == 9);
                DLIB_TEST(test[9] == 10);
                DLIB_TEST(test.size() == 199);

                test.remove(0,a);
                DLIB_TEST(test[0] == 1);
                DLIB_TEST(test.size() == 198);
                DLIB_TEST(a == 0);
                DLIB_TEST(test[9] == 11);
                DLIB_TEST(test[20] == 22);

            }

            {
                test.clear();
                for (int i = 0; i < 100; ++i)
                {
                    int a = 3;
                    test.add(0,a);
                }
                DLIB_TEST(test.size() == 100);
                remover<int>& go = test;
                for (int i = 0; i < 100; ++i)
                {
                    int a = 9;
                    go.remove_any(a);
                    DLIB_TEST(a == 3);
                }
                DLIB_TEST(go.size() == 0);
            }

        }

        class sequence_tester : public tester
        {
        public:
            sequence_tester (
            ) :
                tester ("test_sequence",
                        "Runs tests on the sequence component.")
            {}

            void perform_test (
            )
            {
                dlog << LINFO << "testing sort_1a";
                sequence_sort_test<sequence<int>::sort_1a>  ();
                dlog << LINFO << "testing sort_1a_c";
                sequence_sort_test<sequence<int>::sort_1a_c>();
                dlog << LINFO << "testing sort_2a";
                sequence_sort_test<sequence<int>::sort_2a>  ();
                dlog << LINFO << "testing sort_2a_c";
                sequence_sort_test<sequence<int>::sort_2a_c>();
            }
        } a;

    }