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

#include "static_set_kernel_abstract.h"
#include "../interfaces/enumerable.h"
#include "../interfaces/remover.h"
#include "../algs.h"
#include "../sort.h"
#include "../serialize.h"
#include <functional>

namespace dlib
{

    template <
        typename T,
        typename compare = std::less<T>
        >
    class static_set_kernel_1 : public enumerable<const T>
    {

        /*!
            INITIAL VALUE
                - set_size == 0
                - d == 0
                - at_start_ == true
                - cur == 0

            CONVENTION
                - size() == set_size
                - if (set_size > 0) then
                    - d == pointer to an array containing all the elements of the set                
                    - d is sorted according to operator<
                - else  
                    - d == 0

                - current_element_valid() == (cur != 0)
                - at_start() == (at_start_)
                - if (current_element_valid()) then
                    - element() == *cur
        !*/

        // I would define this outside the class but Borland 5.5 has some problems
        // with non-inline templated friend functions.
        friend void deserialize (
            static_set_kernel_1& item, 
            std::istream& in
        )    
        {
            try
            {
                item.clear();
                unsigned long size;
                deserialize(size,in);
                item.set_size = size;
                item.d = new T[size];
                for (unsigned long i = 0; i < size; ++i)
                {
                    deserialize(item.d[i],in);
                }
            }
            catch (serialization_error e)
            { 
                item.set_size = 0;
                if (item.d)
                {
                    delete [] item.d;
                    item.d = 0;
                }

                throw serialization_error(e.info + "\n   while deserializing object of type static_set_kernel_1"); 
            }
            catch (...)
            {
                item.set_size = 0;
                if (item.d)
                {
                    delete [] item.d;
                    item.d = 0;
                }

                throw;
            }
        } 

        public:

            typedef T type;
            typedef compare compare_type;

            static_set_kernel_1(
            );

            virtual ~static_set_kernel_1(
            ); 

            void clear (
            );

            void load (
                remover<T>& source
            );

            void load (
                asc_remover<T,compare>& source
            );

            bool is_member (
                const T& item
            ) const;

            inline void swap (
                static_set_kernel_1& item
            );
    
            // functions from the enumerable interface
            inline unsigned long size (
            ) const;

            inline bool at_start (
            ) const;

            inline void reset (
            ) const;

            inline bool current_element_valid (
            ) const;

            inline const T& element (
            ) const;

            inline const T& element (
            );

            inline bool move_next (
            ) const;


        private:

   
            // data members
            unsigned long set_size;
            T* d;
            mutable T* cur;
            mutable bool at_start_;

            // restricted functions
            static_set_kernel_1(static_set_kernel_1&);        // copy constructor
            static_set_kernel_1& operator=(static_set_kernel_1&);    // assignment operator
    };

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

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

    template <
        typename T,
        typename compare
        >
    static_set_kernel_1<T,compare>::
    static_set_kernel_1(
    ) :
        set_size(0),
        d(0),
        cur(0),
        at_start_(true)
    {
    }

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

    template <
        typename T,
        typename compare
        >
    static_set_kernel_1<T,compare>::
    ~static_set_kernel_1(
    )
    {
        if (set_size > 0)
            delete [] d;
    } 

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

    template <
        typename T,
        typename compare
        >
    void static_set_kernel_1<T,compare>::
    clear(
    )
    {
        if (set_size > 0)
        {
            set_size = 0;
            delete [] d;
            d = 0;
        }
        reset();
    } 

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

    template <
        typename T,
        typename compare
        >
    void static_set_kernel_1<T,compare>::
    load (
        remover<T>& source
    )
    {
        if (source.size() > 0)
        {
            d = new T[source.size()];

            set_size = source.size();

            for (unsigned long i = 0; source.size() > 0; ++i)
                source.remove_any(d[i]);
            
            compare comp;
            qsort_array(d,0,set_size-1,comp);
        }
        else
        {
            clear();
        }
        reset();
    }

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

    template <
        typename T,
        typename compare
        >
    void static_set_kernel_1<T,compare>::
    load (
        asc_remover<T,compare>& source
    )
    {
        if (source.size() > 0)
        {
            d = new T[source.size()];

            set_size = source.size();

            for (unsigned long i = 0; source.size() > 0; ++i)
                source.remove_any(d[i]);
        }
        else
        {
            clear();
        }
        reset();
    }

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

    template <
        typename T,
        typename compare
        >
    bool static_set_kernel_1<T,compare>::
    is_member (
        const T& item
    ) const
    {
        unsigned long high = set_size;
        unsigned long low = 0;
        unsigned long p = set_size;
        unsigned long idx;
        while (p > 0)
        {
            p = (high-low)>>1;
            idx = p+low;
            if (item < d[idx])
                high = idx;
            else if (d[idx] < item)
                low = idx;
            else
                return true;
        }
        return false;
    }

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

    template <
        typename T,
        typename compare
        >
    unsigned long static_set_kernel_1<T,compare>::
    size (
    ) const
    {
        return set_size;
    }

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

    template <
        typename T,
        typename compare
        >
    void static_set_kernel_1<T,compare>::
    swap (
        static_set_kernel_1<T,compare>& item
    )
    {
        exchange(set_size,item.set_size);
        exchange(d,item.d);
        exchange(cur,item.cur);
        exchange(at_start_,item.at_start_);
    }

// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
    // enumerable function definitions
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------

    template <
        typename T,
        typename compare
        >
    bool static_set_kernel_1<T,compare>::
    at_start (
    ) const
    {
        return at_start_;
    }
    
// ----------------------------------------------------------------------------------------

    template <
        typename T,
        typename compare
        >
    void static_set_kernel_1<T,compare>::
    reset (
    ) const
    {
        at_start_ = true;
        cur = 0;
    }
    
// ----------------------------------------------------------------------------------------

    template <
        typename T,
        typename compare
        >
    bool static_set_kernel_1<T,compare>::
    current_element_valid (
    ) const
    {   
        return (cur != 0);
    }
    
// ----------------------------------------------------------------------------------------

    template <
        typename T,
        typename compare
        >
    const T& static_set_kernel_1<T,compare>::
    element (
    ) const
    {
        return *cur;
    }
    
// ----------------------------------------------------------------------------------------

    template <
        typename T,
        typename compare
        >
    const T& static_set_kernel_1<T,compare>::
    element (
    )
    {
        return *cur;
    }
    
// ----------------------------------------------------------------------------------------

    template <
        typename T,
        typename compare
        >
    bool static_set_kernel_1<T,compare>::
    move_next (
    ) const
    {      
        // if at_start() && size() > 0
        if (at_start_ && set_size > 0)
        {
            at_start_ = false;
            cur = d;
            return true;
        }
        // else if current_element_valid()
        else if (cur != 0)
        {
            ++cur;
            if (static_cast<unsigned long>(cur - d) < set_size)
            {
                return true;
            }
            else
            {
                cur = 0;
                return false;
            }
        }
        else
        {
            at_start_ = false;
            return false;
        }
    }
    
// ----------------------------------------------------------------------------------------

}

#endif // DLIB_STATIC_SET_KERNEl_1_