// 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;
}