    // Copyright (C) 2005  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/entropy_encoder_model.h>
    #include <dlib/entropy_decoder_model.h>
    #include <dlib/entropy_encoder.h>
    #include <dlib/entropy_decoder.h>
    #include "tester.h"

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

        logger dlog("test.entropy_coder_model");

        template <
            typename ee,
            typename ed
            >
        void entropy_encoder_model_kernel_test (
        )
        /*!
            requires
                - ee is an implementation of entropy_encoder_model/entropy_encoder_model_kernel_abstract.h
                  the alphabet_size for ee is 256
                - ed is an implementation of entropy_decoder_model/entropy_decoder_model_kernel_abstract.h
                  the alphabet_size for ed is 256
                - ee and ed must share the same kernel number
            ensures
                - runs tests on ee and ed for compliance with the specs
        !*/
        {

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

            typedef typename ee::entropy_encoder_type ee_type;
            typedef typename ed::entropy_decoder_type ed_type;

            {

                ee_type ecoder;
                ed_type dcoder;

                ee elen(ecoder);
                ed dlen(dcoder);
                ee elit(ecoder);
                ed dlit(dcoder);

                istringstream sin;
                ostringstream sout;

                ecoder.set_stream(sout);

                unsigned long temp;

                elen.encode(0);
                elit.encode(9);

                elen.encode(0);
                elit.encode(0);

                elen.encode(0);
                elit.encode(4);

                elen.encode(0);
                elit.encode(0);

                elen.encode(0);
                elit.encode(2);

                elen.encode(0);
                elit.encode(0);

                ecoder.clear();
                sin.str(sout.str());
                dcoder.set_stream(sin);

                dlen.decode(temp);
                DLIB_TEST(temp == 0);
                dlit.decode(temp);
                DLIB_TEST(temp == 9);

                dlen.decode(temp);
                DLIB_TEST(temp == 0);
                dlit.decode(temp);
                DLIB_TEST(temp == 0);

                dlen.decode(temp);
                DLIB_TEST(temp == 0);
                dlit.decode(temp);
                DLIB_TEST(temp == 4);

                dlen.decode(temp);
                DLIB_TEST(temp == 0);
                dlit.decode(temp);
                DLIB_TEST(temp == 0);

                dlen.decode(temp);
                DLIB_TEST(temp == 0);
                dlit.decode(temp);
                DLIB_TEST(temp == 2);

                dlen.decode(temp);
                DLIB_TEST(temp == 0);
                dlit.decode(temp);
                DLIB_TEST(temp == 0);

            }

        }

        class entropy_encoder_model_tester : public tester
        {
        public:
            entropy_encoder_model_tester (
            ) :
                tester ("test_entropy_coder_model",
                        "Runs tests on the entropy_encoder_model and entropy_decoder_model components.")
            {}

            void perform_test (
            )
            {
                typedef entropy_encoder::kernel_2a_c ee;
                typedef entropy_decoder::kernel_2a_c ed;

                dlog << LINFO << "testing kernel_1a";
                entropy_encoder_model_kernel_test<
                    entropy_encoder_model<256,ee>::kernel_1a,
                    entropy_decoder_model<256,ed>::kernel_1a>();

                dlog << LINFO << "testing kernel_2a";
                entropy_encoder_model_kernel_test<
                    entropy_encoder_model<256,ee>::kernel_2a,
                    entropy_decoder_model<256,ed>::kernel_2a>();

                dlog << LINFO << "testing kernel_3a";
                entropy_encoder_model_kernel_test<
                    entropy_encoder_model<256,ee>::kernel_3a,
                    entropy_decoder_model<256,ed>::kernel_3a>();

                dlog << LINFO << "testing kernel_4a";
                entropy_encoder_model_kernel_test<
                    entropy_encoder_model<256,ee>::kernel_4a,
                    entropy_decoder_model<256,ed>::kernel_4a>();

                dlog << LINFO << "testing kernel_4b";
                entropy_encoder_model_kernel_test<
                    entropy_encoder_model<256,ee>::kernel_4b,
                    entropy_decoder_model<256,ed>::kernel_4b>();

                dlog << LINFO << "testing kernel_5a";
                entropy_encoder_model_kernel_test<
                    entropy_encoder_model<256,ee>::kernel_5a,
                    entropy_decoder_model<256,ed>::kernel_5a>();

                dlog << LINFO << "testing kernel_5c";
                entropy_encoder_model_kernel_test<
                    entropy_encoder_model<256,ee>::kernel_5c,
                    entropy_decoder_model<256,ed>::kernel_5c>();

                dlog << LINFO << "testing kernel_6a";
                entropy_encoder_model_kernel_test<
                    entropy_encoder_model<256,ee>::kernel_6a,
                    entropy_decoder_model<256,ed>::kernel_6a>();

            }
        } a;

    }