GCC Code Coverage Report
Directory: next/ Exec Total Coverage
File: next/combiners.hpp Lines: 74 74 100.0 %
Date: 2023-04-03 07:16:25 Branches: 24 38 63.2 %

Line Branch Exec Source
1
#pragma once
2
3
#include <tuple>
4
5
#include "interfaces.hpp"
6
7
8
namespace next {
9
10
#pragma region join range
11
12
namespace details {
13
template <std::size_t M, typename Tuple, typename T> struct join_helper {
14
  static constexpr std::size_t N = std::tuple_size<Tuple>::value;
15
16
10
  static void initialize(Tuple &ranges) {
17
10
    std::get<M>(ranges).initialize();
18
    if constexpr (M < N - 1)
19
6
      join_helper<M + 1, Tuple, T>::initialize(ranges);
20
10
  }
21
22
68
  static std::optional<T> next_value(Tuple &ranges) {
23

68
    if (const auto v = std::get<M>(ranges).next())
24
30
      return v;
25
26
    if constexpr (M < N - 1)
27
34
      return join_helper<M + 1, Tuple, T>::next_value(ranges);
28
29
4
    return std::nullopt;
30
  }
31
};
32
33
} // namespace details
34
35
template <typename... Ranges> struct join : next_interface<join<Ranges...>> {
36
37
  static constexpr std::size_t N = sizeof...(Ranges);
38
39
  using type = join<Ranges...>;
40
  using range_type = std::tuple<Ranges &...>;
41
  using value_type =
42
      typename std::common_type<typename Ranges::value_type...>::type;
43
44
  range_type ranges;
45
46
4
  join(Ranges &...r) : ranges(r...) {}
47
1
  join(Ranges &&...r) : ranges(r...) {}
48
49
4
  void initialize() {
50
4
    details::join_helper<0, range_type, value_type>::initialize(ranges);
51
4
  }
52
53
34
  std::optional<value_type> next() {
54
34
    return details::join_helper<0, range_type, value_type>::next_value(ranges);
55
  }
56
};
57
58
#pragma endregion
59
60
#pragma region zip range
61
62
namespace details {
63
template <std::size_t M, typename Tuple, typename T> struct zip_helper {
64
  static constexpr std::size_t N = std::tuple_size<Tuple>::value;
65
66
14
  static void initialize(Tuple &ranges) {
67
14
    std::get<M>(ranges).initialize();
68
    if constexpr (M < N - 1)
69
8
      zip_helper<M + 1, Tuple, T>::initialize(ranges);
70
14
  }
71
72
54
  static std::optional<T> next_value(Tuple &ranges, T &tuple) {
73

54
    if (const auto v = std::get<M>(ranges).next()) {
74
48
      std::get<M>(tuple) = *v;
75
76
      if constexpr (M < N - 1)
77
28
        return zip_helper<M + 1, Tuple, T>::next_value(ranges, tuple);
78
79
      if constexpr (M == N - 1) {
80
20
        return {tuple};
81
      }
82
    }
83
6
    return std::nullopt;
84
  }
85
};
86
87
}; // namespace details
88
89
// Range which applies a transform to another range
90
template <typename... Ranges> struct zip : next_interface<zip<Ranges...>> {
91
92
  static constexpr std::size_t N = sizeof...(Ranges);
93
94
  using type = zip<Ranges...>;
95
  using range_type = std::tuple<Ranges &...>;
96
  using value_type = std::tuple<typename Ranges::value_type...>;
97
98
  range_type ranges;
99
  value_type tuple;
100
101
6
  zip(Ranges &...r) : ranges(r...) {}
102
1
  zip(Ranges &&...r) : ranges(r...) {}
103
104
6
  void initialize() {
105
6
    details::zip_helper<0, range_type, value_type>::initialize(ranges);
106
6
  }
107
108
26
  std::optional<value_type> next() {
109
26
    return details::zip_helper<0, range_type, value_type>::next_value(ranges,
110
26
                                                                      tuple);
111
  }
112
};
113
114
#pragma endregion
115
116
#pragma region product range
117
118
namespace details {
119
// recursive step
120
template <std::size_t M, std::size_t N, typename Tuple, typename T>
121
struct product_helper {
122
6
  static void initialize(Tuple &ranges, T &tuple) {
123
6
    std::get<M>(ranges).initialize();
124
6
    std::get<M>(tuple) = *std::get<M>(ranges).next();
125
6
    product_helper<M + 1, N, Tuple, T>::initialize(ranges, tuple);
126
6
  }
127
128
36
  static std::optional<T> next_value(Tuple &ranges, T &tuple) {
129

36
    if (const auto v = std::get<M>(ranges).next()) {
130
20
      std::get<M>(tuple) = *v;
131
20
      return product_helper<0, N, Tuple, T>::next_value(ranges, tuple);
132
    } else {
133
16
      std::get<M>(ranges).initialize();
134

16
      if (const auto v = std::get<M>(ranges).next()) {
135
16
        std::get<M>(tuple) = *v;
136
      }
137
16
      return product_helper<M + 1, N, Tuple, T>::next_value(ranges, tuple);
138
    }
139
  }
140
};
141
142
// start condition
143
template <std::size_t N, typename Tuple, typename T>
144
struct product_helper<0, N, Tuple, T> {
145
6
  static void initialize(Tuple &ranges, T &tuple) {
146
6
    std::get<0>(ranges).initialize();
147
6
    product_helper<1, N, Tuple, T>::initialize(ranges, tuple);
148
6
  }
149
150
102
  static std::optional<T> next_value(Tuple &ranges, T &tuple) {
151

102
    if (const auto v = std::get<0>(ranges).next()) {
152
68
      std::get<0>(tuple) = *v;
153
68
      return {tuple};
154
    } else {
155
34
      std::get<0>(ranges).initialize();
156
34
      return product_helper<1, N, Tuple, T>::next_value(ranges, tuple);
157
    }
158
  }
159
};
160
161
// stop condition
162
template <std::size_t N, typename Tuple, typename T>
163
struct product_helper<N, N, Tuple, T> {
164
6
  static void initialize(Tuple &ranges, T &tuple) {
165
6
    std::get<N>(ranges).initialize();
166
6
    std::get<N>(tuple) = *std::get<N>(ranges).next();
167
6
  }
168
169
14
  static std::optional<T> next_value(Tuple &ranges, T &tuple) {
170

14
    if (const auto v = std::get<N>(ranges).next()) {
171
8
      std::get<N>(tuple) = *v;
172
8
      return product_helper<0, N, Tuple, T>::next_value(ranges, tuple);
173
    } else {
174
6
      return std::nullopt;
175
    }
176
  }
177
};
178
179
} // namespace details
180
181
// Range which applies a transform to another range
182
template <typename... Ranges>
183
struct product : next_interface<product<Ranges...>> {
184
185
  static constexpr std::size_t N = sizeof...(Ranges);
186
187
  using type = product<Ranges...>;
188
  using range_type = std::tuple<Ranges &...>;
189
  using value_type = std::tuple<typename Ranges::value_type...>;
190
191
  range_type ranges;
192
  value_type tuple;
193
194
6
  product(Ranges &...r) : ranges(r...) {}
195
1
  product(Ranges &&...r) : ranges(r...) {}
196
197
6
  void initialize() {
198
6
    details::product_helper<0, N - 1, range_type, value_type>::initialize(
199
6
        ranges, tuple);
200
6
  }
201
202
37
  std::optional<value_type> next() {
203
    return details::product_helper<0, N - 1, range_type,
204
37
                                   value_type>::next_value(ranges, tuple);
205
  }
206
};
207
208
#pragma endregion
209
210
} // namespace next