// Copyright (c) 2023, QuantStack and Mamba Contributors // // Distributed under the terms of the BSD 3-Clause License. // // The full license is in the file LICENSE, distributed with this software. #ifndef MAMBA_UTILFLAT_SET_HPP #define MAMBA_UTILFLAT_SET_HPP #include #include #include namespace mamba::util { /** * A sorted vector behaving like a set. * * Like, ``std::set``, uniqueness is determined by using the equivalence relation. * In imprecise terms, two objects ``a`` and ``b`` are considered equivalent if neither * compares less than the other: ``!comp(a, b) && !comp(b, a)`` * * @todo C++23 This is implemented in */ template , typename Allocator = std::allocator> class flat_set : private std::vector { public: using Base = std::vector; using typename Base::allocator_type; using typename Base::const_iterator; using typename Base::const_reverse_iterator; using typename Base::size_type; using typename Base::value_type; using key_compare = Compare; using value_compare = Compare; using Base::cbegin; using Base::cend; using Base::crbegin; using Base::crend; using Base::clear; using Base::empty; using Base::reserve; using Base::size; flat_set() = default; flat_set( std::initializer_list il, key_compare compare = key_compare(), const allocator_type& alloc = allocator_type() ); template flat_set( InputIterator first, InputIterator last, key_compare compare = key_compare(), const allocator_type& alloc = Allocator() ); flat_set(const flat_set&) = default; flat_set(flat_set&&) = default; explicit flat_set(std::vector&& other, key_compare compare = key_compare()); explicit flat_set(const std::vector& other, key_compare compare = key_compare()); flat_set& operator=(const flat_set&) = default; flat_set& operator=(flat_set&&) = default; bool contains(const value_type&) const; const value_type& front() const noexcept; const value_type& back() const noexcept; const value_type& operator[](size_type pos) const; const value_type& at(size_type pos) const; const_iterator begin() const noexcept; const_iterator end() const noexcept; const_reverse_iterator rbegin() const noexcept; const_reverse_iterator rend() const noexcept; /** Insert an element in the set. * * Like std::vector and unlike std::set, inserting an element invalidates iterators. */ std::pair insert(value_type&& value); std::pair insert(const value_type& value); template void insert(InputIterator first, InputIterator last); const_iterator erase(const_iterator pos); const_iterator erase(const_iterator first, const_iterator last); size_type erase(const value_type& value); private: key_compare m_compare; bool key_eq(const value_type& a, const value_type& b) const; template std::pair insert_impl(U&& value); void sort_and_remove_duplicates(); template friend bool operator==(const flat_set& lhs, const flat_set& rhs); }; template , class Allocator = std::allocator> flat_set(std::initializer_list, Compare = Compare(), Allocator = Allocator()) -> flat_set; template < class InputIt, class Comp = std::less::value_type>, class Alloc = std::allocator::value_type>> flat_set(InputIt, InputIt, Comp = Comp(), Alloc = Alloc()) -> flat_set::value_type, Comp, Alloc>; template , class Allocator = std::allocator> flat_set(std::vector&&, Compare compare = Compare()) -> flat_set; template , class Allocator = std::allocator> flat_set(const std::vector&, Compare compare = Compare()) -> flat_set; template bool operator==(const flat_set& lhs, const flat_set& rhs); template bool operator!=(const flat_set& lhs, const flat_set& rhs); /******************************* * vector_set Implementation * *******************************/ template flat_set::flat_set( std::initializer_list il, key_compare compare, const allocator_type& alloc ) : Base(std::move(il), alloc) , m_compare(std::move(compare)) { sort_and_remove_duplicates(); } template template flat_set::flat_set( InputIterator first, InputIterator last, key_compare compare, const allocator_type& alloc ) : Base(first, last, alloc) , m_compare(std::move(compare)) { sort_and_remove_duplicates(); } template flat_set::flat_set(std::vector&& other, C compare) : Base(std::move(other)) , m_compare(std::move(compare)) { sort_and_remove_duplicates(); } template flat_set::flat_set(const std::vector& other, C compare) : Base(std::move(other)) , m_compare(std::move(compare)) { sort_and_remove_duplicates(); } template auto flat_set::contains(const value_type& value) const -> bool { return std::binary_search(begin(), end(), value); } template auto flat_set::front() const noexcept -> const value_type& { return Base::front(); } template auto flat_set::back() const noexcept -> const value_type& { return Base::back(); } template auto flat_set::operator[](size_type pos) const -> const value_type& { return Base::operator[](pos); } template auto flat_set::at(size_type pos) const -> const value_type& { return Base::at(pos); } template auto flat_set::begin() const noexcept -> const_iterator { return Base::begin(); } template auto flat_set::end() const noexcept -> const_iterator { return Base::end(); } template auto flat_set::rbegin() const noexcept -> const_reverse_iterator { return Base::rbegin(); } template auto flat_set::rend() const noexcept -> const_reverse_iterator { return Base::rend(); } template auto flat_set::insert(const value_type& value) -> std::pair { return insert_impl(value); } template auto flat_set::insert(value_type&& value) -> std::pair { return insert_impl(std::move(value)); } template bool flat_set::key_eq(const value_type& a, const value_type& b) const { return !m_compare(a, b) && !m_compare(b, a); } template void flat_set::sort_and_remove_duplicates() { std::sort(Base::begin(), Base::end(), m_compare); auto is_eq = [this](const value_type& a, const value_type& b) { return key_eq(a, b); }; Base::erase(std::unique(Base::begin(), Base::end(), is_eq), Base::end()); } template template void flat_set::insert(InputIterator first, InputIterator last) { Base::insert(Base::end(), first, last); sort_and_remove_duplicates(); } template template auto flat_set::insert_impl(U&& value) -> std::pair { auto it = std::lower_bound(begin(), end(), value, m_compare); if ((it != end()) && (key_eq(*it, value))) { return { it, false }; } it = Base::insert(it, std::forward(value)); return { it, true }; } template auto flat_set::erase(const_iterator pos) -> const_iterator { // No need to sort or remove duplicates again return Base::erase(pos); } template auto flat_set::erase(const_iterator first, const_iterator last) -> const_iterator { // No need to sort or remove duplicates again return Base::erase(first, last); } template auto flat_set::erase(const value_type& value) -> size_type { auto it = std::lower_bound(begin(), end(), value, m_compare); if ((it == end()) || (!(key_eq(*it, value)))) { return 0; } erase(it); return 1; } template bool operator==(const flat_set& lhs, const flat_set& rhs) { auto is_eq = [&lhs](const auto& a, const auto& b) { return lhs.key_eq(a, b); }; return std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend(), is_eq); } template bool operator!=(const flat_set& lhs, const flat_set& rhs) { return !(lhs == rhs); } } #endif