10 #ifndef SYMBOLIC_UTILS_COMBINATION_GENERATOR_H_
11 #define SYMBOLIC_UTILS_COMBINATION_GENERATOR_H_
20 #include <type_traits>
34 template <
typename ContainerT>
49 template <
typename IteratorT>
50 class ReverseIterator;
54 using iterator =
typename std::conditional_t<std::is_const<ContainerT>::value,
55 Iterator<true>, Iterator<false>>;
56 using const_iterator = Iterator<true>;
58 using reverse_iterator = ReverseIterator<iterator>;
59 using const_reverse_iterator = ReverseIterator<const_iterator>;
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) +
".");
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()); };
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());
92 const_reverse_iterator rend()
const {
93 return const_reverse_iterator(begin());
95 const_reverse_iterator crbegin()
const {
96 return const_reverse_iterator(end());
98 const_reverse_iterator crend()
const {
99 return const_reverse_iterator(begin());
105 size_t size()
const {
return size_; }
110 bool empty()
const {
return size_ == 0; }
115 typename iterator::value_type
at(
int i)
const {
117 if (i < 0) i +=
size();
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()) +
".");
132 return *(begin() + i);
138 int find(
const typename iterator::value_type& combination)
const {
139 assert(combination.size() == options_.size());
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);
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());
157 const size_t idx_element = it_element - option.begin();
158 idx += size_groups_[i] * idx_element;
172 static std::vector<size_t> ComputeGroupSizes(
173 const std::vector<ContainerT*>& options) {
174 std::vector<size_t> size_groups(options.size());
177 if (options.empty())
return size_groups;
180 size_groups.back() = 1;
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];
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();
201 std::vector<ContainerT*> options_;
202 std::vector<size_t> size_groups_;
206 template <
typename ContainerT>
207 template <
bool Const>
208 class CombinationGenerator<ContainerT>::Iterator {
211 typename std::conditional_t<Const,
typename ContainerT::const_iterator,
212 typename ContainerT::iterator>;
213 using ValueT =
typename std::iterator_traits<IteratorT>::value_type;
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&;
223 Iterator(
const CombinationGenerator* gen,
int idx) : gen_(gen), idx_(idx) {
228 Iterator& operator++() {
return operator+=(1); }
230 Iterator operator++(
int) {
236 reference operator*()
const {
return combination_; };
238 pointer operator->()
const {
return &combination_; }
240 bool operator==(
const Iterator& rhs)
const {
241 return gen_ == rhs.gen_ && idx_ == rhs.idx_;
244 bool operator!=(
const Iterator& rhs)
const {
return !(*
this == rhs); }
247 Iterator& operator--() {
return operator+=(-1); }
249 Iterator operator--(
int) {
256 bool operator<(
const Iterator& rhs)
const {
257 return gen_ == rhs.gen_ && idx_ < rhs.idx_;
260 bool operator>(
const Iterator& rhs)
const {
return rhs < *
this; };
262 bool operator<=(
const Iterator& rhs)
const {
263 return gen_ == rhs.gen_ && idx_ <= rhs.idx_;
266 bool operator>=(
const Iterator& rhs)
const {
return rhs <= *
this; };
268 Iterator& operator+=(difference_type n) {
269 UpdateCombination(idx_ + n);
273 Iterator operator+(difference_type n)
const {
274 Iterator temp = *
this;
278 friend Iterator operator+(difference_type n,
const Iterator& it) {
282 Iterator& operator-=(difference_type n) {
return operator+=(-n); }
284 Iterator operator-(difference_type n)
const {
285 Iterator temp = *
this;
289 difference_type operator-(
const Iterator& rhs)
const {
290 return idx_ - rhs.idx_;
293 value_type
operator[](difference_type n)
const {
return *(operator+(n)); }
301 void UpdateCombination() {
303 if (idx_ >= gen_->size() || idx_ < 0)
return;
306 const size_t num_options = gen_->options_.size();
307 if (combination_.empty()) combination_.resize(num_options);
309 int remaining = idx_;
310 for (
size_t i = 0; i < num_options; i++) {
312 int idx_i = remaining / gen_->size_groups_[i];
313 remaining -= idx_i * gen_->size_groups_[i];
316 combination_[i] = gen_->options_[i]->at(idx_i);
318 assert(remaining == 0);
326 void UpdateCombination(
int idx_new) {
328 if (idx_ == idx_new)
return;
331 if (idx_new >= gen_->size() || idx_new < 0) {
337 const size_t num_options = gen_->options_.size();
338 if (combination_.empty()) combination_.resize(num_options);
340 int remaining = idx_;
341 int remaining_new = idx_new;
342 for (
size_t i = 0; i < num_options; i++) {
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];
350 if (idx_i == idx_i_new)
continue;
353 combination_[i] = gen_->options_[i]->at(idx_i_new);
356 assert(remaining == 0);
359 const CombinationGenerator* gen_ =
nullptr;
360 std::vector<ValueT> combination_;
363 friend class CombinationGenerator;
366 template <
typename ContainerT>
367 template <
typename IteratorT>
368 class CombinationGenerator<ContainerT>::ReverseIterator {
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;
379 explicit ReverseIterator(IteratorT&& it) : it_(std::move(--it)) {}
382 ReverseIterator& operator++() {
387 ReverseIterator operator++(
int) {
388 ReverseIterator it = *
this;
393 reference operator*()
const {
return *it_; }
395 pointer operator->()
const {
return it_.operator->(); }
397 bool operator==(
const ReverseIterator& rhs)
const {
return it_ == rhs.it_; }
399 bool operator!=(
const ReverseIterator& rhs)
const {
return it_ != rhs.it_; }
402 ReverseIterator& operator--() {
407 ReverseIterator operator--(
int) {
408 ReverseIterator it = *
this;
414 bool operator<(
const ReverseIterator& rhs)
const {
return it_ > rhs.it_; }
416 bool operator>(
const ReverseIterator& rhs)
const {
return rhs < *
this; };
418 bool operator<=(
const ReverseIterator& rhs)
const {
return it_ >= rhs.it_; }
420 bool operator>=(
const ReverseIterator& rhs)
const {
return rhs <= *
this; };
422 ReverseIterator& operator+=(difference_type n) {
427 ReverseIterator operator+(difference_type n)
const {
428 ReverseIterator temp = *
this;
432 friend ReverseIterator operator+(difference_type n,
433 const ReverseIterator& it) {
437 ReverseIterator& operator-=(difference_type n) {
return operator+=(-n); }
439 ReverseIterator operator-(difference_type n)
const {
440 Iterator temp = *
this;
444 difference_type operator-(
const ReverseIterator& rhs)
const {
445 return rhs.it_ - it_;
448 value_type
operator[](difference_type n)
const {
return *(operator+(n)); }
456 #endif // SYMBOLIC_UTILS_COMBINATION_GENERATOR_H_