ProvSQL C/C++ API
Adding support for provenance and uncertainty management to PostgreSQL databases
Loading...
Searching...
No Matches
flat_set.hpp
Go to the documentation of this file.
1/**
2 * @file flat_set.hpp
3 * @brief Flat (unsorted, contiguous-storage) set template.
4 *
5 * Heavily inspired from https://stackoverflow.com/a/30938947, credits to
6 * Yakk - Adam Nevraum @ StackOverflow
7 *
8 * @c flat_set<T, Storage, hash> is a lightweight set container that stores
9 * elements in a contiguous sequence. It is the set analogue of @c flat_map,
10 * with the same trade-offs: O(n) membership tests but cache-friendly
11 * performance for small sets, and pluggable storage for stack allocation.
12 *
13 * ProvSQL uses this as the @c suspicious_t type in
14 * @c dDNNFTreeDecompositionBuilder (a set of gates bounded by the
15 * treewidth + 1 of the circuit), and as the bag type @c Bag in
16 * @c TreeDecomposition.
17 *
18 * A @c std::hash specialisation is provided so that @c flat_set can be
19 * used as a key in @c std::unordered_map (via the @c hash template
20 * parameter for the element hash).
21 */
22#ifndef FLAT_SET_H
23#define FLAT_SET_H
24
25// Heavily inspired from https://stackoverflow.com/a/30938947, credits to
26// Yakk - Adam Nevraum @ StackOverflow
27
28#include <vector>
29#include <utility>
30#include <algorithm>
31
32/**
33 * @brief Flat set with pluggable storage.
34 *
35 * @tparam T Element type. Must be equality-comparable.
36 * @tparam Storage Container template for storage (default: @c std::vector).
37 * @tparam hash Hash functor for elements (used by the @c std::hash
38 * specialisation, default: @c std::hash<T>).
39 */
40template<class T, template<class ...>class Storage=std::vector, class hash=std::hash<T> >
41struct flat_set {
42 using storage_t = Storage<T>; ///< Underlying container type
43 using hash_t = hash; ///< Hash functor type for elements
44 storage_t storage; ///< Contiguous element storage
45
46 using iterator=decltype(std::begin(std::declval<storage_t&>())); ///< Mutable iterator
47 using const_iterator=decltype(std::begin(std::declval<const storage_t&>())); ///< Const iterator
48
49 // boilerplate:
50 /** @brief Return iterator to the first element. @return Mutable begin iterator. */
52 using std::begin;
53 return begin(storage);
54 }
55 /** @brief Return const iterator to the first element. @return Const begin iterator. */
57 using std::begin;
58 return begin(storage);
59 }
60 /** @brief Return const iterator to the first element. @return Const begin iterator. */
62 using std::begin;
63 return begin(storage);
64 }
65 /** @brief Return iterator past the last element. @return Mutable end iterator. */
67 using std::end;
68 return end(storage);
69 }
70 /** @brief Return const iterator past the last element. @return Const end iterator. */
72 using std::end;
73 return end(storage);
74 }
75 /** @brief Return const iterator past the last element. @return Const end iterator. */
77 using std::end;
78 return end(storage);
79 }
80 /** @brief Return the number of elements in the set. @return Element count. */
81 size_t size() const {
82 return storage.size();
83 }
84 /** @brief Return @c true if the set is empty. @return @c true if no elements are stored. */
85 bool empty() const {
86 return storage.empty();
87 }
88 // these only have to be valid if called:
89 /** @brief Reserve storage for at least @p n elements. @param n Minimum capacity. */
90 void reserve(size_t n) {
91 storage.reserve(n);
92 }
93 /** @brief Return the current storage capacity. @return Number of elements that can be stored without reallocation. */
94 size_t capacity() const {
95 return storage.capacity();
96 }
97public:
98 /**
99 * @brief Find an element equal to @p k.
100 * @param k Element to search for.
101 * @return Iterator to the element, or @c end() if not found.
102 */
103 template<class K, class=std::enable_if_t<std::is_convertible<K, T>{}> >
104 iterator find(K&& k) {
105 return std::find( begin(), end(), k );
106 }
107 /**
108 * @brief Find an element equal to @p k (const overload).
109 * @param k Element to search for.
110 * @return Const iterator to the element, or @c end() if not found.
111 */
112 template<class K, class=std::enable_if_t<std::is_convertible<K, T>{}> >
113 const_iterator find(K&& k) const {
114 return const_cast<flat_set*>(this)->find(k);
115 }
116 /**
117 * @brief Remove the element at @p it.
118 * @param it Iterator to the element to remove.
119 * @return Iterator to the element following the removed one.
120 */
122 return storage.erase(it);
123 }
124 /**
125 * @brief Insert @p value if not already present (const-ref overload).
126 * @param value Element to insert.
127 */
128 template<class K, class=std::enable_if_t<std::is_convertible<K,T>{}> >
129 void insert( const K& value )
130 {
131 auto it = find(value);
132 if (it == end())
133 storage.emplace_back( value );
134 }
135 /**
136 * @brief Insert @p value if not already present (rvalue overload).
137 * @param value Element to insert (moved).
138 */
139 template<class K, class=std::enable_if_t<std::is_convertible<K,T>{}> >
140 void insert( K&& value )
141 {
142 auto it = find(value);
143 if (it == end())
144 storage.emplace_back( std::forward<T>(value) );
145 }
146
147 /**
148 * @brief Compare two flat_sets for equality (order-independent).
149 * @param rhs Right-hand side set.
150 * @return @c true if both sets contain the same elements.
151 */
152 bool operator==(const flat_set &rhs) const
153 {
154 if(size()!=rhs.size())
155 return false;
156 for(auto i: rhs)
157 if(find(i)==end())
158 return false;
159
160 return true;
161 }
162
163 /**
164 * @brief Less-than comparison based on minimum differing element.
165 * @param rhs Right-hand side set.
166 * @return @c true if this set is less than @p rhs.
167 */
168 bool operator<(const flat_set &rhs) const
169 {
170 if(size()<rhs.size())
171 return true;
172 else if(size()>rhs.size())
173 return false;
174 T min{}; bool found=false;
175 for(const auto &i: *this) {
176 if(rhs.find(i)!=rhs.end())
177 continue;
178 if(found) {
179 if(i<min)
180 min=i;
181 } else {
182 min=i;
183 found=true;
184 }
185 }
186 if(!found)
187 return false;
188 T min2{}; found=false;
189 for(const auto &i: rhs) {
190 if(find(i)!=end())
191 continue;
192 if(found) {
193 if(i<min2)
194 min2=i;
195 } else {
196 min2=i;
197 found=true;
198 }
199 }
200 return min<min2;
201 }
202};
203
204namespace std {
205/**
206 * @brief @c std::hash specialisation for @c flat_set, enabling use as an unordered container key.
207 */
208template<class T, template<class ...> class Storage, class h>
209struct hash<flat_set<T, Storage, h> > {
210 /**
211 * @brief Compute the hash of a @c flat_set by XOR-folding element hashes.
212 * @param key The set to hash.
213 * @return Combined hash value.
214 */
215 size_t operator()(const flat_set<T, Storage> &key) const
216 {
217 size_t result = 0xDEADBEEF;
218 for(const auto &i: key)
219 result ^= h()(i);
220 return result;
221 }
222};
223}
224#endif /* FLAT_SET_H */
Flat set with pluggable storage.
Definition flat_set.hpp:41
decltype(std::begin(std::declval< const storage_t & >())) const_iterator
Const iterator.
Definition flat_set.hpp:47
iterator erase(const_iterator it)
Remove the element at it.
Definition flat_set.hpp:121
bool empty() const
Return true if the set is empty.
Definition flat_set.hpp:85
hash hash_t
Hash functor type for elements.
Definition flat_set.hpp:43
bool operator==(const flat_set &rhs) const
Compare two flat_sets for equality (order-independent).
Definition flat_set.hpp:152
iterator begin()
Return iterator to the first element.
Definition flat_set.hpp:51
const_iterator cend() const
Return const iterator past the last element.
Definition flat_set.hpp:76
iterator end()
Return iterator past the last element.
Definition flat_set.hpp:66
bool operator<(const flat_set &rhs) const
Less-than comparison based on minimum differing element.
Definition flat_set.hpp:168
Storage< T > storage_t
Underlying container type.
Definition flat_set.hpp:42
void insert(K &&value)
Insert value if not already present (rvalue overload).
Definition flat_set.hpp:140
void reserve(size_t n)
Reserve storage for at least n elements.
Definition flat_set.hpp:90
size_t capacity() const
Return the current storage capacity.
Definition flat_set.hpp:94
const_iterator find(K &&k) const
Find an element equal to k (const overload).
Definition flat_set.hpp:113
decltype(std::begin(std::declval< storage_t & >())) iterator
Mutable iterator.
Definition flat_set.hpp:46
size_t size() const
Return the number of elements in the set.
Definition flat_set.hpp:81
storage_t storage
Contiguous element storage.
Definition flat_set.hpp:44
void insert(const K &value)
Insert value if not already present (const-ref overload).
Definition flat_set.hpp:129
iterator find(K &&k)
Find an element equal to k.
Definition flat_set.hpp:104
const_iterator end() const
Return const iterator past the last element.
Definition flat_set.hpp:71
const_iterator begin() const
Return const iterator to the first element.
Definition flat_set.hpp:56
const_iterator cbegin() const
Return const iterator to the first element.
Definition flat_set.hpp:61
size_t operator()(const flat_set< T, Storage > &key) const
Compute the hash of a flat_set by XOR-folding element hashes.
Definition flat_set.hpp:215