OR-Tools  9.2
id_set.h
Go to the documentation of this file.
1// Copyright 2010-2021 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14#ifndef OR_TOOLS_MATH_OPT_CPP_ID_SET_H_
15#define OR_TOOLS_MATH_OPT_CPP_ID_SET_H_
16
17#include <initializer_list>
18#include <iterator>
19#include <utility>
20
22#include "absl/container/flat_hash_set.h"
26
27namespace operations_research {
28namespace math_opt {
29
30// Similar to a absl::flat_hash_set<K> for K as Variable or LinearConstraint.
31//
32// Important differences:
33// * The storage is more efficient, as we store the underlying ids directly.
34// * The consequence of that is that the keys are usually returned by value in
35// situations where the flat_hash_set would return references.
36// * You cannot mix variables/constraints from multiple models in these maps.
37// Doing so results in a CHECK failure.
38//
39// Implementation notes:
40// * Emptying the set (with clear() or erase()) resets the underlying model to
41// nullptr, enabling reusing the same instance with a different model.
42// * Operator= and swap() support operating with different models by
43// respectively replacing or swapping it.
44// * For details requirements on K, see key_types.h.
45//
46// See also IdMap for the equivalent class for maps.
47template <typename K>
48class IdSet {
49 public:
50 using IdType = typename K::IdType;
51 using StorageType = absl::flat_hash_set<IdType>;
52 using key_type = K;
54 using size_type = typename StorageType::size_type;
55 using difference_type = typename StorageType::difference_type;
56 using reference = K;
57 using const_reference = const K;
58 using pointer = void;
59 using const_pointer = void;
60
62 public:
67 using iterator_category = std::forward_iterator_tag;
68
69 const_iterator() = default;
70
71 inline const_reference operator*() const;
73 inline const_iterator& operator++();
74 inline const_iterator operator++(int);
75
76 friend bool operator==(const const_iterator& lhs,
77 const const_iterator& rhs) {
78 return lhs.storage_iterator_ == rhs.storage_iterator_;
79 }
80 friend bool operator!=(const const_iterator& lhs,
81 const const_iterator& rhs) {
82 return lhs.storage_iterator_ != rhs.storage_iterator_;
83 }
84
85 private:
86 friend class IdSet;
87
88 inline const_iterator(
89 const IdSet* set,
90 typename StorageType::const_iterator storage_iterator);
91
92 const IdSet* set_ = nullptr;
93 typename StorageType::const_iterator storage_iterator_;
94 };
95
96 // All iterators on sets are const; but STL still defines the `iterator`
97 // type. The `flat_hash_set` defines two classes the but the policy makes both
98 // constant. Here to simplify the code we use the same type.
100
101 IdSet() = default;
102 template <typename InputIt>
103 inline IdSet(InputIt first, InputIt last);
104 inline IdSet(std::initializer_list<value_type> ilist);
105
106 // Typically for internal use only.
107 inline IdSet(const ModelStorage* storage, StorageType values);
108
109 inline const_iterator cbegin() const;
110 inline const_iterator begin() const;
111
112 inline const_iterator cend() const;
113 inline const_iterator end() const;
114
115 bool empty() const { return set_.empty(); }
116 size_type size() const { return set_.size(); }
117 inline void clear();
118 void reserve(size_type count) { set_.reserve(count); }
119
120 inline std::pair<const_iterator, bool> insert(const K& k);
121 template <typename InputIt>
122 inline void insert(InputIt first, InputIt last);
123 inline void insert(std::initializer_list<value_type> ilist);
124
125 inline std::pair<const_iterator, bool> emplace(const K& k);
126
127 // Returns the number of elements erased (zero or one).
128 inline int erase(const K& k);
129 // In STL erase(const_iterator) returns an iterator. But flat_hash_set instead
130 // has void return types. So here we also use void.
131 inline void erase(const_iterator pos);
132 inline const_iterator erase(const_iterator first, const_iterator last);
133
134 inline void swap(IdSet& other);
135
136 inline size_type count(const K& k) const;
137 inline bool contains(const K& k) const;
138 inline const_iterator find(const K& k) const;
139 inline std::pair<const_iterator, const_iterator> equal_range(
140 const K& k) const;
141
142 const StorageType& raw_set() const { return set_; }
143 const ModelStorage* storage() const { return storage_; }
144
145 friend bool operator==(const IdSet& lhs, const IdSet& rhs) {
146 return lhs.storage_ == rhs.storage_ && lhs.set_ == rhs.set_;
147 }
148 friend bool operator!=(const IdSet& lhs, const IdSet& rhs) {
149 return !(lhs == rhs);
150 }
151
152 private:
153 // CHECKs that storage_ and k.storage() matches when this set is not empty
154 // (i.e. its storage_ is not null). When it is empty, simply check that
155 // k.storage() is not null.
156 inline void CheckModel(const K& k) const;
157 // Sets storage_ to k.storage() if this set is empty (i.e. its storage_ is
158 // null). Else CHECK that it has the same storage. It also CHECK that
159 // k.storage() is not null.
160 inline void CheckOrSetModel(const K& k);
161
162 // Invariant: storage == nullptr if and only if set_.empty().
163 const ModelStorage* storage_ = nullptr;
164 StorageType set_;
165};
166
167// Calls a.swap(b).
168//
169// This function is used for making IdSet "swappable".
170// Ref: https://en.cppreference.com/w/cpp/named_req/Swappable.
171template <typename K>
173 a.swap(b);
174}
175
177// Inline implementations
179
181// IdSet::const_iterator
183
184template <typename K>
187 return K(set_->storage_, *storage_iterator_);
188}
189
190template <typename K>
194}
195
196template <typename K>
198 ++storage_iterator_;
199 return *this;
200}
201
202template <typename K>
204 const_iterator ret = *this;
205 ++(*this);
206 return ret;
207}
208
209template <typename K>
211 const IdSet* set, typename StorageType::const_iterator storage_iterator)
212 : set_(set), storage_iterator_(std::move(storage_iterator)) {}
213
215// IdSet
217
218template <typename K>
220 : storage_(storage), set_(std::move(values)) {
221 if (!set_.empty()) {
222 CHECK(storage_ != nullptr);
223 }
224}
225
226template <typename K>
227template <typename InputIt>
228IdSet<K>::IdSet(InputIt first, InputIt last) {
229 insert(first, last);
230}
231
232template <typename K>
233IdSet<K>::IdSet(std::initializer_list<value_type> ilist) {
234 insert(ilist);
235}
236
237template <typename K>
239 return const_iterator(this, set_.cbegin());
240}
241
242template <typename K>
244 return cbegin();
245}
246
247template <typename K>
249 return const_iterator(this, set_.cend());
250}
251
252template <typename K>
254 return cend();
255}
256
257template <typename K>
259 storage_ = nullptr;
260 set_.clear();
261}
262
263template <typename K>
264std::pair<typename IdSet<K>::const_iterator, bool> IdSet<K>::insert(
265 const K& k) {
266 return emplace(k);
267}
268
269template <typename K>
270template <typename InputIt>
271void IdSet<K>::insert(const InputIt first, const InputIt last) {
272 for (InputIt it = first; it != last; ++it) {
273 insert(*it);
274 }
275}
276
277template <typename K>
278void IdSet<K>::insert(std::initializer_list<value_type> ilist) {
279 insert(ilist.begin(), ilist.end());
280}
281
282template <typename K>
283std::pair<typename IdSet<K>::const_iterator, bool> IdSet<K>::emplace(
284 const K& k) {
285 CheckOrSetModel(k);
286 auto initial_ret = set_.emplace(k.typed_id());
287 return std::make_pair(const_iterator(this, std::move(initial_ret.first)),
288 initial_ret.second);
289}
290
291template <typename K>
292int IdSet<K>::erase(const K& k) {
293 CheckModel(k);
294 const int ret = set_.erase(k.typed_id());
295 if (set_.empty()) {
296 storage_ = nullptr;
297 }
298 return ret;
299}
300
301template <typename K>
303 set_.erase(pos.storage_iterator_);
304 if (set_.empty()) {
305 storage_ = nullptr;
306 }
307}
308
309template <typename K>
311 const const_iterator last) {
312 auto ret = set_.erase(first.storage_iterator_, last.storage_iterator_);
313 if (set_.empty()) {
314 storage_ = nullptr;
315 }
316 return const_iterator(this, std::move(ret));
317}
318
319template <typename K>
320void IdSet<K>::swap(IdSet& other) {
321 using std::swap;
322 swap(storage_, other.storage_);
323 swap(set_, other.set_);
324}
325
326template <typename K>
327typename IdSet<K>::size_type IdSet<K>::count(const K& k) const {
328 CheckModel(k);
329 return set_.count(k.typed_id());
330}
331
332template <typename K>
333bool IdSet<K>::contains(const K& k) const {
334 CheckModel(k);
335 return set_.contains(k.typed_id());
336}
337
338template <typename K>
339typename IdSet<K>::const_iterator IdSet<K>::find(const K& k) const {
340 CheckModel(k);
341 return const_iterator(this, set_.find(k.typed_id()));
342}
343
344template <typename K>
345std::pair<typename IdSet<K>::const_iterator, typename IdSet<K>::const_iterator>
346IdSet<K>::equal_range(const K& k) const {
347 const auto it = find(k);
348 if (it == end()) {
349 return {it, it};
350 }
351 return {it, std::next(it)};
352}
353
354template <typename K>
355void IdSet<K>::CheckModel(const K& k) const {
356 CHECK(k.storage() != nullptr) << internal::kKeyHasNullModelStorage;
357 CHECK(storage_ == nullptr || storage_ == k.storage())
359}
360
361template <typename K>
362void IdSet<K>::CheckOrSetModel(const K& k) {
363 CHECK(k.storage() != nullptr) << internal::kKeyHasNullModelStorage;
364 if (storage_ == nullptr) {
365 storage_ = k.storage();
366 } else {
367 CHECK_EQ(storage_, k.storage()) << internal::kObjectsFromOtherModelStorage;
368 }
369}
370
371} // namespace math_opt
372} // namespace operations_research
373
374#endif // OR_TOOLS_MATH_OPT_CPP_ID_SET_H_
#define CHECK(condition)
Definition: base/logging.h:495
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:702
friend bool operator!=(const const_iterator &lhs, const const_iterator &rhs)
Definition: id_set.h:80
std::forward_iterator_tag iterator_category
Definition: id_set.h:67
friend bool operator==(const const_iterator &lhs, const const_iterator &rhs)
Definition: id_set.h:76
internal::ArrowOperatorProxy< reference > operator->() const
Definition: id_set.h:192
size_type count(const K &k) const
Definition: id_set.h:327
void reserve(size_type count)
Definition: id_set.h:118
absl::flat_hash_set< IdType > StorageType
Definition: id_set.h:51
const_iterator end() const
Definition: id_set.h:253
std::pair< const_iterator, bool > emplace(const K &k)
Definition: id_set.h:283
const StorageType & raw_set() const
Definition: id_set.h:142
bool contains(const K &k) const
Definition: id_set.h:333
const_iterator cend() const
Definition: id_set.h:248
typename StorageType::size_type size_type
Definition: id_set.h:54
const_iterator begin() const
Definition: id_set.h:243
std::pair< const_iterator, const_iterator > equal_range(const K &k) const
Definition: id_set.h:346
typename K::IdType IdType
Definition: id_set.h:50
friend bool operator==(const IdSet &lhs, const IdSet &rhs)
Definition: id_set.h:145
std::pair< const_iterator, bool > insert(const K &k)
Definition: id_set.h:264
const_iterator cbegin() const
Definition: id_set.h:238
typename StorageType::difference_type difference_type
Definition: id_set.h:55
const_iterator find(const K &k) const
Definition: id_set.h:339
friend bool operator!=(const IdSet &lhs, const IdSet &rhs)
Definition: id_set.h:148
const ModelStorage * storage() const
Definition: id_set.h:143
int64_t b
int64_t a
Block * next
constexpr absl::string_view kKeyHasNullModelStorage
Definition: key_types.h:51
constexpr absl::string_view kObjectsFromOtherModelStorage
Definition: key_types.h:56
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:262
void swap(IdSet< K > &a, IdSet< K > &b)
Definition: id_set.h:172
Collection of objects used to extend the Constraint Solver library.
STL namespace.
std::optional< int64_t > end