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

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

    namespace dlib
    {

        template <
            unsigned long alphabet_size
            >
        class conditioning_class_kernel_2
        {
            /*!
                INITIAL VALUE
                    total == 1
                    symbols == pointer to array of alphabet_size data structs
                    for all i except i == alphabet_size-1: symbols[i].count == 0
                                                           symbols[i].left_count == 0

                    symbols[alphabet_size-1].count == 1
                    symbols[alpahbet_size-1].left_count == 0

                CONVENTION
                    symbols == pointer to array of alphabet_size data structs
                    get_total() == total
                    get_count(symbol) == symbols[symbol].count

                    symbols is organized as a tree with symbols[0] as the root.

                    the left subchild of symbols[i] is symbols[i*2+1] and
                    the right subchild is symbols[i*2+2].
                    the partent of symbols[i] == symbols[(i-1)/2]

                    symbols[i].left_count == the sum of the counts of all the
                                             symbols to the left of symbols[i]

                    get_memory_usage() == global_state.memory_usage
            !*/

        public:

            class global_state_type
            {
            public:
                global_state_type () : memory_usage(0) {}
            private:
                unsigned long memory_usage;

                friend class conditioning_class_kernel_2<alphabet_size>;
            };

            conditioning_class_kernel_2 (
                global_state_type& global_state_
            );

            ~conditioning_class_kernel_2 (
            );

            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:

            // restricted functions
            conditioning_class_kernel_2(conditioning_class_kernel_2<alphabet_size>&);        // copy constructor
            conditioning_class_kernel_2& operator=(conditioning_class_kernel_2<alphabet_size>&);    // assignment operator

            // data members
            unsigned short total;
            struct data
            {
                unsigned short count;
                unsigned short left_count;
            };

            data* symbols;
            global_state_type& global_state;

        };

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

        template <
            unsigned long alphabet_size
            >
        conditioning_class_kernel_2<alphabet_size>::
        conditioning_class_kernel_2 (
            global_state_type& global_state_
        ) :
            total(1),
            symbols(new data[alphabet_size]),
            global_state(global_state_)
        {
            COMPILE_TIME_ASSERT( 1 < alphabet_size && alphabet_size < 65536 );

            data* start = symbols;
            data* end = symbols + alphabet_size-1;

            while (start != end)
            {
                start->count = 0;
                start->left_count = 0;
                ++start;
            }

            start->count = 1;
            start->left_count = 0;

            // update the left_counts for the symbol alphabet_size-1
            unsigned short temp;
            unsigned long symbol = alphabet_size-1;
            while (symbol != 0)
            {
                // temp will be 1 if symbol is odd, 0 if it is even
                temp = static_cast<unsigned short>(symbol&0x1);

                // set symbol to its parent
                symbol = (symbol-1)>>1;

                // note that all left subchidren are odd and also that
                // if symbol was a left subchild then we want to increment
                // its parents left_count
                if (temp)
                    ++symbols[symbol].left_count;
            }

            global_state.memory_usage += sizeof(data)*alphabet_size +
                                         sizeof(conditioning_class_kernel_2);
        }

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

        template <
            unsigned long alphabet_size
            >
        conditioning_class_kernel_2<alphabet_size>::
        ~conditioning_class_kernel_2 (
        )
        {
            delete [] symbols;
            global_state.memory_usage -= sizeof(data)*alphabet_size +
                                         sizeof(conditioning_class_kernel_2);
        }

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

        template <
            unsigned long alphabet_size
            >
        void conditioning_class_kernel_2<alphabet_size>::
        clear(
        )
        {
            data* start = symbols;
            data* end = symbols + alphabet_size-1;

            total = 1;

            while (start != end)
            {
                start->count = 0;
                start->left_count = 0;
                ++start;
            }

            start->count = 1;
            start->left_count = 0;

            // update the left_counts
            unsigned short temp;
            unsigned long symbol = alphabet_size-1;
            while (symbol != 0)
            {
                // temp will be 1 if symbol is odd, 0 if it is even
                temp = static_cast<unsigned short>(symbol&0x1);

                // set symbol to its parent
                symbol = (symbol-1)>>1;

                // note that all left subchidren are odd and also that
                // if symbol was a left subchild then we want to increment
                // its parents left_count
                symbols[symbol].left_count += temp;
            }
        }

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

        template <
            unsigned long alphabet_size
            >
        unsigned long conditioning_class_kernel_2<alphabet_size>::
        get_memory_usage(
        ) const
        {
            return global_state.memory_usage;
        }

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

        template <
            unsigned long alphabet_size
            >
        typename conditioning_class_kernel_2<alphabet_size>::global_state_type& conditioning_class_kernel_2<alphabet_size>::
        get_global_state(
        )
        {
            return global_state;
        }

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

        template <
            unsigned long alphabet_size
            >
        bool conditioning_class_kernel_2<alphabet_size>::
        increment_count (
            unsigned long symbol,
            unsigned short amount
        )
        {
            // if we need to renormalize then do so
            if (static_cast<unsigned long>(total)+static_cast<unsigned long>(amount) >= 65536)
            {
                unsigned long s;
                unsigned short temp;
                for (unsigned short i = 0; i < alphabet_size-1; ++i)
                {
                    s = i;

                    // divide the count for this symbol by 2
                    symbols[i].count >>= 1;

                    symbols[i].left_count = 0;

                    // bubble this change up though the tree
                    while (s != 0)
                    {
                        // temp will be 1 if symbol is odd, 0 if it is even
                        temp = static_cast<unsigned short>(s&0x1);

                        // set s to its parent
                        s = (s-1)>>1;

                        // note that all left subchidren are odd and also that
                        // if s was a left subchild then we want to increment
                        // its parents left_count
                        if (temp)
                            symbols[s].left_count += symbols[i].count;
                    }
                }

                // update symbols alphabet_size-1
                {
                    s = alphabet_size-1;

                    // divide alphabet_size-1 symbol by 2 if it's > 1
                    if (symbols[alphabet_size-1].count > 1)
                        symbols[alphabet_size-1].count >>= 1;

                    // bubble this change up though the tree
                    while (s != 0)
                    {
                        // temp will be 1 if symbol is odd, 0 if it is even
                        temp = static_cast<unsigned short>(s&0x1);

                        // set s to its parent
                        s = (s-1)>>1;

                        // note that all left subchidren are odd and also that
                        // if s was a left subchild then we want to increment
                        // its parents left_count
                        if (temp)
                            symbols[s].left_count += symbols[alphabet_size-1].count;
                    }
                }

                // calculate the new total
                total = 0;
                unsigned long m = 0;
                while (m < alphabet_size)
                {
                    total += symbols[m].count + symbols[m].left_count;
                    m = (m<<1) + 2;
                }

            }

            // increment the count for the specified symbol
            symbols[symbol].count += amount;;
            total += amount;

            unsigned short temp;
            while (symbol != 0)
            {
                // temp will be 1 if symbol is odd, 0 if it is even
                temp = static_cast<unsigned short>(symbol&0x1);

                // set symbol to its parent
                symbol = (symbol-1)>>1;

                // note that all left subchidren are odd and also that
                // if symbol was a left subchild then we want to increment
                // its parents left_count
                if (temp)
                    symbols[symbol].left_count += amount;
            }

            return true;
        }

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

        template <
            unsigned long alphabet_size
            >
        unsigned long conditioning_class_kernel_2<alphabet_size>::
        get_count (
            unsigned long symbol
        ) const
        {
            return symbols[symbol].count;
        }

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

        template <
            unsigned long alphabet_size
            >
        unsigned long conditioning_class_kernel_2<alphabet_size>::
        get_alphabet_size (
        )
        {
            return alphabet_size;
        }

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

        template <
            unsigned long alphabet_size
            >
        unsigned long conditioning_class_kernel_2<alphabet_size>::
        get_total (
        ) const
        {
            return total;
        }

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

        template <
            unsigned long alphabet_size
            >
        unsigned long conditioning_class_kernel_2<alphabet_size>::
        get_range (
            unsigned long symbol,
            unsigned long& low_count,
            unsigned long& high_count,
            unsigned long& total_count
        ) const
        {
            if (symbols[symbol].count == 0)
                return 0;

            unsigned long current = symbol;
            total_count = total;
            unsigned long high_count_temp = 0;
            bool came_from_right = true;
            while (true)
            {
                if (came_from_right)
                {
                    high_count_temp += symbols[current].count + symbols[current].left_count;
                }

                // note that if current is even then it is a right child
                came_from_right = !(current&0x1);

                if (current == 0)
                    break;

                // set current to its parent
                current = (current-1)>>1 ;
            }

            low_count = high_count_temp - symbols[symbol].count;
            high_count = high_count_temp;

            return symbols[symbol].count;
        }

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

        template <
            unsigned long alphabet_size
            >
        void conditioning_class_kernel_2<alphabet_size>::
        get_symbol (
            unsigned long target,
            unsigned long& symbol,
            unsigned long& low_count,
            unsigned long& high_count
        ) const
        {
            unsigned long current = 0;
            unsigned long low_count_temp = 0;

            while (true)
            {
                if (static_cast<unsigned short>(target) < symbols[current].left_count)
                {
                    // we should go left
                    current = (current<<1) + 1;
                }
                else
                {
                    target -= symbols[current].left_count;
                    low_count_temp += symbols[current].left_count;
                    if (static_cast<unsigned short>(target) < symbols[current].count)
                    {
                        // we have found our target
                        symbol = current;
                        high_count = low_count_temp + symbols[current].count;
                        low_count = low_count_temp;
                        break;
                    }
                    else
                    {
                        // go right
                        target -= symbols[current].count;
                        low_count_temp += symbols[current].count;
                        current = (current<<1) + 2;
                    }
                }

            }

        }

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

    }

    #endif // DLIB_CONDITIONING_CLASS_KERNEl_1_