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