GCC Code Coverage Report
Directory: next/ Exec Total Coverage
File: next/adapters.hpp Lines: 77 77 100.0 %
Date: 2023-04-03 07:16:25 Branches: 26 34 76.5 %

Line Branch Exec Source
1
#pragma once
2
3
#include <tuple>
4
#include <concepts>
5
6
#include "interfaces.hpp"
7
8
9
namespace next {
10
11
#pragma region filtered range
12
13
// Range which filters elements of another range, based on a predicate
14
template <InputRange Range, Predicate Pred>
15
struct filtered_range : next_interface<filtered_range<Range, Pred>> {
16
  Range range;
17
  const Pred pred;
18
19
  using type = filtered_range<Range, Pred>;
20
  using value_type = typename Range::value_type;
21
22
8
  filtered_range(Range range, const Pred &pred)
23
8
      : range(std::move(range)), pred(pred) {}
24
25
6
  void initialize() { range.initialize(); }
26
27
42
  std::optional<value_type> next() {
28

42
    while (const auto value = range.next())
29
38
      if (pred(*value))
30
18
        return value;
31
4
    return std::nullopt;
32
  }
33
};
34
35
template <InputRange Range, Predicate Pred>
36
filtered_range(Range, Pred) -> filtered_range<Range, Pred>;
37
38
template <Predicate Pred> struct filter_proxy { Pred pred; };
39
40
8
template <typename Pred> auto filter(Pred &&pred) {
41
8
  return filter_proxy<Pred>{std::forward<Pred>(pred)};
42
}
43
44
template <InputRange Range, Predicate Pred>
45
8
auto operator|(Range &&range, filter_proxy<Pred> proxy) {
46
8
  return filtered_range{std::forward<Range>(range), std::move(proxy.pred)};
47
}
48
49
#pragma endregion
50
51
#pragma region take from range
52
53
// Range which filters elements of another range, based on a predicate
54
template <InputRange Range>
55
struct take_range : next_interface<take_range<Range>> {
56
  Range range;
57
  std::size_t take;
58
59
  using type = take_range<Range>;
60
  using value_type = typename Range::value_type;
61
62
6
  take_range(Range r, std::size_t t) : range(std::move(r)), take(t) {}
63
64
4
  void initialize() { range.initialize(); }
65
66
18
  std::optional<value_type> next() {
67
18
    if (take > 0) {
68
14
      --take;
69
14
      return range.next();
70
    }
71
4
    return std::nullopt;
72
  }
73
};
74
75
template <typename Range> take_range(Range) -> take_range<Range>;
76
77
struct take_proxy {
78
  std::size_t take;
79
};
80
81
2
auto take(std::size_t take) { return take_proxy{take}; }
82
83
4
template <InputRange Range> auto operator|(Range &&range, take_proxy proxy) {
84
4
  return take_range{std::forward<Range>(range), proxy.take};
85
}
86
87
#pragma endregion
88
89
#pragma region drop from range
90
91
// Range which filters elements of another range, based on a predicate
92
template <InputRange Range>
93
struct drop_range : next_interface<drop_range<Range>> {
94
  Range range;
95
  std::size_t drop;
96
97
  using type = drop_range<Range>;
98
  using value_type = value_t<Range>;
99
100
2
  drop_range(Range r, std::size_t d) : range(std::move(r)), drop(d) {}
101
102
1
  void initialize() { range.initialize(); }
103
104
10
  std::optional<value_type> next() {
105
10
    while (drop > 0) {
106
5
      range.next();
107
5
      --drop;
108
    }
109
110
5
    return range.next();
111
  }
112
};
113
114
template <typename Range> drop_range(Range) -> drop_range<Range>;
115
116
struct drop_proxy {
117
  std::size_t drop;
118
};
119
120
2
auto drop(std::size_t drop) { return drop_proxy{drop}; }
121
122
4
template <InputRange Range> auto operator|(Range &&range, drop_proxy proxy) {
123
4
  return drop_range{std::forward<Range>(range), proxy.drop};
124
}
125
126
#pragma endregion
127
128
#pragma region take while
129
130
// Range which filters elements of another range, based on a predicate
131
template <InputRange Range, Predicate Pred>
132
struct take_while_range : next_interface<take_while_range<Range, Pred>> {
133
  Range range;
134
  Pred pred;
135
  bool toggle = true;
136
137
  using type = take_while_range<Range, Pred>;
138
  using value_type = typename Range::value_type;
139
140
4
  take_while_range(Range r, const Pred &p) : range(std::move(r)), pred(p) {}
141
142
1
  void initialize() { range.initialize(); }
143
144
4
  std::optional<value_type> next() {
145
4
    auto value = range.next();
146
4
    if (pred(*value))
147
3
      return value;
148
1
    return std::nullopt;
149
  }
150
};
151
152
template <typename Range, Predicate Pred>
153
take_while_range(Range, Pred) -> take_while_range<Range, Pred>;
154
155
template <Predicate Pred> struct take_while_proxy { Pred pred; };
156
157
4
template <Predicate Pred> auto take_while(Pred &&pred) {
158
4
  return take_while_proxy<Pred>{std::forward<Pred>(pred)};
159
}
160
161
template <InputRange Range, Predicate Pred>
162
4
auto operator|(Range &&range, take_while_proxy<Pred> proxy) {
163
4
  return take_while_range{std::forward<Range>(range), std::move(proxy.pred)};
164
}
165
166
#pragma endregion
167
168
#pragma region drop while
169
170
// Range which filters elements of another range, based on a predicate
171
template <InputRange Range, Predicate Pred>
172
struct drop_while_range : next_interface<drop_while_range<Range, Pred>> {
173
  Range range;
174
  Pred pred;
175
  bool toggle = true;
176
177
  using type = drop_while_range<Range, Pred>;
178
  using value_type = typename Range::value_type;
179
180
4
  drop_while_range(Range r, const Pred &p) : range(std::move(r)), pred(p) {}
181
182
1
  void initialize() { range.initialize(); }
183
184
5
  std::optional<value_type> next() {
185
5
    auto value = range.next();
186
5
    if (toggle) {
187
6
      while (pred(*value)) {
188
5
        value = range.next();
189
      }
190
1
      toggle = false;
191
    }
192
5
    return value;
193
  }
194
};
195
196
template <typename Range, Predicate Pred>
197
drop_while_range(Range, Pred) -> drop_while_range<Range, Pred>;
198
199
template <Predicate Pred> struct drop_while_proxy { Pred pred; };
200
201
4
template <typename Pred> auto drop_while(Pred &&pred) {
202
4
  return drop_while_proxy<Pred>{std::forward<Pred>(pred)};
203
}
204
205
template <InputRange Range, Predicate Pred>
206
4
auto operator|(Range &&range, drop_while_proxy<Pred> proxy) {
207
4
  return drop_while_range{std::forward<Range>(range), std::move(proxy.pred)};
208
}
209
210
#pragma endregion
211
212
#pragma region transformed range
213
214
// Range which applies a transform to another range
215
template <InputRange Range, Callable Func>
216
struct transformed_range : next_interface<transformed_range<Range, Func>> {
217
  Range range;
218
  const Func func;
219
220
  using type = transformed_range<Range, Func>;
221
  using value_type = decltype(func(*range.next()));
222
223
6
  transformed_range(Range r, const Func &f) : range(std::move(r)), func(f) {}
224
225
4
  void initialize() { range.initialize(); }
226
227
16
  std::optional<value_type> next() {
228

16
    if (const auto value = range.next())
229
14
      return func(*value);
230
    else
231
2
      return std::nullopt;
232
  }
233
};
234
235
template <InputRange Range, Callable Func>
236
transformed_range(Range, Func) -> transformed_range<Range, Func>;
237
238
// Pipe-syntax enabler structs and operator overloads
239
template <Callable Func> struct transform_proxy { Func func; };
240
241
6
template <typename Func> auto transform(Func &&func) {
242
6
  return transform_proxy<Func>{std::forward<Func>(func)};
243
}
244
245
template <InputRange Range, Callable Func>
246
6
auto operator|(Range &&range, transform_proxy<Func> proxy) {
247
6
  return transformed_range{std::forward<Range>(range), std::move(proxy.func)};
248
}
249
250
#pragma endregion
251
252
#pragma region enumerate
253
254
// Range which filters elements of another range, based on a predicate
255
template <InputRange Range>
256
struct enumerate : next_interface<enumerate<Range>> {
257
  Range &range;
258
  std::size_t count = 0;
259
260
  using type = enumerate<Range>;
261
  using value_type = std::pair<std::size_t, typename Range::value_type>;
262
263
1
  enumerate(Range &r) : range(r) {}
264
1
  enumerate(Range &&r) : range(r) {}
265
266
1
  void initialize() {
267
1
    range.initialize();
268
1
    count = 0;
269
1
  }
270
271
5
  std::optional<value_type> next() {
272
5
    auto next = range.next();
273
5
    if (next.has_value())
274
4
      return value_type{count++, *next};
275
276
1
    return std::nullopt;
277
  }
278
};
279
280
#pragma endregion
281
282
} // namespace next