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

    #include "lz77_buffer_kernel_abstract.h"
    #include "../algs.h"

    namespace dlib
    {

        template <
            typename sliding_buffer
            >
        class lz77_buffer_kernel_1
        {
            /*!
                REQUIREMENTS ON sliding_buffer
                    sliding_buffer must be an implementation of sliding_buffer/sliding_buffer_kernel_abstract.h

                INITIAL VALUE
                    history_limit == defined by constructor arguments
                    lookahead_limit == defined by constructor arguments
                    history_size == 0
                    lookahead_size == 0
                    buffer.size() == history_limit + lookahead_limit

                CONVENTION
                    history_limit == get_history_buffer_limit()
                    lookahead_limit == get_lookahead_buffer_limit()
                    history_size == get_history_buffer_size()
                    lookahead_limit == get_lookahead_buffer_size()

                    buffer.size() == history_limit + lookahead_limit

                    lookahead_buffer(i) == buffer[lookahead_limit-1-i]
                    history_buffer(i) == buffer[lookahead_limit+i]
            !*/

        public:

            lz77_buffer_kernel_1 (
                unsigned long total_limit_,
                unsigned long lookahead_limit_
            );

            virtual ~lz77_buffer_kernel_1 (
            ) {}

            void clear(
            );

            void add (
                unsigned char symbol
            );

            void find_match (
                unsigned long& index,
                unsigned long& length,
                unsigned long min_match_length
            );

            inline unsigned long get_history_buffer_limit (
            ) const { return history_limit; }

            inline unsigned long get_lookahead_buffer_limit (
            ) const { return lookahead_limit; }

            inline unsigned long get_history_buffer_size (
            ) const { return history_size; }

            inline unsigned long get_lookahead_buffer_size (
            ) const { return lookahead_size; }

            inline unsigned char lookahead_buffer (
                unsigned long index
            ) const { return buffer[lookahead_limit-1-index]; }

            inline unsigned char history_buffer (
                unsigned long index
            ) const { return buffer[lookahead_limit+index]; }

            inline void shift_buffers (
                unsigned long N
            ) { shift_buffer(N); }

        private:

            inline void shift_buffer (
                unsigned long N
            )
            /*!
                requires
                    - N <= lookahead_size
                ensuers
                    - #lookahead_size == lookahead_size - N
                    - if (history_size+N < history_limit) then
                        - #history_size == history_size+N
                    - else
                        - #history_size == history_limit
                    - for all i where 0 <= i < N:
                      #history_buffer(N-1-i) == lookahead_buffer(i)
                    - for all i where 0 <= i < #history_size-N:
                      #history_buffer(N+i) == history_buffer(i)
                    - for all i where 0 <= i < #lookahead_size
                      #lookahead_buffer(i) == lookahead_buffer(N+i)
            !*/
            {
                unsigned long temp = history_size+N;
                buffer.rotate_left(N);
                lookahead_size -= N;
                if (temp < history_limit)
                    history_size = temp;
                else
                    history_size = history_limit;
            }

            // member data
            sliding_buffer buffer;
            unsigned long lookahead_limit;
            unsigned long history_limit;

            unsigned long lookahead_size;
            unsigned long history_size;

            // restricted functions
            lz77_buffer_kernel_1(lz77_buffer_kernel_1<sliding_buffer>&);        // copy constructor
            lz77_buffer_kernel_1<sliding_buffer>& operator=(lz77_buffer_kernel_1<sliding_buffer>&);    // assignment operator
        };

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // member function definitions
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        template <
            typename sliding_buffer
            >
        lz77_buffer_kernel_1<sliding_buffer>::
        lz77_buffer_kernel_1 (
            unsigned long total_limit_,
            unsigned long lookahead_limit_
        ) :
            lookahead_size(0),
            history_size(0)
        {
            buffer.set_size(total_limit_);
            lookahead_limit = lookahead_limit_;
            history_limit = buffer.size() - lookahead_limit_;
        }

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

        template <
            typename sliding_buffer
            >
        void lz77_buffer_kernel_1<sliding_buffer>::
        clear(
        )
        {
            lookahead_size = 0;
            history_size = 0;
        }

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

        template <
            typename sliding_buffer
            >
        void lz77_buffer_kernel_1<sliding_buffer>::
        add (
            unsigned char symbol
        )
        {
            if (lookahead_size == lookahead_limit)
            {
                shift_buffer(1);
            }
            buffer[lookahead_limit-1-lookahead_size] = symbol;
            ++lookahead_size;
        }

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

        template <
            typename sliding_buffer
            >
        void lz77_buffer_kernel_1<sliding_buffer>::
        find_match (
            unsigned long& index,
            unsigned long& length,
            unsigned long min_match_length
        )
        {
            unsigned long hpos = history_size;  // current position in the history buffer
            unsigned long lpos = 0;             // current position in the lookahead buffer

            unsigned long match_length = 0;   // the length of the longest match we find
            unsigned long match_index = 0;    // the index of the longest match we find

            // try to find a match
            while (hpos != 0)
            {
                --hpos;
                // if we are finding a match
                if (history_buffer(hpos) == lookahead_buffer(lpos))
                {
                    ++lpos;
                    // if we have found a match that is as long as the lookahead buffer
                    // then we are done
                    if (lpos == lookahead_size)
                        break;
                }
                // else if we found the end of a match
                else if (lpos > 0)
                {
                    // if this match is longer than the last match we saw
                    if (lpos > match_length)
                    {
                        match_length = lpos;
                        match_index = hpos + lpos;
                    }
                    lpos = 0;
                }
            } // while (hpos != 0)

            // if we found a match at the end of the loop that is greater than
            // the match in match_index
            if (lpos > match_length)
            {
                match_length = lpos;
                match_index = hpos + lpos - 1;
            }

            // if we found a match that was long enough then report it
            if (match_length >= min_match_length)
            {
                shift_buffer(match_length);
                index = match_index;
                length = match_length;
            }
            else
            {
                length = 0;
            }
        }

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

    }

    #endif // DLIB_LZ77_BUFFER_KERNEl_1_