#pragma once #include #include #include "StarFlatHashSet.hpp" #include "StarList.hpp" namespace Star { STAR_EXCEPTION(SetException, StarException); template class SetMixin : public BaseSet { public: typedef BaseSet Base; typedef typename Base::iterator iterator; typedef typename Base::const_iterator const_iterator; typedef typename Base::value_type value_type; using Base::Base; List values() const; bool contains(value_type const& v) const; bool add(value_type const& v); // Like add, but always adds new value, potentially replacing another equal // (comparing equal, may not be actually equal) value. Returns whether an // existing value was replaced. bool replace(value_type v); template void addAll(Container const& s); bool remove(value_type const& v); template void removeAll(Container const& s); value_type first(); Maybe maybeFirst(); value_type takeFirst(); Maybe maybeTakeFirst(); value_type last(); Maybe maybeLast(); value_type takeLast(); Maybe maybeTakeLast(); bool hasIntersection(SetMixin const& s) const; }; template std::ostream& operator<<(std::ostream& os, SetMixin const& set); template , typename Allocator = std::allocator> class Set : public SetMixin> { public: typedef SetMixin> Base; typedef typename Base::iterator iterator; typedef typename Base::const_iterator const_iterator; typedef typename Base::value_type value_type; template static Set from(Container const& c); using Base::Base; // Returns set of elements that are in this set and the given set. Set intersection(Set const& s) const; Set intersection(Set const& s, std::function compare) const; // Returns elements in this set that are not in the given set Set difference(Set const& s) const; Set difference(Set const& s, std::function compare) const; // Returns elements in either this set or the given set Set combination(Set const& s) const; }; template class HashSetMixin : public SetMixin { public: typedef SetMixin Base; typedef typename Base::iterator iterator; typedef typename Base::const_iterator const_iterator; typedef typename Base::value_type value_type; template static HashSetMixin from(Container const& c); using Base::Base; HashSetMixin intersection(HashSetMixin const& s) const; HashSetMixin difference(HashSetMixin const& s) const; HashSetMixin combination(HashSetMixin const& s) const; }; template , typename Equals = std::equal_to, typename Allocator = std::allocator> using HashSet = HashSetMixin>; template , typename Equals = std::equal_to, typename Allocator = std::allocator> using StableHashSet = HashSetMixin>; template auto SetMixin::values() const -> List { return List(Base::begin(), Base::end()); } template bool SetMixin::contains(value_type const& v) const { return Base::find(v) != Base::end(); } template bool SetMixin::add(value_type const& v) { return Base::insert(v).second; } template bool SetMixin::replace(value_type v) { bool replaced = remove(v); Base::insert(std::move(v)); return replaced; } template template void SetMixin::addAll(Container const& s) { return Base::insert(s.begin(), s.end()); } template bool SetMixin::remove(value_type const& v) { return Base::erase(v) != 0; } template template void SetMixin::removeAll(Container const& s) { for (auto const& v : s) remove(v); } template auto SetMixin::first() -> value_type { if (Base::empty()) throw SetException("first called on empty set"); return *Base::begin(); } template auto SetMixin::maybeFirst() -> Maybe { if (Base::empty()) return {}; return *Base::begin(); } template auto SetMixin::takeFirst() -> value_type { if (Base::empty()) throw SetException("takeFirst called on empty set"); auto i = Base::begin(); value_type v = std::move(*i); Base::erase(i); return v; } template auto SetMixin::maybeTakeFirst() -> Maybe { if (Base::empty()) return {}; auto i = Base::begin(); value_type v = std::move(*i); Base::erase(i); return std::move(v); } template auto SetMixin::last() -> value_type { if (Base::empty()) throw SetException("last called on empty set"); return *prev(Base::end()); } template auto SetMixin::maybeLast() -> Maybe { if (Base::empty()) return {}; return *prev(Base::end()); } template auto SetMixin::takeLast() -> value_type { if (Base::empty()) throw SetException("takeLast called on empty set"); auto i = prev(Base::end()); value_type v = std::move(*i); Base::erase(i); return v; } template auto SetMixin::maybeTakeLast() -> Maybe { if (Base::empty()) return {}; auto i = prev(Base::end()); value_type v = std::move(*i); Base::erase(i); return std::move(v); } template bool SetMixin::hasIntersection(SetMixin const& s) const { for (auto const& v : s) { if (contains(v)) { return true; } } return false; } template std::ostream& operator<<(std::ostream& os, SetMixin const& set) { os << "("; for (auto i = set.begin(); i != set.end(); ++i) { if (i != set.begin()) os << ", "; os << *i; } os << ")"; return os; } template template Set Set::from(Container const& c) { return Set(c.begin(), c.end()); } template Set Set::intersection(Set const& s) const { Set res; std::set_intersection(Base::begin(), Base::end(), s.begin(), s.end(), std::inserter(res, res.end())); return res; } template Set Set::intersection(Set const& s, std::function compare) const { Set res; std::set_intersection(Base::begin(), Base::end(), s.begin(), s.end(), std::inserter(res, res.end()), compare); return res; } template Set Set::difference(Set const& s) const { Set res; std::set_difference(Base::begin(), Base::end(), s.begin(), s.end(), std::inserter(res, res.end())); return res; } template Set Set::difference(Set const& s, std::function compare) const { Set res; std::set_difference(Base::begin(), Base::end(), s.begin(), s.end(), std::inserter(res, res.end()), compare); return res; } template Set Set::combination(Set const& s) const { Set ret(*this); ret.addAll(s); return ret; } template template HashSetMixin HashSetMixin::from(Container const& c) { return HashSetMixin(c.begin(), c.end()); } template HashSetMixin HashSetMixin::intersection(HashSetMixin const& s) const { // Can't use std::set_intersection, since not sorted, naive version is fine. HashSetMixin ret; for (auto const& e : s) { if (contains(e)) ret.add(e); } return ret; } template HashSetMixin HashSetMixin::difference(HashSetMixin const& s) const { // Can't use std::set_difference, since not sorted, naive version is fine. HashSetMixin ret; for (auto const& e : *this) { if (!s.contains(e)) ret.add(e); } return ret; } template HashSetMixin HashSetMixin::combination(HashSetMixin const& s) const { HashSetMixin ret(*this); ret.addAll(s); return ret; } }