From 26a4fe7bfd33321625be9c9b8a8920e3a74fe4ec Mon Sep 17 00:00:00 2001 From: AntoinePrv Date: Wed, 31 May 2023 17:40:19 +0200 Subject: [PATCH] Add Python-like set operations to flat_set --- libmamba/include/mamba/util/flat_set.hpp | 233 ++++++++++++++++++++- libmamba/src/solv-cpp/queue.cpp | 5 + libmamba/src/solv-cpp/queue.hpp | 2 + libmamba/tests/src/solv-cpp/test_queue.cpp | 7 + libmamba/tests/src/util/test_flat_set.cpp | 102 +++++++++ 5 files changed, 342 insertions(+), 7 deletions(-) diff --git a/libmamba/include/mamba/util/flat_set.hpp b/libmamba/include/mamba/util/flat_set.hpp index 443a1413e9..ba8fb14096 100644 --- a/libmamba/include/mamba/util/flat_set.hpp +++ b/libmamba/include/mamba/util/flat_set.hpp @@ -30,6 +30,7 @@ namespace mamba::util public: using Base = std::vector; + using Self = flat_set; using typename Base::allocator_type; using typename Base::const_iterator; using typename Base::const_reverse_iterator; @@ -69,7 +70,6 @@ namespace mamba::util 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; @@ -91,6 +91,16 @@ namespace mamba::util const_iterator erase(const_iterator first, const_iterator last); size_type erase(const value_type& value); + bool contains(const value_type&) const; + bool is_disjoint_of(const Self& other) const; + bool is_subset_of(const Self& other) const; + bool is_superset_of(const Self& other) const; + + static Self union_(const Self& lhs, const Self& rhs); + static Self intersection(const Self& lhs, const Self& rhs); + static Self difference(const Self& lhs, const Self& rhs); + static Self symetric_difference(const Self& lhs, const Self& rhs); + private: key_compare m_compare; @@ -131,6 +141,46 @@ namespace mamba::util bool operator!=(const flat_set& lhs, const flat_set& rhs); + /** Return whether the first set is a subset of the second. */ + template + bool + operator<=(const flat_set& lhs, const flat_set& rhs); + + /** Return whether the first set is a strict subset of the second. */ + template + bool + operator<(const flat_set& lhs, const flat_set& rhs); + + /** Return whether the first set is a superset of the second. */ + template + bool + operator>=(const flat_set& lhs, const flat_set& rhs); + + /** Return whether the first set is a strict superset of the second. */ + template + bool + operator>(const flat_set& lhs, const flat_set& rhs); + + /** Compute the set union. */ + template + flat_set + operator|(const flat_set& lhs, const flat_set& rhs); + + /** Compute the set intersection. */ + template + flat_set + operator&(const flat_set& lhs, const flat_set& rhs); + + /** Compute the set difference. */ + template + flat_set + operator-(const flat_set& lhs, const flat_set& rhs); + + /** Compute the set symetric difference. */ + template + flat_set + operator^(const flat_set& lhs, const flat_set& rhs); + /******************************* * vector_set Implementation * *******************************/ @@ -177,12 +227,6 @@ namespace mamba::util 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& { @@ -292,6 +336,51 @@ namespace mamba::util return 1; } + template + auto flat_set::contains(const value_type& value) const -> bool + { + return std::binary_search(begin(), end(), value); + } + + namespace detail + { + /** + * Check if two sorted range have an empty intersection. + * + * Edited from https://en.cppreference.com/w/cpp/algorithm/set_intersection + * Distributed under the terms of the Copyright/CC-BY-SA License. + * The full license can be found at the address + * https://en.cppreference.com/w/Cppreference:Copyright/CC-BY-SA + */ + template + bool + set_disjoint(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, Compare comp) + { + while (first1 != last1 && first2 != last2) + { + if (comp(*first1, *first2)) + { + ++first1; + } + else + { + if (!comp(*first2, *first1)) + { + return false; // *first1 and *first2 are equivalent. + } + ++first2; + } + } + return true; + } + } + + template + auto flat_set::is_disjoint_of(const Self& other) const -> bool + { + return detail::set_disjoint(cbegin(), cend(), other.cbegin(), other.cend(), m_compare); + } + template bool operator==(const flat_set& lhs, const flat_set& rhs) { @@ -305,5 +394,135 @@ namespace mamba::util return !(lhs == rhs); } + template + auto flat_set::is_subset_of(const Self& other) const -> bool + { + return std::includes(other.cbegin(), other.cend(), cbegin(), cend(), m_compare); + } + + template + bool + operator<=(const flat_set& lhs, const flat_set& rhs) + { + return lhs.is_subset_of(rhs); + } + + template + bool + operator<(const flat_set& lhs, const flat_set& rhs) + { + return (lhs.size() < rhs.size()) && (lhs <= rhs); + } + + template + auto flat_set::is_superset_of(const Self& other) const -> bool + { + return other.is_subset_of(*this); + } + + template + bool + operator>=(const flat_set& lhs, const flat_set& rhs) + { + return lhs.is_superset_of(rhs); + } + + template + bool + operator>(const flat_set& lhs, const flat_set& rhs) + { + return rhs < lhs; + } + + template + auto flat_set::union_(const Self& lhs, const Self& rhs) -> Self + { + auto out = flat_set(); + out.reserve(std::max(lhs.size(), rhs.size())); // lower bound + std::set_union( + lhs.cbegin(), + lhs.cend(), + rhs.cbegin(), + rhs.cend(), + std::back_inserter(static_cast(out)), + lhs.m_compare + ); + return out; + } + + template + flat_set + operator|(const flat_set& lhs, const flat_set& rhs) + { + return flat_set::union_(lhs, rhs); + } + + template + auto flat_set::intersection(const Self& lhs, const Self& rhs) -> Self + { + auto out = flat_set(); + std::set_intersection( + lhs.cbegin(), + lhs.cend(), + rhs.cbegin(), + rhs.cend(), + std::back_inserter(static_cast(out)), + lhs.m_compare + ); + return out; + } + + template + flat_set + operator&(const flat_set& lhs, const flat_set& rhs) + { + return flat_set::intersection(lhs, rhs); + } + + template + auto flat_set::difference(const Self& lhs, const Self& rhs) -> Self + { + auto out = flat_set(); + std::set_difference( + lhs.cbegin(), + lhs.cend(), + rhs.cbegin(), + rhs.cend(), + std::back_inserter(static_cast(out)), + lhs.m_compare + ); + return out; + } + + template + flat_set + operator-(const flat_set& lhs, const flat_set& rhs) + { + return flat_set::difference(lhs, rhs); + } + + template + auto flat_set::symetric_difference(const Self& lhs, const Self& rhs) + -> Self + { + auto out = flat_set(); + std::set_symmetric_difference( + lhs.cbegin(), + lhs.cend(), + rhs.cbegin(), + rhs.cend(), + std::back_inserter(static_cast(out)), + lhs.m_compare + ); + return out; + } + + template + flat_set + operator^(const flat_set& lhs, const flat_set& rhs) + { + return flat_set::symetric_difference(lhs, rhs); + } + } #endif diff --git a/libmamba/src/solv-cpp/queue.cpp b/libmamba/src/solv-cpp/queue.cpp index b19b9e6816..84582ca731 100644 --- a/libmamba/src/solv-cpp/queue.cpp +++ b/libmamba/src/solv-cpp/queue.cpp @@ -250,6 +250,11 @@ namespace mamba::solv return m_queue.elements; } + auto ObjQueue::contains(value_type id) const -> bool + { + return std::find(cbegin(), cend(), id) != cend(); + } + auto ObjQueue::raw() const -> const ::Queue* { return &m_queue; diff --git a/libmamba/src/solv-cpp/queue.hpp b/libmamba/src/solv-cpp/queue.hpp index edc546016e..ddc8589601 100644 --- a/libmamba/src/solv-cpp/queue.hpp +++ b/libmamba/src/solv-cpp/queue.hpp @@ -84,6 +84,8 @@ namespace mamba::solv auto data() -> pointer; auto data() const -> const_pointer; + auto contains(value_type id) const -> bool; + template