    // 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_1_
    #define DLIB_CONDITIONING_CLASS_KERNEl_1_

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

    namespace dlib
    {

        template <
            unsigned long alphabet_size
            >
        class conditioning_class_kernel_1
        {
            /*!
                INITIAL VALUE
                    total == 1
                    counts == pointer to an array of alphabet_size unsigned shorts
                    for all i except i == alphabet_size-1: counts[i] == 0
                    counts[alphabet_size-1] == 1

                CONVENTION
                    counts == pointer to an array of alphabet_size unsigned shorts
                    get_total() == total
                    get_count(symbol) == counts[symbol]

                    LOW_COUNT(symbol) == sum of counts[0] though counts[symbol-1]
                                         or 0 if symbol == 0

                    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_1<alphabet_size>;
            };

            conditioning_class_kernel_1 (
                global_state_type& global_state_
            );

            ~conditioning_class_kernel_1 (
            );

            void clear(
            );

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

            unsigned long get_count (
                unsigned long symbol
            ) const;

            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_1(conditioning_class_kernel_1<alphabet_size>&);        // copy constructor
            conditioning_class_kernel_1& operator=(conditioning_class_kernel_1<alphabet_size>&);    // assignment operator

            // data members
            unsigned short total;
            unsigned short* counts;
            global_state_type& global_state;

        };

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

        template <
            unsigned long alphabet_size
            >
        conditioning_class_kernel_1<alphabet_size>::
        conditioning_class_kernel_1 (
            global_state_type& global_state_
        ) :
            total(1),
            counts(new unsigned short[alphabet_size]),
            global_state(global_state_)
        {
            COMPILE_TIME_ASSERT( 1 < alphabet_size && alphabet_size < 65536 );

            unsigned short* start = counts;
            unsigned short* end = counts+alphabet_size-1;
            while (start != end)
            {
                *start = 0;
                ++start;
            }
            *start = 1;

            // update memory usage
            global_state.memory_usage += sizeof(unsigned short)*alphabet_size +
                                         sizeof(conditioning_class_kernel_1);
        }

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

        template <
            unsigned long alphabet_size
            >
        conditioning_class_kernel_1<alphabet_size>::
        ~conditioning_class_kernel_1 (
        )
        {
            delete [] counts;
            // update memory usage
            global_state.memory_usage -= sizeof(unsigned short)*alphabet_size +
                                         sizeof(conditioning_class_kernel_1);
        }

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

        template <
            unsigned long alphabet_size
            >
        void conditioning_class_kernel_1<alphabet_size>::
        clear(
        )
        {
            total = 1;
            unsigned short* start = counts;
            unsigned short* end = counts+alphabet_size-1;
            while (start != end)
            {
                *start = 0;
                ++start;
            }
            *start = 1;
        }

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

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

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

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

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

        template <
            unsigned long alphabet_size
            >
        bool conditioning_class_kernel_1<alphabet_size>::
        increment_count (
            unsigned long symbol,
            unsigned short amount
        )
        {
            // if we are going over a total of 65535 then scale down all counts by 2
            if (static_cast<unsigned long>(total)+static_cast<unsigned long>(amount) >= 65536)
            {
                total = 0;
                unsigned short* start = counts;
                unsigned short* end = counts+alphabet_size;
                while (start != end)
                {
                    *start >>= 1;
                    total += *start;
                    ++start;
                }
                // make sure it is at least one
                if (counts[alphabet_size-1]==0)
                {
                    ++total;
                    counts[alphabet_size-1] = 1;
                }
            }
            counts[symbol] += amount;
            total += amount;
            return true;
        }

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

        template <
            unsigned long alphabet_size
            >
        unsigned long conditioning_class_kernel_1<alphabet_size>::
        get_count (
            unsigned long symbol
        ) const
        {
            return counts[symbol];
        }

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

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

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

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

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

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

            total_count = total;

            const unsigned short* start = counts;
            const unsigned short* end = counts+symbol;
            unsigned short high_count_temp = *start;
            while (start != end)
            {
                ++start;
                high_count_temp += *start;
            }
            low_count = high_count_temp - *start;
            high_count = high_count_temp;
            return *start;
        }

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

        template <
            unsigned long alphabet_size
            >
        void conditioning_class_kernel_1<alphabet_size>::
        get_symbol (
            unsigned long target,
            unsigned long& symbol,
            unsigned long& low_count,
            unsigned long& high_count
        ) const
        {
            unsigned long high_count_temp = *counts;
            const unsigned short* start = counts;
            while (target >= high_count_temp)
            {
                ++start;
                high_count_temp += *start;
            }

            low_count = high_count_temp - *start;
            high_count = high_count_temp;
            symbol = static_cast<unsigned long>(start-counts);
        }

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

    }

    #endif // DLIB_CONDITIONING_CLASS_KERNEl_1_