/*=============================================================================
    Copyright (c) 2001-2011 Joel de Guzman
    Copyright (c) 2001-2011 Hartmut Kaiser
    Copyright (c)      2011 Bryce Lelbach

    Distributed under the Boost Software License, Version 1.0. (See accompanying
    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
#if !defined(BOOST_SPIRIT_UTREE_DETAIL2)
#define BOOST_SPIRIT_UTREE_DETAIL2

#if defined(BOOST_MSVC)
# pragma warning(push)
# pragma warning(disable: 4800)
#endif

#include <boost/type_traits/remove_pointer.hpp>
#include <boost/type_traits/is_pointer.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/throw_exception.hpp>
#include <boost/iterator/iterator_traits.hpp>

namespace boost { namespace spirit { namespace detail
{
    inline char& fast_string::info()
    {
        return buff[small_string_size];
    }

    inline char fast_string::info() const
    {
        return buff[small_string_size];
    }

    inline int fast_string::get_type() const
    {
        return info() >> 1;
    }

    inline void fast_string::set_type(int t)
    {
        info() = (t << 1) | (info() & 1);
    }

    inline short fast_string::tag() const
    {
        return (int(buff[small_string_size-2]) << 8) + (unsigned char)buff[small_string_size-1];
    }

    inline void fast_string::tag(short tag)
    {
        buff[small_string_size-2] = tag >> 8;
        buff[small_string_size-1] = tag & 0xff;
    }

    inline bool fast_string::is_heap_allocated() const
    {
        return info() & 1;
    }

    inline std::size_t fast_string::size() const
    {
        if (is_heap_allocated())
            return heap.size;
        else
            return max_string_len - buff[max_string_len];
    }

    inline char const* fast_string::str() const
    {
        if (is_heap_allocated())
            return heap.str;
        else
            return buff;
    }

    template <typename Iterator>
    inline void fast_string::construct(Iterator f, Iterator l)
    {
        unsigned const size = l-f;
        char* str;
        if (size < max_string_len)
        {
            // if it fits, store it in-situ; small_string_size minus the length
            // of the string is placed in buff[small_string_size - 1]
            str = buff;
            buff[max_string_len] = static_cast<char>(max_string_len - size);
            info() &= ~0x1;
        }
        else
        {
            // else, store it in the heap
            str = new char[size + 1]; // add one for the null char
            heap.str = str;
            heap.size = size;
            info() |= 0x1;
        }
        for (std::size_t i = 0; i != size; ++i)
        {
            *str++ = *f++;
        }
        *str = '\0'; // add the null char
    }

    inline void fast_string::swap(fast_string& other)
    {
        std::swap(*this, other);
    }

    inline void fast_string::free()
    {
        if (is_heap_allocated())
        {
            delete [] heap.str;
        }
    }

    inline void fast_string::copy(fast_string const& other)
    {
        construct(other.str(), other.str() + other.size());
    }

    inline void fast_string::initialize()
    {
        for (std::size_t i = 0; i != buff_size / (sizeof(long)/sizeof(char)); ++i)
            lbuff[i] = 0;
    }

    struct list::node : boost::noncopyable
    {
        template <typename T>
        node(T const& val, node* next, node* prev)
          : val(val), next(next), prev(prev) {}

        void unlink()
        {
            prev->next = next;
            next->prev = prev;
        }

        utree val;
        node* next;
        node* prev;
    };

    template <typename Value>
    class list::node_iterator
      : public boost::iterator_facade<
            node_iterator<Value>
          , Value
          , boost::bidirectional_traversal_tag>
    {
    public:

        node_iterator()
          : node(0), prev(0) {}

        node_iterator(list::node* node, list::node* prev)
          : node(node), prev(prev) {}

    private:

        friend class boost::iterator_core_access;
        friend class boost::spirit::utree;
        friend struct boost::spirit::detail::list;

        void increment()
        {
            if (node != 0) // not at end
            {
                prev = node;
                node = node->next;
            }
        }

        void decrement()
        {
            if (prev != 0) // not at begin
            {
                node = prev;
                prev = prev->prev;
            }
        }

        bool equal(node_iterator const& other) const
        {
            return node == other.node;
        }

        typename node_iterator::reference dereference() const
        {
            return node->val;
        }

        list::node* node;
        list::node* prev;
    };

    template <typename Value>
    class list::node_iterator<boost::reference_wrapper<Value> >
      : public boost::iterator_facade<
            node_iterator<boost::reference_wrapper<Value> >
          , boost::reference_wrapper<Value>
          , boost::bidirectional_traversal_tag>
    {
    public:

        node_iterator()
          : node(0), prev(0), curr(nil_node) {}

        node_iterator(list::node* node, list::node* prev)
          : node(node), prev(prev), curr(node ? node->val : nil_node) {}

    private:

        friend class boost::iterator_core_access;
        friend class boost::spirit::utree;
        friend struct boost::spirit::detail::list;

        void increment()
        {
            if (node != 0) // not at end
            {
                prev = node;
                node = node->next;
                curr = boost::ref(node ? node->val : nil_node);
            }
        }

        void decrement()
        {
            if (prev != 0) // not at begin
            {
                node = prev;
                prev = prev->prev;
                curr = boost::ref(node ? node->val : nil_node);
            }
        }

        bool equal(node_iterator const& other) const
        {
            return node == other.node;
        }

        typename node_iterator::reference dereference() const
        {
            return curr;
        }

        list::node* node;
        list::node* prev;

        static Value nil_node;
        mutable boost::reference_wrapper<Value> curr;
    };

    template <typename Value>
    Value list::node_iterator<boost::reference_wrapper<Value> >::nil_node = Value();

    inline void list::free()
    {
        node* p = first;
        while (p != 0)
        {
            node* next = p->next;
            delete p;
            p = next;
        }
    }

    inline void list::copy(list const& other)
    {
        node* p = other.first;
        while (p != 0)
        {
            push_back(p->val);
            p = p->next;
        }
    }

    inline void list::default_construct()
    {
        first = last = 0;
        size = 0;
    }

    template <typename T, typename Iterator>
    inline void list::insert(T const& val, Iterator pos)
    {
        if (!pos.node)
        {
            push_back(val);
            return;
        }

        detail::list::node* new_node =
            new detail::list::node(val, pos.node, pos.node->prev);

        if (pos.node->prev)
            pos.node->prev->next = new_node;
        else
            first = new_node;

        pos.node->prev = new_node;
        ++size;
    }

    template <typename T>
    inline void list::push_front(T const& val)
    {
        detail::list::node* new_node;
        if (first == 0)
        {
            new_node = new detail::list::node(val, 0, 0);
            first = last = new_node;
            ++size;
        }
        else
        {
            new_node = new detail::list::node(val, first, first->prev);
            first->prev = new_node;
            first = new_node;
            ++size;
        }
    }

    template <typename T>
    inline void list::push_back(T const& val)
    {
        if (last == 0)
            push_front(val);
        else {
            detail::list::node* new_node =
                new detail::list::node(val, last->next, last);
            last->next = new_node;
            last = new_node;
            ++size;
        }
    }

    inline void list::pop_front()
    {
        BOOST_ASSERT(size != 0);
        if (first == last) // there's only one item
        {
            delete first;
            size = 0;
            first = last = 0;
        }
        else
        {
            node* np = first;
            first = first->next;
            first->prev = 0;
            delete np;
            --size;
        }
    }

    inline void list::pop_back()
    {
        BOOST_ASSERT(size != 0);
        if (first == last) // there's only one item
        {
            delete first;
            size = 0;
            first = last = 0;
        }
        else
        {
            node* np = last;
            last = last->prev;
            last->next = 0;
            delete np;
            --size;
        }
    }

    inline list::node* list::erase(node* pos)
    {
        BOOST_ASSERT(pos != 0);
        if (pos == first)
        {
            pop_front();
            return first;
        }
        else if (pos == last)
        {
            pop_back();
            return 0;
        }
        else
        {
            node* next(pos->next);
            pos->unlink();
            delete pos;
            --size;
            return next;
        }
    }

    ///////////////////////////////////////////////////////////////////////////
    // simple binder for binary visitation (we don't want to bring in the big guns)
    template <typename F, typename X>
    struct bind_impl
    {
        typedef typename F::result_type result_type;
        X& x; // always by reference
        F f;
        bind_impl(F f, X& x) : x(x), f(f) {}

        template <typename Y>
        typename F::result_type operator()(Y& y) const
        {
            return f(x, y);
        }

        template <typename Y>
        typename F::result_type operator()(Y const& y) const
        {
            return f(x, y);
        }
    };

    template <typename F, typename X>
    bind_impl<F, X const> bind(F f, X const& x)
    {
        return bind_impl<F, X const>(f, x);
    }

    template <typename F, typename X>
    bind_impl<F, X> bind(F f, X& x)
    {
        return bind_impl<F, X>(f, x);
    }

    template <typename UTreeX, typename UTreeY = UTreeX>
    struct visit_impl
    {
        template <typename F>
        typename F::result_type
        static apply(UTreeX& x, F f) // single dispatch
        {
            typedef typename
                boost::mpl::if_<boost::is_const<UTreeX>,
                typename UTreeX::const_iterator,
                typename UTreeX::iterator>::type
            iterator;

            typedef boost::iterator_range<iterator> list_range;
            typedef utree_type type;

            switch (x.get_type())
            {
                default:
                    BOOST_THROW_EXCEPTION(
                        bad_type_exception("corrupt utree type", x.get_type()));
                    break;

                case type::invalid_type:
                    return f(invalid);

                case type::nil_type:
                    return f(nil);

                case type::bool_type:
                    return f(x.b);

                case type::int_type:
                    return f(x.i);

                case type::double_type:
                    return f(x.d);

                case type::list_type:
                    return f(list_range(iterator(x.l.first, 0), iterator(0, x.l.last)));

                case type::range_type:
                    return f(list_range(iterator(x.r.first, 0), iterator(0, x.r.last)));

                case type::string_type:
                    return f(utf8_string_range_type(x.s.str(), x.s.size()));

                case type::string_range_type:
                    return f(utf8_string_range_type(x.sr.first, x.sr.last));

                case type::symbol_type:
                    return f(utf8_symbol_range_type(x.s.str(), x.s.size()));

                case type::binary_type:
                    return f(binary_range_type(x.s.str(), x.s.size()));

                case type::reference_type:
                    return apply(*x.p, f);

                case type::any_type:
                    return f(any_ptr(x.v.p, x.v.i));

                case type::function_type:
                    return f(*x.pf);
            }
        }

        template <typename F>
        typename F::result_type
        static apply(UTreeX& x, UTreeY& y, F f) // double dispatch
        {
            typedef typename
                boost::mpl::if_<boost::is_const<UTreeX>,
                typename UTreeX::const_iterator,
                typename UTreeX::iterator>::type
            iterator;

            typedef boost::iterator_range<iterator> list_range;
            typedef utree_type type;

            switch (x.get_type())
            {
                default:
                    BOOST_THROW_EXCEPTION(
                        bad_type_exception("corrupt utree type", x.get_type()));
                    break;

                case type::invalid_type:
                    return visit_impl::apply(y, detail::bind(f, invalid));

                case type::nil_type:
                    return visit_impl::apply(y, detail::bind(f, nil));

                case type::bool_type:
                    return visit_impl::apply(y, detail::bind(f, x.b));

                case type::int_type:
                    return visit_impl::apply(y, detail::bind(f, x.i));

                case type::double_type:
                    return visit_impl::apply(y, detail::bind(f, x.d));

                case type::list_type:
                    return visit_impl::apply(
                        y, detail::bind<F, list_range>(f,
                        list_range(iterator(x.l.first, 0), iterator(0, x.l.last))));

                case type::range_type:
                    return visit_impl::apply(
                        y, detail::bind<F, list_range>(f,
                        list_range(iterator(x.r.first, 0), iterator(0, x.r.last))));

                case type::string_type:
                    return visit_impl::apply(y, detail::bind(
                        f, utf8_string_range_type(x.s.str(), x.s.size())));

                case type::string_range_type:
                    return visit_impl::apply(y, detail::bind(
                        f, utf8_string_range_type(x.sr.first, x.sr.last)));

                case type::symbol_type:
                    return visit_impl::apply(y, detail::bind(
                        f, utf8_symbol_range_type(x.s.str(), x.s.size())));

                case type::binary_type:
                    return visit_impl::apply(y, detail::bind(
                        f, binary_range_type(x.s.str(), x.s.size())));

                case type::reference_type:
                    return apply(*x.p, y, f);

                case type::any_type:
                    return visit_impl::apply(
                        y, detail::bind(f, any_ptr(x.v.p, x.v.i)));

                case type::function_type:
                    return visit_impl::apply(y, detail::bind(f, *x.pf));
            }
        }
    };

    struct index_impl
    {
        static utree& apply(utree& ut, std::size_t i)
        {
            switch (ut.get_type())
            {
                case utree_type::reference_type:
                    return apply(ut.deref(), i);
                case utree_type::range_type:
                    return apply(ut.r.first, i);
                case utree_type::list_type:
                    return apply(ut.l.first, i);
                default:
                    BOOST_THROW_EXCEPTION(
                        bad_type_exception
                            ("index operation performed on non-list utree type",
                             ut.get_type()));
            }
        }

        static utree const& apply(utree const& ut, std::size_t i)
        {
            switch (ut.get_type())
            {
                case utree_type::reference_type:
                    return apply(ut.deref(), i);
                case utree_type::range_type:
                    return apply(ut.r.first, i);
                case utree_type::list_type:
                    return apply(ut.l.first, i);
                default:
                    BOOST_THROW_EXCEPTION(
                        bad_type_exception
                            ("index operation performed on non-list utree type",
                             ut.get_type()));
            }
        }

        static utree& apply(list::node* node, std::size_t i)
        {
            for (; i > 0; --i)
                node = node->next;
            return node->val;
        }

        static utree const& apply(list::node const* node, std::size_t i)
        {
            for (; i > 0; --i)
                node = node->next;
            return node->val;
        }
    };
}}}

namespace boost { namespace spirit
{
    template <typename F>
    stored_function<F>::stored_function(F f)
      : f(f)
    {
    }

    template <typename F>
    stored_function<F>::~stored_function()
    {
    }

    template <typename F>
    utree stored_function<F>::operator()(utree const& env) const
    {
        return f(env);
    }

    template <typename F>
    utree stored_function<F>::operator()(utree& env) const
    {
        return f(env);
    }

    template <typename F>
    function_base*
    stored_function<F>::clone() const
    {
        return new stored_function<F>(f);
    }

    template <typename F>
    referenced_function<F>::referenced_function(F& f)
      : f(f)
    {
    }

    template <typename F>
    referenced_function<F>::~referenced_function()
    {
    }

    template <typename F>
    utree referenced_function<F>::operator()(utree const& env) const
    {
        return f(env);
    }

    template <typename F>
    utree referenced_function<F>::operator()(utree& env) const
    {
        return f(env);
    }

    template <typename F>
    function_base*
    referenced_function<F>::clone() const
    {
        return new referenced_function<F>(f);
    }

    inline utree::utree(utree::invalid_type)
    {
        s.initialize();
        set_type(type::invalid_type);
    }

    inline utree::utree(utree::nil_type)
    {
        s.initialize();
        set_type(type::nil_type);
    }

    inline utree::utree(bool b_)
    {
        s.initialize();
        b = b_;
        set_type(type::bool_type);
    }

    inline utree::utree(char c)
    {
        s.initialize();
        // char constructs a single element string
        s.construct(&c, &c+1);
        set_type(type::string_type);
    }

    inline utree::utree(unsigned int i_)
    {
        s.initialize();
        i = i_;
        set_type(type::int_type);
    }

    inline utree::utree(int i_)
    {
        s.initialize();
        i = i_;
        set_type(type::int_type);
    }

    inline utree::utree(double d_)
    {
        s.initialize();
        d = d_;
        set_type(type::double_type);
    }

    inline utree::utree(char const* str)
    {
        s.initialize();
        s.construct(str, str + strlen(str));
        set_type(type::string_type);
    }

    inline utree::utree(char const* str, std::size_t len)
    {
        s.initialize();
        s.construct(str, str + len);
        set_type(type::string_type);
    }

    inline utree::utree(std::string const& str)
    {
        s.initialize();
        s.construct(str.begin(), str.end());
        set_type(type::string_type);
    }

    template <typename Base, utree_type::info type_>
    inline utree::utree(basic_string<Base, type_> const& bin)
    {
        s.initialize();
        s.construct(bin.begin(), bin.end());
        set_type(type_);
    }

    inline utree::utree(boost::reference_wrapper<utree> ref)
    {
        s.initialize();
        p = ref.get_pointer();
        set_type(type::reference_type);
    }

    inline utree::utree(any_ptr const& p)
    {
        s.initialize();
        v.p = p.p;
        v.i = p.i;
        set_type(type::any_type);
    }

    inline utree::utree(function_base const& pf_)
    {
        s.initialize();
        pf = pf_.clone();
        set_type(type::function_type);
    }

    inline utree::utree(function_base* pf_)
    {
        s.initialize();
        pf = pf_;
        set_type(type::function_type);
    }

    template <typename Iter>
    inline utree::utree(boost::iterator_range<Iter> r)
    {
        s.initialize();

        assign(r.begin(), r.end());
    }

    inline utree::utree(range r, shallow_tag)
    {
        s.initialize();
        this->r.first = r.begin().node;
        this->r.last = r.end().prev;
        set_type(type::range_type);
    }

    inline utree::utree(const_range r, shallow_tag)
    {
        s.initialize();
        this->r.first = r.begin().node;
        this->r.last = r.end().prev;
        set_type(type::range_type);
    }

    inline utree::utree(utf8_string_range_type const& str, shallow_tag)
    {
        s.initialize();
        this->sr.first = str.begin();
        this->sr.last = str.end();
        set_type(type::string_range_type);
    }

    inline utree::utree(utree const& other)
    {
        s.initialize();
        copy(other);
    }

    inline utree::~utree()
    {
        free();
    }

    inline utree& utree::operator=(utree const& other)
    {
        if (this != &other)
        {
            free();
            copy(other);
        }
        return *this;
    }

    inline utree& utree::operator=(nil_type)
    {
        free();
        set_type(type::nil_type);
        return *this;
    }

    inline utree& utree::operator=(bool b_)
    {
        free();
        b = b_;
        set_type(type::bool_type);
        return *this;
    }

    inline utree& utree::operator=(char c)
    {
        // char constructs a single element string
        free();
        s.construct(&c, &c+1);
        set_type(type::string_type);
        return *this;
    }

    inline utree& utree::operator=(unsigned int i_)
    {
        free();
        i = i_;
        set_type(type::int_type);
        return *this;
    }

    inline utree& utree::operator=(int i_)
    {
        free();
        i = i_;
        set_type(type::int_type);
        return *this;
    }

    inline utree& utree::operator=(double d_)
    {
        free();
        d = d_;
        set_type(type::double_type);
        return *this;
    }

    inline utree& utree::operator=(char const* s_)
    {
        free();
        s.construct(s_, s_ + strlen(s_));
        set_type(type::string_type);
        return *this;
    }

    inline utree& utree::operator=(std::string const& s_)
    {
        free();
        s.construct(s_.begin(), s_.end());
        set_type(type::string_type);
        return *this;
    }

    template <typename Base, utree_type::info type_>
    inline utree& utree::operator=(basic_string<Base, type_> const& bin)
    {
        free();
        s.construct(bin.begin(), bin.end());
        set_type(type_);
        return *this;
    }

    inline utree& utree::operator=(boost::reference_wrapper<utree> ref)
    {
        free();
        p = ref.get_pointer();
        set_type(type::reference_type);
        return *this;
    }

    inline utree& utree::operator=(any_ptr const& p)
    {
        free();
        v.p = p.p;
        v.i = p.i;
        set_type(type::any_type);
        return *this;
    }

    inline utree& utree::operator=(function_base const& pf_)
    {
        free();
        pf = pf_.clone();
        set_type(type::function_type);
        return *this;
    }

    inline utree& utree::operator=(function_base* pf_)
    {
        free();
        pf = pf_;
        set_type(type::function_type);
        return *this;
    }

    template <typename Iter>
    inline utree& utree::operator=(boost::iterator_range<Iter> r)
    {
        free();
        assign(r.begin(), r.end());
        return *this;
    }

    template <typename F>
    typename boost::result_of<F(utree const&)>::type
    inline utree::visit(utree const& x, F f)
    {
        return detail::visit_impl<utree const>::apply(x, f);
    }

    template <typename F>
    typename boost::result_of<F(utree&)>::type
    inline utree::visit(utree& x, F f)
    {
        return detail::visit_impl<utree>::apply(x, f);
    }

    template <typename F>
    typename boost::result_of<F(utree const&, utree const&)>::type
    inline utree::visit(utree const& x, utree const& y, F f)
    {
        return detail::visit_impl<utree const, utree const>::apply(x, y, f);
    }

    template <typename F>
    typename boost::result_of<F(utree const&, utree&)>::type
    inline utree::visit(utree const& x, utree& y, F f)
    {
        return detail::visit_impl<utree const, utree>::apply(x, y, f);
    }

    template <typename F>
    typename boost::result_of<F(utree&, utree const&)>::type
    inline utree::visit(utree& x, utree const& y, F f)
    {
        return detail::visit_impl<utree, utree const>::apply(x, y, f);
    }

    template <typename F>
    typename boost::result_of<F(utree&, utree&)>::type
    inline utree::visit(utree& x, utree& y, F f)
    {
        return detail::visit_impl<utree, utree>::apply(x, y, f);
    }

    inline utree::reference get(utree::reference ut, utree::size_type i)
    { return detail::index_impl::apply(ut, i); }

    inline utree::const_reference
    get(utree::const_reference ut, utree::size_type i)
    { return detail::index_impl::apply(ut, i); }

    template <typename T>
    inline void utree::push_front(T const& val)
    {
        if (get_type() == type::reference_type)
            return p->push_front(val);

        ensure_list_type("push_front()");
        l.push_front(val);
    }

    template <typename T>
    inline void utree::push_back(T const& val)
    {
        if (get_type() == type::reference_type)
            return p->push_back(val);

        ensure_list_type("push_back()");
        l.push_back(val);
    }

    template <typename T>
    inline utree::iterator utree::insert(iterator pos, T const& val)
    {
        if (get_type() == type::reference_type)
            return p->insert(pos, val);

        ensure_list_type("insert()");
        if (!pos.node)
        {
            l.push_back(val);
            return utree::iterator(l.last, l.last->prev);
        }
        l.insert(val, pos);
        return utree::iterator(pos.node->prev, pos.node->prev->prev);
    }

    template <typename T>
    inline void utree::insert(iterator pos, std::size_t n, T const& val)
    {
        if (get_type() == type::reference_type)
            return p->insert(pos, n, val);

        ensure_list_type("insert()");
        for (std::size_t i = 0; i != n; ++i)
            insert(pos, val);
    }

    template <typename Iterator>
    inline void utree::insert(iterator pos, Iterator first, Iterator last)
    {
        if (get_type() == type::reference_type)
            return p->insert(pos, first, last);

        ensure_list_type("insert()");
        while (first != last)
            insert(pos, *first++);
    }

    template <typename Iterator>
    inline void utree::assign(Iterator first, Iterator last)
    {
        if (get_type() == type::reference_type)
            return p->assign(first, last);

        clear();
        set_type(type::list_type);

        while (first != last)
        {
            push_back(*first);
            ++first;
        }
    }

    inline void utree::clear()
    {
        if (get_type() == type::reference_type)
            return p->clear();

        // clear will always make this an invalid type
        free();
        set_type(type::invalid_type);
    }

    inline void utree::pop_front()
    {
        if (get_type() == type::reference_type)
            return p->pop_front();
        if (get_type() != type::list_type)
            BOOST_THROW_EXCEPTION(
                bad_type_exception
                    ("pop_front() called on non-list utree type",
                     get_type()));

        l.pop_front();
    }

    inline void utree::pop_back()
    {
        if (get_type() == type::reference_type)
            return p->pop_back();
        if (get_type() != type::list_type)
            BOOST_THROW_EXCEPTION(
                bad_type_exception
                    ("pop_back() called on non-list utree type",
                     get_type()));

        l.pop_back();
    }

    inline utree::iterator utree::erase(iterator pos)
    {
        if (get_type() == type::reference_type)
            return p->erase(pos);
        if (get_type() != type::list_type)
            BOOST_THROW_EXCEPTION(
                bad_type_exception
                    ("erase() called on non-list utree type",
                     get_type()));

        detail::list::node* np = l.erase(pos.node);
        return iterator(np, np?np->prev:l.last);
    }

    inline utree::iterator utree::erase(iterator first, iterator last)
    {
        if (get_type() == type::reference_type)
            return p->erase(first, last);

        if (get_type() != type::list_type)
            BOOST_THROW_EXCEPTION(
                bad_type_exception
                    ("erase() called on non-list utree type",
                     get_type()));
        while (first != last)
            erase(first++);
        return last;
    }

    inline utree::iterator utree::begin()
    {
        if (get_type() == type::reference_type)
            return p->begin();
        else if (get_type() == type::range_type)
            return iterator(r.first, 0);

        // otherwise...
        ensure_list_type("begin()");
        return iterator(l.first, 0);
    }

    inline utree::iterator utree::end()
    {
        if (get_type() == type::reference_type)
            return p->end();
        else if (get_type() == type::range_type)
            return iterator(0, r.first);

        // otherwise...
        ensure_list_type("end()");
        return iterator(0, l.last);
    }

    inline utree::ref_iterator utree::ref_begin()
    {
        if (get_type() == type::reference_type)
            return p->ref_begin();
        else if (get_type() == type::range_type)
            return ref_iterator(r.first, 0);

        // otherwise...
        ensure_list_type("ref_begin()");
        return ref_iterator(l.first, 0);
    }

    inline utree::ref_iterator utree::ref_end()
    {
        if (get_type() == type::reference_type)
            return p->ref_end();
        else if (get_type() == type::range_type)
            return ref_iterator(0, r.first);

        // otherwise...
        ensure_list_type("ref_end()");
        return ref_iterator(0, l.last);
    }

    inline utree::const_iterator utree::begin() const
    {
        if (get_type() == type::reference_type)
            return ((utree const*)p)->begin();
        if (get_type() == type::range_type)
            return const_iterator(r.first, 0);

        // otherwise...
        if (get_type() != type::list_type)
            BOOST_THROW_EXCEPTION(
                bad_type_exception
                    ("begin() called on non-list utree type",
                     get_type()));

        return const_iterator(l.first, 0);
    }

    inline utree::const_iterator utree::end() const
    {
        if (get_type() == type::reference_type)
            return ((utree const*)p)->end();
        if (get_type() == type::range_type)
            return const_iterator(0, r.first);

        // otherwise...
        if (get_type() != type::list_type)
            BOOST_THROW_EXCEPTION(
                bad_type_exception
                    ("end() called on non-list utree type",
                     get_type()));

        return const_iterator(0, l.last);
    }

    inline bool utree::empty() const
    {
        type::info t = get_type();
        if (t == type::reference_type)
            return ((utree const*)p)->empty();

        if (t == type::range_type)
            return r.first == 0;
        if (t == type::list_type)
            return l.size == 0;

        return t == type::nil_type || t == type::invalid_type;
    }

    inline std::size_t utree::size() const
    {
        type::info t = get_type();
        if (t == type::reference_type)
            return ((utree const*)p)->size();

        if (t == type::range_type)
        {
            // FIXME: O(n), and we have the room to store the size of a range
            // in the union if we compute it when assigned/constructed.
            std::size_t size = 0;
            detail::list::node* n = r.first;
            while (n)
            {
                n = n->next;
                ++size;
            }
            return size;
        }
        if (t == type::list_type)
            return l.size;

        if (t == type::string_type)
            return s.size();

        if (t == type::symbol_type)
            return s.size();

        if (t == type::binary_type)
            return s.size();

        if (t == type::string_range_type)
            return sr.last - sr.first;

        if (t != type::nil_type)
            BOOST_THROW_EXCEPTION(
                bad_type_exception
                    ("size() called on non-list and non-string utree type",
                     get_type()));

        return 0;
    }

    inline utree_type::info utree::which() const
    {
        return get_type();
    }

    inline utree& utree::front()
    {
        if (get_type() == type::reference_type)
            return p->front();
        if (get_type() == type::range_type)
        {
            if (!r.first)
                BOOST_THROW_EXCEPTION(
                    empty_exception("front() called on empty utree range"));
            return r.first->val;
        }

        // otherwise...
        if (get_type() != type::list_type)
            BOOST_THROW_EXCEPTION(
                bad_type_exception
                    ("front() called on non-list utree type", get_type()));
        else if (!l.first)
            BOOST_THROW_EXCEPTION(
                empty_exception("front() called on empty utree list"));

        return l.first->val;
    }

    inline utree& utree::back()
    {
        if (get_type() == type::reference_type)
            return p->back();
        if (get_type() == type::range_type)
        {
            if (!r.last)
                BOOST_THROW_EXCEPTION(
                    empty_exception("back() called on empty utree range"));
            return r.last->val;
        }

        // otherwise...
        if (get_type() != type::list_type)
            BOOST_THROW_EXCEPTION(
                bad_type_exception
                    ("back() called on non-list utree type", get_type()));
        else if (!l.last)
            BOOST_THROW_EXCEPTION(
                empty_exception("back() called on empty utree list"));

        return l.last->val;
    }

    inline utree const& utree::front() const
    {
        if (get_type() == type::reference_type)
            return ((utree const*)p)->front();
        if (get_type() == type::range_type)
        {
            if (!r.first)
                BOOST_THROW_EXCEPTION(
                    empty_exception("front() called on empty utree range"));
            return r.first->val;
        }

        // otherwise...
        if (get_type() != type::list_type)
            BOOST_THROW_EXCEPTION(
                bad_type_exception
                    ("front() called on non-list utree type", get_type()));
        else if (!l.first)
            BOOST_THROW_EXCEPTION(
                empty_exception("front() called on empty utree list"));

        return l.first->val;
    }

    inline utree const& utree::back() const
    {
        if (get_type() == type::reference_type)
            return ((utree const*)p)->back();
        if (get_type() == type::range_type)
        {
            if (!r.last)
                BOOST_THROW_EXCEPTION(
                    empty_exception("back() called on empty utree range"));
            return r.last->val;
        }

        // otherwise...
        if (get_type() != type::list_type)
            BOOST_THROW_EXCEPTION(
                bad_type_exception
                    ("back() called on non-list utree type", get_type()));
        else if (!l.last)
            BOOST_THROW_EXCEPTION(
                empty_exception("back() called on empty utree list"));

        return l.last->val;
    }

    inline void utree::swap(utree& other)
    {
        s.swap(other.s);
    }

    inline utree::type::info utree::get_type() const
    {
        // the fast string holds the type info
        return static_cast<utree::type::info>(s.get_type());
    }

    inline void utree::set_type(type::info t)
    {
        // the fast string holds the type info
        s.set_type(t);
    }

    inline void utree::ensure_list_type(char const* failed_in)
    {
        type::info t = get_type();
        if (t == type::invalid_type)
        {
            set_type(type::list_type);
            l.default_construct();
        }
        else if (get_type() != type::list_type)
        {
            std::string msg = failed_in;
            msg += "called on non-list and non-invalid utree type";
            BOOST_THROW_EXCEPTION(bad_type_exception(msg.c_str(), get_type()));
        }
    }

    inline void utree::free()
    {
        switch (get_type())
        {
            case type::binary_type:
            case type::symbol_type:
            case type::string_type:
                s.free();
                break;
            case type::list_type:
                l.free();
                break;
            case type::function_type:
                delete pf;
                break;
            default:
                break;
        };
        s.initialize();
    }

    inline void utree::copy(utree const& other)
    {
        set_type(other.get_type());
        switch (other.get_type())
        {
            default:
                BOOST_THROW_EXCEPTION(
                    bad_type_exception("corrupt utree type", other.get_type()));
                break;
            case type::invalid_type:
            case type::nil_type:
                s.tag(other.s.tag());
                break;
            case type::bool_type:
                b = other.b;
                s.tag(other.s.tag());
                break;
            case type::int_type:
                i = other.i;
                s.tag(other.s.tag());
                break;
            case type::double_type:
                d = other.d;
                s.tag(other.s.tag());
                break;
            case type::reference_type:
                p = other.p;
                s.tag(other.s.tag());
                break;
            case type::any_type:
                v = other.v;
                s.tag(other.s.tag());
                break;
            case type::range_type:
                r = other.r;
                s.tag(other.s.tag());
                break;
            case type::string_range_type:
                sr = other.sr;
                s.tag(other.s.tag());
                break;
            case type::function_type:
                pf = other.pf->clone();
                s.tag(other.s.tag());
                break;
            case type::string_type:
            case type::symbol_type:
            case type::binary_type:
                s.copy(other.s);
                s.tag(other.s.tag());
                break;
            case type::list_type:
                l.copy(other.l);
                s.tag(other.s.tag());
                break;
        }
    }

    template <typename T>
    struct is_iterator_range
      : boost::mpl::false_
    {};

    template <typename Iterator>
    struct is_iterator_range<boost::iterator_range<Iterator> >
      : boost::mpl::true_
    {};

    template <typename To>
    struct utree_cast
    {
        typedef To result_type;

        template <typename From>
        To dispatch(From const& val, boost::mpl::true_) const
        {
            return To(val); // From is convertible to To
        }

        template <typename From>
        To dispatch(From const&, boost::mpl::false_) const
        {
            // From is NOT convertible to To !!!
            throw std::bad_cast();
            return To();
        }

        template <typename From>
        To operator()(From const& val) const
        {
            // boost::iterator_range has a templated constructor, accepting
            // any argument and hence any type is 'convertible' to it.
            typedef typename boost::mpl::eval_if<
                is_iterator_range<To>
              , boost::is_same<From, To>, boost::is_convertible<From, To>
            >::type is_convertible;
            return dispatch(val, is_convertible());
        }
    };

    template <typename T>
    struct utree_cast<T*>
    {
        typedef T* result_type;

        template <typename From>
        T* operator()(From const&) const
        {
            // From is NOT convertible to T !!!
            throw std::bad_cast();
            return 0;
        }

        T* operator()(any_ptr const& p) const
        {
            return p.get<T*>();
        }
    };

    template <typename T>
    inline T utree::get() const
    {
        return utree::visit(*this, utree_cast<T>());
    }

    inline utree& utree::deref()
    {
        return (get_type() == type::reference_type) ? *p : *this;
    }

    inline utree const& utree::deref() const
    {
        return (get_type() == type::reference_type) ? *p : *this;
    }

    inline short utree::tag() const
    {
        return s.tag();
    }

    inline void utree::tag(short tag)
    {
        s.tag(tag);
    }

    inline utree utree::eval(utree const& env) const
    {
        if (get_type() == type::reference_type)
            return deref().eval(env);

        if (get_type() != type::function_type)
            BOOST_THROW_EXCEPTION(
                bad_type_exception(
                    "eval() called on non-function utree type", get_type()));
        return (*pf)(env);
    }

    inline utree utree::eval(utree& env) const
    {
        if (get_type() == type::reference_type)
            return deref().eval(env);

        if (get_type() != type::function_type)
            BOOST_THROW_EXCEPTION(
                bad_type_exception(
                    "eval() called on non-function utree type", get_type()));
        return (*pf)(env);
    }

    inline utree utree::operator() (utree const& env) const
    {
        return eval(env);
    }

    inline utree utree::operator() (utree& env) const
    {
        return eval(env);
    }
}}

#if defined(BOOST_MSVC)
# pragma warning(pop)
#endif
#endif
