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

    #include "sliding_buffer_kernel_abstract.h"
    #include "../algs.h"
    #include "../interfaces/enumerable.h"
    #include "../serialize.h"

    namespace dlib
    {

        template <
            typename T
            >
        class sliding_buffer_kernel_1 : public enumerable<T>
        {
            /*!
                INITIAL VALUE
                    - buffer_size == 0
                    - buffer == 0
                    - buffer_start == 0
                    - current == 0
                    - at_start_ == true

                CONVENTION
                    - buffer_size == size()

                    - element() == (*this)[current]
                    - current_element_valid() == (current < buffer_size) && at_start_ == false
                    - at_start() == at_start_

                    - if (buffer_size != 0) then
                        - buffer[(buffer_start+i)&(mask)] == operator[](i)
                        - mask == buffer_size-1
                    - else
                        - buffer == 0
                        - buffer_size == 0
            !*/

        public:

            typedef T type;

            sliding_buffer_kernel_1 (
            ) :
                buffer_start(0),
                buffer_size(0),
                buffer(0),
                current(0),
                at_start_(true)
            {}

            virtual ~sliding_buffer_kernel_1 (
            ) { if (buffer) delete [] buffer; }

            void clear(
            )
            {
                buffer_size = 0;
                if (buffer) delete [] buffer;
                buffer = 0;
                at_start_ = true;
                current = 0;
            }

            void set_size (
                unsigned long exp_size
            )
            {
                at_start_ = true;
                if (buffer) delete [] buffer;
                buffer_size = 1;
                while (exp_size != 0)
                {
                    --exp_size;
                    buffer_size <<= 1;
                }
                mask = buffer_size-1;
                try { buffer = new T[buffer_size]; }
                catch (...) { buffer = 0; buffer_size = 0; throw; }
            }

            size_t size (
            ) const { return buffer_size; }

            void rotate_left (
                unsigned long amount
            ) { buffer_start = ((buffer_start-amount)&mask); at_start_ = true; }

            void rotate_right (
                unsigned long amount
            ) { buffer_start = ((buffer_start+amount)&mask); at_start_ = true;}

            const T& operator[] (
                unsigned long index
            ) const { return buffer[(buffer_start+index)&mask]; }

            T& operator[] (
                unsigned long index
            ) { return buffer[(buffer_start+index)&mask]; }

            unsigned long get_element_id(
                unsigned long index
            ) const { return ((buffer_start+index)&mask); }

            unsigned long get_element_index (
                unsigned long element_id
            ) const { return ((element_id-buffer_start)&mask);}

            void swap (
                sliding_buffer_kernel_1<T>& item
            )
            {
                exchange(buffer_start,item.buffer_start);
                exchange(buffer_size,item.buffer_size);
                exchange(buffer,item.buffer);
                exchange(mask,item.mask);
                exchange(current,item.current);
                exchange(at_start_,item.at_start_);
            }

            bool at_start (
            ) const { return at_start_; }

            void reset (
            ) const { at_start_ = true; }

            bool current_element_valid (
            ) const { return (current < buffer_size) && (at_start_ == false); }

            const T& element (
            ) const { return (*this)[current]; }

            T& element (
            ) { return (*this)[current]; }

            bool move_next (
            ) const
            {
                if (at_start_ == false)
                {
                    if (current+1 < buffer_size)
                    {
                        ++current;
                        return true;
                    }
                    else
                    {
                        current = buffer_size;
                        return false;
                    }
                }
                else
                {
                    at_start_ = false;
                    current = 0;
                    return (buffer_size != 0);
                }
            }

        private:

            // data members
            unsigned long buffer_start;
            unsigned long buffer_size;
            T* buffer;
            unsigned long mask;

            mutable unsigned long current;
            mutable bool at_start_;

            // restricted functions
            sliding_buffer_kernel_1(sliding_buffer_kernel_1<T>&);        // copy constructor
            sliding_buffer_kernel_1<T>& operator=(sliding_buffer_kernel_1<T>&);    // assignment operator

        };

        template <
            typename T
            >
        inline void swap (
            sliding_buffer_kernel_1<T>& a,
            sliding_buffer_kernel_1<T>& b
        ) { a.swap(b); }

        template <
            typename T
            >
        void deserialize (
            sliding_buffer_kernel_1<T>& item,
            std::istream& in
        )
        {
            try
            {
                item.clear();
                unsigned long size;
                deserialize(size,in);
                if (size > 0)
                {
                    int count = 0;
                    while (size != 1)
                    {
                        size /= 2;
                        ++count;
                    }
                    item.set_size(count);

                    for (unsigned long i = 0; i < item.size(); ++i)
                        deserialize(item[i],in);
                }
            }
            catch (serialization_error& e)
            {
                item.clear();
                throw serialization_error(e.info + "\n   while deserializing object of type sliding_buffer_kernel_1");
            }
        }
    }

    #endif // DLIB_SLIDING_BUFFER_KERNEl_1_