PerceMon
product.hpp
1#pragma once
2
3#ifndef __PERCEMON_ITER_PRODUCT_HPP__
4#define __PERCEMON_ITER_PRODUCT_HPP__
5
6#include <algorithm>
7#include <iterator>
8#include <vector>
9
10namespace percemon {
11namespace iter_helpers {
12namespace details {
16template <typename Iter>
18 public:
19 using ElementType = typename std::iterator_traits<Iter>::value_type;
20
21 using iterator_category = std::forward_iterator_tag;
22 using difference_type = std::ptrdiff_t;
23 using value_type = std::vector<ElementType>;
24 using pointer = std::vector<ElementType>*;
25 using reference = std::vector<ElementType>&;
26
27 product_iterator(Iter first, Iter last, size_t k) :
28 _begin{first}, _end{last}, _k{k}, _curr{std::vector<Iter>(k, first)} {};
29
30 bool operator!=(const product_iterator<Iter>& other) const {
31 for (size_t i = 0; i < _curr.size(); i++) {
32 if (_curr.at(i) != other._curr.at(i)) {
33 return true;
34 }
35 }
36 return false;
37 }
38 bool operator==(const product_iterator<Iter>& other) const {
39 return !(*this != other);
40 }
41
42 value_type operator*() const {
43 auto _curr_val = std::vector<ElementType>{};
44 std::transform(
45 std::begin(_curr),
46 std::end(_curr),
47 std::back_inserter(_curr_val),
48 [](const auto i) { return *i; });
49 return _curr_val;
50 }
51
52 product_iterator& operator++() {
53 increment(_k - 1);
54 return *this;
55 }
56
57 static product_iterator get_last(Iter first, Iter last, size_t k) {
58 auto ret = product_iterator{first, last, k, last};
59 return ret;
60 }
61
62 private:
63 product_iterator(Iter first, Iter last, size_t k, Iter fill) :
64 _begin{first}, _end{last}, _k{k}, _curr{std::vector<Iter>{k, fill}} {};
65
69 const Iter _begin;
70 const Iter _end;
74 size_t _k;
75
79 std::vector<Iter> _curr;
80
81 bool increment(int i) {
82 if (i >= 0) {
83 auto& it = _curr.at(i);
84 it++; // Increment the current place.
85 if (it == _end) {
86 bool to_reset = increment(i - 1);
87 if (to_reset) {
88 // Increment the next value and then reset the current.
89 // Order matters, or else, all the iterators will never be std::end together.
90 it = _begin;
91 }
92 return to_reset;
93 }
94 return true;
95 } else {
96 // This prevents the thing from being reset in the last iteration.
97 return false;
98 }
99 }
100};
101
102template <
103 typename Container,
104 typename Iter = decltype(std::begin(std::declval<Container>())),
105 typename = decltype(std::end(std::declval<Container>()))>
107 Container iterable;
108 size_t k;
109
110 auto begin() {
111 return product_iterator<Iter>{std::begin(iterable), std::end(iterable), k};
112 }
113 auto end() {
115 std::begin(iterable), std::end(iterable), k);
116 }
117};
118
119} // namespace details
120
121template <
122 typename Container,
123 typename Iter = decltype(std::begin(std::declval<Container>())),
124 typename = decltype(std::end(std::declval<Container>()))>
125constexpr auto product(Container&& iterable, size_t k = 1) {
126 return details::product_range<Container, Iter>{iterable, k};
127}
128
129} // namespace iter_helpers
130} // namespace percemon
131
132#endif /* end of include guard: __PERCEMON_ITER_PRODUCT_HPP__ */