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

    #include "conditioning_class_kernel_abstract.h"
    #include "../assert.h"
    #include "../algs.h"

    namespace dlib
    {
        template <
            unsigned long alphabet_size,
            unsigned long pool_size,
            typename mem_manager
            >
        class conditioning_class_kernel_4
        {
            /*!
                REQUIREMENTS ON pool_size
                    pool_size > 0
                    this will be the number of nodes contained in our memory pool

                REQUIREMENTS ON mem_manager
                    mem_manager is an implementation of memory_manager/memory_manager_kernel_abstract.h

                INITIAL VALUE
                    total == 1
                    escapes == 1
                    next == 0

                CONVENTION
                    get_total() == total
                    get_count(alphabet_size-1) == escapes

                    if (next != 0) then
                        next == pointer to the start of a linked list and the linked list
                                is terminated by a node with a next pointer of 0.

                    get_count(symbol) == node::count for the node where node::symbol==symbol
                                         or 0 if no such node currently exists.

                    if (there is a node for the symbol) then
                        LOW_COUNT(symbol) == the sum of all node's counts in the linked list
                        up to but not including the node for the symbol.

                    get_memory_usage() == global_state.memory_usage
            !*/

            struct node
            {
                unsigned short symbol;
                unsigned short count;
                node* next;
            };

        public:

            class global_state_type
            {
            public:
                global_state_type (
                ) :
                    memory_usage(pool_size*sizeof(node)+sizeof(global_state_type))
                    {}
            private:
                unsigned long memory_usage;

                typename mem_manager::template rebind<node>::other pool;

                friend class conditioning_class_kernel_4<alphabet_size,pool_size,mem_manager>;
            };

            conditioning_class_kernel_4 (
                global_state_type& global_state_
            );

            ~conditioning_class_kernel_4 (
            );

            void clear(
            );

            bool increment_count (
                unsigned long symbol,
                unsigned short amount = 1
            );

            unsigned long get_count (
                unsigned long symbol
            ) const;

            inline unsigned long get_total (
            ) const;

            unsigned long get_range (
                unsigned long symbol,
                unsigned long& low_count,
                unsigned long& high_count,
                unsigned long& total_count
            ) const;

            void get_symbol (
                unsigned long target,
                unsigned long& symbol,
                unsigned long& low_count,
                unsigned long& high_count
            ) const;

            unsigned long get_memory_usage (
            ) const;

            global_state_type& get_global_state (
            );

            static unsigned long get_alphabet_size (
            );

        private:

            void half_counts (
            );
            /*!
                ensures
                    - divides all counts by 2 but ensures that escapes is always at least 1
            !*/

            // restricted functions
            conditioning_class_kernel_4(conditioning_class_kernel_4<alphabet_size,pool_size,mem_manager>&);        // copy constructor
            conditioning_class_kernel_4& operator=(conditioning_class_kernel_4<alphabet_size,pool_size,mem_manager>&);    // assignment operator

            // data members
            unsigned short total;
            unsigned short escapes;
            node* next;
            global_state_type& global_state;

        };

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

        template <
            unsigned long alphabet_size,
            unsigned long pool_size,
            typename mem_manager
            >
        conditioning_class_kernel_4<alphabet_size,pool_size,mem_manager>::
        conditioning_class_kernel_4 (
            global_state_type& global_state_
        ) :
            total(1),
            escapes(1),
            next(0),
            global_state(global_state_)
        {
            COMPILE_TIME_ASSERT( 1 < alphabet_size && alphabet_size < 65536 );

            // update memory usage
            global_state.memory_usage += sizeof(conditioning_class_kernel_4);
        }

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

        template <
            unsigned long alphabet_size,
            unsigned long pool_size,
            typename mem_manager
            >
        conditioning_class_kernel_4<alphabet_size,pool_size,mem_manager>::
        ~conditioning_class_kernel_4 (
        )
        {
            clear();
            // update memory usage
            global_state.memory_usage -= sizeof(conditioning_class_kernel_4);
        }

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

        template <
            unsigned long alphabet_size,
            unsigned long pool_size,
            typename mem_manager
            >
        void conditioning_class_kernel_4<alphabet_size,pool_size,mem_manager>::
        clear(
        )
        {
            total = 1;
            escapes = 1;
            while (next)
            {
                node* temp = next;
                next = next->next;
                global_state.pool.deallocate(temp);
            }
        }

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

        template <
            unsigned long alphabet_size,
            unsigned long pool_size,
            typename mem_manager
            >
        unsigned long conditioning_class_kernel_4<alphabet_size,pool_size,mem_manager>::
        get_memory_usage(
        ) const
        {
            return global_state.memory_usage;
        }

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

        template <
            unsigned long alphabet_size,
            unsigned long pool_size,
            typename mem_manager
            >
        typename conditioning_class_kernel_4<alphabet_size,pool_size,mem_manager>::global_state_type& conditioning_class_kernel_4<alphabet_size,pool_size,mem_manager>::
        get_global_state(
        )
        {
            return global_state;
        }

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

        template <
            unsigned long alphabet_size,
            unsigned long pool_size,
            typename mem_manager
            >
        bool conditioning_class_kernel_4<alphabet_size,pool_size,mem_manager>::
        increment_count (
            unsigned long symbol,
            unsigned short amount
        )
        {
            if (symbol == alphabet_size-1)
            {
                // make sure we won't cause any overflow
                if (total >= 65536 - amount )
                    half_counts();

                escapes += amount;
                total += amount;
                return true;
            }

            // find the symbol and increment it or add a new node to the list
            if (next)
            {
                node* temp = next;
                node* previous = 0;
                while (true)
                {
                    if (temp->symbol == static_cast<unsigned short>(symbol))
                    {
                        // make sure we won't cause any overflow
                        if (total >= 65536 - amount )
                            half_counts();

                        // we have found the symbol
                        total += amount;
                        temp->count += amount;

                        // if this node now has a count greater than its parent node
                        if (previous && temp->count > previous->count)
                        {
                            // swap the nodes so that the nodes will be in semi-sorted order
                            swap(temp->count,previous->count);
                            swap(temp->symbol,previous->symbol);
                        }
                        return true;
                    }
                    else if (temp->next == 0)
                    {
                        // we did not find the symbol so try to add it to the list
                        if (global_state.pool.get_number_of_allocations() < pool_size)
                        {
                            // make sure we won't cause any overflow
                            if (total >= 65536 - amount )
                                half_counts();

                            node* t = global_state.pool.allocate();
                            t->next = 0;
                            t->symbol = static_cast<unsigned short>(symbol);
                            t->count = amount;
                            temp->next = t;
                            total += amount;
                            return true;
                        }
                        else
                        {
                            // no memory left
                            return false;
                        }
                    }
                    else if (temp->count == 0)
                    {
                        // remove nodes that have a zero count
                        if (previous)
                        {
                            previous->next = temp->next;
                            node* t = temp;
                            temp = temp->next;
                            global_state.pool.deallocate(t);
                        }
                        else
                        {
                            next = temp->next;
                            node* t = temp;
                            temp = temp->next;
                            global_state.pool.deallocate(t);
                        }
                    }
                    else
                    {
                        previous = temp;
                        temp = temp->next;
                    }
                } // while (true)
            }
            // if there aren't any nodes in the list yet then do this instead
            else
            {
                if (global_state.pool.get_number_of_allocations() < pool_size)
                {
                    // make sure we won't cause any overflow
                    if (total >= 65536 - amount )
                        half_counts();

                    next = global_state.pool.allocate();
                    next->next = 0;
                    next->symbol = static_cast<unsigned short>(symbol);
                    next->count = amount;
                    total += amount;
                    return true;
                }
                else
                {
                    // no memory left
                    return false;
                }
            }
        }

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

        template <
            unsigned long alphabet_size,
            unsigned long pool_size,
            typename mem_manager
            >
        unsigned long conditioning_class_kernel_4<alphabet_size,pool_size,mem_manager>::
        get_count (
            unsigned long symbol
        ) const
        {
            if (symbol == alphabet_size-1)
            {
                return escapes;
            }
            else
            {
                node* temp = next;
                while (temp)
                {
                    if (temp->symbol == symbol)
                        return temp->count;
                    temp = temp->next;
                }
                return 0;
            }
        }

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

        template <
            unsigned long alphabet_size,
            unsigned long pool_size,
            typename mem_manager
            >
        unsigned long conditioning_class_kernel_4<alphabet_size,pool_size,mem_manager>::
        get_alphabet_size (
        )
        {
            return alphabet_size;
        }

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

        template <
            unsigned long alphabet_size,
            unsigned long pool_size,
            typename mem_manager
            >
        unsigned long conditioning_class_kernel_4<alphabet_size,pool_size,mem_manager>::
        get_total (
        ) const
        {
            return total;
        }

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

        template <
            unsigned long alphabet_size,
            unsigned long pool_size,
            typename mem_manager
            >
        unsigned long conditioning_class_kernel_4<alphabet_size,pool_size,mem_manager>::
        get_range (
            unsigned long symbol,
            unsigned long& low_count,
            unsigned long& high_count,
            unsigned long& total_count
        ) const
        {
            if (symbol != alphabet_size-1)
            {
                node* temp = next;
                unsigned long low = 0;
                while (temp)
                {
                    if (temp->symbol == static_cast<unsigned short>(symbol))
                    {
                        high_count = temp->count + low;
                        low_count = low;
                        total_count = total;
                        return temp->count;
                    }
                    low += temp->count;
                    temp = temp->next;
                }
                return 0;
            }
            else
            {
                total_count = total;
                high_count = total;
                low_count = total-escapes;
                return escapes;
            }
        }

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

        template <
            unsigned long alphabet_size,
            unsigned long pool_size,
            typename mem_manager
            >
        void conditioning_class_kernel_4<alphabet_size,pool_size,mem_manager>::
        get_symbol (
            unsigned long target,
            unsigned long& symbol,
            unsigned long& low_count,
            unsigned long& high_count
        ) const
        {
            node* temp = next;
            unsigned long high = 0;
            while (true)
            {
                if (temp != 0)
                {
                    high += temp->count;
                    if (target < high)
                    {
                        symbol = temp->symbol;
                        high_count = high;
                        low_count = high - temp->count;
                        return;
                    }
                    temp = temp->next;
                }
                else
                {
                    // this must be the escape symbol
                    symbol = alphabet_size-1;
                    low_count = total-escapes;
                    high_count = total;
                    return;
                }
            }
        }

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

        template <
            unsigned long alphabet_size,
            unsigned long pool_size,
            typename mem_manager
            >
        void conditioning_class_kernel_4<alphabet_size,pool_size,mem_manager>::
        half_counts (
        )
        {
            total = 0;
            if (escapes > 1)
                escapes >>= 1;

            //divide all counts by 2
            node* temp = next;
            while (temp)
            {
                temp->count >>= 1;
                total += temp->count;
                temp = temp->next;
            }
            total += escapes;
        }

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

    }

    #endif // DLIB_CONDITIONING_CLASS_KERNEl_4_