Index: libraries/win32/boost/include/boost/container/detail/flat_tree.hpp =================================================================== --- /dev/null +++ libraries/win32/boost/include/boost/container/detail/flat_tree.hpp @@ -0,0 +1,982 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2015. 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) +// +// See http://www.boost.org/libs/container for documentation. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef BOOST_CONTAINER_FLAT_TREE_HPP +#define BOOST_CONTAINER_FLAT_TREE_HPP + +#ifndef BOOST_CONFIG_HPP +# include +#endif + +#if defined(BOOST_HAS_PRAGMA_ONCE) +# pragma once +#endif + +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include //algo_equal(), algo_lexicographical_compare +#include +#include +#ifdef BOOST_CONTAINER_VECTOR_ITERATOR_IS_POINTER +#include +#endif +#include +#include +#include +#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) +#include +#endif + +#include //pair + +namespace boost { +namespace container { +namespace container_detail { + +template +class flat_tree_value_compare + : private Compare +{ + typedef Value first_argument_type; + typedef Value second_argument_type; + typedef bool return_type; + public: + flat_tree_value_compare() + : Compare() + {} + + flat_tree_value_compare(const Compare &pred) + : Compare(pred) + {} + + bool operator()(const Value& lhs, const Value& rhs) const + { + KeyOfValue key_extract; + return Compare::operator()(key_extract(lhs), key_extract(rhs)); + } + + const Compare &get_comp() const + { return *this; } + + Compare &get_comp() + { return *this; } +}; + +template +struct get_flat_tree_iterators +{ + #ifdef BOOST_CONTAINER_VECTOR_ITERATOR_IS_POINTER + typedef Pointer iterator; + typedef typename boost::intrusive:: + pointer_traits::element_type iterator_element_type; + typedef typename boost::intrusive:: + pointer_traits:: template + rebind_pointer::type const_iterator; + #else //BOOST_CONTAINER_VECTOR_ITERATOR_IS_POINTER + typedef typename boost::container::container_detail:: + vec_iterator iterator; + typedef typename boost::container::container_detail:: + vec_iterator const_iterator; + #endif //BOOST_CONTAINER_VECTOR_ITERATOR_IS_POINTER + typedef boost::container::reverse_iterator reverse_iterator; + typedef boost::container::reverse_iterator const_reverse_iterator; +}; + +template +class flat_tree +{ + typedef boost::container::vector vector_t; + typedef Allocator allocator_t; + typedef allocator_traits allocator_traits_type; + + public: + typedef flat_tree_value_compare value_compare; + + private: + struct Data + //Inherit from value_compare to do EBO + : public value_compare + { + BOOST_COPYABLE_AND_MOVABLE(Data) + + public: + Data() + : value_compare(), m_vect() + {} + + explicit Data(const Data &d) + : value_compare(static_cast(d)), m_vect(d.m_vect) + {} + + Data(BOOST_RV_REF(Data) d) + : value_compare(boost::move(static_cast(d))), m_vect(boost::move(d.m_vect)) + {} + + Data(const Data &d, const Allocator &a) + : value_compare(static_cast(d)), m_vect(d.m_vect, a) + {} + + Data(BOOST_RV_REF(Data) d, const Allocator &a) + : value_compare(boost::move(static_cast(d))), m_vect(boost::move(d.m_vect), a) + {} + + explicit Data(const Compare &comp) + : value_compare(comp), m_vect() + {} + + Data(const Compare &comp, const allocator_t &alloc) + : value_compare(comp), m_vect(alloc) + {} + + explicit Data(const allocator_t &alloc) + : value_compare(), m_vect(alloc) + {} + + Data& operator=(BOOST_COPY_ASSIGN_REF(Data) d) + { + this->value_compare::operator=(d); + m_vect = d.m_vect; + return *this; + } + + Data& operator=(BOOST_RV_REF(Data) d) + { + this->value_compare::operator=(boost::move(static_cast(d))); + m_vect = boost::move(d.m_vect); + return *this; + } + + void swap(Data &d) + { + value_compare& mycomp = *this, & othercomp = d; + boost::adl_move_swap(mycomp, othercomp); + this->m_vect.swap(d.m_vect); + } + + vector_t m_vect; + }; + + Data m_data; + BOOST_COPYABLE_AND_MOVABLE(flat_tree) + + public: + + typedef typename vector_t::value_type value_type; + typedef typename vector_t::pointer pointer; + typedef typename vector_t::const_pointer const_pointer; + typedef typename vector_t::reference reference; + typedef typename vector_t::const_reference const_reference; + typedef Key key_type; + typedef Compare key_compare; + typedef typename vector_t::allocator_type allocator_type; + typedef typename vector_t::size_type size_type; + typedef typename vector_t::difference_type difference_type; + typedef typename vector_t::iterator iterator; + typedef typename vector_t::const_iterator const_iterator; + typedef typename vector_t::reverse_iterator reverse_iterator; + typedef typename vector_t::const_reverse_iterator const_reverse_iterator; + + //!Standard extension + typedef allocator_type stored_allocator_type; + + private: + typedef allocator_traits stored_allocator_traits; + + public: + flat_tree() + : m_data() + { } + + explicit flat_tree(const Compare& comp) + : m_data(comp) + { } + + flat_tree(const Compare& comp, const allocator_type& a) + : m_data(comp, a) + { } + + explicit flat_tree(const allocator_type& a) + : m_data(a) + { } + + flat_tree(const flat_tree& x) + : m_data(x.m_data) + { } + + flat_tree(BOOST_RV_REF(flat_tree) x) + : m_data(boost::move(x.m_data)) + { } + + flat_tree(const flat_tree& x, const allocator_type &a) + : m_data(x.m_data, a) + { } + + flat_tree(BOOST_RV_REF(flat_tree) x, const allocator_type &a) + : m_data(boost::move(x.m_data), a) + { } + + template + flat_tree( ordered_range_t, InputIterator first, InputIterator last + , const Compare& comp = Compare() + , const allocator_type& a = allocator_type()) + : m_data(comp, a) + { this->m_data.m_vect.insert(this->m_data.m_vect.end(), first, last); } + + template + flat_tree( bool unique_insertion + , InputIterator first, InputIterator last + , const Compare& comp = Compare() + , const allocator_type& a = allocator_type()) + : m_data(comp, a) + { + //Use cend() as hint to achieve linear time for + //ordered ranges as required by the standard + //for the constructor + //Call end() every iteration as reallocation might have invalidated iterators + if(unique_insertion){ + for ( ; first != last; ++first){ + this->insert_unique(this->cend(), *first); + } + } + else{ + for ( ; first != last; ++first){ + this->insert_equal(this->cend(), *first); + } + } + } + + ~flat_tree() + {} + + flat_tree& operator=(BOOST_COPY_ASSIGN_REF(flat_tree) x) + { m_data = x.m_data; return *this; } + + flat_tree& operator=(BOOST_RV_REF(flat_tree) x) + BOOST_NOEXCEPT_IF( allocator_traits_type::is_always_equal::value + && boost::container::container_detail::is_nothrow_move_assignable::value ) + { m_data = boost::move(x.m_data); return *this; } + + public: + // accessors: + Compare key_comp() const + { return this->m_data.get_comp(); } + + value_compare value_comp() const + { return this->m_data; } + + allocator_type get_allocator() const + { return this->m_data.m_vect.get_allocator(); } + + const stored_allocator_type &get_stored_allocator() const + { return this->m_data.m_vect.get_stored_allocator(); } + + stored_allocator_type &get_stored_allocator() + { return this->m_data.m_vect.get_stored_allocator(); } + + iterator begin() + { return this->m_data.m_vect.begin(); } + + const_iterator begin() const + { return this->cbegin(); } + + const_iterator cbegin() const + { return this->m_data.m_vect.begin(); } + + iterator end() + { return this->m_data.m_vect.end(); } + + const_iterator end() const + { return this->cend(); } + + const_iterator cend() const + { return this->m_data.m_vect.end(); } + + reverse_iterator rbegin() + { return reverse_iterator(this->end()); } + + const_reverse_iterator rbegin() const + { return this->crbegin(); } + + const_reverse_iterator crbegin() const + { return const_reverse_iterator(this->cend()); } + + reverse_iterator rend() + { return reverse_iterator(this->begin()); } + + const_reverse_iterator rend() const + { return this->crend(); } + + const_reverse_iterator crend() const + { return const_reverse_iterator(this->cbegin()); } + + bool empty() const + { return this->m_data.m_vect.empty(); } + + size_type size() const + { return this->m_data.m_vect.size(); } + + size_type max_size() const + { return this->m_data.m_vect.max_size(); } + + void swap(flat_tree& other) + BOOST_NOEXCEPT_IF( allocator_traits_type::is_always_equal::value + && boost::container::container_detail::is_nothrow_swappable::value ) + { this->m_data.swap(other.m_data); } + + public: + // insert/erase + std::pair insert_unique(const value_type& val) + { + std::pair ret; + insert_commit_data data; + ret.second = this->priv_insert_unique_prepare(val, data); + ret.first = ret.second ? this->priv_insert_commit(data, val) + : iterator(vector_iterator_get_ptr(data.position)); + return ret; + } + + std::pair insert_unique(BOOST_RV_REF(value_type) val) + { + std::pair ret; + insert_commit_data data; + ret.second = this->priv_insert_unique_prepare(val, data); + ret.first = ret.second ? this->priv_insert_commit(data, boost::move(val)) + : iterator(vector_iterator_get_ptr(data.position)); + return ret; + } + + iterator insert_equal(const value_type& val) + { + iterator i = this->upper_bound(KeyOfValue()(val)); + i = this->m_data.m_vect.insert(i, val); + return i; + } + + iterator insert_equal(BOOST_RV_REF(value_type) mval) + { + iterator i = this->upper_bound(KeyOfValue()(mval)); + i = this->m_data.m_vect.insert(i, boost::move(mval)); + return i; + } + + iterator insert_unique(const_iterator hint, const value_type& val) + { + BOOST_ASSERT(this->priv_in_range_or_end(hint)); + std::pair ret; + insert_commit_data data; + return this->priv_insert_unique_prepare(hint, val, data) + ? this->priv_insert_commit(data, val) + : iterator(vector_iterator_get_ptr(data.position)); + } + + iterator insert_unique(const_iterator hint, BOOST_RV_REF(value_type) val) + { + BOOST_ASSERT(this->priv_in_range_or_end(hint)); + std::pair ret; + insert_commit_data data; + return this->priv_insert_unique_prepare(hint, val, data) + ? this->priv_insert_commit(data, boost::move(val)) + : iterator(vector_iterator_get_ptr(data.position)); + } + + iterator insert_equal(const_iterator hint, const value_type& val) + { + BOOST_ASSERT(this->priv_in_range_or_end(hint)); + insert_commit_data data; + this->priv_insert_equal_prepare(hint, val, data); + return this->priv_insert_commit(data, val); + } + + iterator insert_equal(const_iterator hint, BOOST_RV_REF(value_type) mval) + { + BOOST_ASSERT(this->priv_in_range_or_end(hint)); + insert_commit_data data; + this->priv_insert_equal_prepare(hint, mval, data); + return this->priv_insert_commit(data, boost::move(mval)); + } + + template + void insert_unique(InIt first, InIt last) + { + for ( ; first != last; ++first){ + this->insert_unique(*first); + } + } + + template + void insert_equal(InIt first, InIt last + #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + , typename container_detail::enable_if_c + < container_detail::is_input_iterator::value + >::type * = 0 + #endif + ) + { this->priv_insert_equal_loop(first, last); } + + template + void insert_equal(InIt first, InIt last + #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + , typename container_detail::enable_if_c + < !container_detail::is_input_iterator::value + >::type * = 0 + #endif + ) + { + const size_type len = static_cast(boost::container::iterator_distance(first, last)); + this->reserve(this->size()+len); + this->priv_insert_equal_loop(first, last); + } + + //Ordered + + template + void insert_equal(ordered_range_t, InIt first, InIt last + #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + , typename container_detail::enable_if_c + < container_detail::is_input_iterator::value + >::type * = 0 + #endif + ) + { this->priv_insert_equal_loop_ordered(first, last); } + + template + void insert_equal(ordered_range_t, FwdIt first, FwdIt last + #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + , typename container_detail::enable_if_c + < !container_detail::is_input_iterator::value && + container_detail::is_forward_iterator::value + >::type * = 0 + #endif + ) + { + const size_type len = static_cast(boost::container::iterator_distance(first, last)); + this->reserve(this->size()+len); + this->priv_insert_equal_loop_ordered(first, last); + } + + template + void insert_equal(ordered_range_t, BidirIt first, BidirIt last + #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + , typename container_detail::disable_if_or + < void + , container_detail::is_input_iterator + , container_detail::is_forward_iterator + >::type * = 0 + #endif + ) + { this->m_data.m_vect.merge(first, last); } + + template + void insert_unique(ordered_unique_range_t, InIt first, InIt last + #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + , typename container_detail::enable_if_or + < void + , container_detail::is_input_iterator + , container_detail::is_forward_iterator + >::type * = 0 + #endif + ) + { + const_iterator pos(this->cend()); + for ( ; first != last; ++first){ + pos = this->insert_unique(pos, *first); + ++pos; + } + } + + template + void insert_unique(ordered_unique_range_t, BidirIt first, BidirIt last + #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + , typename container_detail::enable_if_c + < !(container_detail::is_input_iterator::value || + container_detail::is_forward_iterator::value) + >::type * = 0 + #endif + ) + { this->m_data.m_vect.merge_unique(first, last, value_compare()); } + + #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + + template + std::pair emplace_unique(BOOST_FWD_REF(Args)... args) + { + typename aligned_storage::value>::type v; + value_type &val = *static_cast(static_cast(&v)); + stored_allocator_type &a = this->get_stored_allocator(); + stored_allocator_traits::construct(a, &val, ::boost::forward(args)... ); + value_destructor d(a, val); + return this->insert_unique(::boost::move(val)); + } + + template + iterator emplace_hint_unique(const_iterator hint, BOOST_FWD_REF(Args)... args) + { + //hint checked in insert_unique + typename aligned_storage::value>::type v; + value_type &val = *static_cast(static_cast(&v)); + stored_allocator_type &a = this->get_stored_allocator(); + stored_allocator_traits::construct(a, &val, ::boost::forward(args)... ); + value_destructor d(a, val); + return this->insert_unique(hint, ::boost::move(val)); + } + + template + iterator emplace_equal(BOOST_FWD_REF(Args)... args) + { + typename aligned_storage::value>::type v; + value_type &val = *static_cast(static_cast(&v)); + stored_allocator_type &a = this->get_stored_allocator(); + stored_allocator_traits::construct(a, &val, ::boost::forward(args)... ); + value_destructor d(a, val); + return this->insert_equal(::boost::move(val)); + } + + template + iterator emplace_hint_equal(const_iterator hint, BOOST_FWD_REF(Args)... args) + { + //hint checked in insert_equal + typename aligned_storage::value>::type v; + value_type &val = *static_cast(static_cast(&v)); + stored_allocator_type &a = this->get_stored_allocator(); + stored_allocator_traits::construct(a, &val, ::boost::forward(args)... ); + value_destructor d(a, val); + return this->insert_equal(hint, ::boost::move(val)); + } + + #else // !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + + #define BOOST_CONTAINER_FLAT_TREE_EMPLACE_CODE(N) \ + BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \ + std::pair emplace_unique(BOOST_MOVE_UREF##N)\ + {\ + typename aligned_storage::value>::type v;\ + value_type &val = *static_cast(static_cast(&v));\ + stored_allocator_type &a = this->get_stored_allocator();\ + stored_allocator_traits::construct(a, &val BOOST_MOVE_I##N BOOST_MOVE_FWD##N);\ + value_destructor d(a, val);\ + return this->insert_unique(::boost::move(val));\ + }\ + \ + BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \ + iterator emplace_hint_unique(const_iterator hint BOOST_MOVE_I##N BOOST_MOVE_UREF##N)\ + {\ + typename aligned_storage::value>::type v;\ + value_type &val = *static_cast(static_cast(&v));\ + stored_allocator_type &a = this->get_stored_allocator();\ + stored_allocator_traits::construct(a, &val BOOST_MOVE_I##N BOOST_MOVE_FWD##N);\ + value_destructor d(a, val);\ + return this->insert_unique(hint, ::boost::move(val));\ + }\ + \ + BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \ + iterator emplace_equal(BOOST_MOVE_UREF##N)\ + {\ + typename aligned_storage::value>::type v;\ + value_type &val = *static_cast(static_cast(&v));\ + stored_allocator_type &a = this->get_stored_allocator();\ + stored_allocator_traits::construct(a, &val BOOST_MOVE_I##N BOOST_MOVE_FWD##N);\ + value_destructor d(a, val);\ + return this->insert_equal(::boost::move(val));\ + }\ + \ + BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \ + iterator emplace_hint_equal(const_iterator hint BOOST_MOVE_I##N BOOST_MOVE_UREF##N)\ + {\ + typename aligned_storage ::value>::type v;\ + value_type &val = *static_cast(static_cast(&v));\ + stored_allocator_type &a = this->get_stored_allocator();\ + stored_allocator_traits::construct(a, &val BOOST_MOVE_I##N BOOST_MOVE_FWD##N);\ + value_destructor d(a, val);\ + return this->insert_equal(hint, ::boost::move(val));\ + }\ + // + BOOST_MOVE_ITERATE_0TO9(BOOST_CONTAINER_FLAT_TREE_EMPLACE_CODE) + #undef BOOST_CONTAINER_FLAT_TREE_EMPLACE_CODE + + #endif // !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + + iterator erase(const_iterator position) + { return this->m_data.m_vect.erase(position); } + + size_type erase(const key_type& k) + { + std::pair itp = this->equal_range(k); + size_type ret = static_cast(itp.second-itp.first); + if (ret){ + this->m_data.m_vect.erase(itp.first, itp.second); + } + return ret; + } + + iterator erase(const_iterator first, const_iterator last) + { return this->m_data.m_vect.erase(first, last); } + + void clear() + { this->m_data.m_vect.clear(); } + + //! Effects: Tries to deallocate the excess of memory created + // with previous allocations. The size of the vector is unchanged + //! + //! Throws: If memory allocation throws, or T's copy constructor throws. + //! + //! Complexity: Linear to size(). + void shrink_to_fit() + { this->m_data.m_vect.shrink_to_fit(); } + + iterator nth(size_type n) BOOST_NOEXCEPT_OR_NOTHROW + { return this->m_data.m_vect.nth(n); } + + const_iterator nth(size_type n) const BOOST_NOEXCEPT_OR_NOTHROW + { return this->m_data.m_vect.nth(n); } + + size_type index_of(iterator p) BOOST_NOEXCEPT_OR_NOTHROW + { return this->m_data.m_vect.index_of(p); } + + size_type index_of(const_iterator p) const BOOST_NOEXCEPT_OR_NOTHROW + { return this->m_data.m_vect.index_of(p); } + + // set operations: + iterator find(const key_type& k) + { + iterator i = this->lower_bound(k); + iterator end_it = this->end(); + if (i != end_it && this->m_data.get_comp()(k, KeyOfValue()(*i))){ + i = end_it; + } + return i; + } + + const_iterator find(const key_type& k) const + { + const_iterator i = this->lower_bound(k); + + const_iterator end_it = this->cend(); + if (i != end_it && this->m_data.get_comp()(k, KeyOfValue()(*i))){ + i = end_it; + } + return i; + } + + // set operations: + size_type count(const key_type& k) const + { + std::pair p = this->equal_range(k); + size_type n = p.second - p.first; + return n; + } + + iterator lower_bound(const key_type& k) + { return this->priv_lower_bound(this->begin(), this->end(), k); } + + const_iterator lower_bound(const key_type& k) const + { return this->priv_lower_bound(this->cbegin(), this->cend(), k); } + + iterator upper_bound(const key_type& k) + { return this->priv_upper_bound(this->begin(), this->end(), k); } + + const_iterator upper_bound(const key_type& k) const + { return this->priv_upper_bound(this->cbegin(), this->cend(), k); } + + std::pair equal_range(const key_type& k) + { return this->priv_equal_range(this->begin(), this->end(), k); } + + std::pair equal_range(const key_type& k) const + { return this->priv_equal_range(this->cbegin(), this->cend(), k); } + + std::pair lower_bound_range(const key_type& k) + { return this->priv_lower_bound_range(this->begin(), this->end(), k); } + + std::pair lower_bound_range(const key_type& k) const + { return this->priv_lower_bound_range(this->cbegin(), this->cend(), k); } + + size_type capacity() const + { return this->m_data.m_vect.capacity(); } + + void reserve(size_type cnt) + { this->m_data.m_vect.reserve(cnt); } + + friend bool operator==(const flat_tree& x, const flat_tree& y) + { + return x.size() == y.size() && ::boost::container::algo_equal(x.begin(), x.end(), y.begin()); + } + + friend bool operator<(const flat_tree& x, const flat_tree& y) + { + return ::boost::container::algo_lexicographical_compare(x.begin(), x.end(), y.begin(), y.end()); + } + + friend bool operator!=(const flat_tree& x, const flat_tree& y) + { return !(x == y); } + + friend bool operator>(const flat_tree& x, const flat_tree& y) + { return y < x; } + + friend bool operator<=(const flat_tree& x, const flat_tree& y) + { return !(y < x); } + + friend bool operator>=(const flat_tree& x, const flat_tree& y) + { return !(x < y); } + + friend void swap(flat_tree& x, flat_tree& y) + { x.swap(y); } + + private: + + bool priv_in_range_or_end(const_iterator pos) const + { + return (this->begin() <= pos) && (pos <= this->end()); + } + + struct insert_commit_data + { + const_iterator position; + }; + + // insert/erase + void priv_insert_equal_prepare + (const_iterator pos, const value_type& val, insert_commit_data &data) + { + // N1780 + // To insert val at pos: + // if pos == end || val <= *pos + // if pos == begin || val >= *(pos-1) + // insert val before pos + // else + // insert val before upper_bound(val) + // else + // insert val before lower_bound(val) + const value_compare &val_cmp = this->m_data; + + if(pos == this->cend() || !val_cmp(*pos, val)){ + if (pos == this->cbegin() || !val_cmp(val, pos[-1])){ + data.position = pos; + } + else{ + data.position = + this->priv_upper_bound(this->cbegin(), pos, KeyOfValue()(val)); + } + } + else{ + data.position = + this->priv_lower_bound(pos, this->cend(), KeyOfValue()(val)); + } + } + + bool priv_insert_unique_prepare + (const_iterator b, const_iterator e, const value_type& val, insert_commit_data &commit_data) + { + const value_compare &val_cmp = this->m_data; + commit_data.position = this->priv_lower_bound(b, e, KeyOfValue()(val)); + return commit_data.position == e || val_cmp(val, *commit_data.position); + } + + bool priv_insert_unique_prepare + (const value_type& val, insert_commit_data &commit_data) + { return this->priv_insert_unique_prepare(this->cbegin(), this->cend(), val, commit_data); } + + bool priv_insert_unique_prepare + (const_iterator pos, const value_type& val, insert_commit_data &commit_data) + { + //N1780. Props to Howard Hinnant! + //To insert val at pos: + //if pos == end || val <= *pos + // if pos == begin || val >= *(pos-1) + // insert val before pos + // else + // insert val before upper_bound(val) + //else if pos+1 == end || val <= *(pos+1) + // insert val after pos + //else + // insert val before lower_bound(val) + const value_compare &val_cmp = this->m_data; + const const_iterator cend_it = this->cend(); + if(pos == cend_it || val_cmp(val, *pos)){ //Check if val should go before end + const const_iterator cbeg = this->cbegin(); + commit_data.position = pos; + if(pos == cbeg){ //If container is empty then insert it in the beginning + return true; + } + const_iterator prev(pos); + --prev; + if(val_cmp(*prev, val)){ //If previous element was less, then it should go between prev and pos + return true; + } + else if(!val_cmp(val, *prev)){ //If previous was equal then insertion should fail + commit_data.position = prev; + return false; + } + else{ //Previous was bigger so insertion hint was pointless, dispatch to hintless insertion + //but reduce the search between beg and prev as prev is bigger than val + return this->priv_insert_unique_prepare(cbeg, prev, val, commit_data); + } + } + else{ + //The hint is before the insertion position, so insert it + //in the remaining range [pos, end) + return this->priv_insert_unique_prepare(pos, cend_it, val, commit_data); + } + } + + template + iterator priv_insert_commit + (insert_commit_data &commit_data, BOOST_FWD_REF(Convertible) convertible) + { + return this->m_data.m_vect.insert + ( commit_data.position + , boost::forward(convertible)); + } + + template + RanIt priv_lower_bound(RanIt first, const RanIt last, + const key_type & key) const + { + const Compare &key_cmp = this->m_data.get_comp(); + KeyOfValue key_extract; + size_type len = static_cast(last - first); + RanIt middle; + + while (len) { + size_type step = len >> 1; + middle = first; + middle += step; + + if (key_cmp(key_extract(*middle), key)) { + first = ++middle; + len -= step + 1; + } + else{ + len = step; + } + } + return first; + } + + template + RanIt priv_upper_bound + (RanIt first, const RanIt last,const key_type & key) const + { + const Compare &key_cmp = this->m_data.get_comp(); + KeyOfValue key_extract; + size_type len = static_cast(last - first); + RanIt middle; + + while (len) { + size_type step = len >> 1; + middle = first; + middle += step; + + if (key_cmp(key, key_extract(*middle))) { + len = step; + } + else{ + first = ++middle; + len -= step + 1; + } + } + return first; + } + + template + std::pair + priv_equal_range(RanIt first, RanIt last, const key_type& key) const + { + const Compare &key_cmp = this->m_data.get_comp(); + KeyOfValue key_extract; + size_type len = static_cast(last - first); + RanIt middle; + + while (len) { + size_type step = len >> 1; + middle = first; + middle += step; + + if (key_cmp(key_extract(*middle), key)){ + first = ++middle; + len -= step + 1; + } + else if (key_cmp(key, key_extract(*middle))){ + len = step; + } + else { + //Middle is equal to key + last = first; + last += len; + RanIt const first_ret = this->priv_lower_bound(first, middle, key); + return std::pair + ( first_ret, this->priv_upper_bound(++middle, last, key)); + } + } + return std::pair(first, first); + } + + template + std::pair priv_lower_bound_range(RanIt first, RanIt last, const key_type& k) const + { + const Compare &key_cmp = this->m_data.get_comp(); + KeyOfValue key_extract; + RanIt lb(this->priv_lower_bound(first, last, k)), ub(lb); + if(lb != last && static_cast(!key_cmp(k, key_extract(*lb)))){ + ++ub; + } + return std::pair(lb, ub); + } + + template + void priv_insert_equal_loop(InIt first, InIt last) + { + for ( ; first != last; ++first){ + this->insert_equal(*first); + } + } + + template + void priv_insert_equal_loop_ordered(InIt first, InIt last) + { + const_iterator pos(this->cend()); + for ( ; first != last; ++first){ + //If ordered, then try hint version + //to achieve constant-time complexity per insertion + //in some cases + pos = this->insert_equal(pos, *first); + ++pos; + } + } +}; + +} //namespace container_detail { + +} //namespace container { + +//!has_trivial_destructor_after_move<> == true_type +//!specialization for optimizations +template +struct has_trivial_destructor_after_move > +{ + typedef typename ::boost::container::allocator_traits::pointer pointer; + static const bool value = ::boost::has_trivial_destructor_after_move::value && + ::boost::has_trivial_destructor_after_move::value; +}; + +} //namespace boost { + +#include + +#endif // BOOST_CONTAINER_FLAT_TREE_HPP Index: libraries/win32/boost/include/boost/container/flat_map.hpp =================================================================== --- /dev/null +++ libraries/win32/boost/include/boost/container/flat_map.hpp @@ -0,0 +1,2014 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2013. 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) +// +// See http://www.boost.org/libs/container for documentation. +// +////////////////////////////////////////////////////////////////////////////// +#ifndef BOOST_CONTAINER_FLAT_MAP_HPP +#define BOOST_CONTAINER_FLAT_MAP_HPP + +#ifndef BOOST_CONFIG_HPP +# include +#endif + +#if defined(BOOST_HAS_PRAGMA_ONCE) +# pragma once +#endif + +#include +#include +// container +#include +#include +#include //new_allocator +#include +// container/detail +#include +#include +#include +#include //equal() +// move +#include +#include +// move/detail +#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) +#include +#endif +#include +// intrusive +#include //pair +#include //less, equal +//others +#include + +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) +#include +#endif + +namespace boost { +namespace container { + +#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + +namespace container_detail{ + +template +static D &force(const S &s) +{ return *const_cast((reinterpret_cast(&s))); } + +template +static D force_copy(S s) +{ + D *vp = reinterpret_cast(&s); + return D(*vp); +} + +} //namespace container_detail{ + +#endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + +//! A flat_map is a kind of associative container that supports unique keys (contains at +//! most one of each key value) and provides for fast retrieval of values of another +//! type T based on the keys. The flat_map class supports random-access iterators. +//! +//! A flat_map satisfies all of the requirements of a container and of a reversible +//! container and of an associative container. A flat_map also provides +//! most operations described for unique keys. For a +//! flat_map the key_type is Key and the value_type is std::pair +//! (unlike std::map which value_type is std::pair<const Key, T>). +//! +//! Compare is the ordering function for Keys (e.g. std::less). +//! +//! Allocator is the allocator to allocate the value_types +//! (e.g. allocator< std::pair >). +//! +//! flat_map is similar to std::map but it's implemented like an ordered vector. +//! This means that inserting a new element into a flat_map invalidates +//! previous iterators and references +//! +//! Erasing an element invalidates iterators and references +//! pointing to elements that come after (their keys are bigger) the erased element. +//! +//! This container provides random-access iterators. +//! +//! \tparam Key is the key_type of the map +//! \tparam Value is the mapped_type +//! \tparam Compare is the ordering function for Keys (e.g. std::less). +//! \tparam Allocator is the allocator to allocate the value_types +//! (e.g. allocator< std::pair > ). +#ifdef BOOST_CONTAINER_DOXYGEN_INVOKED +template , class Allocator = new_allocator< std::pair< Key, T> > > +#else +template +#endif +class flat_map +{ + #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + private: + BOOST_COPYABLE_AND_MOVABLE(flat_map) + //This is the tree that we should store if pair was movable + typedef container_detail::flat_tree, + container_detail::select1st< std::pair >, + Compare, + Allocator> tree_t; + + //This is the real tree stored here. It's based on a movable pair + typedef container_detail::flat_tree, + container_detail::select1st >, + Compare, + typename allocator_traits::template portable_rebind_alloc + >::type> impl_tree_t; + impl_tree_t m_flat_tree; // flat tree representing flat_map + + typedef typename impl_tree_t::value_type impl_value_type; + typedef typename impl_tree_t::const_iterator impl_const_iterator; + typedef typename impl_tree_t::iterator impl_iterator; + typedef typename impl_tree_t::allocator_type impl_allocator_type; + typedef container_detail::flat_tree_value_compare + < Compare + , container_detail::select1st< std::pair > + , std::pair > value_compare_impl; + typedef typename container_detail::get_flat_tree_iterators + ::pointer>::iterator iterator_impl; + typedef typename container_detail::get_flat_tree_iterators + ::pointer>::const_iterator const_iterator_impl; + typedef typename container_detail::get_flat_tree_iterators + ::pointer>::reverse_iterator reverse_iterator_impl; + typedef typename container_detail::get_flat_tree_iterators + ::pointer>::const_reverse_iterator const_reverse_iterator_impl; + public: + typedef typename impl_tree_t::stored_allocator_type impl_stored_allocator_type; + private: + #endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + + public: + + ////////////////////////////////////////////// + // + // types + // + ////////////////////////////////////////////// + typedef Key key_type; + typedef T mapped_type; + typedef std::pair value_type; + typedef ::boost::container::allocator_traits allocator_traits_type; + typedef typename boost::container::allocator_traits::pointer pointer; + typedef typename boost::container::allocator_traits::const_pointer const_pointer; + typedef typename boost::container::allocator_traits::reference reference; + typedef typename boost::container::allocator_traits::const_reference const_reference; + typedef typename boost::container::allocator_traits::size_type size_type; + typedef typename boost::container::allocator_traits::difference_type difference_type; + typedef Allocator allocator_type; + typedef BOOST_CONTAINER_IMPDEF(Allocator) stored_allocator_type; + typedef BOOST_CONTAINER_IMPDEF(value_compare_impl) value_compare; + typedef Compare key_compare; + typedef BOOST_CONTAINER_IMPDEF(iterator_impl) iterator; + typedef BOOST_CONTAINER_IMPDEF(const_iterator_impl) const_iterator; + typedef BOOST_CONTAINER_IMPDEF(reverse_iterator_impl) reverse_iterator; + typedef BOOST_CONTAINER_IMPDEF(const_reverse_iterator_impl) const_reverse_iterator; + typedef BOOST_CONTAINER_IMPDEF(impl_value_type) movable_value_type; + + public: + ////////////////////////////////////////////// + // + // construct/copy/destroy + // + ////////////////////////////////////////////// + + //! Effects: Default constructs an empty flat_map. + //! + //! Complexity: Constant. + flat_map() + : m_flat_tree() + { + //A type must be std::pair + BOOST_STATIC_ASSERT((container_detail::is_same, typename Allocator::value_type>::value)); + } + + //! Effects: Constructs an empty flat_map using the specified + //! comparison object and allocator. + //! + //! Complexity: Constant. + explicit flat_map(const Compare& comp, const allocator_type& a = allocator_type()) + : m_flat_tree(comp, container_detail::force(a)) + { + //A type must be std::pair + BOOST_STATIC_ASSERT((container_detail::is_same, typename Allocator::value_type>::value)); + } + + //! Effects: Constructs an empty flat_map using the specified allocator. + //! + //! Complexity: Constant. + explicit flat_map(const allocator_type& a) + : m_flat_tree(container_detail::force(a)) + { + //A type must be std::pair + BOOST_STATIC_ASSERT((container_detail::is_same, typename Allocator::value_type>::value)); + } + + //! Effects: Constructs an empty flat_map using the specified comparison object and + //! allocator, and inserts elements from the range [first ,last ). + //! + //! Complexity: Linear in N if the range [first ,last ) is already sorted using + //! comp and otherwise N logN, where N is last - first. + template + flat_map(InputIterator first, InputIterator last, const Compare& comp = Compare(), + const allocator_type& a = allocator_type()) + : m_flat_tree(true, first, last, comp, container_detail::force(a)) + { + //A type must be std::pair + BOOST_STATIC_ASSERT((container_detail::is_same, typename Allocator::value_type>::value)); + } + + //! Effects: Constructs an empty flat_map using the specified + //! allocator, and inserts elements from the range [first ,last ). + //! + //! Complexity: Linear in N if the range [first ,last ) is already sorted using + //! comp and otherwise N logN, where N is last - first. + template + flat_map(InputIterator first, InputIterator last, const allocator_type& a) + : m_flat_tree(true, first, last, Compare(), container_detail::force(a)) + { + //A type must be std::pair + BOOST_STATIC_ASSERT((container_detail::is_same, typename Allocator::value_type>::value)); + } + + //! Effects: Constructs an empty flat_map using the specified comparison object and + //! allocator, and inserts elements from the ordered unique range [first ,last). This function + //! is more efficient than the normal range creation for ordered ranges. + //! + //! Requires: [first ,last) must be ordered according to the predicate and must be + //! unique values. + //! + //! Complexity: Linear in N. + //! + //! Note: Non-standard extension. + template + flat_map( ordered_unique_range_t, InputIterator first, InputIterator last + , const Compare& comp = Compare(), const allocator_type& a = allocator_type()) + : m_flat_tree(ordered_range, first, last, comp, a) + { + //A type must be std::pair + BOOST_STATIC_ASSERT((container_detail::is_same, typename Allocator::value_type>::value)); + } + +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + //! Effects: Constructs an empty flat_map using the specified comparison object and + //! allocator, and inserts elements from the range [il.begin() ,il.end()). + //! + //! Complexity: Linear in N if the range [il.begin(), il.end()) is already sorted using + //! comp and otherwise N logN, where N is last - first. + flat_map(std::initializer_list il, const Compare& comp = Compare(), + const allocator_type& a = allocator_type()) + : m_flat_tree(true, il.begin(), il.end(), comp, container_detail::force(a)) + { + //A type must be std::pair + BOOST_STATIC_ASSERT((container_detail::is_same, typename Allocator::value_type>::value)); + } + + //! Effects: Constructs an empty flat_map using the specified + //! allocator, and inserts elements from the range [il.begin() ,il.end()). + //! + //! Complexity: Linear in N if the range [il.begin(), il.end()) is already sorted using + //! comp and otherwise N logN, where N is last - first. + flat_map(std::initializer_list il, const allocator_type& a) + : m_flat_tree(true, il.begin(), il.end(), Compare(), container_detail::force(a)) + { + //A type must be std::pair + BOOST_STATIC_ASSERT((container_detail::is_same, typename Allocator::value_type>::value)); + } + + //! Effects: Constructs an empty flat_map using the specified comparison object and + //! allocator, and inserts elements from the ordered unique range [il.begin(), il.end()). This function + //! is more efficient than the normal range creation for ordered ranges. + //! + //! Requires: [il.begin(), il.end()) must be ordered according to the predicate and must be + //! unique values. + //! + //! Complexity: Linear in N. + //! + //! Note: Non-standard extension. + flat_map(ordered_unique_range_t, std::initializer_list il, const Compare& comp = Compare(), + const allocator_type& a = allocator_type()) + : m_flat_tree(ordered_range, il.begin(), il.end(), comp, a) + { + //A type must be std::pair + BOOST_STATIC_ASSERT((container_detail::is_same, typename Allocator::value_type>::value)); + } +#endif + + //! Effects: Copy constructs a flat_map. + //! + //! Complexity: Linear in x.size(). + flat_map(const flat_map& x) + : m_flat_tree(x.m_flat_tree) + { + //A type must be std::pair + BOOST_STATIC_ASSERT((container_detail::is_same, typename Allocator::value_type>::value)); + } + + //! Effects: Move constructs a flat_map. + //! Constructs *this using x's resources. + //! + //! Complexity: Constant. + //! + //! Postcondition: x is emptied. + flat_map(BOOST_RV_REF(flat_map) x) + : m_flat_tree(boost::move(x.m_flat_tree)) + { + //A type must be std::pair + BOOST_STATIC_ASSERT((container_detail::is_same, typename Allocator::value_type>::value)); + } + + //! Effects: Copy constructs a flat_map using the specified allocator. + //! + //! Complexity: Linear in x.size(). + flat_map(const flat_map& x, const allocator_type &a) + : m_flat_tree(x.m_flat_tree, a) + { + //A type must be std::pair + BOOST_STATIC_ASSERT((container_detail::is_same, typename Allocator::value_type>::value)); + } + + //! Effects: Move constructs a flat_map using the specified allocator. + //! Constructs *this using x's resources. + //! + //! Complexity: Constant if x.get_allocator() == a, linear otherwise. + flat_map(BOOST_RV_REF(flat_map) x, const allocator_type &a) + : m_flat_tree(boost::move(x.m_flat_tree), a) + { + //A type must be std::pair + BOOST_STATIC_ASSERT((container_detail::is_same, typename Allocator::value_type>::value)); + } + + //! Effects: Makes *this a copy of x. + //! + //! Complexity: Linear in x.size(). + flat_map& operator=(BOOST_COPY_ASSIGN_REF(flat_map) x) + { m_flat_tree = x.m_flat_tree; return *this; } + + //! Effects: Move constructs a flat_map. + //! Constructs *this using x's resources. + //! + //! Throws: If allocator_traits_type::propagate_on_container_move_assignment + //! is false and (allocation throws or value_type's move constructor throws) + //! + //! Complexity: Constant if allocator_traits_type:: + //! propagate_on_container_move_assignment is true or + //! this->get>allocator() == x.get_allocator(). Linear otherwise. + flat_map& operator=(BOOST_RV_REF(flat_map) x) + BOOST_NOEXCEPT_IF( allocator_traits_type::is_always_equal::value + && boost::container::container_detail::is_nothrow_move_assignable::value ) + { m_flat_tree = boost::move(x.m_flat_tree); return *this; } + +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + //! Effects: Assign elements from il to *this + flat_map& operator=(std::initializer_list il) + { + this->clear(); + this->insert(il.begin(), il.end()); + return *this; + } +#endif + + //! Effects: Returns a copy of the allocator that + //! was passed to the object's constructor. + //! + //! Complexity: Constant. + allocator_type get_allocator() const BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.get_allocator()); } + + //! Effects: Returns a reference to the internal allocator. + //! + //! Throws: Nothing + //! + //! Complexity: Constant. + //! + //! Note: Non-standard extension. + stored_allocator_type &get_stored_allocator() BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force(m_flat_tree.get_stored_allocator()); } + + //! Effects: Returns a reference to the internal allocator. + //! + //! Throws: Nothing + //! + //! Complexity: Constant. + //! + //! Note: Non-standard extension. + const stored_allocator_type &get_stored_allocator() const BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force(m_flat_tree.get_stored_allocator()); } + + ////////////////////////////////////////////// + // + // iterators + // + ////////////////////////////////////////////// + + //! Effects: Returns an iterator to the first element contained in the container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + iterator begin() BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.begin()); } + + //! Effects: Returns a const_iterator to the first element contained in the container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + const_iterator begin() const BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.begin()); } + + //! Effects: Returns an iterator to the end of the container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + iterator end() BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.end()); } + + //! Effects: Returns a const_iterator to the end of the container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + const_iterator end() const BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.end()); } + + //! Effects: Returns a reverse_iterator pointing to the beginning + //! of the reversed container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + reverse_iterator rbegin() BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.rbegin()); } + + //! Effects: Returns a const_reverse_iterator pointing to the beginning + //! of the reversed container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + const_reverse_iterator rbegin() const BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.rbegin()); } + + //! Effects: Returns a reverse_iterator pointing to the end + //! of the reversed container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + reverse_iterator rend() BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.rend()); } + + //! Effects: Returns a const_reverse_iterator pointing to the end + //! of the reversed container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + const_reverse_iterator rend() const BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.rend()); } + + //! Effects: Returns a const_iterator to the first element contained in the container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + const_iterator cbegin() const BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.cbegin()); } + + //! Effects: Returns a const_iterator to the end of the container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + const_iterator cend() const BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.cend()); } + + //! Effects: Returns a const_reverse_iterator pointing to the beginning + //! of the reversed container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + const_reverse_iterator crbegin() const BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.crbegin()); } + + //! Effects: Returns a const_reverse_iterator pointing to the end + //! of the reversed container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + const_reverse_iterator crend() const BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.crend()); } + + ////////////////////////////////////////////// + // + // capacity + // + ////////////////////////////////////////////// + + //! Effects: Returns true if the container contains no elements. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + bool empty() const BOOST_NOEXCEPT_OR_NOTHROW + { return m_flat_tree.empty(); } + + //! Effects: Returns the number of the elements contained in the container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + size_type size() const BOOST_NOEXCEPT_OR_NOTHROW + { return m_flat_tree.size(); } + + //! Effects: Returns the largest possible size of the container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + size_type max_size() const BOOST_NOEXCEPT_OR_NOTHROW + { return m_flat_tree.max_size(); } + + //! Effects: Number of elements for which memory has been allocated. + //! capacity() is always greater than or equal to size(). + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + size_type capacity() const BOOST_NOEXCEPT_OR_NOTHROW + { return m_flat_tree.capacity(); } + + //! Effects: If n is less than or equal to capacity(), this call has no + //! effect. Otherwise, it is a request for allocation of additional memory. + //! If the request is successful, then capacity() is greater than or equal to + //! n; otherwise, capacity() is unchanged. In either case, size() is unchanged. + //! + //! Throws: If memory allocation allocation throws or T's copy constructor throws. + //! + //! Note: If capacity() is less than "cnt", iterators and references to + //! to values might be invalidated. + void reserve(size_type cnt) + { m_flat_tree.reserve(cnt); } + + //! Effects: Tries to deallocate the excess of memory created + // with previous allocations. The size of the vector is unchanged + //! + //! Throws: If memory allocation throws, or T's copy constructor throws. + //! + //! Complexity: Linear to size(). + void shrink_to_fit() + { m_flat_tree.shrink_to_fit(); } + + ////////////////////////////////////////////// + // + // element access + // + ////////////////////////////////////////////// + + #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + //! Effects: If there is no key equivalent to x in the flat_map, inserts + //! value_type(x, T()) into the flat_map. + //! + //! Returns: A reference to the mapped_type corresponding to x in *this. + //! + //! Complexity: Logarithmic. + mapped_type &operator[](const key_type& k); + + //! Effects: If there is no key equivalent to x in the flat_map, inserts + //! value_type(move(x), T()) into the flat_map (the key is move-constructed) + //! + //! Returns: A reference to the mapped_type corresponding to x in *this. + //! + //! Complexity: Logarithmic. + mapped_type &operator[](key_type &&k) ; + #elif defined(BOOST_MOVE_HELPERS_RETURN_SFINAE_BROKEN) + //in compilers like GCC 3.4, we can't catch temporaries + mapped_type& operator[](const key_type &k) { return this->priv_subscript(k); } + mapped_type& operator[](BOOST_RV_REF(key_type) k) { return this->priv_subscript(::boost::move(k)); } + #else + BOOST_MOVE_CONVERSION_AWARE_CATCH( operator[] , key_type, mapped_type&, this->priv_subscript) + #endif + + //! @copydoc ::boost::container::flat_set::nth(size_type) + iterator nth(size_type n) BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.nth(n)); } + + //! @copydoc ::boost::container::flat_set::nth(size_type) const + const_iterator nth(size_type n) const BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.nth(n)); } + + //! @copydoc ::boost::container::flat_set::index_of(iterator) + size_type index_of(iterator p) BOOST_NOEXCEPT_OR_NOTHROW + { return m_flat_tree.index_of(container_detail::force_copy(p)); } + + //! @copydoc ::boost::container::flat_set::index_of(const_iterator) const + size_type index_of(const_iterator p) const BOOST_NOEXCEPT_OR_NOTHROW + { return m_flat_tree.index_of(container_detail::force_copy(p)); } + + //! Returns: A reference to the element whose key is equivalent to x. + //! + //! Throws: An exception object of type out_of_range if no such element is present. + //! + //! Complexity: logarithmic. + T& at(const key_type& k) + { + iterator i = this->find(k); + if(i == this->end()){ + throw_out_of_range("flat_map::at key not found"); + } + return i->second; + } + + //! Returns: A reference to the element whose key is equivalent to x. + //! + //! Throws: An exception object of type out_of_range if no such element is present. + //! + //! Complexity: logarithmic. + const T& at(const key_type& k) const + { + const_iterator i = this->find(k); + if(i == this->end()){ + throw_out_of_range("flat_map::at key not found"); + } + return i->second; + } + + ////////////////////////////////////////////// + // + // modifiers + // + ////////////////////////////////////////////// + + #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) || defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + + //! Effects: Inserts an object x of type T constructed with + //! std::forward(args)... if and only if there is no element in the container + //! with key equivalent to the key of x. + //! + //! Returns: The bool component of the returned pair is true if and only + //! if the insertion takes place, and the iterator component of the pair + //! points to the element with key equivalent to the key of x. + //! + //! Complexity: Logarithmic search time plus linear insertion + //! to the elements with bigger keys than x. + //! + //! Note: If an element is inserted it might invalidate elements. + template + std::pair emplace(BOOST_FWD_REF(Args)... args) + { return container_detail::force_copy< std::pair >(m_flat_tree.emplace_unique(boost::forward(args)...)); } + + //! Effects: Inserts an object of type T constructed with + //! std::forward(args)... in the container if and only if there is + //! no element in the container with key equivalent to the key of x. + //! p is a hint pointing to where the insert should start to search. + //! + //! Returns: An iterator pointing to the element with key equivalent + //! to the key of x. + //! + //! Complexity: Logarithmic search time (constant if x is inserted + //! right before p) plus insertion linear to the elements with bigger keys than x. + //! + //! Note: If an element is inserted it might invalidate elements. + template + iterator emplace_hint(const_iterator hint, BOOST_FWD_REF(Args)... args) + { + return container_detail::force_copy + (m_flat_tree.emplace_hint_unique( container_detail::force_copy(hint) + , boost::forward(args)...)); + } + + #else // !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + + #define BOOST_CONTAINER_FLAT_MAP_EMPLACE_CODE(N) \ + BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \ + std::pair emplace(BOOST_MOVE_UREF##N)\ + {\ + return container_detail::force_copy< std::pair >\ + (m_flat_tree.emplace_unique(BOOST_MOVE_FWD##N));\ + }\ + \ + BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \ + iterator emplace_hint(const_iterator hint BOOST_MOVE_I##N BOOST_MOVE_UREF##N)\ + {\ + return container_detail::force_copy(m_flat_tree.emplace_hint_unique\ + (container_detail::force_copy(hint) BOOST_MOVE_I##N BOOST_MOVE_FWD##N));\ + }\ + // + BOOST_MOVE_ITERATE_0TO9(BOOST_CONTAINER_FLAT_MAP_EMPLACE_CODE) + #undef BOOST_CONTAINER_FLAT_MAP_EMPLACE_CODE + + #endif // !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + + //! Effects: Inserts x if and only if there is no element in the container + //! with key equivalent to the key of x. + //! + //! Returns: The bool component of the returned pair is true if and only + //! if the insertion takes place, and the iterator component of the pair + //! points to the element with key equivalent to the key of x. + //! + //! Complexity: Logarithmic search time plus linear insertion + //! to the elements with bigger keys than x. + //! + //! Note: If an element is inserted it might invalidate elements. + std::pair insert(const value_type& x) + { return container_detail::force_copy >( + m_flat_tree.insert_unique(container_detail::force(x))); } + + //! Effects: Inserts a new value_type move constructed from the pair if and + //! only if there is no element in the container with key equivalent to the key of x. + //! + //! Returns: The bool component of the returned pair is true if and only + //! if the insertion takes place, and the iterator component of the pair + //! points to the element with key equivalent to the key of x. + //! + //! Complexity: Logarithmic search time plus linear insertion + //! to the elements with bigger keys than x. + //! + //! Note: If an element is inserted it might invalidate elements. + std::pair insert(BOOST_RV_REF(value_type) x) + { return container_detail::force_copy >( + m_flat_tree.insert_unique(boost::move(container_detail::force(x)))); } + + //! Effects: Inserts a new value_type move constructed from the pair if and + //! only if there is no element in the container with key equivalent to the key of x. + //! + //! Returns: The bool component of the returned pair is true if and only + //! if the insertion takes place, and the iterator component of the pair + //! points to the element with key equivalent to the key of x. + //! + //! Complexity: Logarithmic search time plus linear insertion + //! to the elements with bigger keys than x. + //! + //! Note: If an element is inserted it might invalidate elements. + std::pair insert(BOOST_RV_REF(movable_value_type) x) + { + return container_detail::force_copy > + (m_flat_tree.insert_unique(boost::move(x))); + } + + //! Effects: Inserts a copy of x in the container if and only if there is + //! no element in the container with key equivalent to the key of x. + //! p is a hint pointing to where the insert should start to search. + //! + //! Returns: An iterator pointing to the element with key equivalent + //! to the key of x. + //! + //! Complexity: Logarithmic search time (constant if x is inserted + //! right before p) plus insertion linear to the elements with bigger keys than x. + //! + //! Note: If an element is inserted it might invalidate elements. + iterator insert(const_iterator p, const value_type& x) + { + return container_detail::force_copy( + m_flat_tree.insert_unique( container_detail::force_copy(p) + , container_detail::force(x))); + } + + //! Effects: Inserts an element move constructed from x in the container. + //! p is a hint pointing to where the insert should start to search. + //! + //! Returns: An iterator pointing to the element with key equivalent to the key of x. + //! + //! Complexity: Logarithmic search time (constant if x is inserted + //! right before p) plus insertion linear to the elements with bigger keys than x. + //! + //! Note: If an element is inserted it might invalidate elements. + iterator insert(const_iterator p, BOOST_RV_REF(value_type) x) + { + return container_detail::force_copy + (m_flat_tree.insert_unique( container_detail::force_copy(p) + , boost::move(container_detail::force(x)))); + } + + //! Effects: Inserts an element move constructed from x in the container. + //! p is a hint pointing to where the insert should start to search. + //! + //! Returns: An iterator pointing to the element with key equivalent to the key of x. + //! + //! Complexity: Logarithmic search time (constant if x is inserted + //! right before p) plus insertion linear to the elements with bigger keys than x. + //! + //! Note: If an element is inserted it might invalidate elements. + iterator insert(const_iterator p, BOOST_RV_REF(movable_value_type) x) + { + return container_detail::force_copy( + m_flat_tree.insert_unique(container_detail::force_copy(p), boost::move(x))); + } + + //! Requires: first, last are not iterators into *this. + //! + //! Effects: inserts each element from the range [first,last) if and only + //! if there is no element with key equivalent to the key of that element. + //! + //! Complexity: At most N log(size()+N) (N is the distance from first to last) + //! search time plus N*size() insertion time. + //! + //! Note: If an element is inserted it might invalidate elements. + template + void insert(InputIterator first, InputIterator last) + { m_flat_tree.insert_unique(first, last); } + + //! Requires: first, last are not iterators into *this. + //! + //! Requires: [first ,last) must be ordered according to the predicate and must be + //! unique values. + //! + //! Effects: inserts each element from the range [first,last) if and only + //! if there is no element with key equivalent to the key of that element. This + //! function is more efficient than the normal range creation for ordered ranges. + //! + //! Complexity: At most N log(size()+N) (N is the distance from first to last) + //! search time plus N*size() insertion time. + //! + //! Note: If an element is inserted it might invalidate elements. + //! + //! Note: Non-standard extension. + template + void insert(ordered_unique_range_t, InputIterator first, InputIterator last) + { m_flat_tree.insert_unique(ordered_unique_range, first, last); } + +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + //! Effects: inserts each element from the range [il.begin(), il.end()) if and only + //! if there is no element with key equivalent to the key of that element. + //! + //! Complexity: At most N log(size()+N) (N is the distance from il.first() to il.end()) + //! search time plus N*size() insertion time. + //! + //! Note: If an element is inserted it might invalidate elements. + void insert(std::initializer_list il) + { m_flat_tree.insert_unique(il.begin(), il.end()); } + + //! Requires: [il.begin(), il.end()) must be ordered according to the predicate and must be + //! unique values. + //! + //! Effects: inserts each element from the range [il.begin(), il.end()) if and only + //! if there is no element with key equivalent to the key of that element. This + //! function is more efficient than the normal range creation for ordered ranges. + //! + //! Complexity: At most N log(size()+N) (N is the distance from first to last) + //! search time plus N*size() insertion time. + //! + //! Note: If an element is inserted it might invalidate elements. + //! + //! Note: Non-standard extension. + void insert(ordered_unique_range_t, std::initializer_list il) + { m_flat_tree.insert_unique(ordered_unique_range, il.begin(), il.end()); } +#endif + + //! Effects: Erases the element pointed to by p. + //! + //! Returns: Returns an iterator pointing to the element immediately + //! following q prior to the element being erased. If no such element exists, + //! returns end(). + //! + //! Complexity: Linear to the elements with keys bigger than p + //! + //! Note: Invalidates elements with keys + //! not less than the erased element. + iterator erase(const_iterator p) + { + return container_detail::force_copy + (m_flat_tree.erase(container_detail::force_copy(p))); + } + + //! Effects: Erases all elements in the container with key equivalent to x. + //! + //! Returns: Returns the number of erased elements. + //! + //! Complexity: Logarithmic search time plus erasure time + //! linear to the elements with bigger keys. + size_type erase(const key_type& x) + { return m_flat_tree.erase(x); } + + //! Effects: Erases all the elements in the range [first, last). + //! + //! Returns: Returns last. + //! + //! Complexity: size()*N where N is the distance from first to last. + //! + //! Complexity: Logarithmic search time plus erasure time + //! linear to the elements with bigger keys. + iterator erase(const_iterator first, const_iterator last) + { + return container_detail::force_copy( + m_flat_tree.erase( container_detail::force_copy(first) + , container_detail::force_copy(last))); + } + + //! Effects: Swaps the contents of *this and x. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + void swap(flat_map& x) + BOOST_NOEXCEPT_IF( allocator_traits_type::is_always_equal::value + && boost::container::container_detail::is_nothrow_swappable::value ) + { m_flat_tree.swap(x.m_flat_tree); } + + //! Effects: erase(a.begin(),a.end()). + //! + //! Postcondition: size() == 0. + //! + //! Complexity: linear in size(). + void clear() BOOST_NOEXCEPT_OR_NOTHROW + { m_flat_tree.clear(); } + + ////////////////////////////////////////////// + // + // observers + // + ////////////////////////////////////////////// + + //! Effects: Returns the comparison object out + //! of which a was constructed. + //! + //! Complexity: Constant. + key_compare key_comp() const + { return container_detail::force_copy(m_flat_tree.key_comp()); } + + //! Effects: Returns an object of value_compare constructed out + //! of the comparison object. + //! + //! Complexity: Constant. + value_compare value_comp() const + { return value_compare(container_detail::force_copy(m_flat_tree.key_comp())); } + + ////////////////////////////////////////////// + // + // map operations + // + ////////////////////////////////////////////// + + //! Returns: An iterator pointing to an element with the key + //! equivalent to x, or end() if such an element is not found. + //! + //! Complexity: Logarithmic. + iterator find(const key_type& x) + { return container_detail::force_copy(m_flat_tree.find(x)); } + + //! Returns: A const_iterator pointing to an element with the key + //! equivalent to x, or end() if such an element is not found. + //! + //! Complexity: Logarithmic. + const_iterator find(const key_type& x) const + { return container_detail::force_copy(m_flat_tree.find(x)); } + + //! Returns: The number of elements with key equivalent to x. + //! + //! Complexity: log(size())+count(k) + size_type count(const key_type& x) const + { return static_cast(m_flat_tree.find(x) != m_flat_tree.end()); } + + //! Returns: An iterator pointing to the first element with key not less + //! than k, or a.end() if such an element is not found. + //! + //! Complexity: Logarithmic. + iterator lower_bound(const key_type& x) + { return container_detail::force_copy(m_flat_tree.lower_bound(x)); } + + //! Returns: A const iterator pointing to the first element with key not + //! less than k, or a.end() if such an element is not found. + //! + //! Complexity: Logarithmic. + const_iterator lower_bound(const key_type& x) const + { return container_detail::force_copy(m_flat_tree.lower_bound(x)); } + + //! Returns: An iterator pointing to the first element with key not less + //! than x, or end() if such an element is not found. + //! + //! Complexity: Logarithmic. + iterator upper_bound(const key_type& x) + { return container_detail::force_copy(m_flat_tree.upper_bound(x)); } + + //! Returns: A const iterator pointing to the first element with key not + //! less than x, or end() if such an element is not found. + //! + //! Complexity: Logarithmic. + const_iterator upper_bound(const key_type& x) const + { return container_detail::force_copy(m_flat_tree.upper_bound(x)); } + + //! Effects: Equivalent to std::make_pair(this->lower_bound(k), this->upper_bound(k)). + //! + //! Complexity: Logarithmic. + std::pair equal_range(const key_type& x) + { return container_detail::force_copy >(m_flat_tree.lower_bound_range(x)); } + + //! Effects: Equivalent to std::make_pair(this->lower_bound(k), this->upper_bound(k)). + //! + //! Complexity: Logarithmic. + std::pair equal_range(const key_type& x) const + { return container_detail::force_copy >(m_flat_tree.lower_bound_range(x)); } + + //! Effects: Returns true if x and y are equal + //! + //! Complexity: Linear to the number of elements in the container. + friend bool operator==(const flat_map& x, const flat_map& y) + { return x.size() == y.size() && ::boost::container::algo_equal(x.begin(), x.end(), y.begin()); } + + //! Effects: Returns true if x and y are unequal + //! + //! Complexity: Linear to the number of elements in the container. + friend bool operator!=(const flat_map& x, const flat_map& y) + { return !(x == y); } + + //! Effects: Returns true if x is less than y + //! + //! Complexity: Linear to the number of elements in the container. + friend bool operator<(const flat_map& x, const flat_map& y) + { return ::boost::container::algo_lexicographical_compare(x.begin(), x.end(), y.begin(), y.end()); } + + //! Effects: Returns true if x is greater than y + //! + //! Complexity: Linear to the number of elements in the container. + friend bool operator>(const flat_map& x, const flat_map& y) + { return y < x; } + + //! Effects: Returns true if x is equal or less than y + //! + //! Complexity: Linear to the number of elements in the container. + friend bool operator<=(const flat_map& x, const flat_map& y) + { return !(y < x); } + + //! Effects: Returns true if x is equal or greater than y + //! + //! Complexity: Linear to the number of elements in the container. + friend bool operator>=(const flat_map& x, const flat_map& y) + { return !(x < y); } + + //! Effects: x.swap(y) + //! + //! Complexity: Constant. + friend void swap(flat_map& x, flat_map& y) + { x.swap(y); } + + #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + private: + mapped_type &priv_subscript(const key_type& k) + { + iterator i = lower_bound(k); + // i->first is greater than or equivalent to k. + if (i == end() || key_comp()(k, (*i).first)){ + container_detail::value_init m; + i = insert(i, impl_value_type(k, ::boost::move(m.m_t))); + } + return (*i).second; + } + mapped_type &priv_subscript(BOOST_RV_REF(key_type) mk) + { + key_type &k = mk; + iterator i = lower_bound(k); + // i->first is greater than or equivalent to k. + if (i == end() || key_comp()(k, (*i).first)){ + container_detail::value_init m; + i = insert(i, impl_value_type(boost::move(k), ::boost::move(m.m_t))); + } + return (*i).second; + } + #endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED +}; + +#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + +} //namespace container { + +//!has_trivial_destructor_after_move<> == true_type +//!specialization for optimizations +template +struct has_trivial_destructor_after_move > +{ + typedef typename ::boost::container::allocator_traits::pointer pointer; + static const bool value = ::boost::has_trivial_destructor_after_move::value && + ::boost::has_trivial_destructor_after_move::value && + ::boost::has_trivial_destructor_after_move::value; +}; + +namespace container { + +#endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + +//! A flat_multimap is a kind of associative container that supports equivalent keys +//! (possibly containing multiple copies of the same key value) and provides for +//! fast retrieval of values of another type T based on the keys. The flat_multimap +//! class supports random-access iterators. +//! +//! A flat_multimap satisfies all of the requirements of a container and of a reversible +//! container and of an associative container. For a +//! flat_multimap the key_type is Key and the value_type is std::pair +//! (unlike std::multimap which value_type is std::pair<const Key, T>). +//! +//! Compare is the ordering function for Keys (e.g. std::less). +//! +//! Allocator is the allocator to allocate the value_types +//! (e.g. allocator< std::pair >). +//! +//! flat_multimap is similar to std::multimap but it's implemented like an ordered vector. +//! This means that inserting a new element into a flat_map invalidates +//! previous iterators and references +//! +//! Erasing an element invalidates iterators and references +//! pointing to elements that come after (their keys are bigger) the erased element. +//! +//! This container provides random-access iterators. +//! +//! \tparam Key is the key_type of the map +//! \tparam Value is the mapped_type +//! \tparam Compare is the ordering function for Keys (e.g. std::less). +//! \tparam Allocator is the allocator to allocate the value_types +//! (e.g. allocator< std::pair > ). +#ifdef BOOST_CONTAINER_DOXYGEN_INVOKED +template , class Allocator = new_allocator< std::pair< Key, T> > > +#else +template +#endif +class flat_multimap +{ + #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + private: + BOOST_COPYABLE_AND_MOVABLE(flat_multimap) + typedef container_detail::flat_tree, + container_detail::select1st< std::pair >, + Compare, + Allocator> tree_t; + //This is the real tree stored here. It's based on a movable pair + typedef container_detail::flat_tree, + container_detail::select1st >, + Compare, + typename allocator_traits::template portable_rebind_alloc + >::type> impl_tree_t; + impl_tree_t m_flat_tree; // flat tree representing flat_map + + typedef typename impl_tree_t::value_type impl_value_type; + typedef typename impl_tree_t::const_iterator impl_const_iterator; + typedef typename impl_tree_t::iterator impl_iterator; + typedef typename impl_tree_t::allocator_type impl_allocator_type; + typedef container_detail::flat_tree_value_compare + < Compare + , container_detail::select1st< std::pair > + , std::pair > value_compare_impl; + typedef typename container_detail::get_flat_tree_iterators + ::pointer>::iterator iterator_impl; + typedef typename container_detail::get_flat_tree_iterators + ::pointer>::const_iterator const_iterator_impl; + typedef typename container_detail::get_flat_tree_iterators + ::pointer>::reverse_iterator reverse_iterator_impl; + typedef typename container_detail::get_flat_tree_iterators + ::pointer>::const_reverse_iterator const_reverse_iterator_impl; + public: + typedef typename impl_tree_t::stored_allocator_type impl_stored_allocator_type; + private: + #endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + + public: + + ////////////////////////////////////////////// + // + // types + // + ////////////////////////////////////////////// + typedef Key key_type; + typedef T mapped_type; + typedef std::pair value_type; + typedef ::boost::container::allocator_traits allocator_traits_type; + typedef typename boost::container::allocator_traits::pointer pointer; + typedef typename boost::container::allocator_traits::const_pointer const_pointer; + typedef typename boost::container::allocator_traits::reference reference; + typedef typename boost::container::allocator_traits::const_reference const_reference; + typedef typename boost::container::allocator_traits::size_type size_type; + typedef typename boost::container::allocator_traits::difference_type difference_type; + typedef Allocator allocator_type; + typedef BOOST_CONTAINER_IMPDEF(Allocator) stored_allocator_type; + typedef BOOST_CONTAINER_IMPDEF(value_compare_impl) value_compare; + typedef Compare key_compare; + typedef BOOST_CONTAINER_IMPDEF(iterator_impl) iterator; + typedef BOOST_CONTAINER_IMPDEF(const_iterator_impl) const_iterator; + typedef BOOST_CONTAINER_IMPDEF(reverse_iterator_impl) reverse_iterator; + typedef BOOST_CONTAINER_IMPDEF(const_reverse_iterator_impl) const_reverse_iterator; + typedef BOOST_CONTAINER_IMPDEF(impl_value_type) movable_value_type; + + ////////////////////////////////////////////// + // + // construct/copy/destroy + // + ////////////////////////////////////////////// + + //! Effects: Default constructs an empty flat_map. + //! + //! Complexity: Constant. + flat_multimap() + : m_flat_tree() + { + //A type must be std::pair + BOOST_STATIC_ASSERT((container_detail::is_same, typename Allocator::value_type>::value)); + } + + //! Effects: Constructs an empty flat_multimap using the specified comparison + //! object and allocator. + //! + //! Complexity: Constant. + explicit flat_multimap(const Compare& comp, + const allocator_type& a = allocator_type()) + : m_flat_tree(comp, container_detail::force(a)) + { + //A type must be std::pair + BOOST_STATIC_ASSERT((container_detail::is_same, typename Allocator::value_type>::value)); + } + + //! Effects: Constructs an empty flat_multimap using the specified allocator. + //! + //! Complexity: Constant. + explicit flat_multimap(const allocator_type& a) + : m_flat_tree(container_detail::force(a)) + { + //A type must be std::pair + BOOST_STATIC_ASSERT((container_detail::is_same, typename Allocator::value_type>::value)); + } + + //! Effects: Constructs an empty flat_multimap using the specified comparison object + //! and allocator, and inserts elements from the range [first ,last ). + //! + //! Complexity: Linear in N if the range [first ,last ) is already sorted using + //! comp and otherwise N logN, where N is last - first. + template + flat_multimap(InputIterator first, InputIterator last, + const Compare& comp = Compare(), + const allocator_type& a = allocator_type()) + : m_flat_tree(false, first, last, comp, container_detail::force(a)) + { + //A type must be std::pair + BOOST_STATIC_ASSERT((container_detail::is_same, typename Allocator::value_type>::value)); + } + + //! Effects: Constructs an empty flat_multimap using the specified + //! allocator, and inserts elements from the range [first ,last ). + //! + //! Complexity: Linear in N if the range [first ,last ) is already sorted using + //! comp and otherwise N logN, where N is last - first. + template + flat_multimap(InputIterator first, InputIterator last, const allocator_type& a) + : m_flat_tree(false, first, last, Compare(), container_detail::force(a)) + { + //A type must be std::pair + BOOST_STATIC_ASSERT((container_detail::is_same, typename Allocator::value_type>::value)); + } + + //! Effects: Constructs an empty flat_multimap using the specified comparison object and + //! allocator, and inserts elements from the ordered range [first ,last). This function + //! is more efficient than the normal range creation for ordered ranges. + //! + //! Requires: [first ,last) must be ordered according to the predicate. + //! + //! Complexity: Linear in N. + //! + //! Note: Non-standard extension. + template + flat_multimap(ordered_range_t, InputIterator first, InputIterator last, + const Compare& comp = Compare(), + const allocator_type& a = allocator_type()) + : m_flat_tree(ordered_range, first, last, comp, a) + { + //A type must be std::pair + BOOST_STATIC_ASSERT((container_detail::is_same, typename Allocator::value_type>::value)); + } + +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + //! Effects: Constructs an empty flat_map using the specified comparison object and + //! allocator, and inserts elements from the range [il.begin(), il.end()). + //! + //! Complexity: Linear in N if the range [il.begin(), il.end()) is already sorted using + //! comp and otherwise N logN, where N is last - first. + flat_multimap(std::initializer_list il, const Compare& comp = Compare(), const allocator_type& a = allocator_type()) + : m_flat_tree(false, il.begin(), il.end(), comp, container_detail::force(a)) + { + //A type must be std::pair + BOOST_STATIC_ASSERT((container_detail::is_same, typename Allocator::value_type>::value)); + } + + //! Effects: Constructs an empty flat_map using the specified + //! allocator, and inserts elements from the range [il.begin(), il.end()). + //! + //! Complexity: Linear in N if the range [il.begin(), il.end()) is already sorted using + //! comp and otherwise N logN, where N is last - first. + flat_multimap(std::initializer_list il, const allocator_type& a) + : m_flat_tree(false, il.begin(), il.end(), Compare(), container_detail::force(a)) + { + //A type must be std::pair + BOOST_STATIC_ASSERT((container_detail::is_same, typename Allocator::value_type>::value)); + } + + //! Effects: Constructs an empty flat_multimap using the specified comparison object and + //! allocator, and inserts elements from the ordered range [il.begin(), il.end()). This function + //! is more efficient than the normal range creation for ordered ranges. + //! + //! Requires: [il.begin(), il.end()) must be ordered according to the predicate. + //! + //! Complexity: Linear in N. + //! + //! Note: Non-standard extension. + flat_multimap(ordered_range_t, std::initializer_list il, const Compare& comp = Compare(), + const allocator_type& a = allocator_type()) + : m_flat_tree(ordered_range, il.begin(), il.end(), comp, a) + { + //A type must be std::pair + BOOST_STATIC_ASSERT((container_detail::is_same, typename Allocator::value_type>::value)); + } +#endif + + //! Effects: Copy constructs a flat_multimap. + //! + //! Complexity: Linear in x.size(). + flat_multimap(const flat_multimap& x) + : m_flat_tree(x.m_flat_tree) + { + //A type must be std::pair + BOOST_STATIC_ASSERT((container_detail::is_same, typename Allocator::value_type>::value)); + } + + //! Effects: Move constructs a flat_multimap. Constructs *this using x's resources. + //! + //! Complexity: Constant. + //! + //! Postcondition: x is emptied. + flat_multimap(BOOST_RV_REF(flat_multimap) x) + : m_flat_tree(boost::move(x.m_flat_tree)) + { + //A type must be std::pair + BOOST_STATIC_ASSERT((container_detail::is_same, typename Allocator::value_type>::value)); + } + + //! Effects: Copy constructs a flat_multimap using the specified allocator. + //! + //! Complexity: Linear in x.size(). + flat_multimap(const flat_multimap& x, const allocator_type &a) + : m_flat_tree(x.m_flat_tree, a) + { + //A type must be std::pair + BOOST_STATIC_ASSERT((container_detail::is_same, typename Allocator::value_type>::value)); + } + + //! Effects: Move constructs a flat_multimap using the specified allocator. + //! Constructs *this using x's resources. + //! + //! Complexity: Constant if a == x.get_allocator(), linear otherwise. + flat_multimap(BOOST_RV_REF(flat_multimap) x, const allocator_type &a) + : m_flat_tree(boost::move(x.m_flat_tree), a) + { + //A type must be std::pair + BOOST_STATIC_ASSERT((container_detail::is_same, typename Allocator::value_type>::value)); + } + + //! Effects: Makes *this a copy of x. + //! + //! Complexity: Linear in x.size(). + flat_multimap& operator=(BOOST_COPY_ASSIGN_REF(flat_multimap) x) + { m_flat_tree = x.m_flat_tree; return *this; } + + //! Effects: this->swap(x.get()). + //! + //! Complexity: Constant. + flat_multimap& operator=(BOOST_RV_REF(flat_multimap) x) + BOOST_NOEXCEPT_IF( allocator_traits_type::is_always_equal::value + && boost::container::container_detail::is_nothrow_move_assignable::value ) + { m_flat_tree = boost::move(x.m_flat_tree); return *this; } + +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + //! Effects: Assign content of il to *this + //! + //! Complexity: Linear in il.size(). + flat_multimap& operator=(std::initializer_list il) + { + this->clear(); + this->insert(il.begin(), il.end()); + return *this; + } +#endif + + //! Effects: Returns a copy of the allocator that + //! was passed to the object's constructor. + //! + //! Complexity: Constant. + allocator_type get_allocator() const BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.get_allocator()); } + + //! Effects: Returns a reference to the internal allocator. + //! + //! Throws: Nothing + //! + //! Complexity: Constant. + //! + //! Note: Non-standard extension. + stored_allocator_type &get_stored_allocator() BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force(m_flat_tree.get_stored_allocator()); } + + //! Effects: Returns a reference to the internal allocator. + //! + //! Throws: Nothing + //! + //! Complexity: Constant. + //! + //! Note: Non-standard extension. + const stored_allocator_type &get_stored_allocator() const BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force(m_flat_tree.get_stored_allocator()); } + + ////////////////////////////////////////////// + // + // iterators + // + ////////////////////////////////////////////// + + //! Effects: Returns an iterator to the first element contained in the container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + iterator begin() BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.begin()); } + + //! Effects: Returns a const_iterator to the first element contained in the container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + const_iterator begin() const BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.begin()); } + + //! Effects: Returns an iterator to the end of the container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + iterator end() BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.end()); } + + //! Effects: Returns a const_iterator to the end of the container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + const_iterator end() const BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.end()); } + + //! Effects: Returns a reverse_iterator pointing to the beginning + //! of the reversed container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + reverse_iterator rbegin() BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.rbegin()); } + + //! Effects: Returns a const_reverse_iterator pointing to the beginning + //! of the reversed container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + const_reverse_iterator rbegin() const BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.rbegin()); } + + //! Effects: Returns a reverse_iterator pointing to the end + //! of the reversed container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + reverse_iterator rend() BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.rend()); } + + //! Effects: Returns a const_reverse_iterator pointing to the end + //! of the reversed container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + const_reverse_iterator rend() const BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.rend()); } + + //! Effects: Returns a const_iterator to the first element contained in the container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + const_iterator cbegin() const BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.cbegin()); } + + //! Effects: Returns a const_iterator to the end of the container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + const_iterator cend() const BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.cend()); } + + //! Effects: Returns a const_reverse_iterator pointing to the beginning + //! of the reversed container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + const_reverse_iterator crbegin() const BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.crbegin()); } + + //! Effects: Returns a const_reverse_iterator pointing to the end + //! of the reversed container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + const_reverse_iterator crend() const BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.crend()); } + + ////////////////////////////////////////////// + // + // capacity + // + ////////////////////////////////////////////// + + //! Effects: Returns true if the container contains no elements. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + bool empty() const BOOST_NOEXCEPT_OR_NOTHROW + { return m_flat_tree.empty(); } + + //! Effects: Returns the number of the elements contained in the container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + size_type size() const BOOST_NOEXCEPT_OR_NOTHROW + { return m_flat_tree.size(); } + + //! Effects: Returns the largest possible size of the container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + size_type max_size() const BOOST_NOEXCEPT_OR_NOTHROW + { return m_flat_tree.max_size(); } + + //! Effects: Number of elements for which memory has been allocated. + //! capacity() is always greater than or equal to size(). + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + size_type capacity() const BOOST_NOEXCEPT_OR_NOTHROW + { return m_flat_tree.capacity(); } + + //! Effects: If n is less than or equal to capacity(), this call has no + //! effect. Otherwise, it is a request for allocation of additional memory. + //! If the request is successful, then capacity() is greater than or equal to + //! n; otherwise, capacity() is unchanged. In either case, size() is unchanged. + //! + //! Throws: If memory allocation allocation throws or T's copy constructor throws. + //! + //! Note: If capacity() is less than "cnt", iterators and references to + //! to values might be invalidated. + void reserve(size_type cnt) + { m_flat_tree.reserve(cnt); } + + //! Effects: Tries to deallocate the excess of memory created + // with previous allocations. The size of the vector is unchanged + //! + //! Throws: If memory allocation throws, or T's copy constructor throws. + //! + //! Complexity: Linear to size(). + void shrink_to_fit() + { m_flat_tree.shrink_to_fit(); } + + //! @copydoc ::boost::container::flat_set::nth(size_type) + iterator nth(size_type n) BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.nth(n)); } + + //! @copydoc ::boost::container::flat_set::nth(size_type) const + const_iterator nth(size_type n) const BOOST_NOEXCEPT_OR_NOTHROW + { return container_detail::force_copy(m_flat_tree.nth(n)); } + + //! @copydoc ::boost::container::flat_set::index_of(iterator) + size_type index_of(iterator p) BOOST_NOEXCEPT_OR_NOTHROW + { return m_flat_tree.index_of(container_detail::force_copy(p)); } + + //! @copydoc ::boost::container::flat_set::index_of(const_iterator) const + size_type index_of(const_iterator p) const BOOST_NOEXCEPT_OR_NOTHROW + { return m_flat_tree.index_of(container_detail::force_copy(p)); } + + #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) || defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + + //! Effects: Inserts an object of type T constructed with + //! std::forward(args)... and returns the iterator pointing to the + //! newly inserted element. + //! + //! Complexity: Logarithmic search time plus linear insertion + //! to the elements with bigger keys than x. + //! + //! Note: If an element is inserted it might invalidate elements. + template + iterator emplace(BOOST_FWD_REF(Args)... args) + { return container_detail::force_copy(m_flat_tree.emplace_equal(boost::forward(args)...)); } + + //! Effects: Inserts an object of type T constructed with + //! std::forward(args)... in the container. + //! p is a hint pointing to where the insert should start to search. + //! + //! Returns: An iterator pointing to the element with key equivalent + //! to the key of x. + //! + //! Complexity: Logarithmic search time (constant time if the value + //! is to be inserted before p) plus linear insertion + //! to the elements with bigger keys than x. + //! + //! Note: If an element is inserted it might invalidate elements. + template + iterator emplace_hint(const_iterator hint, BOOST_FWD_REF(Args)... args) + { + return container_detail::force_copy(m_flat_tree.emplace_hint_equal + (container_detail::force_copy(hint), boost::forward(args)...)); + } + + #else // !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + + #define BOOST_CONTAINER_FLAT_MULTIMAP_EMPLACE_CODE(N) \ + BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \ + iterator emplace(BOOST_MOVE_UREF##N)\ + { return container_detail::force_copy(m_flat_tree.emplace_equal(BOOST_MOVE_FWD##N)); }\ + \ + BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \ + iterator emplace_hint(const_iterator hint BOOST_MOVE_I##N BOOST_MOVE_UREF##N)\ + {\ + return container_detail::force_copy(m_flat_tree.emplace_hint_equal\ + (container_detail::force_copy(hint) BOOST_MOVE_I##N BOOST_MOVE_FWD##N));\ + }\ + // + BOOST_MOVE_ITERATE_0TO9(BOOST_CONTAINER_FLAT_MULTIMAP_EMPLACE_CODE) + #undef BOOST_CONTAINER_FLAT_MULTIMAP_EMPLACE_CODE + + #endif // !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + + //! Effects: Inserts x and returns the iterator pointing to the + //! newly inserted element. + //! + //! Complexity: Logarithmic search time plus linear insertion + //! to the elements with bigger keys than x. + //! + //! Note: If an element is inserted it might invalidate elements. + iterator insert(const value_type& x) + { + return container_detail::force_copy( + m_flat_tree.insert_equal(container_detail::force(x))); + } + + //! Effects: Inserts a new value move-constructed from x and returns + //! the iterator pointing to the newly inserted element. + //! + //! Complexity: Logarithmic search time plus linear insertion + //! to the elements with bigger keys than x. + //! + //! Note: If an element is inserted it might invalidate elements. + iterator insert(BOOST_RV_REF(value_type) x) + { return container_detail::force_copy(m_flat_tree.insert_equal(boost::move(x))); } + + //! Effects: Inserts a new value move-constructed from x and returns + //! the iterator pointing to the newly inserted element. + //! + //! Complexity: Logarithmic search time plus linear insertion + //! to the elements with bigger keys than x. + //! + //! Note: If an element is inserted it might invalidate elements. + iterator insert(BOOST_RV_REF(impl_value_type) x) + { return container_detail::force_copy(m_flat_tree.insert_equal(boost::move(x))); } + + //! Effects: Inserts a copy of x in the container. + //! p is a hint pointing to where the insert should start to search. + //! + //! Returns: An iterator pointing to the element with key equivalent + //! to the key of x. + //! + //! Complexity: Logarithmic search time (constant time if the value + //! is to be inserted before p) plus linear insertion + //! to the elements with bigger keys than x. + //! + //! Note: If an element is inserted it might invalidate elements. + iterator insert(const_iterator p, const value_type& x) + { + return container_detail::force_copy + (m_flat_tree.insert_equal( container_detail::force_copy(p) + , container_detail::force(x))); + } + + //! Effects: Inserts a value move constructed from x in the container. + //! p is a hint pointing to where the insert should start to search. + //! + //! Returns: An iterator pointing to the element with key equivalent + //! to the key of x. + //! + //! Complexity: Logarithmic search time (constant time if the value + //! is to be inserted before p) plus linear insertion + //! to the elements with bigger keys than x. + //! + //! Note: If an element is inserted it might invalidate elements. + iterator insert(const_iterator p, BOOST_RV_REF(value_type) x) + { + return container_detail::force_copy + (m_flat_tree.insert_equal(container_detail::force_copy(p) + , boost::move(x))); + } + + //! Effects: Inserts a value move constructed from x in the container. + //! p is a hint pointing to where the insert should start to search. + //! + //! Returns: An iterator pointing to the element with key equivalent + //! to the key of x. + //! + //! Complexity: Logarithmic search time (constant time if the value + //! is to be inserted before p) plus linear insertion + //! to the elements with bigger keys than x. + //! + //! Note: If an element is inserted it might invalidate elements. + iterator insert(const_iterator p, BOOST_RV_REF(impl_value_type) x) + { + return container_detail::force_copy( + m_flat_tree.insert_equal(container_detail::force_copy(p), boost::move(x))); + } + + //! Requires: first, last are not iterators into *this. + //! + //! Effects: inserts each element from the range [first,last) . + //! + //! Complexity: At most N log(size()+N) (N is the distance from first to last) + //! search time plus N*size() insertion time. + //! + //! Note: If an element is inserted it might invalidate elements. + template + void insert(InputIterator first, InputIterator last) + { m_flat_tree.insert_equal(first, last); } + + //! Requires: first, last are not iterators into *this. + //! + //! Requires: [first ,last) must be ordered according to the predicate. + //! + //! Effects: inserts each element from the range [first,last) if and only + //! if there is no element with key equivalent to the key of that element. This + //! function is more efficient than the normal range creation for ordered ranges. + //! + //! Complexity: At most N log(size()+N) (N is the distance from first to last) + //! search time plus N*size() insertion time. + //! + //! Note: If an element is inserted it might invalidate elements. + //! + //! Note: Non-standard extension. + template + void insert(ordered_range_t, InputIterator first, InputIterator last) + { m_flat_tree.insert_equal(ordered_range, first, last); } + +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + //! Effects: inserts each element from the range [il.begin(), il.end()) . + //! + //! Complexity: At most N log(size()+N) (N is the distance from first to last) + //! search time plus N*size() insertion time. + //! + //! Note: If an element is inserted it might invalidate elements. + void insert(std::initializer_list il) + { m_flat_tree.insert_equal(il.begin(), il.end()); } + + //! Requires: [il.begin(), il.end()) must be ordered according to the predicate. + //! + //! Effects: inserts each element from the range [il.begin(), il.end()) if and only + //! if there is no element with key equivalent to the key of that element. This + //! function is more efficient than the normal range creation for ordered ranges. + //! + //! Complexity: At most N log(size()+N) (N is the distance from first to last) + //! search time plus N*size() insertion time. + //! + //! Note: If an element is inserted it might invalidate elements. + //! + //! Note: Non-standard extension. + void insert(ordered_range_t, std::initializer_list il) + { m_flat_tree.insert_equal(ordered_range, il.begin(), il.end()); } +#endif + + //! Effects: Erases the element pointed to by p. + //! + //! Returns: Returns an iterator pointing to the element immediately + //! following q prior to the element being erased. If no such element exists, + //! returns end(). + //! + //! Complexity: Linear to the elements with keys bigger than p + //! + //! Note: Invalidates elements with keys + //! not less than the erased element. + iterator erase(const_iterator p) + { + return container_detail::force_copy( + m_flat_tree.erase(container_detail::force_copy(p))); + } + + //! Effects: Erases all elements in the container with key equivalent to x. + //! + //! Returns: Returns the number of erased elements. + //! + //! Complexity: Logarithmic search time plus erasure time + //! linear to the elements with bigger keys. + size_type erase(const key_type& x) + { return m_flat_tree.erase(x); } + + //! Effects: Erases all the elements in the range [first, last). + //! + //! Returns: Returns last. + //! + //! Complexity: size()*N where N is the distance from first to last. + //! + //! Complexity: Logarithmic search time plus erasure time + //! linear to the elements with bigger keys. + iterator erase(const_iterator first, const_iterator last) + { + return container_detail::force_copy + (m_flat_tree.erase( container_detail::force_copy(first) + , container_detail::force_copy(last))); + } + + //! Effects: Swaps the contents of *this and x. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + void swap(flat_multimap& x) + BOOST_NOEXCEPT_IF( allocator_traits_type::is_always_equal::value + && boost::container::container_detail::is_nothrow_swappable::value ) + { m_flat_tree.swap(x.m_flat_tree); } + + //! Effects: erase(a.begin(),a.end()). + //! + //! Postcondition: size() == 0. + //! + //! Complexity: linear in size(). + void clear() BOOST_NOEXCEPT_OR_NOTHROW + { m_flat_tree.clear(); } + + ////////////////////////////////////////////// + // + // observers + // + ////////////////////////////////////////////// + + //! Effects: Returns the comparison object out + //! of which a was constructed. + //! + //! Complexity: Constant. + key_compare key_comp() const + { return container_detail::force_copy(m_flat_tree.key_comp()); } + + //! Effects: Returns an object of value_compare constructed out + //! of the comparison object. + //! + //! Complexity: Constant. + value_compare value_comp() const + { return value_compare(container_detail::force_copy(m_flat_tree.key_comp())); } + + ////////////////////////////////////////////// + // + // map operations + // + ////////////////////////////////////////////// + + //! Returns: An iterator pointing to an element with the key + //! equivalent to x, or end() if such an element is not found. + //! + //! Complexity: Logarithmic. + iterator find(const key_type& x) + { return container_detail::force_copy(m_flat_tree.find(x)); } + + //! Returns: An const_iterator pointing to an element with the key + //! equivalent to x, or end() if such an element is not found. + //! + //! Complexity: Logarithmic. + const_iterator find(const key_type& x) const + { return container_detail::force_copy(m_flat_tree.find(x)); } + + //! Returns: The number of elements with key equivalent to x. + //! + //! Complexity: log(size())+count(k) + size_type count(const key_type& x) const + { return m_flat_tree.count(x); } + + //! Returns: An iterator pointing to the first element with key not less + //! than k, or a.end() if such an element is not found. + //! + //! Complexity: Logarithmic + iterator lower_bound(const key_type& x) + { return container_detail::force_copy(m_flat_tree.lower_bound(x)); } + + //! Returns: A const iterator pointing to the first element with key + //! not less than k, or a.end() if such an element is not found. + //! + //! Complexity: Logarithmic + const_iterator lower_bound(const key_type& x) const + { return container_detail::force_copy(m_flat_tree.lower_bound(x)); } + + //! Returns: An iterator pointing to the first element with key not less + //! than x, or end() if such an element is not found. + //! + //! Complexity: Logarithmic + iterator upper_bound(const key_type& x) + {return container_detail::force_copy(m_flat_tree.upper_bound(x)); } + + //! Returns: A const iterator pointing to the first element with key + //! not less than x, or end() if such an element is not found. + //! + //! Complexity: Logarithmic + const_iterator upper_bound(const key_type& x) const + { return container_detail::force_copy(m_flat_tree.upper_bound(x)); } + + //! Effects: Equivalent to std::make_pair(this->lower_bound(k), this->upper_bound(k)). + //! + //! Complexity: Logarithmic + std::pair equal_range(const key_type& x) + { return container_detail::force_copy >(m_flat_tree.equal_range(x)); } + + //! Effects: Equivalent to std::make_pair(this->lower_bound(k), this->upper_bound(k)). + //! + //! Complexity: Logarithmic + std::pair equal_range(const key_type& x) const + { return container_detail::force_copy >(m_flat_tree.equal_range(x)); } + + //! Effects: Returns true if x and y are equal + //! + //! Complexity: Linear to the number of elements in the container. + friend bool operator==(const flat_multimap& x, const flat_multimap& y) + { return x.size() == y.size() && ::boost::container::algo_equal(x.begin(), x.end(), y.begin()); } + + //! Effects: Returns true if x and y are unequal + //! + //! Complexity: Linear to the number of elements in the container. + friend bool operator!=(const flat_multimap& x, const flat_multimap& y) + { return !(x == y); } + + //! Effects: Returns true if x is less than y + //! + //! Complexity: Linear to the number of elements in the container. + friend bool operator<(const flat_multimap& x, const flat_multimap& y) + { return ::boost::container::algo_lexicographical_compare(x.begin(), x.end(), y.begin(), y.end()); } + + //! Effects: Returns true if x is greater than y + //! + //! Complexity: Linear to the number of elements in the container. + friend bool operator>(const flat_multimap& x, const flat_multimap& y) + { return y < x; } + + //! Effects: Returns true if x is equal or less than y + //! + //! Complexity: Linear to the number of elements in the container. + friend bool operator<=(const flat_multimap& x, const flat_multimap& y) + { return !(y < x); } + + //! Effects: Returns true if x is equal or greater than y + //! + //! Complexity: Linear to the number of elements in the container. + friend bool operator>=(const flat_multimap& x, const flat_multimap& y) + { return !(x < y); } + + //! Effects: x.swap(y) + //! + //! Complexity: Constant. + friend void swap(flat_multimap& x, flat_multimap& y) + { x.swap(y); } +}; + +}} + +#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + +namespace boost { + +//!has_trivial_destructor_after_move<> == true_type +//!specialization for optimizations +template +struct has_trivial_destructor_after_move< boost::container::flat_multimap > +{ + typedef typename ::boost::container::allocator_traits::pointer pointer; + static const bool value = ::boost::has_trivial_destructor_after_move::value && + ::boost::has_trivial_destructor_after_move::value && + ::boost::has_trivial_destructor_after_move::value; +}; + +} //namespace boost { + +#endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + +#include + +#endif // BOOST_CONTAINER_FLAT_MAP_HPP Index: libraries/win32/boost/include/boost/container/flat_set.hpp =================================================================== --- /dev/null +++ libraries/win32/boost/include/boost/container/flat_set.hpp @@ -0,0 +1,1359 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2013. 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) +// +// See http://www.boost.org/libs/container for documentation. +// +////////////////////////////////////////////////////////////////////////////// +#ifndef BOOST_CONTAINER_FLAT_SET_HPP +#define BOOST_CONTAINER_FLAT_SET_HPP + +#ifndef BOOST_CONFIG_HPP +# include +#endif + +#if defined(BOOST_HAS_PRAGMA_ONCE) +# pragma once +#endif + +#include +#include + +// container +#include +#include +#include //new_allocator +// container/detail +#include +#include +// move +#include +#include +// move/detail +#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) +#include +#endif +#include +// intrusive/detail +#include //pair +#include //less, equal +// std +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) +#include +#endif + +namespace boost { +namespace container { + +//! flat_set is a Sorted Associative Container that stores objects of type Key. +//! It is also a Unique Associative Container, meaning that no two elements are the same. +//! +//! flat_set is similar to std::set but it's implemented like an ordered vector. +//! This means that inserting a new element into a flat_set invalidates +//! previous iterators and references +//! +//! Erasing an element of a flat_set invalidates iterators and references +//! pointing to elements that come after (their keys are bigger) the erased element. +//! +//! This container provides random-access iterators. +//! +//! \tparam Key is the type to be inserted in the set, which is also the key_type +//! \tparam Compare is the comparison functor used to order keys +//! \tparam Allocator is the allocator to be used to allocate memory for this container +#ifdef BOOST_CONTAINER_DOXYGEN_INVOKED +template , class Allocator = new_allocator > +#else +template +#endif +class flat_set + ///@cond + : public container_detail::flat_tree, Compare, Allocator> + ///@endcond +{ + #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + private: + BOOST_COPYABLE_AND_MOVABLE(flat_set) + typedef container_detail::flat_tree, Compare, Allocator> base_t; + #endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + + public: + ////////////////////////////////////////////// + // + // types + // + ////////////////////////////////////////////// + typedef Key key_type; + typedef Key value_type; + typedef Compare key_compare; + typedef Compare value_compare; + typedef ::boost::container::allocator_traits allocator_traits_type; + typedef typename ::boost::container::allocator_traits::pointer pointer; + typedef typename ::boost::container::allocator_traits::const_pointer const_pointer; + typedef typename ::boost::container::allocator_traits::reference reference; + typedef typename ::boost::container::allocator_traits::const_reference const_reference; + typedef typename ::boost::container::allocator_traits::size_type size_type; + typedef typename ::boost::container::allocator_traits::difference_type difference_type; + typedef Allocator allocator_type; + typedef typename BOOST_CONTAINER_IMPDEF(base_t::stored_allocator_type) stored_allocator_type; + typedef typename BOOST_CONTAINER_IMPDEF(base_t::iterator) iterator; + typedef typename BOOST_CONTAINER_IMPDEF(base_t::const_iterator) const_iterator; + typedef typename BOOST_CONTAINER_IMPDEF(base_t::reverse_iterator) reverse_iterator; + typedef typename BOOST_CONTAINER_IMPDEF(base_t::const_reverse_iterator) const_reverse_iterator; + + public: + ////////////////////////////////////////////// + // + // construct/copy/destroy + // + ////////////////////////////////////////////// + + //! Effects: Default constructs an empty container. + //! + //! Complexity: Constant. + explicit flat_set() + : base_t() + {} + + //! Effects: Constructs an empty container using the specified + //! comparison object and allocator. + //! + //! Complexity: Constant. + explicit flat_set(const Compare& comp, + const allocator_type& a = allocator_type()) + : base_t(comp, a) + {} + + //! Effects: Constructs an empty container using the specified allocator. + //! + //! Complexity: Constant. + explicit flat_set(const allocator_type& a) + : base_t(a) + {} + + //! Effects: Constructs an empty container using the specified comparison object and + //! allocator, and inserts elements from the range [first ,last ). + //! + //! Complexity: Linear in N if the range [first ,last ) is already sorted using + //! comp and otherwise N logN, where N is last - first. + template + flat_set(InputIterator first, InputIterator last, + const Compare& comp = Compare(), + const allocator_type& a = allocator_type()) + : base_t(true, first, last, comp, a) + {} + + //! Effects: Constructs an empty container using the specified + //! allocator, and inserts elements from the range [first ,last ). + //! + //! Complexity: Linear in N if the range [first ,last ) is already sorted using + //! comp and otherwise N logN, where N is last - first. + template + flat_set(InputIterator first, InputIterator last, const allocator_type& a) + : base_t(true, first, last, Compare(), a) + {} + + //! Effects: Constructs an empty container using the specified comparison object and + //! allocator, and inserts elements from the ordered unique range [first ,last). This function + //! is more efficient than the normal range creation for ordered ranges. + //! + //! Requires: [first ,last) must be ordered according to the predicate and must be + //! unique values. + //! + //! Complexity: Linear in N. + //! + //! Note: Non-standard extension. + template + flat_set(ordered_unique_range_t, InputIterator first, InputIterator last, + const Compare& comp = Compare(), + const allocator_type& a = allocator_type()) + : base_t(ordered_range, first, last, comp, a) + {} + +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + //! Effects: Constructs an empty container using the specified comparison object and + //! allocator, and inserts elements from the range [il.begin(), il.end()). + //! + //! Complexity: Linear in N if the range [il.begin(), il.end()) is already sorted using + //! comp and otherwise N logN, where N is il.begin() - il.end(). + flat_set(std::initializer_list il, const Compare& comp = Compare(), + const allocator_type& a = allocator_type()) + : base_t(true, il.begin(), il.end(), comp, a) + {} + + //! Effects: Constructs an empty container using the specified + //! allocator, and inserts elements from the range [il.begin(), il.end()). + //! + //! Complexity: Linear in N if the range [il.begin(), il.end()) is already sorted using + //! comp and otherwise N logN, where N is il.begin() - il.end(). + flat_set(std::initializer_list il, const allocator_type& a) + : base_t(true, il.begin(), il.end(), Compare(), a) + {} + + //! Effects: Constructs an empty container using the specified comparison object and + //! allocator, and inserts elements from the ordered unique range [il.begin(), il.end()). This function + //! is more efficient than the normal range creation for ordered ranges. + //! + //! Requires: [il.begin(), il.end()) must be ordered according to the predicate and must be + //! unique values. + //! + //! Complexity: Linear in N. + //! + //! Note: Non-standard extension. + flat_set(ordered_unique_range_t, std::initializer_list il, + const Compare& comp = Compare(), const allocator_type& a = allocator_type()) + : base_t(ordered_range, il.begin(), il.end(), comp, a) + {} +#endif + + //! Effects: Copy constructs the container. + //! + //! Complexity: Linear in x.size(). + flat_set(const flat_set& x) + : base_t(static_cast(x)) + {} + + //! Effects: Move constructs thecontainer. Constructs *this using x's resources. + //! + //! Complexity: Constant. + //! + //! Postcondition: x is emptied. + flat_set(BOOST_RV_REF(flat_set) x) + : base_t(BOOST_MOVE_BASE(base_t, x)) + {} + + //! Effects: Copy constructs a container using the specified allocator. + //! + //! Complexity: Linear in x.size(). + flat_set(const flat_set& x, const allocator_type &a) + : base_t(static_cast(x), a) + {} + + //! Effects: Move constructs a container using the specified allocator. + //! Constructs *this using x's resources. + //! + //! Complexity: Constant if a == x.get_allocator(), linear otherwise + flat_set(BOOST_RV_REF(flat_set) x, const allocator_type &a) + : base_t(BOOST_MOVE_BASE(base_t, x), a) + {} + + //! Effects: Makes *this a copy of x. + //! + //! Complexity: Linear in x.size(). + flat_set& operator=(BOOST_COPY_ASSIGN_REF(flat_set) x) + { return static_cast(this->base_t::operator=(static_cast(x))); } + + //! Throws: If allocator_traits_type::propagate_on_container_move_assignment + //! is false and (allocation throws or value_type's move constructor throws) + //! + //! Complexity: Constant if allocator_traits_type:: + //! propagate_on_container_move_assignment is true or + //! this->get>allocator() == x.get_allocator(). Linear otherwise. + flat_set& operator=(BOOST_RV_REF(flat_set) x) + BOOST_NOEXCEPT_IF( allocator_traits_type::is_always_equal::value + && boost::container::container_detail::is_nothrow_move_assignable::value ) + { return static_cast(this->base_t::operator=(BOOST_MOVE_BASE(base_t, x))); } + +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + //! Effects: Copy all elements from il to *this. + //! + //! Complexity: Linear in il.size(). + flat_set& operator=(std::initializer_list il) + { + this->clear(); + this->insert(il.begin(), il.end()); + return *this; + } +#endif + + #ifdef BOOST_CONTAINER_DOXYGEN_INVOKED + //! Effects: Returns a copy of the allocator that + //! was passed to the object's constructor. + //! + //! Complexity: Constant. + allocator_type get_allocator() const BOOST_NOEXCEPT_OR_NOTHROW; + + //! Effects: Returns a reference to the internal allocator. + //! + //! Throws: Nothing + //! + //! Complexity: Constant. + //! + //! Note: Non-standard extension. + stored_allocator_type &get_stored_allocator() BOOST_NOEXCEPT_OR_NOTHROW; + + //! Effects: Returns a reference to the internal allocator. + //! + //! Throws: Nothing + //! + //! Complexity: Constant. + //! + //! Note: Non-standard extension. + const stored_allocator_type &get_stored_allocator() const BOOST_NOEXCEPT_OR_NOTHROW; + + //! Effects: Returns an iterator to the first element contained in the container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + iterator begin() BOOST_NOEXCEPT_OR_NOTHROW; + + //! Effects: Returns a const_iterator to the first element contained in the container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + const_iterator begin() const BOOST_NOEXCEPT_OR_NOTHROW; + + //! Effects: Returns an iterator to the end of the container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + iterator end() BOOST_NOEXCEPT_OR_NOTHROW; + + //! Effects: Returns a const_iterator to the end of the container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + const_iterator end() const BOOST_NOEXCEPT_OR_NOTHROW; + + //! Effects: Returns a reverse_iterator pointing to the beginning + //! of the reversed container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + reverse_iterator rbegin() BOOST_NOEXCEPT_OR_NOTHROW; + + //! Effects: Returns a const_reverse_iterator pointing to the beginning + //! of the reversed container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + const_reverse_iterator rbegin() const BOOST_NOEXCEPT_OR_NOTHROW; + + //! Effects: Returns a reverse_iterator pointing to the end + //! of the reversed container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + reverse_iterator rend() BOOST_NOEXCEPT_OR_NOTHROW; + + //! Effects: Returns a const_reverse_iterator pointing to the end + //! of the reversed container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + const_reverse_iterator rend() const BOOST_NOEXCEPT_OR_NOTHROW; + + //! Effects: Returns a const_iterator to the first element contained in the container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + const_iterator cbegin() const BOOST_NOEXCEPT_OR_NOTHROW; + + //! Effects: Returns a const_iterator to the end of the container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + const_iterator cend() const BOOST_NOEXCEPT_OR_NOTHROW; + + //! Effects: Returns a const_reverse_iterator pointing to the beginning + //! of the reversed container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + const_reverse_iterator crbegin() const BOOST_NOEXCEPT_OR_NOTHROW; + + //! Effects: Returns a const_reverse_iterator pointing to the end + //! of the reversed container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + const_reverse_iterator crend() const BOOST_NOEXCEPT_OR_NOTHROW; + + //! Effects: Returns true if the container contains no elements. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + bool empty() const BOOST_NOEXCEPT_OR_NOTHROW; + + //! Effects: Returns the number of the elements contained in the container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + size_type size() const BOOST_NOEXCEPT_OR_NOTHROW; + + //! Effects: Returns the largest possible size of the container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + size_type max_size() const BOOST_NOEXCEPT_OR_NOTHROW; + + //! Effects: Number of elements for which memory has been allocated. + //! capacity() is always greater than or equal to size(). + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + size_type capacity() const BOOST_NOEXCEPT_OR_NOTHROW; + + //! Effects: If n is less than or equal to capacity(), this call has no + //! effect. Otherwise, it is a request for allocation of additional memory. + //! If the request is successful, then capacity() is greater than or equal to + //! n; otherwise, capacity() is unchanged. In either case, size() is unchanged. + //! + //! Throws: If memory allocation allocation throws or Key's copy constructor throws. + //! + //! Note: If capacity() is less than "cnt", iterators and references to + //! to values might be invalidated. + void reserve(size_type cnt); + + //! Effects: Tries to deallocate the excess of memory created + // with previous allocations. The size of the vector is unchanged + //! + //! Throws: If memory allocation throws, or Key's copy constructor throws. + //! + //! Complexity: Linear to size(). + void shrink_to_fit(); + + #endif // #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + + ////////////////////////////////////////////// + // + // modifiers + // + ////////////////////////////////////////////// + + #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) || defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + + //! Effects: Inserts an object x of type Key constructed with + //! std::forward(args)... if and only if there is no element in the container + //! with key equivalent to the key of x. + //! + //! Returns: The bool component of the returned pair is true if and only + //! if the insertion takes place, and the iterator component of the pair + //! points to the element with key equivalent to the key of x. + //! + //! Complexity: Logarithmic search time plus linear insertion + //! to the elements with bigger keys than x. + //! + //! Note: If an element is inserted it might invalidate elements. + template + std::pair emplace(BOOST_FWD_REF(Args)... args) + { return this->base_t::emplace_unique(boost::forward(args)...); } + + //! Effects: Inserts an object of type Key constructed with + //! std::forward(args)... in the container if and only if there is + //! no element in the container with key equivalent to the key of x. + //! p is a hint pointing to where the insert should start to search. + //! + //! Returns: An iterator pointing to the element with key equivalent + //! to the key of x. + //! + //! Complexity: Logarithmic search time (constant if x is inserted + //! right before p) plus insertion linear to the elements with bigger keys than x. + //! + //! Note: If an element is inserted it might invalidate elements. + template + iterator emplace_hint(const_iterator p, BOOST_FWD_REF(Args)... args) + { return this->base_t::emplace_hint_unique(p, boost::forward(args)...); } + + #else // !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + + #define BOOST_CONTAINER_FLAT_SET_EMPLACE_CODE(N) \ + BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \ + std::pair emplace(BOOST_MOVE_UREF##N)\ + { return this->base_t::emplace_unique(BOOST_MOVE_FWD##N); }\ + \ + BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \ + iterator emplace_hint(const_iterator hint BOOST_MOVE_I##N BOOST_MOVE_UREF##N)\ + { return this->base_t::emplace_hint_unique(hint BOOST_MOVE_I##N BOOST_MOVE_FWD##N); }\ + // + BOOST_MOVE_ITERATE_0TO9(BOOST_CONTAINER_FLAT_SET_EMPLACE_CODE) + #undef BOOST_CONTAINER_FLAT_SET_EMPLACE_CODE + + #endif // !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + + #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + //! Effects: Inserts x if and only if there is no element in the container + //! with key equivalent to the key of x. + //! + //! Returns: The bool component of the returned pair is true if and only + //! if the insertion takes place, and the iterator component of the pair + //! points to the element with key equivalent to the key of x. + //! + //! Complexity: Logarithmic search time plus linear insertion + //! to the elements with bigger keys than x. + //! + //! Note: If an element is inserted it might invalidate elements. + std::pair insert(const value_type &x); + + //! Effects: Inserts a new value_type move constructed from the pair if and + //! only if there is no element in the container with key equivalent to the key of x. + //! + //! Returns: The bool component of the returned pair is true if and only + //! if the insertion takes place, and the iterator component of the pair + //! points to the element with key equivalent to the key of x. + //! + //! Complexity: Logarithmic search time plus linear insertion + //! to the elements with bigger keys than x. + //! + //! Note: If an element is inserted it might invalidate elements. + std::pair insert(value_type &&x); + #else + private: + typedef std::pair insert_return_pair; + public: + BOOST_MOVE_CONVERSION_AWARE_CATCH(insert, value_type, insert_return_pair, this->priv_insert) + #endif + + #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + //! Effects: Inserts a copy of x in the container if and only if there is + //! no element in the container with key equivalent to the key of x. + //! p is a hint pointing to where the insert should start to search. + //! + //! Returns: An iterator pointing to the element with key equivalent + //! to the key of x. + //! + //! Complexity: Logarithmic search time (constant if x is inserted + //! right before p) plus insertion linear to the elements with bigger keys than x. + //! + //! Note: If an element is inserted it might invalidate elements. + iterator insert(const_iterator p, const value_type &x); + + //! Effects: Inserts an element move constructed from x in the container. + //! p is a hint pointing to where the insert should start to search. + //! + //! Returns: An iterator pointing to the element with key equivalent to the key of x. + //! + //! Complexity: Logarithmic search time (constant if x is inserted + //! right before p) plus insertion linear to the elements with bigger keys than x. + //! + //! Note: If an element is inserted it might invalidate elements. + iterator insert(const_iterator p, value_type &&x); + #else + BOOST_MOVE_CONVERSION_AWARE_CATCH_1ARG(insert, value_type, iterator, this->priv_insert, const_iterator, const_iterator) + #endif + + //! Requires: first, last are not iterators into *this. + //! + //! Effects: inserts each element from the range [first,last) if and only + //! if there is no element with key equivalent to the key of that element. + //! + //! Complexity: At most N log(size()+N) (N is the distance from first to last) + //! search time plus N*size() insertion time. + //! + //! Note: If an element is inserted it might invalidate elements. + template + void insert(InputIterator first, InputIterator last) + { this->base_t::insert_unique(first, last); } + + //! Requires: first, last are not iterators into *this and + //! must be ordered according to the predicate and must be + //! unique values. + //! + //! Effects: inserts each element from the range [first,last) .This function + //! is more efficient than the normal range creation for ordered ranges. + //! + //! Complexity: At most N log(size()+N) (N is the distance from first to last) + //! search time plus N*size() insertion time. + //! + //! Note: Non-standard extension. If an element is inserted it might invalidate elements. + template + void insert(ordered_unique_range_t, InputIterator first, InputIterator last) + { this->base_t::insert_unique(ordered_unique_range, first, last); } + +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + //! Effects: inserts each element from the range [il.begin(), il.end()) if and only + //! if there is no element with key equivalent to the key of that element. + //! + //! Complexity: At most N log(size()+N) (N is the distance from il.begin() to il.end()) + //! search time plus N*size() insertion time. + //! + //! Note: If an element is inserted it might invalidate elements. + void insert(std::initializer_list il) + { this->base_t::insert_unique(il.begin(), il.end()); } + + //! Requires: Range [il.begin(), il.end()) must be ordered according to the predicate + //! and must be unique values. + //! + //! Effects: inserts each element from the range [il.begin(), il.end()) .This function + //! is more efficient than the normal range creation for ordered ranges. + //! + //! Complexity: At most N log(size()+N) (N is the distance from il.begin() to il.end()) + //! search time plus N*size() insertion time. + //! + //! Note: Non-standard extension. If an element is inserted it might invalidate elements. + void insert(ordered_unique_range_t, std::initializer_list il) + { this->base_t::insert_unique(ordered_unique_range, il.begin(), il.end()); } +#endif + + #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + + //! Effects: Erases the element pointed to by p. + //! + //! Returns: Returns an iterator pointing to the element immediately + //! following q prior to the element being erased. If no such element exists, + //! returns end(). + //! + //! Complexity: Linear to the elements with keys bigger than p + //! + //! Note: Invalidates elements with keys + //! not less than the erased element. + iterator erase(const_iterator p); + + //! Effects: Erases all elements in the container with key equivalent to x. + //! + //! Returns: Returns the number of erased elements. + //! + //! Complexity: Logarithmic search time plus erasure time + //! linear to the elements with bigger keys. + size_type erase(const key_type& x); + + //! Effects: Erases all the elements in the range [first, last). + //! + //! Returns: Returns last. + //! + //! Complexity: size()*N where N is the distance from first to last. + //! + //! Complexity: Logarithmic search time plus erasure time + //! linear to the elements with bigger keys. + iterator erase(const_iterator first, const_iterator last); + + //! Effects: Swaps the contents of *this and x. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + void swap(flat_set& x) + BOOST_NOEXCEPT_IF( allocator_traits_type::is_always_equal::value + && boost::container::container_detail::is_nothrow_swappable::value ); + + //! Effects: erase(a.begin(),a.end()). + //! + //! Postcondition: size() == 0. + //! + //! Complexity: linear in size(). + void clear() BOOST_NOEXCEPT_OR_NOTHROW; + + //! Effects: Returns the comparison object out + //! of which a was constructed. + //! + //! Complexity: Constant. + key_compare key_comp() const; + + //! Effects: Returns an object of value_compare constructed out + //! of the comparison object. + //! + //! Complexity: Constant. + value_compare value_comp() const; + + //! Returns: An iterator pointing to an element with the key + //! equivalent to x, or end() if such an element is not found. + //! + //! Complexity: Logarithmic. + iterator find(const key_type& x); + + //! Returns: A const_iterator pointing to an element with the key + //! equivalent to x, or end() if such an element is not found. + //! + //! Complexity: Logarithmic. + const_iterator find(const key_type& x) const; + + //! Requires: size() >= n. + //! + //! Effects: Returns an iterator to the nth element + //! from the beginning of the container. Returns end() + //! if n == size(). + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + //! + //! Note: Non-standard extension + iterator nth(size_type n) BOOST_NOEXCEPT_OR_NOTHROW; + + //! Requires: size() >= n. + //! + //! Effects: Returns a const_iterator to the nth element + //! from the beginning of the container. Returns end() + //! if n == size(). + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + //! + //! Note: Non-standard extension + const_iterator nth(size_type n) const BOOST_NOEXCEPT_OR_NOTHROW; + + //! Requires: size() >= n. + //! + //! Effects: Returns an iterator to the nth element + //! from the beginning of the container. Returns end() + //! if n == size(). + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + //! + //! Note: Non-standard extension + size_type index_of(iterator p) BOOST_NOEXCEPT_OR_NOTHROW; + + //! Requires: begin() <= p <= end(). + //! + //! Effects: Returns the index of the element pointed by p + //! and size() if p == end(). + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + //! + //! Note: Non-standard extension + size_type index_of(const_iterator p) const BOOST_NOEXCEPT_OR_NOTHROW; + + #endif // #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + + //! Returns: The number of elements with key equivalent to x. + //! + //! Complexity: log(size())+count(k) + size_type count(const key_type& x) const + { return static_cast(this->base_t::find(x) != this->base_t::cend()); } + + #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + //! Returns: An iterator pointing to the first element with key not less + //! than k, or a.end() if such an element is not found. + //! + //! Complexity: Logarithmic + iterator lower_bound(const key_type& x); + + //! Returns: A const iterator pointing to the first element with key not + //! less than k, or a.end() if such an element is not found. + //! + //! Complexity: Logarithmic + const_iterator lower_bound(const key_type& x) const; + + //! Returns: An iterator pointing to the first element with key not less + //! than x, or end() if such an element is not found. + //! + //! Complexity: Logarithmic + iterator upper_bound(const key_type& x); + + //! Returns: A const iterator pointing to the first element with key not + //! less than x, or end() if such an element is not found. + //! + //! Complexity: Logarithmic + const_iterator upper_bound(const key_type& x) const; + + #endif // #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + + //! Effects: Equivalent to std::make_pair(this->lower_bound(k), this->upper_bound(k)). + //! + //! Complexity: Logarithmic + std::pair equal_range(const key_type& x) const + { return this->base_t::lower_bound_range(x); } + + //! Effects: Equivalent to std::make_pair(this->lower_bound(k), this->upper_bound(k)). + //! + //! Complexity: Logarithmic + std::pair equal_range(const key_type& x) + { return this->base_t::lower_bound_range(x); } + + #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + + //! Effects: Returns true if x and y are equal + //! + //! Complexity: Linear to the number of elements in the container. + friend bool operator==(const flat_set& x, const flat_set& y); + + //! Effects: Returns true if x and y are unequal + //! + //! Complexity: Linear to the number of elements in the container. + friend bool operator!=(const flat_set& x, const flat_set& y); + + //! Effects: Returns true if x is less than y + //! + //! Complexity: Linear to the number of elements in the container. + friend bool operator<(const flat_set& x, const flat_set& y); + + //! Effects: Returns true if x is greater than y + //! + //! Complexity: Linear to the number of elements in the container. + friend bool operator>(const flat_set& x, const flat_set& y); + + //! Effects: Returns true if x is equal or less than y + //! + //! Complexity: Linear to the number of elements in the container. + friend bool operator<=(const flat_set& x, const flat_set& y); + + //! Effects: Returns true if x is equal or greater than y + //! + //! Complexity: Linear to the number of elements in the container. + friend bool operator>=(const flat_set& x, const flat_set& y); + + //! Effects: x.swap(y) + //! + //! Complexity: Constant. + friend void swap(flat_set& x, flat_set& y); + + #endif //#ifdef BOOST_CONTAINER_DOXYGEN_INVOKED + + #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + private: + template + std::pair priv_insert(BOOST_FWD_REF(KeyType) x) + { return this->base_t::insert_unique(::boost::forward(x)); } + + template + iterator priv_insert(const_iterator p, BOOST_FWD_REF(KeyType) x) + { return this->base_t::insert_unique(p, ::boost::forward(x)); } + #endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED +}; + +#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + +} //namespace container { + +//!has_trivial_destructor_after_move<> == true_type +//!specialization for optimizations +template +struct has_trivial_destructor_after_move > +{ + typedef typename ::boost::container::allocator_traits::pointer pointer; + static const bool value = ::boost::has_trivial_destructor_after_move::value && + ::boost::has_trivial_destructor_after_move::value && + ::boost::has_trivial_destructor_after_move::value; +}; + +namespace container { + +#endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + +//! flat_multiset is a Sorted Associative Container that stores objects of type Key. +//! +//! flat_multiset can store multiple copies of the same key value. +//! +//! flat_multiset is similar to std::multiset but it's implemented like an ordered vector. +//! This means that inserting a new element into a flat_multiset invalidates +//! previous iterators and references +//! +//! Erasing an element invalidates iterators and references +//! pointing to elements that come after (their keys are bigger) the erased element. +//! +//! This container provides random-access iterators. +//! +//! \tparam Key is the type to be inserted in the multiset, which is also the key_type +//! \tparam Compare is the comparison functor used to order keys +//! \tparam Allocator is the allocator to be used to allocate memory for this container +#ifdef BOOST_CONTAINER_DOXYGEN_INVOKED +template , class Allocator = new_allocator > +#else +template +#endif +class flat_multiset + ///@cond + : public container_detail::flat_tree, Compare, Allocator> + ///@endcond +{ + #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + private: + BOOST_COPYABLE_AND_MOVABLE(flat_multiset) + typedef container_detail::flat_tree, Compare, Allocator> base_t; + #endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + + public: + ////////////////////////////////////////////// + // + // types + // + ////////////////////////////////////////////// + typedef Key key_type; + typedef Key value_type; + typedef Compare key_compare; + typedef Compare value_compare; + typedef ::boost::container::allocator_traits allocator_traits_type; + typedef typename ::boost::container::allocator_traits::pointer pointer; + typedef typename ::boost::container::allocator_traits::const_pointer const_pointer; + typedef typename ::boost::container::allocator_traits::reference reference; + typedef typename ::boost::container::allocator_traits::const_reference const_reference; + typedef typename ::boost::container::allocator_traits::size_type size_type; + typedef typename ::boost::container::allocator_traits::difference_type difference_type; + typedef Allocator allocator_type; + typedef typename BOOST_CONTAINER_IMPDEF(base_t::stored_allocator_type) stored_allocator_type; + typedef typename BOOST_CONTAINER_IMPDEF(base_t::iterator) iterator; + typedef typename BOOST_CONTAINER_IMPDEF(base_t::const_iterator) const_iterator; + typedef typename BOOST_CONTAINER_IMPDEF(base_t::reverse_iterator) reverse_iterator; + typedef typename BOOST_CONTAINER_IMPDEF(base_t::const_reverse_iterator) const_reverse_iterator; + + //! @copydoc ::boost::container::flat_set::flat_set() + explicit flat_multiset() + : base_t() + {} + + //! @copydoc ::boost::container::flat_set::flat_set(const Compare&, const allocator_type&) + explicit flat_multiset(const Compare& comp, + const allocator_type& a = allocator_type()) + : base_t(comp, a) + {} + + //! @copydoc ::boost::container::flat_set::flat_set(const allocator_type&) + explicit flat_multiset(const allocator_type& a) + : base_t(a) + {} + + //! @copydoc ::boost::container::flat_set::flat_set(InputIterator, InputIterator, const Compare& comp, const allocator_type&) + template + flat_multiset(InputIterator first, InputIterator last, + const Compare& comp = Compare(), + const allocator_type& a = allocator_type()) + : base_t(false, first, last, comp, a) + {} + + //! @copydoc ::boost::container::flat_set::flat_set(InputIterator, InputIterator, const allocator_type&) + template + flat_multiset(InputIterator first, InputIterator last, const allocator_type& a) + : base_t(false, first, last, Compare(), a) + {} + + //! Effects: Constructs an empty flat_multiset using the specified comparison object and + //! allocator, and inserts elements from the ordered range [first ,last ). This function + //! is more efficient than the normal range creation for ordered ranges. + //! + //! Requires: [first ,last) must be ordered according to the predicate. + //! + //! Complexity: Linear in N. + //! + //! Note: Non-standard extension. + template + flat_multiset(ordered_range_t, InputIterator first, InputIterator last, + const Compare& comp = Compare(), + const allocator_type& a = allocator_type()) + : base_t(ordered_range, first, last, comp, a) + {} + +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + //! @copydoc ::boost::container::flat_set::flat_set(std::initializer_list, const Compare& comp, const allocator_type&) + flat_multiset(std::initializer_list il, const Compare& comp = Compare(), + const allocator_type& a = allocator_type()) + : base_t(false, il.begin(), il.end(), comp, a) + {} + + //! @copydoc ::boost::container::flat_set::flat_set(std::initializer_list, const allocator_type&) + flat_multiset(std::initializer_list il, const allocator_type& a) + : base_t(false, il.begin(), il.end(), Compare(), a) + {} + + //! @copydoc ::boost::container::flat_set::flat_set(ordered_unique_range_t, std::initializer_list, const Compare& comp, const allocator_type&) + flat_multiset(ordered_unique_range_t, std::initializer_list il, + const Compare& comp = Compare(), const allocator_type& a = allocator_type()) + : base_t(ordered_range, il.begin(), il.end(), comp, a) + {} +#endif + + //! @copydoc ::boost::container::flat_set::flat_set(const flat_set &) + flat_multiset(const flat_multiset& x) + : base_t(static_cast(x)) + {} + + //! @copydoc ::boost::container::flat_set::flat_set(flat_set &&) + flat_multiset(BOOST_RV_REF(flat_multiset) x) + : base_t(boost::move(static_cast(x))) + {} + + //! @copydoc ::boost::container::flat_set::flat_set(const flat_set &, const allocator_type &) + flat_multiset(const flat_multiset& x, const allocator_type &a) + : base_t(static_cast(x), a) + {} + + //! @copydoc ::boost::container::flat_set::flat_set(flat_set &&, const allocator_type &) + flat_multiset(BOOST_RV_REF(flat_multiset) x, const allocator_type &a) + : base_t(BOOST_MOVE_BASE(base_t, x), a) + {} + + //! @copydoc ::boost::container::flat_set::operator=(const flat_set &) + flat_multiset& operator=(BOOST_COPY_ASSIGN_REF(flat_multiset) x) + { return static_cast(this->base_t::operator=(static_cast(x))); } + + //! @copydoc ::boost::container::flat_set::operator=(flat_set &&) + flat_multiset& operator=(BOOST_RV_REF(flat_multiset) x) + BOOST_NOEXCEPT_IF( allocator_traits_type::is_always_equal::value + && boost::container::container_detail::is_nothrow_move_assignable::value ) + { return static_cast(this->base_t::operator=(BOOST_MOVE_BASE(base_t, x))); } + +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + //! @copydoc ::boost::container::flat_set::operator=(std::initializer_list) + flat_multiset& operator=(std::initializer_list il) + { + this->clear(); + this->insert(il.begin(), il.end()); + return *this; + } +#endif + + #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + + //! @copydoc ::boost::container::flat_set::get_allocator() + allocator_type get_allocator() const BOOST_NOEXCEPT_OR_NOTHROW; + + //! @copydoc ::boost::container::flat_set::get_stored_allocator() + stored_allocator_type &get_stored_allocator() BOOST_NOEXCEPT_OR_NOTHROW; + + //! @copydoc ::boost::container::flat_set::get_stored_allocator() const + const stored_allocator_type &get_stored_allocator() const BOOST_NOEXCEPT_OR_NOTHROW; + + //! @copydoc ::boost::container::flat_set::begin() + iterator begin() BOOST_NOEXCEPT_OR_NOTHROW; + + //! @copydoc ::boost::container::flat_set::begin() const + const_iterator begin() const; + + //! @copydoc ::boost::container::flat_set::cbegin() const + const_iterator cbegin() const BOOST_NOEXCEPT_OR_NOTHROW; + + //! @copydoc ::boost::container::flat_set::end() + iterator end() BOOST_NOEXCEPT_OR_NOTHROW; + + //! @copydoc ::boost::container::flat_set::end() const + const_iterator end() const BOOST_NOEXCEPT_OR_NOTHROW; + + //! @copydoc ::boost::container::flat_set::cend() const + const_iterator cend() const BOOST_NOEXCEPT_OR_NOTHROW; + + //! @copydoc ::boost::container::flat_set::rbegin() + reverse_iterator rbegin() BOOST_NOEXCEPT_OR_NOTHROW; + + //! @copydoc ::boost::container::flat_set::rbegin() const + const_reverse_iterator rbegin() const BOOST_NOEXCEPT_OR_NOTHROW; + + //! @copydoc ::boost::container::flat_set::crbegin() const + const_reverse_iterator crbegin() const BOOST_NOEXCEPT_OR_NOTHROW; + + //! @copydoc ::boost::container::flat_set::rend() + reverse_iterator rend() BOOST_NOEXCEPT_OR_NOTHROW; + + //! @copydoc ::boost::container::flat_set::rend() const + const_reverse_iterator rend() const BOOST_NOEXCEPT_OR_NOTHROW; + + //! @copydoc ::boost::container::flat_set::crend() const + const_reverse_iterator crend() const BOOST_NOEXCEPT_OR_NOTHROW; + + //! @copydoc ::boost::container::flat_set::empty() const + bool empty() const BOOST_NOEXCEPT_OR_NOTHROW; + + //! @copydoc ::boost::container::flat_set::size() const + size_type size() const BOOST_NOEXCEPT_OR_NOTHROW; + + //! @copydoc ::boost::container::flat_set::max_size() const + size_type max_size() const BOOST_NOEXCEPT_OR_NOTHROW; + + //! @copydoc ::boost::container::flat_set::capacity() const + size_type capacity() const BOOST_NOEXCEPT_OR_NOTHROW; + + //! @copydoc ::boost::container::flat_set::reserve(size_type) + void reserve(size_type cnt); + + //! @copydoc ::boost::container::flat_set::shrink_to_fit() + void shrink_to_fit(); + + #endif // #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + + ////////////////////////////////////////////// + // + // modifiers + // + ////////////////////////////////////////////// + + #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) || defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + + //! Effects: Inserts an object of type Key constructed with + //! std::forward(args)... and returns the iterator pointing to the + //! newly inserted element. + //! + //! Complexity: Logarithmic search time plus linear insertion + //! to the elements with bigger keys than x. + //! + //! Note: If an element is inserted it might invalidate elements. + template + iterator emplace(BOOST_FWD_REF(Args)... args) + { return this->base_t::emplace_equal(boost::forward(args)...); } + + //! Effects: Inserts an object of type Key constructed with + //! std::forward(args)... in the container. + //! p is a hint pointing to where the insert should start to search. + //! + //! Returns: An iterator pointing to the element with key equivalent + //! to the key of x. + //! + //! Complexity: Logarithmic search time (constant if x is inserted + //! right before p) plus insertion linear to the elements with bigger keys than x. + //! + //! Note: If an element is inserted it might invalidate elements. + template + iterator emplace_hint(const_iterator p, BOOST_FWD_REF(Args)... args) + { return this->base_t::emplace_hint_equal(p, boost::forward(args)...); } + + #else // !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + + #define BOOST_CONTAINER_FLAT_MULTISET_EMPLACE_CODE(N) \ + BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \ + iterator emplace(BOOST_MOVE_UREF##N)\ + { return this->base_t::emplace_equal(BOOST_MOVE_FWD##N); }\ + \ + BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \ + iterator emplace_hint(const_iterator hint BOOST_MOVE_I##N BOOST_MOVE_UREF##N)\ + { return this->base_t::emplace_hint_equal(hint BOOST_MOVE_I##N BOOST_MOVE_FWD##N); }\ + // + BOOST_MOVE_ITERATE_0TO9(BOOST_CONTAINER_FLAT_MULTISET_EMPLACE_CODE) + #undef BOOST_CONTAINER_FLAT_MULTISET_EMPLACE_CODE + + #endif // !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + + #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + //! Effects: Inserts x and returns the iterator pointing to the + //! newly inserted element. + //! + //! Complexity: Logarithmic search time plus linear insertion + //! to the elements with bigger keys than x. + //! + //! Note: If an element is inserted it might invalidate elements. + iterator insert(const value_type &x); + + //! Effects: Inserts a new value_type move constructed from x + //! and returns the iterator pointing to the newly inserted element. + //! + //! Complexity: Logarithmic search time plus linear insertion + //! to the elements with bigger keys than x. + //! + //! Note: If an element is inserted it might invalidate elements. + iterator insert(value_type &&x); + #else + BOOST_MOVE_CONVERSION_AWARE_CATCH(insert, value_type, iterator, this->priv_insert) + #endif + + #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + //! Effects: Inserts a copy of x in the container. + //! p is a hint pointing to where the insert should start to search. + //! + //! Returns: An iterator pointing to the element with key equivalent + //! to the key of x. + //! + //! Complexity: Logarithmic search time (constant if x is inserted + //! right before p) plus insertion linear to the elements with bigger keys than x. + //! + //! Note: If an element is inserted it might invalidate elements. + iterator insert(const_iterator p, const value_type &x); + + //! Effects: Inserts a new value move constructed from x in the container. + //! p is a hint pointing to where the insert should start to search. + //! + //! Returns: An iterator pointing to the element with key equivalent + //! to the key of x. + //! + //! Complexity: Logarithmic search time (constant if x is inserted + //! right before p) plus insertion linear to the elements with bigger keys than x. + //! + //! Note: If an element is inserted it might invalidate elements. + iterator insert(const_iterator p, value_type &&x); + #else + BOOST_MOVE_CONVERSION_AWARE_CATCH_1ARG(insert, value_type, iterator, this->priv_insert, const_iterator, const_iterator) + #endif + + //! Requires: first, last are not iterators into *this. + //! + //! Effects: inserts each element from the range [first,last) . + //! + //! Complexity: At most N log(size()+N) (N is the distance from first to last) + //! search time plus N*size() insertion time. + //! + //! Note: If an element is inserted it might invalidate elements. + template + void insert(InputIterator first, InputIterator last) + { this->base_t::insert_equal(first, last); } + + //! Requires: first, last are not iterators into *this and + //! must be ordered according to the predicate. + //! + //! Effects: inserts each element from the range [first,last) .This function + //! is more efficient than the normal range creation for ordered ranges. + //! + //! Complexity: At most N log(size()+N) (N is the distance from first to last) + //! search time plus N*size() insertion time. + //! + //! Note: Non-standard extension. If an element is inserted it might invalidate elements. + template + void insert(ordered_range_t, InputIterator first, InputIterator last) + { this->base_t::insert_equal(ordered_range, first, last); } + +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + //! Effects: inserts each element from the range [il.begin(), il.end()). + //! + //! Complexity: At most N log(size()+N) (N is the distance from first to last) + //! search time plus N*size() insertion time. + //! + //! Note: If an element is inserted it might invalidate elements. + void insert(std::initializer_list il) + { this->base_t::insert_equal(il.begin(), il.end()); } + + //! Requires: Range [il.begin(), il.end()) must be ordered according to the predicate. + //! + //! Effects: inserts each element from the range [il.begin(), il.end()). This function + //! is more efficient than the normal range creation for ordered ranges. + //! + //! Complexity: At most N log(size()+N) (N is the distance from il.begin() to il.end()) + //! search time plus N*size() insertion time. + //! + //! Note: Non-standard extension. If an element is inserted it might invalidate elements. + void insert(ordered_range_t, std::initializer_list il) + { this->base_t::insert_equal(ordered_range, il.begin(), il.end()); } +#endif + + #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + + //! @copydoc ::boost::container::flat_set::erase(const_iterator) + iterator erase(const_iterator p); + + //! @copydoc ::boost::container::flat_set::erase(const key_type&) + size_type erase(const key_type& x); + + //! @copydoc ::boost::container::flat_set::erase(const_iterator,const_iterator) + iterator erase(const_iterator first, const_iterator last); + + //! @copydoc ::boost::container::flat_set::swap + void swap(flat_multiset& x) + BOOST_NOEXCEPT_IF( allocator_traits_type::is_always_equal::value + && boost::container::container_detail::is_nothrow_swappable::value ); + + //! @copydoc ::boost::container::flat_set::clear + void clear() BOOST_NOEXCEPT_OR_NOTHROW; + + //! @copydoc ::boost::container::flat_set::key_comp + key_compare key_comp() const; + + //! @copydoc ::boost::container::flat_set::value_comp + value_compare value_comp() const; + + //! @copydoc ::boost::container::flat_set::find(const key_type& ) + iterator find(const key_type& x); + + //! @copydoc ::boost::container::flat_set::find(const key_type& ) const + const_iterator find(const key_type& x) const; + + //! @copydoc ::boost::container::flat_set::nth(size_type) + iterator nth(size_type n) BOOST_NOEXCEPT_OR_NOTHROW; + + //! @copydoc ::boost::container::flat_set::nth(size_type) const + const_iterator nth(size_type n) const BOOST_NOEXCEPT_OR_NOTHROW; + + //! @copydoc ::boost::container::flat_set::index_of(iterator) + size_type index_of(iterator p) BOOST_NOEXCEPT_OR_NOTHROW; + + //! @copydoc ::boost::container::flat_set::index_of(const_iterator) const + size_type index_of(const_iterator p) const BOOST_NOEXCEPT_OR_NOTHROW; + + //! @copydoc ::boost::container::flat_set::count(const key_type& ) const + size_type count(const key_type& x) const; + + //! @copydoc ::boost::container::flat_set::lower_bound(const key_type& ) + iterator lower_bound(const key_type& x); + + //! @copydoc ::boost::container::flat_set::lower_bound(const key_type& ) const + const_iterator lower_bound(const key_type& x) const; + + //! @copydoc ::boost::container::flat_set::upper_bound(const key_type& ) + iterator upper_bound(const key_type& x); + + //! @copydoc ::boost::container::flat_set::upper_bound(const key_type& ) const + const_iterator upper_bound(const key_type& x) const; + + //! @copydoc ::boost::container::flat_set::equal_range(const key_type& ) const + std::pair equal_range(const key_type& x) const; + + //! @copydoc ::boost::container::flat_set::equal_range(const key_type& ) + std::pair equal_range(const key_type& x); + + //! Effects: Returns true if x and y are equal + //! + //! Complexity: Linear to the number of elements in the container. + friend bool operator==(const flat_multiset& x, const flat_multiset& y); + + //! Effects: Returns true if x and y are unequal + //! + //! Complexity: Linear to the number of elements in the container. + friend bool operator!=(const flat_multiset& x, const flat_multiset& y); + + //! Effects: Returns true if x is less than y + //! + //! Complexity: Linear to the number of elements in the container. + friend bool operator<(const flat_multiset& x, const flat_multiset& y); + + //! Effects: Returns true if x is greater than y + //! + //! Complexity: Linear to the number of elements in the container. + friend bool operator>(const flat_multiset& x, const flat_multiset& y); + + //! Effects: Returns true if x is equal or less than y + //! + //! Complexity: Linear to the number of elements in the container. + friend bool operator<=(const flat_multiset& x, const flat_multiset& y); + + //! Effects: Returns true if x is equal or greater than y + //! + //! Complexity: Linear to the number of elements in the container. + friend bool operator>=(const flat_multiset& x, const flat_multiset& y); + + //! Effects: x.swap(y) + //! + //! Complexity: Constant. + friend void swap(flat_multiset& x, flat_multiset& y); + + #endif //#ifdef BOOST_CONTAINER_DOXYGEN_INVOKED + + #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + private: + template + iterator priv_insert(BOOST_FWD_REF(KeyType) x) + { return this->base_t::insert_equal(::boost::forward(x)); } + + template + iterator priv_insert(const_iterator p, BOOST_FWD_REF(KeyType) x) + { return this->base_t::insert_equal(p, ::boost::forward(x)); } + #endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED +}; + +#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + +} //namespace container { + +//!has_trivial_destructor_after_move<> == true_type +//!specialization for optimizations +template +struct has_trivial_destructor_after_move > +{ + typedef typename ::boost::container::allocator_traits::pointer pointer; + static const bool value = ::boost::has_trivial_destructor_after_move::value && + ::boost::has_trivial_destructor_after_move::value && + ::boost::has_trivial_destructor_after_move::value; +}; + +namespace container { + +#endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + +}} + +#include + +#endif // BOOST_CONTAINER_FLAT_SET_HPP Index: libraries/win32/boost/include/boost/intrusive/detail/minimal_less_equal_header.hpp =================================================================== --- /dev/null +++ libraries/win32/boost/include/boost/intrusive/detail/minimal_less_equal_header.hpp @@ -0,0 +1,30 @@ +///////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2014-2015 +// +// 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) +// +// See http://www.boost.org/libs/intrusive for documentation. +// +///////////////////////////////////////////////////////////////////////////// +#ifndef BOOST_INTRUSIVE_DETAIL_MINIMAL_LESS_EQUAL_HEADER_HPP +#define BOOST_INTRUSIVE_DETAIL_MINIMAL_LESS_EQUAL_HEADER_HPP +# +#ifndef BOOST_CONFIG_HPP +# include +#endif +# +#if defined(BOOST_HAS_PRAGMA_ONCE) +# pragma once +#endif +# +#//Try to avoid including , as it's quite big in C++11 +#if defined(BOOST_GNU_STDLIB) +# include +#else +# include //Fallback +#endif +# +#endif //BOOST_INTRUSIVE_DETAIL_MINIMAL_LESS_EQUAL_HEADER_HPP Index: source/simulation2/components/CCmpPathfinder.cpp =================================================================== --- source/simulation2/components/CCmpPathfinder.cpp +++ source/simulation2/components/CCmpPathfinder.cpp @@ -660,6 +660,35 @@ } } +bool CCmpPathfinder::MakeGoalReachable(entity_pos_t x0, entity_pos_t z0, PathGoal &goal, pass_class_t passClass) +{ + u16 i0, j0; + Pathfinding::NearestNavcell(x0, z0, i0, j0, m_MapSize*Pathfinding::NAVCELLS_PER_TILE, m_MapSize*Pathfinding::NAVCELLS_PER_TILE); + if (!IS_PASSABLE(m_Grid->get(i0, j0), passClass)) + FindNearestPassableNavcell(i0, j0, passClass); + + return m_LongPathfinder.MakeGoalReachable(i0, j0, goal, passClass); +} + +u32 CCmpPathfinder::FindNearestPassableNavcell(entity_pos_t x, entity_pos_t z, u16& outI, u16& outJ, pass_class_t passClass) +{ + Pathfinding::NearestNavcell(x, z, outI, outJ, m_MapSize*Pathfinding::NAVCELLS_PER_TILE, m_MapSize*Pathfinding::NAVCELLS_PER_TILE); + u16 i0 = outI; + u16 j0 = outJ; + FindNearestPassableNavcell(outI, outJ, passClass); + return abs(i0 - outI) + abs(j0 - outJ); +} + +void CCmpPathfinder::FindNearestPassableNavcell(u16& i, u16& j, pass_class_t passClass) +{ + m_LongPathfinder.FindNearestPassableNavcell(i, j, passClass); +} + +bool CCmpPathfinder::NavcellIsReachable(u16 i0, u16 j0, u16 i1, u16 j1, pass_class_t passClass) +{ + return m_LongPathfinder.NavcellIsReachable(i0, j0, i1, j1, passClass); +} + ////////////////////////////////////////////////////////// // Async path requests: Index: source/simulation2/components/CCmpPathfinder_Common.h =================================================================== --- source/simulation2/components/CCmpPathfinder_Common.h +++ source/simulation2/components/CCmpPathfinder_Common.h @@ -245,6 +245,13 @@ virtual Grid ComputeShoreGrid(bool expandOnWater = false); + virtual bool MakeGoalReachable(entity_pos_t x0, entity_pos_t z0, PathGoal &goal, pass_class_t passClass); + + virtual u32 FindNearestPassableNavcell(entity_pos_t x, entity_pos_t z, u16& outI, u16& outJ, pass_class_t passClass); + void FindNearestPassableNavcell(u16& i, u16& j, pass_class_t passClass); + + virtual bool NavcellIsReachable(u16 i0, u16 j0, u16 i1, u16 j1, pass_class_t passClass); + virtual void ComputePath(entity_pos_t x0, entity_pos_t z0, const PathGoal& goal, pass_class_t passClass, WaypointPath& ret) { m_LongPathfinder.ComputePath(x0, z0, goal, passClass, ret); Index: source/simulation2/components/ICmpPathfinder.h =================================================================== --- source/simulation2/components/ICmpPathfinder.h +++ source/simulation2/components/ICmpPathfinder.h @@ -88,6 +88,24 @@ */ virtual Grid ComputeShoreGrid(bool expandOnWater = false) = 0; + /** + * Transform an arbitrary PathGoal into a reachable Point PathGoal, see Hierarchical Pathfinder for details + * Return true if the goal was reachable originally, false otherwise. + */ + virtual bool MakeGoalReachable(entity_pos_t x0, entity_pos_t z0, PathGoal &goal, pass_class_t passClass) = 0; + + /** + * Gives the closest passable navcell from the given position. + * Returns how many navcells away (manhattan) that navcell is. + */ + virtual u32 FindNearestPassableNavcell(entity_pos_t x, entity_pos_t z, u16& outI, u16& outJ, pass_class_t passClass) = 0; + + /** + * Returns true if navcell (i0, j0) has the same global region ID as navcell (i1, j1). + * i.e. you can reach one from the other. + */ + virtual bool NavcellIsReachable(u16 i0, u16 j0, u16 i1, u16 j1, pass_class_t passClass) = 0; + /** * Compute a tile-based path from the given point to the goal, and return the set of waypoints. * The waypoints correspond to the centers of horizontally/vertically adjacent tiles Index: source/simulation2/components/tests/test_Pathfinder.h =================================================================== --- source/simulation2/components/tests/test_Pathfinder.h +++ source/simulation2/components/tests/test_Pathfinder.h @@ -17,8 +17,11 @@ #include "simulation2/system/ComponentTest.h" +#define TEST + #include "simulation2/components/ICmpObstructionManager.h" #include "simulation2/components/ICmpPathfinder.h" +#include "simulation2/components/CCmpPathfinder_Common.h" #include "graphics/MapReader.h" #include "graphics/Terrain.h" @@ -64,6 +67,128 @@ TS_ASSERT_EQUALS((Pathfinding::NAVCELL_SIZE >> 1).ToInt_RoundToZero(), Pathfinding::NAVCELL_SIZE_LOG2); } + void hierarchical_globalRegions_testmap(std::wstring map) + { + CTerrain terrain; + + CSimulation2 sim2(NULL, g_ScriptRuntime, &terrain); + sim2.LoadDefaultScripts(); + sim2.ResetState(); + + CMapReader* mapReader = new CMapReader(); // it'll call "delete this" itself + + LDR_BeginRegistering(); + mapReader->LoadMap(map, + sim2.GetScriptInterface().GetJSRuntime(), JS::UndefinedHandleValue, + &terrain, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + &sim2, &sim2.GetSimContext(), -1, false); + LDR_EndRegistering(); + TS_ASSERT_OK(LDR_NonprogressiveLoad()); + + sim2.Update(0); + + CmpPtr cmpPathfinder(sim2, SYSTEM_ENTITY); + + pass_class_t obstructionsMask = cmpPathfinder->GetPassabilityClass("default"); + HierarchicalPathfinder& hier = ((CCmpPathfinder*)cmpPathfinder.operator->())->m_LongPathfinder.GetHierarchicalPathfinder(); + + std::map globalRegions = hier.m_GlobalRegions[obstructionsMask]; + + for (u8 cj = 0; cj < hier.m_ChunksH; cj += 2) + for (u8 ci = 0; ci < hier.m_ChunksW; ci += 2) + for(u16 i : hier.GetChunk(ci, cj, obstructionsMask).m_RegionsID) + { + // Assert that all reachable regions are indeed the same region ID + // This does not highlight that unreachable regions might (wrongly) have the same ID). + std::set reachables; + hier.FindReachableRegions(HierarchicalPathfinder::RegionID{ci, cj, i}, reachables, obstructionsMask); + HierarchicalPathfinder::GlobalRegionID ID = globalRegions[HierarchicalPathfinder::RegionID{ci, cj, i}]; + for (HierarchicalPathfinder::RegionID region : reachables) + TS_ASSERT_EQUALS(ID, globalRegions[region]); + } + } + + void test_hierarchical_globalRegions() + { + // This test validates that the hierarchical's pathfinder global regions are in accordance with its regions + // IE it asserts that, for any two regions A and B of the hierarchical pathfinder, if one can find a path from A to B + // then A and B have the same global region. + std::vector maps = { L"maps/scenarios/Peloponnese.pmp", L"maps/skirmishes/Corinthian Isthmus (2).pmp", L"maps/skirmishes/Greek Acropolis (2).pmp" }; + +// disable in debug mode, creating the simulation and running the initial turn is too slow and tends to OOM in debug mode. +#ifndef DEBUG + for (std::wstring t : maps) + hierarchical_globalRegions_testmap(t); +#endif + } + + void hierarchical_update_testmap(std::wstring map) + { + CTerrain terrain; + + CSimulation2 sim2(NULL, g_ScriptRuntime, &terrain); + sim2.LoadDefaultScripts(); + sim2.ResetState(); + + CMapReader* mapReader = new CMapReader(); // it'll call "delete this" itself + + LDR_BeginRegistering(); + mapReader->LoadMap(map, + sim2.GetScriptInterface().GetJSRuntime(), JS::UndefinedHandleValue, + &terrain, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + &sim2, &sim2.GetSimContext(), -1, false); + LDR_EndRegistering(); + TS_ASSERT_OK(LDR_NonprogressiveLoad()); + + sim2.Update(0); + + CmpPtr cmpPathfinder(sim2, SYSTEM_ENTITY); + + pass_class_t obstructionsMask = cmpPathfinder->GetPassabilityClass("default"); + HierarchicalPathfinder& hier = ((CCmpPathfinder*)cmpPathfinder.operator->())->m_LongPathfinder.GetHierarchicalPathfinder(); + + // make copies + const auto pristine_GR = hier.m_GlobalRegions; + const auto pristine_Chunks = hier.m_Chunks; + const HierarchicalPathfinder::EdgesMap pristine_Edges = hier.m_Edges.at(obstructionsMask); + + Grid* pathfinderGrid = ((CCmpPathfinder*)cmpPathfinder.operator->())->m_LongPathfinder.m_Grid; + + Grid dirtyGrid(hier.m_ChunksW * HierarchicalPathfinder::CHUNK_SIZE,hier.m_ChunksH * HierarchicalPathfinder::CHUNK_SIZE); + srand(1234); + + size_t tries = 20; + for (size_t i = 0; i < tries; ++i) + { + // Dirty a random one + dirtyGrid.reset(); + u8 ci = rand() % (hier.m_ChunksW-10) + 8; + u8 cj = rand() % (hier.m_ChunksH-10) + 8; + dirtyGrid.set(ci * HierarchicalPathfinder::CHUNK_SIZE + 4, cj * HierarchicalPathfinder::CHUNK_SIZE + 4, 1); + + hier.Update(pathfinderGrid, dirtyGrid); + + // Formally speaking we should rather validate that regions exist with the same pixels, but so far + // re-initing regions will keep the same IDs for the same pixels so this is OK. + TS_ASSERT_EQUALS(hier.m_Chunks.at(obstructionsMask), pristine_Chunks.at(obstructionsMask)); + // same here + TS_ASSERT_EQUALS(pristine_Edges, hier.m_Edges.at(obstructionsMask)); + } + } + + void test_hierarchical_update() + { + // This test validates that the "Update" function of the hierarchical pathfinder + // ends up in a correct state (by comparing it with the clean, "Recompute"-d state). + std::vector maps = { L"maps/scenarios/Peloponnese.pmp", L"maps/skirmishes/Corinthian Isthmus (2).pmp", L"maps/skirmishes/Greek Acropolis (2).pmp" }; + +// disable in debug mode, creating the simulation and running the initial turn is too slow and tends to OOM in debug mode. +#ifndef DEBUG + for (std::wstring t : maps) + hierarchical_update_testmap(t); +#endif + } + void test_performance_DISABLED() { CTerrain terrain; @@ -285,6 +410,333 @@ stream << "\n"; } + static const size_t scale = 1; + + void MakeGoalReachable_testIteration(CStr& map, u16 sx, u16 sz, u16 gx, u16 gz) + { + int colors[26][3] = { + { 255, 0, 0 }, + { 0, 255, 0 }, + { 0, 0, 255 }, + { 255, 255, 0 }, + { 255, 0, 255 }, + { 0, 255, 255 }, + { 255, 255, 255 }, + + { 127, 0, 0 }, + { 0, 127, 0 }, + { 0, 0, 127 }, + { 127, 127, 0 }, + { 127, 0, 127 }, + { 0, 127, 127 }, + { 127, 127, 127}, + + { 255, 127, 0 }, + { 127, 255, 0 }, + { 255, 0, 127 }, + { 127, 0, 255}, + { 0, 255, 127 }, + { 0, 127, 255}, + { 255, 127, 127}, + { 127, 255, 127}, + { 127, 127, 255}, + + { 127, 255, 255 }, + { 255, 127, 255 }, + { 255, 255, 127 }, + }; + + // Load up a map, dump hierarchical regions + // From a few positions test making a few positions reachable. + // Check performance and output results as svg files so user can verify sanity. + + CTerrain terrain; + + CSimulation2 sim2(NULL, g_ScriptRuntime, &terrain); + sim2.LoadDefaultScripts(); + sim2.ResetState(); + + CMapReader* mapReader = new CMapReader(); // it'll call "delete this" itself + + LDR_BeginRegistering(); + mapReader->LoadMap(map.FromUTF8(), + sim2.GetScriptInterface().GetJSRuntime(), JS::UndefinedHandleValue, + &terrain, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + &sim2, &sim2.GetSimContext(), -1, false); + LDR_EndRegistering(); + TS_ASSERT_OK(LDR_NonprogressiveLoad()); + + sim2.Update(0); + + map.Replace(".pmp",""); map.Replace("/",""); + CStr path("MGR_" + map + "_" + CStr::FromUInt(sx) + "_" + CStr::FromUInt(sz) + "_" + CStr::FromUInt(gx) + "_" + CStr::FromUInt(gz) + ".html"); + std::cout << path << std::endl; + std::ofstream stream(OsString(path).c_str(), std::ofstream::out | std::ofstream::trunc); + + CmpPtr cmpObstructionManager(sim2, SYSTEM_ENTITY); + CmpPtr cmpPathfinder(sim2, SYSTEM_ENTITY); + + pass_class_t obstructionsMask = cmpPathfinder->GetPassabilityClass("default"); + const Grid& obstructions = cmpPathfinder->GetPassabilityGrid(); + + // Dump as canvas. This is terrible code but who cares. + stream << "\n"; + stream << "\n" + "\n" + "\n" + ""; + + stream << "

\n"; + stream << "Display Grid"; + stream << "Display Hierarchical grid "; + stream << "Display Global Regions "; + stream << "Display Path search "; + stream << "Display path lookups "; + stream << ""; + stream << "\n"; + stream << "

"; + + stream << ""; + + // set up grid + stream << "\n"; + + // Dump hierarchical regions on another one. + stream << ""; + stream << ""; + + HierarchicalPathfinder& hier = ((CCmpPathfinder*)cmpPathfinder.operator->())->m_LongPathfinder.GetHierarchicalPathfinder(); + + stream << "\n"; + + // Ok let's check out MakeGoalReachable + // pick a point + fixed X,Z; + X = fixed::FromInt(sx); + Z = fixed::FromInt(sz); + u16 gridSize = obstructions.m_W; + // Convert the start coordinates to tile indexes + u16 i0, j0; + Pathfinding::NearestNavcell(X, Z, i0, j0, gridSize, gridSize); + + // Dump as HTML so that it's on top and add fancy shadows so it's easy to see. + stream << "

"; + + hier.FindNearestPassableNavcell(i0, j0, obstructionsMask); + stream << "

"; + + // Make the goal reachable. This includes shortening the path if the goal is in a non-passable + // region, transforming non-point goals to reachable point goals, etc. + + PathGoal goal; + goal.type = PathGoal::POINT; + goal.x = fixed::FromInt(gx); + goal.z = fixed::FromInt(gz); + goal.u = CFixedVector2D(fixed::FromInt(1), fixed::Zero()); + goal.v = CFixedVector2D(fixed::Zero(),fixed::FromInt(1)); + goal.hh = fixed::FromInt(0); + goal.hw = fixed::FromInt(0); + + u16 i1, j1; + Pathfinding::NearestNavcell(goal.x, goal.z, i1, j1, gridSize, gridSize); + stream << "

"; + + stream << ""; + stream << ""; + + stream << "\n"; + + Pathfinding::NearestNavcell(goalCopy.x, goalCopy.z, i1, j1, gridSize, gridSize); + stream << "

"; + stream << "\n"; + stream.close(); + + // Perf test. This is a little primitive, but should work well enough to give an idea of the algo. + double t = timer_Time(); + + srand(1234); + for (size_t j = 0; j < 10000; ++j) + { + PathGoal oldGoal = goal; + hier.MakeGoalReachable(i0, j0, goal, obstructionsMask); + goal = oldGoal; + } + + t = timer_Time() - t; + printf("\nPoint Goal: [%f]\n", t); + + goal.type = PathGoal::CIRCLE; + goal.hh = fixed::FromInt(40); + goal.hw = fixed::FromInt(40); + + t = timer_Time(); + + srand(1234); + for (size_t j = 0; j < 10000; ++j) + { + PathGoal oldGoal = goal; + hier.MakeGoalReachable(i0, j0, goal, obstructionsMask); + goal = oldGoal; + } + + t = timer_Time() - t; + printf("\nCircle Goal: [%f]\n", t); + } + + void test_MakeGoalReachable_performance_DISABLED() + { + struct test + { + CStr map; + u16 sx; + u16 sz; + u16 gx; + u16 gz; + }; + /* + * Initially this was done to compare A* to the earlier flood-fill method, which has since been removed. + * Compare performance on a few cases: + * - short path, good case for the flood fill (it finds immediately the point/circle and stops) + * - short path, bad case for the flood fill (it will not find the correct region right away, so it's literally about 100x slower than the former) + * - long path around the bend, close to worst-case for A* + * - Long unreachable path, but the "closest point" is reachable in almost a straight direction. + * - Inverse of the former (the region to fill is much smaller) + * - large island, A* still has a lot to fill here + * - straight with obstructions + * - straight, fewer obstructions + * - bad case (U shape around the start containing A*) + * - bad case: U shape + unreachable. We need to return something reasonably close, not in the first U + * - bad calse: two U shapes tripping A* + */ + std::vector maps = { + { "maps/scenarios/Peloponnese.pmp", 600, 800, 800, 800 }, + { "maps/scenarios/Peloponnese.pmp", 600, 800, 600, 900 }, + { "maps/scenarios/Peloponnese.pmp", 600, 800, 770, 1400 }, + { "maps/scenarios/Peloponnese.pmp", 1000, 300, 1500, 1450 }, + { "maps/scenarios/Peloponnese.pmp", 1500, 1450, 1000, 300 }, + { "maps/skirmishes/Corsica and Sardinia (4).pmp", 300, 1300, 1300, 300 }, + { "maps/skirmishes/Alpine_Mountains_(3).pmp", 200, 200, 800, 800 }, + { "maps/skirmishes/Corinthian Isthmus (2).pmp", 200, 200, 800, 800 }, + { "maps/skirmishes/Mediterranean Cove (2).pmp", 200, 200, 800, 800 }, + { "maps/skirmishes/Dueling Cliffs (3v3).pmp", 200, 200, 800, 800 }, + { "maps/skirmishes/Dueling Cliffs (3v3).pmp", 350, 200, 900, 900 }, + { "maps/skirmishes/Dueling Cliffs (3v3).pmp", 200, 200, 950, 950 }, + }; + + for (auto t : maps) + { + MakeGoalReachable_testIteration(t.map, t.sx, t.sz, t.gx, t.gz); + } + } + void DumpPath(std::ostream& stream, int i0, int j0, int i1, int j1, CmpPtr& cmpPathfinder) { entity_pos_t x0 = entity_pos_t::FromInt(i0); Index: source/simulation2/helpers/Geometry.h =================================================================== --- source/simulation2/helpers/Geometry.h +++ source/simulation2/helpers/Geometry.h @@ -30,6 +30,21 @@ namespace Geometry { +/* + * Check if we should treat a square as a circle, given the radius + * of the resulting circle and a distance to it + * used by UnitMotion and ObstructionManager + */ +inline bool ShouldTreatTargetAsCircle(const fixed& range, const fixed& circleRadius) +{ + // Given a square, plus a target range we should reach, the shape at that distance + // is a round-cornered square which we can approximate as either a circle or as a square. + // Previously, we used the shape that minimized the worst-case error. + // However that is unsage in some situations. So let's be less clever and + // just check if our range is at least three times bigger than the circleradius + return (range > circleRadius*3); +} + /** * Checks if a point is inside the given rotated rectangle. * Points precisely on an edge are considered to be inside. Index: source/simulation2/helpers/HierarchicalPathfinder.h =================================================================== --- source/simulation2/helpers/HierarchicalPathfinder.h +++ source/simulation2/helpers/HierarchicalPathfinder.h @@ -24,10 +24,13 @@ #include "Render.h" #include "graphics/SColor.h" +#include +#include + /** * Hierarchical pathfinder. * - * It doesn't find shortest paths, but deals with connectivity. + * Deals with connectivity (can point A reach point B?) * * The navcell-grid representation of the map is split into fixed-size chunks. * Within a chunk, each maximal set of adjacently-connected passable navcells @@ -35,18 +38,31 @@ * Each region is a vertex in the hierarchical pathfinder's graph. * When two regions in adjacent chunks are connected by passable navcells, * the graph contains an edge between the corresponding two vertexes. - * (There will never be an edge between two regions in the same chunk.) + * (by design, there can never be an edge between two regions in the same chunk.) + * + * Those fixed-size chunks are used to efficiently compute "global regions" by effectively flood-filling. + * Those can then be used to immediately determine if two reachables points are connected + * + * The main use of this class is to convert an arbitrary PathGoal to a reachable navcell + * This happens in MakeGoalReachable, which implements A* over the chunks. + * Currently, the resulting path is unused. * - * Since regions are typically fairly large, it is possible to determine - * connectivity between any two navcells by mapping them onto their appropriate - * region and then doing a relatively small graph search. */ +#ifdef TEST +class TestCmpPathfinder; +#endif + class HierarchicalOverlay; class HierarchicalPathfinder { +#ifdef TEST + friend class TestCmpPathfinder; +#endif public: + typedef u32 GlobalRegionID; + struct RegionID { u8 ci, cj; // chunk ID @@ -54,7 +70,7 @@ RegionID(u8 ci, u8 cj, u16 r) : ci(ci), cj(cj), r(r) { } - bool operator<(RegionID b) const + bool operator<(const RegionID& b) const { // Sort by chunk ID, then by per-chunk region ID if (ci < b.ci) @@ -68,7 +84,7 @@ return r < b.r; } - bool operator==(RegionID b) const + bool operator==(const RegionID& b) const { return ((ci == b.ci) && (cj == b.cj) && (r == b.r)); } @@ -89,24 +105,31 @@ bool IsChunkDirty(int ci, int cj, const Grid& dirtinessGrid) const; RegionID Get(u16 i, u16 j, pass_class_t passClass); + GlobalRegionID GetGlobalRegion(u16 i, u16 j, pass_class_t passClass); /** - * Updates @p goal so that it's guaranteed to be reachable from the navcell + * Updates @p goal to a point goal guaranteed to be reachable from the original navcell * @p i0, @p j0 (which is assumed to be on a passable navcell). * - * If the goal is not reachable, it is replaced with a point goal nearest to - * the goal center. + * If the goal is not reachable, it is replaced with an acceptable point goal + * This function does not necessarily return the closest navcell to the goal + * but the one with the lowest f score of the A* algorithm. + * This means it is usually a tradeoff between walking time and distance to the goal. * * In the case of a non-point reachable goal, it is replaced with a point goal * at the reachable navcell of the goal which is nearest to the starting navcell. + * TODO: since A* is used, it could return the reachable navcell nearest to the penultimate region visited. + * which is probably better (imagine a path that must bend around). + * + * @returns true if the goal was reachable, false otherwise. */ - void MakeGoalReachable(u16 i0, u16 j0, PathGoal& goal, pass_class_t passClass); + bool MakeGoalReachable(u16 i0, u16 j0, PathGoal& goal, pass_class_t passClass); /** - * Updates @p i, @p j (which is assumed to be an impassable navcell) - * to the nearest passable navcell. + * Updates @p i, @p j to the nearest passable navcell. + * @param limited: only search a few tiles around (i, j) */ - void FindNearestPassableNavcell(u16& i, u16& j, pass_class_t passClass); + void FindNearestPassableNavcell(u16& i, u16& j, pass_class_t passClass, bool limited = false); /** * Generates the connectivity grid associated with the given pass_class @@ -125,15 +148,15 @@ private: static const u8 CHUNK_SIZE = 96; // number of navcells per side - // TODO PATHFINDER: figure out best number. Probably 64 < n < 128 + // TODO: figure out best number. Probably 64 < n < 128 struct Chunk { u8 m_ChunkI, m_ChunkJ; // chunk ID - u16 m_NumRegions; // number of local region IDs (starting from 1) + std::vector m_RegionsID; // IDs of local region, without 0 u16 m_Regions[CHUNK_SIZE][CHUNK_SIZE]; // local region ID per navcell - cassert(CHUNK_SIZE*CHUNK_SIZE/2 < 65536); // otherwise we could overflow m_NumRegions with a checkerboard pattern + cassert(CHUNK_SIZE*CHUNK_SIZE/2 < 65536); // otherwise we could overflow m_RegionsID with a checkerboard pattern void InitRegions(int ci, int cj, Grid* grid, pass_class_t passClass); @@ -141,18 +164,45 @@ void RegionCenter(u16 r, int& i, int& j) const; + void NearestNavcell(int iGoal, int jGoal, int& iBest, int& jBest, u32& dist2Best) const; + void RegionNavcellNearest(u16 r, int iGoal, int jGoal, int& iBest, int& jBest, u32& dist2Best) const; bool RegionNearestNavcellInGoal(u16 r, u16 i0, u16 j0, const PathGoal& goal, u16& iOut, u16& jOut, u32& dist2Best) const; + +#ifdef TEST + bool operator==(const Chunk& b) const + { + return (m_ChunkI == b.m_ChunkI && m_ChunkJ == b.m_ChunkJ && m_RegionsID == b.m_RegionsID && memcmp(&m_Regions, &b.m_Regions, sizeof(u16) * CHUNK_SIZE * CHUNK_SIZE) == 0); + } +#endif }; typedef std::map > EdgesMap; - void FindEdges(u8 ci, u8 cj, pass_class_t passClass, EdgesMap& edges); + void RecomputeAllEdges(pass_class_t passClass, EdgesMap& edges); + void UpdateEdges(u8 ci, u8 cj, pass_class_t passClass, EdgesMap& edges); + + void UpdateGlobalRegions(const std::map>& needNewGlobalRegionMap); void FindReachableRegions(RegionID from, std::set& reachable, pass_class_t passClass); - void FindPassableRegions(std::set& regions, pass_class_t passClass); + void FindGoalRegions(u16 gi, u16 gj, const PathGoal& goal, std::set& regions, pass_class_t passClass); + + /* + * Helpers for the A* implementation of MakeGoalReachable. + * We reuse flat_XX containers to have good cache locality and avoid the cost of allocating memory. Flat_XX implementa map/set as a sorted vector + */ + boost::container::flat_map m_Astar_Predecessor; + boost::container::flat_map m_Astar_GScore; + boost::container::flat_map m_Astar_FScore; + boost::container::flat_set m_Astar_ClosedNodes; + boost::container::flat_set m_Astar_OpenNodes; + + inline int DistBetween(const RegionID& a, const RegionID& b) + { + return (abs(a.ci - b.ci) + abs(a.cj - b.cj)) * CHUNK_SIZE - 30; + }; /** * Updates @p iGoal and @p jGoal to the navcell that is the nearest to the @@ -174,6 +224,9 @@ std::map m_Edges; + std::map> m_GlobalRegions; + GlobalRegionID m_NextGlobalRegionID; + // Passability classes for which grids will be updated when calling Update std::map m_PassClassMasks; Index: source/simulation2/helpers/HierarchicalPathfinder.cpp =================================================================== --- source/simulation2/helpers/HierarchicalPathfinder.cpp +++ source/simulation2/helpers/HierarchicalPathfinder.cpp @@ -22,6 +22,8 @@ #include "graphics/Overlay.h" #include "ps/Profile.h" +#include "Geometry.h" + // Find the root ID of a region, used by InitRegions inline u16 RootID(u16 x, const std::vector& v) { @@ -110,14 +112,14 @@ } } - // Directly point the root ID - m_NumRegions = 0; + // Mark connected regions as being the same ID (i.e. the lowest) for (u16 i = 1; i < regionID+1; ++i) { - if (connect[i] == i) - ++m_NumRegions; - else + if (connect[i] != i) connect[i] = RootID(i, connect); + // register this region ID + if (std::find(m_RegionsID.begin(),m_RegionsID.end(), connect[i]) == m_RegionsID.end()) + m_RegionsID.push_back(connect[i]); } // Replace IDs by the root ID @@ -171,6 +173,39 @@ j_out = m_ChunkJ * CHUNK_SIZE + sj / n; } +/** + * Returns the global navcell coords, and the squared distance to the goal + * navcell, of whichever navcell inside the given chunk is closest to + * that goal. + */ +void HierarchicalPathfinder::Chunk::NearestNavcell(int iGoal, int jGoal, int& iBest, int& jBest, u32& dist2Best) const +{ + iBest = 0; + jBest = 0; + dist2Best = std::numeric_limits::max(); + + for (int j = 0; j < CHUNK_SIZE; ++j) + { + for (int i = 0; i < CHUNK_SIZE; ++i) + { + if (m_Regions[j][i] == 0) + continue; + + u32 dist2 = (i + m_ChunkI*CHUNK_SIZE - iGoal)*(i + m_ChunkI*CHUNK_SIZE - iGoal) + + (j + m_ChunkJ*CHUNK_SIZE - jGoal)*(j + m_ChunkJ*CHUNK_SIZE - jGoal); + if (dist2 < dist2Best) + { + iBest = i + m_ChunkI*CHUNK_SIZE; + jBest = j + m_ChunkJ*CHUNK_SIZE; + dist2Best = dist2; + } + else + // we can safely break, since the first time we'll necessary improve on max, and then if we don't then this one was the best. + break; + } + } +} + /** * Returns the global navcell coords, and the squared distance to the goal * navcell, of whichever navcell inside the given region is closest to @@ -191,7 +226,6 @@ u32 dist2 = (i + m_ChunkI*CHUNK_SIZE - iGoal)*(i + m_ChunkI*CHUNK_SIZE - iGoal) + (j + m_ChunkJ*CHUNK_SIZE - jGoal)*(j + m_ChunkJ*CHUNK_SIZE - jGoal); - if (dist2 < dist2Best) { iBest = i + m_ChunkI*CHUNK_SIZE; @@ -370,6 +404,9 @@ m_Chunks.clear(); m_Edges.clear(); + // reset global regions + m_NextGlobalRegionID = 1; + for (auto& passClassMask : allPassClasses) { pass_class_t passClass = passClassMask.second; @@ -385,16 +422,32 @@ } // Construct the search graph over the regions - EdgesMap& edges = m_Edges[passClass]; - - for (int cj = 0; cj < m_ChunksH; ++cj) - { - for (int ci = 0; ci < m_ChunksW; ++ci) - { - FindEdges(ci, cj, passClass, edges); - } - } + RecomputeAllEdges(passClass, edges); + + // Spread global regions. + std::map& globalRegion = m_GlobalRegions[passClass]; + globalRegion.clear(); + for (u8 cj = 0; cj < m_ChunksH; ++cj) + for (u8 ci = 0; ci < m_ChunksW; ++ci) + for (u16 rid : GetChunk(ci, cj, passClass).m_RegionsID) + { + RegionID reg{ci,cj,rid}; + if (globalRegion.find(reg) == globalRegion.end()) + { + GlobalRegionID ID = m_NextGlobalRegionID++; + + globalRegion.insert({ reg, ID }); + // avoid creating an empty link if possible, FindReachableRegions uses [] which calls the default constructor + if (edges.find(reg) != edges.end()) + { + std::set reachable; + FindReachableRegions(reg, reachable, passClass); + for (const RegionID& region : reachable) + globalRegion.insert({ region, ID }); + } + } + } } if (m_DebugOverlay) @@ -409,9 +462,25 @@ { PROFILE3("Hierarchical Update"); - for (int cj = 0; cj < m_ChunksH; ++cj) + ASSERT(m_NextGlobalRegionID < std::numeric_limits::max()); + + std::map> needNewGlobalRegionMap; + + // Algorithm for the partial update: + // 1. Loop over chunks. + // 2. For any dirty chunk: + // - remove all regions from the global region map + // - remove all edges, by removing the neighbor connection with them and then deleting us + // - recreate regions inside the chunk + // - reconnect the regions. We may do too much work if we reconnect with a dirty chunk, but that's fine. + // 3. Recreate global regions. + // This means that if any chunk changes, we may need to flood (at most once) the whole map. + // That's quite annoying, but I can't think of an easy way around it. + // If we could be sure that a region's topology hasn't changed, we could skip removing its global region + // but that's non trivial as we have no easy way to determine said topology (regions could "switch" IDs on update for now). + for (u8 cj = 0; cj < m_ChunksH; ++cj) { - for (int ci = 0; ci < m_ChunksW; ++ci) + for (u8 ci = 0; ci < m_ChunksW; ++ci) { if (!IsChunkDirty(ci, cj, dirtinessGrid)) continue; @@ -419,27 +488,35 @@ { pass_class_t passClass = passClassMask.second; Chunk& a = m_Chunks[passClass].at(ci + cj*m_ChunksW); + + // Clean up edges and global region ID + EdgesMap& edgeMap = m_Edges[passClass]; + for (u16 i : a.m_RegionsID) + { + RegionID reg{ci, cj, i}; + m_GlobalRegions[passClass].erase(reg); + for (const RegionID& neighbor : edgeMap[reg]) + { + edgeMap[neighbor].erase(reg); + if (edgeMap[neighbor].empty()) + edgeMap.erase(neighbor); + } + edgeMap.erase(reg); + } + + // recompute local regions (not global regions) a.InitRegions(ci, cj, grid, passClass); - } - } - } - // TODO: Also be clever with edges - m_Edges.clear(); - for (const std::pair& passClassMask : m_PassClassMasks) - { - pass_class_t passClass = passClassMask.second; - EdgesMap& edges = m_Edges[passClass]; + for (u16 i : a.m_RegionsID) + needNewGlobalRegionMap[passClassMask.second].push_back(RegionID{ci, cj, i}); - for (int cj = 0; cj < m_ChunksH; ++cj) - { - for (int ci = 0; ci < m_ChunksW; ++ci) - { - FindEdges(ci, cj, passClass, edges); + UpdateEdges(ci, cj, passClass, edgeMap); } } } + UpdateGlobalRegions(needNewGlobalRegionMap); + if (m_DebugOverlay) { PROFILE("debug overlay"); @@ -466,22 +543,15 @@ } /** - * Find edges between regions in this chunk and the adjacent below/left chunks. + * Connect a chunk's regions to their neighbors. Not optimised for global recomputing. + * TODO: reduce code duplication with below */ -void HierarchicalPathfinder::FindEdges(u8 ci, u8 cj, pass_class_t passClass, EdgesMap& edges) +void HierarchicalPathfinder::UpdateEdges(u8 ci, u8 cj, pass_class_t passClass, EdgesMap& edges) { std::vector& chunks = m_Chunks[passClass]; Chunk& a = chunks.at(cj*m_ChunksW + ci); - // For each edge between chunks, we loop over every adjacent pair of - // navcells in the two chunks. If they are both in valid regions - // (i.e. are passable navcells) then add a graph edge between those regions. - // (We don't need to test for duplicates since EdgesMap already uses a - // std::set which will drop duplicate entries.) - // But as set.insert can be quite slow on large collection, and that we usually - // try to insert the same values, we cache the previous one for a fast test. - if (ci > 0) { Chunk& b = chunks.at(cj*m_ChunksW + (ci-1)); @@ -503,6 +573,27 @@ } } + if (ci < m_ChunksW-1) + { + Chunk& b = chunks.at(cj*m_ChunksW + (ci+1)); + RegionID raPrev(0,0,0); + RegionID rbPrev(0,0,0); + for (int j = 0; j < CHUNK_SIZE; ++j) + { + RegionID ra = a.Get(CHUNK_SIZE-1, j); + RegionID rb = b.Get(0, j); + if (ra.r && rb.r) + { + if (ra == raPrev && rb == rbPrev) + continue; + edges[ra].insert(rb); + edges[rb].insert(ra); + raPrev = ra; + rbPrev = rb; + } + } + } + if (cj > 0) { Chunk& b = chunks.at((cj-1)*m_ChunksW + ci); @@ -524,6 +615,94 @@ } } + if (cj < m_ChunksH - 1) + { + Chunk& b = chunks.at((cj+1)*m_ChunksW + ci); + RegionID raPrev(0,0,0); + RegionID rbPrev(0,0,0); + for (int i = 0; i < CHUNK_SIZE; ++i) + { + RegionID ra = a.Get(i, CHUNK_SIZE-1); + RegionID rb = b.Get(i, 0); + if (ra.r && rb.r) + { + if (ra == raPrev && rb == rbPrev) + continue; + edges[ra].insert(rb); + edges[rb].insert(ra); + raPrev = ra; + rbPrev = rb; + } + } + } +} + +/** + * Find edges between regions in all chunks, in an optimised manner (only look at top/left) + */ +void HierarchicalPathfinder::RecomputeAllEdges(pass_class_t passClass, EdgesMap& edges) +{ + std::vector& chunks = m_Chunks[passClass]; + + edges.clear(); + + for (int cj = 0; cj < m_ChunksH; ++cj) + { + for (int ci = 0; ci < m_ChunksW; ++ci) + { + Chunk& a = chunks.at(cj*m_ChunksW + ci); + + // For each edge between chunks, we loop over every adjacent pair of + // navcells in the two chunks. If they are both in valid regions + // (i.e. are passable navcells) then add a graph edge between those regions. + // (We don't need to test for duplicates since EdgesMap already uses a + // std::set which will drop duplicate entries.) + // But as set.insert can be quite slow on large collection, and that we usually + // try to insert the same values, we cache the previous one for a fast test. + + if (ci > 0) + { + Chunk& b = chunks.at(cj*m_ChunksW + (ci-1)); + RegionID raPrev(0,0,0); + RegionID rbPrev(0,0,0); + for (int j = 0; j < CHUNK_SIZE; ++j) + { + RegionID ra = a.Get(0, j); + RegionID rb = b.Get(CHUNK_SIZE-1, j); + if (ra.r && rb.r) + { + if (ra == raPrev && rb == rbPrev) + continue; + edges[ra].insert(rb); + edges[rb].insert(ra); + raPrev = ra; + rbPrev = rb; + } + } + } + + if (cj > 0) + { + Chunk& b = chunks.at((cj-1)*m_ChunksW + ci); + RegionID raPrev(0,0,0); + RegionID rbPrev(0,0,0); + for (int i = 0; i < CHUNK_SIZE; ++i) + { + RegionID ra = a.Get(i, 0); + RegionID rb = b.Get(i, CHUNK_SIZE-1); + if (ra.r && rb.r) + { + if (ra == raPrev && rb == rbPrev) + continue; + edges[ra].insert(rb); + edges[rb].insert(ra); + raPrev = ra; + rbPrev = rb; + } + } + } + } + } } /** @@ -561,12 +740,34 @@ xz.push_back(b.Y.ToFloat()); m_DebugOverlayLines.emplace_back(); - m_DebugOverlayLines.back().m_Color = CColor(1.0, 1.0, 1.0, 1.0); + m_DebugOverlayLines.back().m_Color = CColor(1.0, 0.0, 0.0, 1.0); SimRender::ConstructLineOnGround(*m_SimContext, xz, m_DebugOverlayLines.back(), true); } } } +void HierarchicalPathfinder::UpdateGlobalRegions(const std::map>& needNewGlobalRegionMap) +{ + // Use FindReachableRegions because we cannot be sure, even if we find a non-dirty chunk nearby, + // that we weren't the only bridge connecting that chunk to the rest of the global region. + for (const std::pair>& regionsInNeed : needNewGlobalRegionMap) + for (const RegionID& reg : regionsInNeed.second) + { + std::map& globalRegions = m_GlobalRegions[regionsInNeed.first]; + // if we have already been given a region, skip us. + if (globalRegions.find(reg) != globalRegions.end()) + continue; + + std::set reachable; + FindReachableRegions(reg, reachable, regionsInNeed.first); + + GlobalRegionID ID = m_NextGlobalRegionID++; + + for (const RegionID& reg : reachable) + globalRegions[reg] = ID; + } +} + HierarchicalPathfinder::RegionID HierarchicalPathfinder::Get(u16 i, u16 j, pass_class_t passClass) { int ci = i / CHUNK_SIZE; @@ -575,80 +776,394 @@ return m_Chunks[passClass][cj*m_ChunksW + ci].Get(i % CHUNK_SIZE, j % CHUNK_SIZE); } -void HierarchicalPathfinder::MakeGoalReachable(u16 i0, u16 j0, PathGoal& goal, pass_class_t passClass) +HierarchicalPathfinder::GlobalRegionID HierarchicalPathfinder::GetGlobalRegion(u16 i, u16 j, pass_class_t passClass) +{ + RegionID region = Get(i, j, passClass); + if (region.r == 0) + return (GlobalRegionID)0; + return m_GlobalRegions[passClass][region]; +} + +#define OUTPUT 0 + +#if OUTPUT + #include +#endif + +#if OUTPUT +// also change the header +bool HierarchicalPathfinder::MakeGoalReachable(u16 i0, u16 j0, PathGoal& goal, pass_class_t passClass, std::ofstream& stream) +#else +bool HierarchicalPathfinder::MakeGoalReachable(u16 i0, u16 j0, PathGoal& goal, pass_class_t passClass) +#endif { - PROFILE2("MakeGoalReachable"); + /* + * Relatively straightforward implementation of A* on the hierarchical pathfinder graph. + * Since this isn't a grid, we cannot use JPS (though I'm fairly sure it could sort of be extended to work, but it's probably not trivial/worth it) + * Uses flat_set and flat_map over std::set and std::map since testing proves that reusing the memory ends up being more efficient + * The main optimisations are: + * - picking the best item directly from the open list when we can be sure we know which one it is (see fasttrack) + * - checking whether the goal is reachable or not, and if it isn't stopping early to avoid slowly flood-filling everything + * + * Since we'd like to return the best possible navcell, if the goal is reachable, we'll stop A* once we've reached any goal region + * since then presumably other reachable goal-regions would be reachable following roughtly the same path. + * Then we'll loop over goal regions to get the best navcell and reconstruct the path from there. + * + * NB: since the path is currently unused, this skips the A* part for reachable goals. + */ RegionID source = Get(i0, j0, passClass); - // Find everywhere that's reachable - std::set reachableRegions; - FindReachableRegions(source, reachableRegions, passClass); + u16 gi, gj; + Pathfinding::NearestNavcell(goal.x, goal.z, gi, gj, m_W, m_H); - // Check whether any reachable region contains the goal - // And get the navcell that's the closest to the point + // determine if we will be able to reach the goal. + std::set goalRegions; + FindGoalRegions(gi, gj, goal, goalRegions, passClass); - u16 bestI = 0; - u16 bestJ = 0; - u32 dist2Best = std::numeric_limits::max(); + std::vector reachableGoalRegions; + + GlobalRegionID startID = GetGlobalRegion(i0, j0, passClass); + bool reachable = false; + for (const RegionID& r : goalRegions) + if (m_GlobalRegions[passClass][r] == startID) + { + reachable = true; + reachableGoalRegions.push_back(r); + } + + // If the goal is unreachable, we'd still like to get at least somewhere on the closest reachable region. + // but it is tricky (or rather, slow) to determine even which region is closest to us, and using the region center can lead to wrong choices. + // (see FindNearestNavcellInRegions) + // So the current approach is to use a limited FindNearestPassableNavcell search, for the 5 neighboring navcells (common case: a treeā€¦) + // if that returns a passable, reachable navcell, go there instead. + // if not, we'll just hope we get an acceptable path. + // Hopefully the player will "jitter" the target around and we'll end up working OK after a few tries even in bad cases. + + u16 ngi = gi, ngj = gj; + FindNearestPassableNavcell(ngi, ngj, passClass, true); + + if (GetGlobalRegion(ngi, ngj, passClass) == startID) + { + reachable = true; + gi = ngi; + gj = ngj; + + // change the goal to a point-goal on that cell + goal.u = CFixedVector2D(fixed::FromInt(1), fixed::FromInt(0)); + goal.v = CFixedVector2D(fixed::FromInt(0), fixed::FromInt(1)); + goal.x = fixed::FromInt(gi).MultiplyClamp(Pathfinding::NAVCELL_SIZE_INT) + Pathfinding::NAVCELL_SIZE /2; + goal.z = fixed::FromInt(gj).MultiplyClamp(Pathfinding::NAVCELL_SIZE_INT) + Pathfinding::NAVCELL_SIZE /2; + goal.hh = fixed::FromInt(2)/5; // slightly below 1 to avoid being on the edge + goal.hw = fixed::FromInt(2)/5; + + reachableGoalRegions.push_back(Get(ngi, ngj, passClass)); + } - for (const RegionID& region : reachableRegions) +#if OUTPUT + stream << "context.fillStyle = 'rgba(1,0,1,1)';\n"; + for (const RegionID& r : goalRegions) { - // Skip region if its chunk doesn't contain the goal area - entity_pos_t x0 = Pathfinding::NAVCELL_SIZE * (region.ci * CHUNK_SIZE); - entity_pos_t z0 = Pathfinding::NAVCELL_SIZE * (region.cj * CHUNK_SIZE); + entity_pos_t x0 = Pathfinding::NAVCELL_SIZE * (r.ci * CHUNK_SIZE); + entity_pos_t z0 = Pathfinding::NAVCELL_SIZE * (r.cj * CHUNK_SIZE); + stream << "context2.fillRect(" << x0.ToInt_RoundToZero() << " * scale," << z0.ToInt_RoundToZero() << " * scale," << (int)CHUNK_SIZE << " * scale," << (int)CHUNK_SIZE << " * scale);\n"; + } +#endif + + // In general, our maps are relatively open, so it's usually a better bet to be biaised towards minimal distance over path length. + int (*DistEstimate)(u16, u16, u16, u16) = [](u16 regI, u16 regJ, u16 gi, u16 gj) -> int { return (regI - gi)*(regI - gi) + (regJ - gj)*(regJ - gj); }; + // However, run unbiaised euclidian if we know the goal is unreachable, since we want to get as close as possible efficiently. + // multiply by 20 because we want distance to goal to matter a lot + if (!reachable) + DistEstimate = [](u16 regI, u16 regJ, u16 gi, u16 gj) -> int { + return 20 * isqrt64((regI - gi)*(regI - gi) + (regJ - gj)*(regJ - gj)); + }; + + m_Astar_ClosedNodes.clear(); + m_Astar_OpenNodes.clear(); + m_Astar_OpenNodes.insert(source); + + m_Astar_Predecessor.clear(); + + m_Astar_GScore.clear(); + m_Astar_GScore[source] = 0; + + m_Astar_FScore.clear(); + m_Astar_FScore[source] = DistEstimate(source.ci * CHUNK_SIZE + CHUNK_SIZE/2, source.cj * CHUNK_SIZE + CHUNK_SIZE/2, gi, gj); + + RegionID current {0,0,0}; + + u16 bestI, bestJ; + u32 dist2; + + u32 timeSinceLastFScoreImprovement = 0; +#if OUTPUT + int step = 0; +#endif + + RegionID fastTrack = source; + int currentFScore = m_Astar_FScore[source]; + int secondBestFScore = currentFScore; + int globalBestFScore = currentFScore; + + EdgesMap& edgeMap = m_Edges[passClass]; + + // NB: to re-enable A* for the reachable case (if you want to use the path), remove the "!reachable" part of this check + while (!reachable && !m_Astar_OpenNodes.empty()) + { + // Since we are not using a fancy open list, we have to go through all nodes each time + // So when we are sure that we know the best node (because the last run gave us a node better than us, which was already the best + // we can fast-track and not sort but just pick that one instead. + // In cases where the obvious path is the best, we hardly ever sort and it's a little faster + if (fastTrack.r) + { + current = fastTrack; + currentFScore = m_Astar_FScore[current]; + secondBestFScore = currentFScore; + } + else + { + auto iter = m_Astar_OpenNodes.begin(); + current = *iter; + currentFScore = m_Astar_FScore[current]; + secondBestFScore = currentFScore; + while (++iter != m_Astar_OpenNodes.end()) + { + int score = m_Astar_FScore[*iter]; + if (score < currentFScore) + { + current = *iter; + secondBestFScore = currentFScore; + currentFScore = score; + } + } + } + + m_Astar_OpenNodes.erase(current); + m_Astar_ClosedNodes.emplace(current); + if (reachable) + m_Astar_FScore.erase(current); + m_Astar_GScore.erase(current); + + // Stop heuristic in case we know we cannot reach the goal. + // Indeed this would cause A* to become an inacceptably slow flood fill. + // We keep track of our best fScore, we'll early-exit if we're too far from it + // or we haven't found a better path in a while. + // This will cause us to return largely suboptimal paths now and then, + // but then again those should be rare and the player can just re-order a move. + if (false && !reachable) + { + if (currentFScore < globalBestFScore) + { + globalBestFScore = currentFScore; + timeSinceLastFScoreImprovement = 0; + } + else if ( (++timeSinceLastFScoreImprovement > 3 && currentFScore > globalBestFScore * 2) || timeSinceLastFScoreImprovement > m_ChunksW) + break; + } + + entity_pos_t x0 = Pathfinding::NAVCELL_SIZE * (current.ci * CHUNK_SIZE); + entity_pos_t z0 = Pathfinding::NAVCELL_SIZE * (current.cj * CHUNK_SIZE); entity_pos_t x1 = x0 + Pathfinding::NAVCELL_SIZE * CHUNK_SIZE; entity_pos_t z1 = z0 + Pathfinding::NAVCELL_SIZE * CHUNK_SIZE; - if (!goal.RectContainsGoal(x0, z0, x1, z1)) - continue; - u16 i,j; - u32 dist2; +#if OUTPUT + stream << "context.fillStyle = 'rgba(" < 0 ? 255 : 0) <<",0.8)';\n maxStep = " << step+1 << ";\n"; + stream << "if (step >= " << step << ") context.fillRect(" << x0.ToInt_RoundToZero() << " * scale," << z0.ToInt_RoundToZero() << " * scale," << (int)CHUNK_SIZE << " * scale," << (int)CHUNK_SIZE << " * scale);\n"; +#endif + + fastTrack = RegionID{0,0,0}; - // If the region contains the goal area, the goal is reachable - // Remember the best point for optimization. - if (GetChunk(region.ci, region.cj, passClass).RegionNearestNavcellInGoal(region.r, i0, j0, goal, i, j, dist2)) + // TODO: we should get those first and then validate here, instead of recomputing for each. + if (goal.RectContainsGoal(x0, z0, x1, z1)) + if (GetChunk(current.ci, current.cj, passClass).RegionNearestNavcellInGoal(current.r, i0, j0, goal, bestI, bestJ, dist2)) + break; + + int currScore = m_Astar_GScore[current]; + for (const RegionID& neighbor : edgeMap[current]) { - // If it's a point, no need to move it, we're done - if (goal.type == PathGoal::POINT) - return; - if (dist2 < dist2Best) + if (m_Astar_ClosedNodes.find(neighbor) != m_Astar_ClosedNodes.end()) + continue; + int temp_m_Astar_GScore = currScore + DistBetween(neighbor, current); + auto iter = m_Astar_OpenNodes.emplace(neighbor); + if (!iter.second && temp_m_Astar_GScore >= m_Astar_GScore[neighbor]) + continue; +#if OUTPUT + x0 = Pathfinding::NAVCELL_SIZE * (neighbor.ci * CHUNK_SIZE); + z0 = Pathfinding::NAVCELL_SIZE * (neighbor.cj * CHUNK_SIZE); + stream << "context2.fillStyle = 'rgba(255,255,0,0.3)';\n"; + stream << "if (step >= " << step << ") context2.fillRect(" << x0.ToInt_RoundToZero() << " * scale," << z0.ToInt_RoundToZero() << " * scale," << (int)CHUNK_SIZE << " * scale," << (int)CHUNK_SIZE << " * scale);\n"; +#endif + + m_Astar_GScore[neighbor] = temp_m_Astar_GScore; + // no default constructor so we'll use this hack in the meantime + auto alreadyThere = m_Astar_Predecessor.emplace( boost::container::flat_map::value_type{ neighbor, current }); + alreadyThere.first->second = current; + int score; + // if the goal is reachable, we don't care much about fscore precision so pick the center + if (reachable) + score = temp_m_Astar_GScore + DistEstimate(neighbor.ci * CHUNK_SIZE + CHUNK_SIZE/2, neighbor.cj * CHUNK_SIZE + CHUNK_SIZE/2, gi, gj); + else { - bestI = i; - bestJ = j; - dist2Best = dist2; + // If it's unreachable, it's more important that we estimate our distance to the goal correctly. + // Sadly as said above, this is non-trivial to do efficiently. + // Current implementation uses the region center, it might be better to use a jittered sample of point? + entity_pos_t x0 = Pathfinding::NAVCELL_SIZE * (neighbor.ci * CHUNK_SIZE); + entity_pos_t z0 = Pathfinding::NAVCELL_SIZE * (neighbor.cj * CHUNK_SIZE); + entity_pos_t x1 = x0 + Pathfinding::NAVCELL_SIZE * CHUNK_SIZE; + entity_pos_t z1 = z0 + Pathfinding::NAVCELL_SIZE * CHUNK_SIZE; + if (goal.RectContainsGoal(x0, z0, x1, z1)) + { + int ri, rj; + GetChunk(neighbor.ci, neighbor.cj, passClass).RegionCenter(neighbor.r, ri, rj); + score = temp_m_Astar_GScore + DistEstimate((u16)ri, (u16)rj, gi, gj); + } + else + score = temp_m_Astar_GScore + DistEstimate(neighbor.ci * CHUNK_SIZE + CHUNK_SIZE/2, neighbor.cj * CHUNK_SIZE + CHUNK_SIZE/2, gi, gj); + } + if (score < secondBestFScore) + { + secondBestFScore = score; + fastTrack = neighbor; } + m_Astar_FScore[neighbor] = score; } +#if OUTPUT + step++; +#endif } - // If the goal area wasn't reachable, - // find the navcell that's nearest to the goal's center - if (dist2Best == std::numeric_limits::max()) +#if OUTPUT + fixed x0 = Pathfinding::NAVCELL_SIZE * (current.ci * CHUNK_SIZE); + fixed z0 = Pathfinding::NAVCELL_SIZE * (current.cj * CHUNK_SIZE); + stream << "context.fillStyle = 'rgba(255,0,0,0.6)';\n"; + stream << "if (step >= " << step << ") context.fillRect(" << x0.ToInt_RoundToZero() << " * scale," << z0.ToInt_RoundToZero() << " * scale," << (int)CHUNK_SIZE << " * scale," << (int)CHUNK_SIZE << " * scale);\n"; +#endif + + if (!reachable) { - u16 iGoal, jGoal; - Pathfinding::NearestNavcell(goal.x, goal.z, iGoal, jGoal, m_W, m_H); + // I don't believe this is possible, nor should it. + ENSURE(!m_Astar_ClosedNodes.empty()); - FindNearestNavcellInRegions(reachableRegions, iGoal, jGoal, passClass); + // Pick best and roll with that. + current = *std::min_element(m_Astar_ClosedNodes.begin(), m_Astar_ClosedNodes.end(), + [this](const RegionID& a, const RegionID& b) -> bool { return m_Astar_FScore[a] < m_Astar_FScore[b]; }); - // Construct a new point goal at the nearest reachable navcell - PathGoal newGoal; - newGoal.type = PathGoal::POINT; - Pathfinding::NavcellCenter(iGoal, jGoal, newGoal.x, newGoal.z); - goal = newGoal; - return; + std::set set = { current }; + Pathfinding::NearestNavcell(goal.x, goal.z, bestI, bestJ, m_W, m_H); + + FindNearestNavcellInRegions(set, bestI, bestJ, passClass); + } + else + { + u32 bestDist = std::numeric_limits::max(); + // loop through reachable goal regions and get the best navcell. + // TODO: we probably could skip some of those if our gScore/fScore were good enough. + for (const RegionID& region : reachableGoalRegions) + { + u16 ri, rj; + u32 dist; + // TODO: using the A* path, we should consider from predecessor and not from source + GetChunk(region.ci, region.cj, passClass).RegionNearestNavcellInGoal(region.r, i0, j0, goal, ri, rj, dist); + if (dist < bestDist) + { + bestI = ri; + bestJ = rj; + bestDist = dist; + } + } } - ENSURE(dist2Best != std::numeric_limits::max()); PathGoal newGoal; newGoal.type = PathGoal::POINT; Pathfinding::NavcellCenter(bestI, bestJ, newGoal.x, newGoal.z); + if(!goal.NavcellContainsGoal(bestI, bestJ)) + { + // try to get as close as possible to the goal inside this navcell. + CFixedVector2D relativePos(goal.x - newGoal.x, goal.z - newGoal.z); + CFixedVector2D u(fixed::FromInt(1), fixed::FromInt(0)); + CFixedVector2D v(fixed::FromInt(0), fixed::FromInt(1)); + // use a halfsize slightly below 1, otherwise we can end up on the edge and that confuses the pathfinder. + CFixedVector2D halfsize(fixed::FromInt(2)/5, fixed::FromInt(2)/5); + + relativePos = Geometry::NearestPointOnSquare(relativePos, u, v, halfsize); + newGoal.x += relativePos.X; + newGoal.z += relativePos.Y; + } goal = newGoal; + + return reachable; } -void HierarchicalPathfinder::FindNearestPassableNavcell(u16& i, u16& j, pass_class_t passClass) +void HierarchicalPathfinder::FindNearestPassableNavcell(u16& i, u16& j, pass_class_t passClass, bool limited) { - std::set regions; - FindPassableRegions(regions, passClass); - FindNearestNavcellInRegions(regions, i, j, passClass); + // If our current region is not 0, then this is passable, return immediately. + if (Get(i, j, passClass).r != 0) + return; + + u32 dist2Best = std::numeric_limits::max(); + + u16 oi = i, oj = j; + // in most cases we're < 5 cells away so go for this first + for (u16 tj = std::max(0, oj-5); tj <= std::min(m_H-1, oj+5); ++tj) + for (u16 ti = std::max(0, oi-5); ti <= std::min(m_W-1, oi+5); ++ti) + if (Get(ti, tj, passClass).r != 0) + { + u32 dist = (ti-oi)*(ti-oi) + (tj-oj)*(tj-oj); + if (dist < dist2Best) + { + i = ti; + j = tj; + dist2Best = dist; + } + } + if (dist2Best < std::numeric_limits::max() || limited) + return; + + // We are on an impassable area, so we cannot rely on our accessibility. + // This function does not necessarily need to return the absolute closest navcell, + // since standing on impassable terrain is already a little wonky + // We'll expand in a cross-wise way, returning the best passable cell after each expansion (if any) + // this will ensure that we return an acceptable close navcell in general. + // since the first time we could be close to an edge, we'll expand at least once. + + u16 iBest = i; + u16 jBest = j; + dist2Best = std::numeric_limits::max(); + + u16 si = i/CHUNK_SIZE, sj = j/CHUNK_SIZE; + std::set> visited = { { si, sj } }; + std::vector> openList = { { si, sj } }, openListTemp; + static const int expander[4][2] = { { 1, 0 },{ -1, 0 },{ 0, 1 },{ 0, -1 } }; + for (size_t step = 0;;++step) + { + openListTemp.clear(); + for (std::pair chunk : openList) + { + // go through all regions of a chunk + int tempi, tempj; + u32 dist2; + GetChunk(chunk.first, chunk.second, passClass).NearestNavcell(i, j, tempi, tempj, dist2); + if (dist2 < dist2Best) + { + iBest = tempi; + jBest = tempj; + dist2Best = dist2; + } + // expand cross + for (const int* dir : expander) + { + u8 x = (u8)std::min(std::max((int)si + dir[0], 0), (int)(m_ChunksW-1)); + u8 z = (u8)std::min(std::max((int)sj + dir[1], 0), (int)(m_ChunksH-1)); + if (visited.insert({ x, z }).second) + openListTemp.push_back({ x, z }); + } + } + if (openListTemp.empty() || (step > 0 && dist2Best < std::numeric_limits::max())) + break; + openList.swap(openListTemp); + } + i = iBest; + j = jBest; } void HierarchicalPathfinder::FindNearestNavcellInRegions(const std::set& regions, u16& iGoal, u16& jGoal, pass_class_t passClass) @@ -713,16 +1228,21 @@ // Flood-fill the region graph, starting at 'from', // collecting all the regions that are reachable via edges + reachable.insert(from); + + EdgesMap& edgeMap = m_Edges[passClass]; + if (edgeMap.find(from) == edgeMap.end()) + return; + std::vector open; + open.reserve(64); open.push_back(from); - reachable.insert(from); while (!open.empty()) { RegionID curr = open.back(); open.pop_back(); - - for (const RegionID& region : m_Edges[passClass][curr]) + for (const RegionID& region : edgeMap[curr]) // Add to the reachable set; if this is the first time we added // it then also add it to the open list if (reachable.insert(region).second) @@ -730,15 +1250,36 @@ } } -void HierarchicalPathfinder::FindPassableRegions(std::set& regions, pass_class_t passClass) +void HierarchicalPathfinder::FindGoalRegions(u16 gi, u16 gj, const PathGoal& goal, std::set& regions, pass_class_t passClass) { - // Construct a set of all regions of all chunks for this pass class - for (const Chunk& chunk : m_Chunks[passClass]) + if (goal.type == PathGoal::POINT) { - // region 0 is impassable tiles - for (int r = 1; r <= chunk.m_NumRegions; ++r) - regions.insert(RegionID(chunk.m_ChunkI, chunk.m_ChunkJ, r)); + RegionID region = Get(gi, gj, passClass); + if (region.r > 0) + regions.insert(region); + return; } + + // For non-point cases, we'll test each region inside the bounds of the goal. + // we might occasionally test a few too many for circles but it's not too bad. + // Note that this also works in the Inverse-circle / Inverse-square case + // Since our ranges are inclusive, we will necessarily test at least the perimeter/outer bound of the goal. + // If we find a navcell, great, if not, well then we'll be surrounded by an impassable barrier. + // Since in the Inverse-XX case we're supposed to start inside, then we can't ever reach the goal so it's good enough. + // It's not worth it to skip the "inner" regions since we'd need ranges above CHUNK_SIZE for that to start mattering + // (and even then not always) and that just doesn't happen for Inverse-XX goals + int size = (std::max(goal.hh, goal.hw) * 3 / 2).ToInt_RoundToInfinity(); + + u16 a,b; u32 c; // unused params for RegionNearestNavcellInGoal + + for (u8 sz = std::max(0,(gj - size) / CHUNK_SIZE); sz <= std::min(m_ChunksH-1, (gj + size + 1) / CHUNK_SIZE); ++sz) + for (u8 sx = std::max(0,(gi - size) / CHUNK_SIZE); sx <= std::min(m_ChunksW-1, (gi + size + 1) / CHUNK_SIZE); ++sx) + { + Chunk& chunk = GetChunk(sx, sz, passClass); + for (u16 i : chunk.m_RegionsID) + if (chunk.RegionNearestNavcellInGoal(i, 0, 0, goal, a, b, c)) + regions.insert(RegionID{sx, sz, i}); + } } void HierarchicalPathfinder::FillRegionOnGrid(const RegionID& region, pass_class_t passClass, u16 value, Grid& grid) Index: source/simulation2/helpers/LongPathfinder.h =================================================================== --- source/simulation2/helpers/LongPathfinder.h +++ source/simulation2/helpers/LongPathfinder.h @@ -164,6 +164,10 @@ LongPathfinder(); ~LongPathfinder(); +#ifdef TEST + HierarchicalPathfinder& GetHierarchicalPathfinder() { return m_PathfinderHier; } +#endif + void SetDebugOverlay(bool enabled); void SetHierDebugOverlay(bool enabled, const CSimContext *simContext) @@ -239,6 +243,16 @@ void ComputePath(entity_pos_t x0, entity_pos_t z0, const PathGoal& origGoal, pass_class_t passClass, std::vector excludedRegions, WaypointPath& path); + bool MakeGoalReachable(u16 i0, u16 j0, PathGoal &goal, pass_class_t passClass) { return m_PathfinderHier.MakeGoalReachable(i0, j0, goal, passClass); }; + + void FindNearestPassableNavcell(u16& i, u16& j, pass_class_t passClass) { return m_PathfinderHier.FindNearestPassableNavcell(i, j, passClass); }; + + bool NavcellIsReachable(u16 i0, u16 j0, u16 i1, u16 j1, pass_class_t passClass) + { + return m_PathfinderHier.GetGlobalRegion(i0, j0, passClass) == m_PathfinderHier.GetGlobalRegion(i1, j1, passClass); + }; + + Grid GetConnectivityGrid(pass_class_t passClass) { return m_PathfinderHier.GetConnectivityGrid(passClass); Index: source/simulation2/helpers/PathGoal.h =================================================================== --- source/simulation2/helpers/PathGoal.h +++ source/simulation2/helpers/PathGoal.h @@ -28,6 +28,7 @@ * part of the goal. * Also, it can be an 'inverted' circle/square, where any point outside * the shape is part of the goal. + * In both cases, points on the range (ie at the frontier) are considered inside. */ class PathGoal {