symbolic
combination_generator.h
1 
10 #ifndef SYMBOLIC_UTILS_COMBINATION_GENERATOR_H_
11 #define SYMBOLIC_UTILS_COMBINATION_GENERATOR_H_
12 
13 #include <algorithm> // std::find
14 #include <cassert> // assert
15 #include <cstddef> // ptrdiff_t
16 #include <exception> // std::invalid_argument, std::out_of_range
17 #include <iterator> // std::random_access_iterator_tag, std::iterator_traits
18 #include <sstream> // std::stringstream
19 #include <string> // std::to_string
20 #include <type_traits> // std::conditional_t, std::is_const
21 #include <vector> // std::vector
22 
23 namespace symbolic {
24 
34 template <typename ContainerT>
36  private:
42  template <bool Const>
43  class Iterator;
44 
49  template <typename IteratorT>
50  class ReverseIterator;
51 
52  public:
53  // Iterator types
54  using iterator = typename std::conditional_t<std::is_const<ContainerT>::value,
55  Iterator<true>, Iterator<false>>;
56  using const_iterator = Iterator<true>;
57 
58  using reverse_iterator = ReverseIterator<iterator>;
59  using const_reverse_iterator = ReverseIterator<const_iterator>;
60 
61  // Constructors
62  CombinationGenerator() = default;
63  virtual ~CombinationGenerator() = default;
64 
65  explicit CombinationGenerator(const std::vector<ContainerT*>& options)
66  : options_(options),
67  size_groups_(ComputeGroupSizes(options)),
68  size_(ComputeSize(options, size_groups_)) {
69  for (size_t i = 0; i < options.size(); i++) {
70  if (options[i]->begin() == options[i]->end()) {
71  throw std::invalid_argument(
72  "CombinationGenerator(): Empty option at position " +
73  std::to_string(i) + ".");
74  }
75  }
76  }
77 
78  // Iterators
79  iterator begin() { return iterator(this, 0); };
80  iterator end() { return iterator(this, size()); };
81  const_iterator begin() const { return const_iterator(this, 0); };
82  const_iterator end() const { return const_iterator(this, size()); };
83  const_iterator cbegin() const { return const_iterator(this, 0); };
84  const_iterator cend() const { return const_iterator(this, size()); };
85 
86  // Reverse iterators
87  reverse_iterator rbegin() { return reverse_iterator(end()); }
88  reverse_iterator rend() { return reverse_iterator(begin()); }
89  const_reverse_iterator rbegin() const {
90  return const_reverse_iterator(end());
91  }
92  const_reverse_iterator rend() const {
93  return const_reverse_iterator(begin());
94  }
95  const_reverse_iterator crbegin() const {
96  return const_reverse_iterator(end());
97  }
98  const_reverse_iterator crend() const {
99  return const_reverse_iterator(begin());
100  }
101 
105  size_t size() const { return size_; }
106 
110  bool empty() const { return size_ == 0; }
111 
115  typename iterator::value_type at(int i) const {
116  // Wrap around negative indices
117  if (i < 0) i += size();
118 
119  // Check for bounds
120  if (i >= size() || i < 0) {
121  throw std::out_of_range("ParameterGenerator::at(" + std::to_string(i) +
122  "): index beyond bounds" +
123  std::to_string(size()) + ".");
124  }
125  return operator[](i);
126  }
127 
131  typename iterator::value_type operator[](int i) const {
132  return *(begin() + i);
133  }
134 
138  int find(const typename iterator::value_type& combination) const {
139  assert(combination.size() == options_.size());
140  size_t idx = 0;
141 
142  // Iterate over digit slots
143  for (size_t i = 0; i < combination.size(); i++) {
144  const typename iterator::ValueT& element = combination[i];
145  const ContainerT& option = *options_.at(i);
146 
147  // Find element in current option
148  const auto it_element = std::find(option.begin(), option.end(), element);
149  if (it_element == option.end()) {
150  std::stringstream ss;
151  ss << "CombinationGenerator::find(): No element " << element
152  << " in slot " << i << ".";
153  throw std::out_of_range(ss.str());
154  }
155 
156  // Get index of element
157  const size_t idx_element = it_element - option.begin();
158  idx += size_groups_[i] * idx_element;
159  }
160 
161  return idx;
162  }
163 
164  private:
172  static std::vector<size_t> ComputeGroupSizes(
173  const std::vector<ContainerT*>& options) {
174  std::vector<size_t> size_groups(options.size());
175 
176  // Return if options is empty
177  if (options.empty()) return size_groups;
178 
179  // Last group has digit size 1
180  size_groups.back() = 1;
181 
182  // Compute group sizes from right to left
183  for (int i = options.size() - 2; i >= 0; i--) {
184  const size_t num_next = options[i + 1]->size();
185  size_groups[i] = num_next * size_groups[i + 1];
186  }
187  return size_groups;
188  }
189 
195  static size_t ComputeSize(const std::vector<ContainerT*>& options,
196  const std::vector<size_t>& size_groups) {
197  if (options.empty()) return 0;
198  return options.front()->size() * size_groups.front();
199  }
200 
201  std::vector<ContainerT*> options_;
202  std::vector<size_t> size_groups_;
203  size_t size_ = 0;
204 };
205 
206 template <typename ContainerT>
207 template <bool Const>
208 class CombinationGenerator<ContainerT>::Iterator {
209  public:
210  using IteratorT =
211  typename std::conditional_t<Const, typename ContainerT::const_iterator,
212  typename ContainerT::iterator>;
213  using ValueT = typename std::iterator_traits<IteratorT>::value_type;
214 
215  // Iterator traits
216  using iterator_category = std::random_access_iterator_tag;
217  using value_type = std::vector<ValueT>;
218  using difference_type = ptrdiff_t;
219  using pointer = const value_type*;
220  using reference = const value_type&;
221 
222  // Constructor
223  Iterator(const CombinationGenerator* gen, int idx) : gen_(gen), idx_(idx) {
224  UpdateCombination();
225  }
226 
227  // Forward iterator
228  Iterator& operator++() { return operator+=(1); }
229 
230  Iterator operator++(int) {
231  Iterator it = *this;
232  operator++();
233  return it;
234  }
235 
236  reference operator*() const { return combination_; };
237 
238  pointer operator->() const { return &combination_; }
239 
240  bool operator==(const Iterator& rhs) const {
241  return gen_ == rhs.gen_ && idx_ == rhs.idx_;
242  }
243 
244  bool operator!=(const Iterator& rhs) const { return !(*this == rhs); }
245 
246  // Bidirectional iterator
247  Iterator& operator--() { return operator+=(-1); }
248 
249  Iterator operator--(int) {
250  Iterator it = *this;
251  operator--();
252  return it;
253  }
254 
255  // Random access iterator
256  bool operator<(const Iterator& rhs) const {
257  return gen_ == rhs.gen_ && idx_ < rhs.idx_;
258  }
259 
260  bool operator>(const Iterator& rhs) const { return rhs < *this; };
261 
262  bool operator<=(const Iterator& rhs) const {
263  return gen_ == rhs.gen_ && idx_ <= rhs.idx_;
264  }
265 
266  bool operator>=(const Iterator& rhs) const { return rhs <= *this; };
267 
268  Iterator& operator+=(difference_type n) {
269  UpdateCombination(idx_ + n);
270  return *this;
271  }
272 
273  Iterator operator+(difference_type n) const {
274  Iterator temp = *this;
275  return temp += n;
276  }
277 
278  friend Iterator operator+(difference_type n, const Iterator& it) {
279  return it + n;
280  }
281 
282  Iterator& operator-=(difference_type n) { return operator+=(-n); }
283 
284  Iterator operator-(difference_type n) const {
285  Iterator temp = *this;
286  return temp -= n;
287  }
288 
289  difference_type operator-(const Iterator& rhs) const {
290  return idx_ - rhs.idx_;
291  }
292 
293  value_type operator[](difference_type n) const { return *(operator+(n)); }
294 
295  private:
301  void UpdateCombination() {
302  // Do nothing if the index exceeds the number of combinations
303  if (idx_ >= gen_->size() || idx_ < 0) return;
304 
305  // Preallocate combination if it hasn't been already
306  const size_t num_options = gen_->options_.size();
307  if (combination_.empty()) combination_.resize(num_options);
308 
309  int remaining = idx_;
310  for (size_t i = 0; i < num_options; i++) {
311  // Compute the index for the current bin
312  int idx_i = remaining / gen_->size_groups_[i];
313  remaining -= idx_i * gen_->size_groups_[i];
314 
315  // Set the value
316  combination_[i] = gen_->options_[i]->at(idx_i);
317  }
318  assert(remaining == 0);
319  }
320 
326  void UpdateCombination(int idx_new) {
327  // Do nothing if the index is unchanged
328  if (idx_ == idx_new) return;
329 
330  // If index is outside the bounds, set it to the index and return
331  if (idx_new >= gen_->size() || idx_new < 0) {
332  idx_ = idx_new;
333  return;
334  }
335 
336  // Preallocate combination if it hasn't been already
337  const size_t num_options = gen_->options_.size();
338  if (combination_.empty()) combination_.resize(num_options);
339 
340  int remaining = idx_;
341  int remaining_new = idx_new;
342  for (size_t i = 0; i < num_options; i++) {
343  // Compute the index for the current bin
344  int idx_i = remaining / gen_->size_groups_[i];
345  int idx_i_new = remaining_new / gen_->size_groups_[i];
346  remaining -= idx_i * gen_->size_groups_[i];
347  remaining_new -= idx_i_new * gen_->size_groups_[i];
348 
349  // Skip if the index is unchanged
350  if (idx_i == idx_i_new) continue;
351 
352  // Set the value
353  combination_[i] = gen_->options_[i]->at(idx_i_new);
354  }
355  idx_ = idx_new;
356  assert(remaining == 0);
357  }
358 
359  const CombinationGenerator* gen_ = nullptr;
360  std::vector<ValueT> combination_;
361  int idx_;
362 
363  friend class CombinationGenerator;
364 };
365 
366 template <typename ContainerT>
367 template <typename IteratorT>
368 class CombinationGenerator<ContainerT>::ReverseIterator {
369  public:
370  // Iterator traits
371  using iterator_category =
372  typename std::iterator_traits<IteratorT>::iterator_category;
373  using value_type = typename std::iterator_traits<IteratorT>::value_type;
374  using difference_type =
375  typename std::iterator_traits<IteratorT>::difference_type;
376  using pointer = typename std::iterator_traits<IteratorT>::pointer;
377  using reference = typename std::iterator_traits<IteratorT>::reference;
378 
379  explicit ReverseIterator(IteratorT&& it) : it_(std::move(--it)) {}
380 
381  // Forward iterator
382  ReverseIterator& operator++() {
383  --it_;
384  return *this;
385  }
386 
387  ReverseIterator operator++(int) {
388  ReverseIterator it = *this;
389  operator++();
390  return it;
391  }
392 
393  reference operator*() const { return *it_; }
394 
395  pointer operator->() const { return it_.operator->(); }
396 
397  bool operator==(const ReverseIterator& rhs) const { return it_ == rhs.it_; }
398 
399  bool operator!=(const ReverseIterator& rhs) const { return it_ != rhs.it_; }
400 
401  // Bidirectional iterator
402  ReverseIterator& operator--() {
403  ++it_;
404  return *this;
405  };
406 
407  ReverseIterator operator--(int) {
408  ReverseIterator it = *this;
409  operator--();
410  return it;
411  }
412 
413  // Random access iterator
414  bool operator<(const ReverseIterator& rhs) const { return it_ > rhs.it_; }
415 
416  bool operator>(const ReverseIterator& rhs) const { return rhs < *this; };
417 
418  bool operator<=(const ReverseIterator& rhs) const { return it_ >= rhs.it_; }
419 
420  bool operator>=(const ReverseIterator& rhs) const { return rhs <= *this; };
421 
422  ReverseIterator& operator+=(difference_type n) {
423  it_ -= n;
424  return *this;
425  }
426 
427  ReverseIterator operator+(difference_type n) const {
428  ReverseIterator temp = *this;
429  return temp += n;
430  }
431 
432  friend ReverseIterator operator+(difference_type n,
433  const ReverseIterator& it) {
434  return it + n;
435  }
436 
437  ReverseIterator& operator-=(difference_type n) { return operator+=(-n); }
438 
439  ReverseIterator operator-(difference_type n) const {
440  Iterator temp = *this;
441  return temp -= n;
442  }
443 
444  difference_type operator-(const ReverseIterator& rhs) const {
445  return rhs.it_ - it_;
446  }
447 
448  value_type operator[](difference_type n) const { return *(operator+(n)); }
449 
450  private:
451  IteratorT it_;
452 };
453 
454 } // namespace symbolic
455 
456 #endif // SYMBOLIC_UTILS_COMBINATION_GENERATOR_H_
symbolic
Definition: action.cc:329
symbolic::CombinationGenerator::at
iterator::value_type at(int i) const
Definition: combination_generator.h:115
symbolic::CombinationGenerator::empty
bool empty() const
Definition: combination_generator.h:110
symbolic::CombinationGenerator::size
size_t size() const
Definition: combination_generator.h:105
symbolic::CombinationGenerator::operator[]
iterator::value_type operator[](int i) const
Definition: combination_generator.h:131
symbolic::CombinationGenerator
Definition: combination_generator.h:35
symbolic::CombinationGenerator::find
int find(const typename iterator::value_type &combination) const
Definition: combination_generator.h:138