Lumiera 0.pre.04
»edit your freedom«
Loading...
Searching...
No Matches
iter-zip-test.cpp
Go to the documentation of this file.
1/*
2 IterZip(Test) - verify the iterator-combining iterator
3
4 Copyright (C)
5 2024, Hermann Vosseler <Ichthyostega@web.de>
6
7  **Lumiera** is free software; you can redistribute it and/or modify it
8  under the terms of the GNU General Public License as published by the
9  Free Software Foundation; either version 2 of the License, or (at your
10  option) any later version. See the file COPYING for further details.
11
12* *****************************************************************/
13
20#include "lib/test/run.hpp"
21#include "lib/iter-zip.hpp"
22#include "lib/iter-explorer.hpp"
25#include "lib/format-util.hpp"
26#include "lib/util.hpp"
27
28#include <array>
29#include <vector>
30
31
32namespace lib {
33namespace test{
34
35 using util::join;
36 using util::isnil;
37 using util::noneg;
38 using LERR_(ITER_EXHAUST);
41 using std::make_tuple;
42 using std::tuple;
43 using std::get;
44
45 namespace {// Test Fixture ...
46
47 auto num5() { return NumIter{0,5}; }
48
49 template<uint N, uint S=0>
50 auto numS() { return explore(num5()).transform([](int i){ return i*N + S; }); }
51 auto num31(){ return numS<3,1>(); }
52 auto num32(){ return numS<3,2>(); }
53 auto num33(){ return numS<3,3>(); }
54
55 auto hexed = [](int i){ return util::showHash(i,1); };
56
57
59 template<class II>
60 inline string
61 materialise (II&& ii)
62 {
63 return util::join (std::forward<II> (ii), "-");
64 }
65 }
66
67 #define TYPE(_EXPR_) showType<decltype(_EXPR_)>()
68
69
70
71
72 /*********************************************************************************/
82 class IterZip_test : public Test
83 {
84
85 virtual void
98
99
101 void
103 {
104 auto a = std::array{1u,2u,3u};
105 auto v = std::vector{{2l,3l}};
106
107 // loop over both in lockstep
108 for (auto [u,l] : zip(a,v))
109 CHECK (u + 1 == l);
110
111 // iterate-with index
112 auto it = izip(v);
113 CHECK (it);
114 CHECK (*it == "«tuple<ulong, long&>»──(0,2)"_expect );
115 ++it;
116 CHECK (*it == "«tuple<ulong, long&>»──(1,3)"_expect );
117 CHECK (it);
118 ++it;
119 CHECK (not it);
120 VERIFY_ERROR (ITER_EXHAUST, *it );
121 VERIFY_ERROR (ITER_EXHAUST, ++it );
122 }
123
125 void
127 {
128 CHECK (materialise (num5() ) == "0-1-2-3-4"_expect);
129 CHECK (materialise (num31() ) == "1-4-7-10-13"_expect);
130 CHECK (materialise (num33() ) == "3-6-9-12-15"_expect);
131 CHECK (materialise (num32()
132 .transform(hexed)
133 ) == "02-05-08-0B-0E"_expect);
134 }
135
136
138 void
140 {
141 auto t1 = make_tuple (41u, 0.61803, '6');
142 CHECK (t1 == "«tuple<uint, double, char>»──(41,0.61803,6)"_expect );
143
144 auto t1f = mapEach (t1, [](auto v){ return v+1; });
145 CHECK (t1f == "«tuple<uint, double, int>»──(42,1.61803,55)"_expect ); // ASCII('6') ≙ 54 promoted to int
146
147 auto t1ff = mapEach (t1, [](auto& v){ v += 1; return v; });
148 CHECK (t1ff == "«tuple<uint, double, char>»──(42,1.61803,7)"_expect );
149 CHECK (t1f == "«tuple<uint, double, int>»──(42,1.61803,55)"_expect );
150 CHECK (t1 == "«tuple<uint, double, char>»──(42,1.61803,7)"_expect ); // src-tuple t1 affected by side-effect
151
152 // tuple may hold a reference....
153 tuple<char, char&> t2{get<2>(t1), get<2>(t1ff)};
154 CHECK (t2 == "«tuple<char, char&>»──(7,7)"_expect );
155
156 auto t2f = mapEach (t2, [](auto& v){ v -= 1; return v; });
157 CHECK (t2f == "«tuple<char, char>»──(6,6)"_expect ); // function-result is value, thus res-tuple holds values
158 CHECK (t2 == "«tuple<char, char&>»──(6,6)"_expect); // ...but src-tuple t2 was affected by side-effect
159 CHECK (t1ff == "«tuple<uint, double, char>»──(42,1.61803,6)"_expect ); // ...which in turn holds a ref, so value in t1ff changed
160 CHECK (t1 == "«tuple<uint, double, char>»──(42,1.61803,7)"_expect ); // ...while the other one was picked by value => t1 unchanged
161
162 // function may return references....
163 auto refr = [](auto& v) -> decltype(auto) { return v; };
164 int five{5};
165 int& fiveR{five};
166 CHECK (TYPE (refr(five)) == "int&"_expect);
167 CHECK (TYPE (refr(fiveR)) == "int&"_expect);
168
169 auto t2r = mapEach (t2, refr);
170 CHECK (t2r == "«tuple<char&, char&>»──(6,6)"_expect ); // function yields references, which are placed into res-tuple
171
172 forEach (t2r, [](auto& v){ v +=23; });
173 CHECK (t2r == "«tuple<char&, char&>»──(M,M)"_expect ); // apply operation with side-effect to the last res-tuple t2r
174 CHECK (t2 == "«tuple<char, char&>»──(M,M)"_expect ); // the referred src-tuple t2 is also affected
175 CHECK (t2f == "«tuple<char, char>»──(6,6)"_expect ); // (while previously constructed t2f holds values unaffected)
176 CHECK (t1 == "«tuple<uint, double, char>»──(42,1.61803,7)"_expect ); // the first elm in t2 was bound by value, so no side-effect
177 CHECK (t1ff == "«tuple<uint, double, char>»──(42,1.61803,M)"_expect ); // but the second elm in t2 was bound by ref to t1ff
178 }
179
180
181 template<typename...ITS>
182 auto
183 buildIterTuple (ITS&& ...iters)
184 {
185 return make_tuple (lib::explore (std::forward<ITS> (iters)) ...);
186 }
187
188
190 void
192 {
193 // let's start with the basics...
194 // We can use lib::explore() to construct a suitable iterator,
195 // and thus we can apply it to each var-arg and place the results into a tuple
196 auto arry = std::array{3u,2u,1u};
197 auto iTup = buildIterTuple (num5(), arry);
198
199 CHECK (TYPE(iTup) == "tuple<IterExplorer<iter_explorer::BaseAdapter<NumIter<int> > >, "
200 "IterExplorer<iter_explorer::BaseAdapter<iter_explorer::StlRange<array<uint, 3ul>&> > > >"_expect);
201
202 // and we can use them as iterators...
203 auto iterate_it = [](auto& it){ ++it; };
204 auto access_val = [](auto& it){ return *it; };
205
206 forEach (iTup, iterate_it);
207 auto vTup = mapEach (iTup, access_val);
208 CHECK (vTup == "«tuple<int, uint>»──(1,2)"_expect);
209
210 using ITup = decltype(iTup);
211
212 // Next step: define a »product iterator«
213 // by mapping down each of the base operations onto the tuple elements
214 struct ProductCore
215 {
216 ITup iters_;
217
218 ProductCore(ITup&& iterTup)
219 : iters_{move (iterTup)}
220 { }
221
222 /* === »state core« protocol API === */
223 bool
224 checkPoint() const
225 {
226 bool active{true}; // note: optimiser can unroll this
227 forEach (iters_, [&](auto& it){ active = active and bool(it); });
228 return active;
229 }
230
231 ITup&
232 yield() const
233 {
234 return unConst(iters_); // ◁─────────────── note: we expose the iterator-tuple itself as »product«
235 }
236
237 void
238 iterNext()
239 {
240 forEach (iters_, [](auto& it){ ++it; });
241 }
242 };
243
244 // ....and now we're essentially set!
245 // use library building blocks to construct a tuple-iter-explorer...
246 auto ii = explore (ProductCore{buildIterTuple (num5(), arry)})
247 .transform ([&](ITup& iTup){ return mapEach (iTup, access_val); })
248 ;
249
250 // demonstrate the composed pipeline type...
251 CHECK (TYPE(ii) == "IterExplorer<"
252 "IterableDecorator<"
253 "CheckedCore<"
254 "iter_explorer::Transformer<" // ◁──────────────────────────────── the top-layer is a Transformer (to access the value from each src-iter)
255 "iter_explorer::BaseAdapter<"
256 "IterableDecorator<" // ◁──────────────────────────── the product-iterator we constructed
257 "CheckedCore<" // ◁──────────────────────────── ....and using the given ProductCore as »state core«
258 "IterZip_test::demo_construction()::ProductCore> > >, "
259 "tuple<int, uint> " // ◁──────────────────────────────── back to top-layer: result-type of the Transformer
260 "> "
261 "> "
262 "> "
263 ">"_expect);
264
265 // ....
266 // This is indeed a valid iterator,
267 // that can be iterated for three steps
268 // (limited by the shorter sequence from the array)
269 // (first value from num5(), second from the array)
270 CHECK (materialise (ii) == "«tuple<int, uint>»──(0,3)-"
271 "«tuple<int, uint>»──(1,2)-"
272 "«tuple<int, uint>»──(2,1)"_expect);
273 }
274
275
276
277
281 void
283 {
284 CHECK (materialise (
285 zip (num31(), num32(), num33())
286 )
287 == "«tuple<uint&, uint&, uint&>»──(1,2,3)-"
288 "«tuple<uint&, uint&, uint&>»──(4,5,6)-"
289 "«tuple<uint&, uint&, uint&>»──(7,8,9)-"
290 "«tuple<uint&, uint&, uint&>»──(10,11,12)-"
291 "«tuple<uint&, uint&, uint&>»──(13,14,15)"_expect);
292
293 CHECK (materialise(
294 izip (num31(), num32(), num33())
295 )
296 == "«tuple<ulong, uint&, uint&, uint&>»──(0,1,2,3)-"
297 "«tuple<ulong, uint&, uint&, uint&>»──(1,4,5,6)-"
298 "«tuple<ulong, uint&, uint&, uint&>»──(2,7,8,9)-"
299 "«tuple<ulong, uint&, uint&, uint&>»──(3,10,11,12)-"
300 "«tuple<ulong, uint&, uint&, uint&>»──(4,13,14,15)"_expect);
301
302
303 auto s6 = std::array{1,1,2,3,5,8};
304 auto s3 = {3,2,1};
305 auto s0 = eachNum(5u,5u);
306 CHECK (TYPE(s6) == "array<int, 6ul>"_expect );
307 CHECK (TYPE(s3) == "initializer_list<int>"_expect );
308 CHECK (TYPE(s0) == "NumIter<uint>"_expect );
309
310 CHECK (materialise (
311 zip (s6,s6,s6,eachNum('a'))
312 )
313 == "«tuple<int&, int&, int&, char>»──(1,1,1,a)-"
314 "«tuple<int&, int&, int&, char>»──(1,1,1,b)-"
315 "«tuple<int&, int&, int&, char>»──(2,2,2,c)-"
316 "«tuple<int&, int&, int&, char>»──(3,3,3,d)-"
317 "«tuple<int&, int&, int&, char>»──(5,5,5,e)-"
318 "«tuple<int&, int&, int&, char>»──(8,8,8,f)"_expect);
319
320 CHECK (materialise (
321 zip (s6,s3,s6,eachNum('a'))
322 )
323 == "«tuple<int&, int const&, int&, char>»──(1,3,1,a)-"
324 "«tuple<int&, int const&, int&, char>»──(1,2,1,b)-"
325 "«tuple<int&, int const&, int&, char>»──(2,1,2,c)"_expect);
326
327 CHECK (isnil (s0));
328 CHECK (materialise (
329 zip (s0,s3,s6,eachNum('a'))
330 )
331 == ""_expect);
332
333 CHECK (materialise (
334 zip (eachNum('a'),eachNum(-1),s0,s0)
335 )
336 == ""_expect);
337
338 CHECK (materialise (
339 zip (eachNum('a'),eachNum(-1),s3,s0)
340 )
341 == ""_expect);
342
343 CHECK (materialise (
344 zip (eachNum('a'),eachNum(-1),s3,s3)
345 )
346 == "«tuple<char, int, int const&, int const&>»──(a,-1,3,3)-"
347 "«tuple<char, int, int const&, int const&>»──(b,0,2,2)-"
348 "«tuple<char, int, int const&, int const&>»──(c,1,1,1)"_expect);
349
350 // a wild mix of data sources,
351 // including infinite and virtual ones....
352 CHECK (materialise (
353 izip (s6 // a STL container given by reference
354 ,explore(s6).filter([](int i){ return i%2; }) // IterExplorer pipeline with filtering
355 ,numS<17,170>().transform(hexed) // IterExplorer pipeline with transformer and object value result
356 ,eachNum((1+sqrt(5))/2) // a Lumiera iterator which happens to be almost inexhaustible
357 ,explore(s3).asIterSource() // an IterSource, which is a virtual (OO) iterator interface
358 )
359 )
360 == "«tuple<ulong, int&, int&, string&, double, int const&>»──(0,1,1,AA,1.618034,3)-"
361 "«tuple<ulong, int&, int&, string&, double, int const&>»──(1,1,1,BB,2.618034,2)-"
362 "«tuple<ulong, int&, int&, string&, double, int const&>»──(2,2,3,CC,3.618034,1)"_expect);
363 }
364
365
366
368 void
370 {
371 auto vec = std::vector{1,5};
372 auto arr = std::array{2,3};
373
374 // Case-1 ------
375 auto i1 = izip (vec,arr);
376
377 CHECK (*i1 == "«tuple<ulong, int&, int&>»──(0,1,2)"_expect ); // initial state points to the first elements, prefixed with index≡0
378 get<1>(*i1) = 5; // manipulate through the exposed reference
379 CHECK (*i1 == "«tuple<ulong, int&, int&>»──(0,5,2)"_expect ); // effect of manipulation is visible
380
381 CHECK (join(vec) == "5, 5"_expect ); // manipulation indeed flipped the first element in the vector
382 CHECK (join(arr) == "2, 3"_expect ); // (while the array remains unaffected)
383
384 // Case-2 ------
385 auto i2 = izip (explore(vec).transform([](uint v){ return v-1; }) // this time the first iterator is a pipeline with a transformer
386 ,arr); // while the second one is again a direct iteration of the array
387
388 CHECK (*i2 == "«tuple<ulong, uint&, int&>»──(0,4,2)"_expect ); // again can see the first elements, and the effect of the transformer
389 get<0>(*i2) = 9; // manipulate complete result tuple
390 get<1>(*i2) = 9;
391 get<2>(*i2) = 9;
392 CHECK (*i2 == "«tuple<ulong, uint&, int&>»──(9,9,9)"_expect ); // effect of the manipulation is visible
393
394 ++i2; // ...but iteration re-uses the internal result-tuple storage
395 CHECK (*i2 == "«tuple<ulong, uint&, int&>»──(1,4,3)"_expect ); // and so the effect of the manipulation seems gone
396 CHECK (join(vec) == "5, 5"_expect ); // ...which is in fact true for the vector, due to the transformer
397 CHECK (join(arr) == "9, 3"_expect ); // ...while the array could be reached through the reference
398 }
399
400
401
402
409 void
411 {
412 // for reference: this is the base data.......
413 CHECK (materialise (
414 zip (num31(), num32(), num33())
415 )
416 == "«tuple<uint&, uint&, uint&>»──(1,2,3)-"
417 "«tuple<uint&, uint&, uint&>»──(4,5,6)-"
418 "«tuple<uint&, uint&, uint&>»──(7,8,9)-"
419 "«tuple<uint&, uint&, uint&>»──(10,11,12)-"
420 "«tuple<uint&, uint&, uint&>»──(13,14,15)"_expect);
421
422 // transform the tuple into another data value
423 CHECK (materialise (
424 zip (num31(), num32(), num33())
425 . transform([](auto& it){ auto [a,b,c] = *it;
426 return a+b+c;
427 })
428 )
429 == "6-15-24-33-42"_expect);
430
431 // filter tuples based on inspecting contents
432 CHECK (materialise (
433 zip (num31(), num32(), num33())
434 . filter ([](auto& it){ auto [a,b,c] = *it;
435 return not ((a+b+c) % 2);
436 })
437 )
438 == "«tuple<uint&, uint&, uint&>»──(1,2,3)-"
439 "«tuple<uint&, uint&, uint&>»──(7,8,9)-"
440 "«tuple<uint&, uint&, uint&>»──(13,14,15)"_expect);
441
442 // reduce with accessor and std::plus
443 CHECK (zip (num31(), num32(), num33())
444 . reduce ([](auto& it){ auto [a,b,c] = *it;
445 return a+b+c;
446 })
447 == 6+15+24+33+42);
448 }
449
450
451
468 void
470 {
471 CHECK (materialise (
472 num31()
473 )
474 == "1-4-7-10-13"_expect);
475
476 CHECK (materialise (
477 explore(num31())
478 .expand ([](int i){ return NumIter{noneg(i-1),i}; })
479 .expandAll()
480 )
481 == "1-0-4-3-2-1-0-7-6-5-4-3-2-1-0-10-9-8-7-6-5-4-3-2-1-0-13-12-11-10-9-8-7-6-5-4-3-2-1-0"_expect);
482
483 CHECK (materialise (
484 explore(num31())
485 .expand ([](int i){ return NumIter{noneg(i-2),i-1}; })
486 .expandAll()
487 )
488 == "1-4-2-0-7-5-3-1-10-8-6-4-2-0-13-11-9-7-5-3-1"_expect);
489
490 CHECK (materialise (
491 zip
492 ( eachNum(10)
493 , explore(num31())
494 .expand ([](int i){ return NumIter{noneg(i-1),i}; })
495 .expandAll() // ◁────────────────────────────────────────────── expand triggered in source pipeline, before the zip()
496 , explore(num31())
497 .expand ([](int i){ return NumIter{noneg(i-2),i-1}; })
498 .expandAll()
499 )
500 )
501 == "«tuple<int, uint, uint>»──(10,1,1)-"
502 "«tuple<int, uint, uint>»──(11,0,4)-"
503 "«tuple<int, uint, uint>»──(12,4,2)-"
504 "«tuple<int, uint, uint>»──(13,3,0)-"
505 "«tuple<int, uint, uint>»──(14,2,7)-"
506 "«tuple<int, uint, uint>»──(15,1,5)-"
507 "«tuple<int, uint, uint>»──(16,0,3)-"
508 "«tuple<int, uint, uint>»──(17,7,1)-"
509 "«tuple<int, uint, uint>»──(18,6,10)-"
510 "«tuple<int, uint, uint>»──(19,5,8)-"
511 "«tuple<int, uint, uint>»──(20,4,6)-"
512 "«tuple<int, uint, uint>»──(21,3,4)-"
513 "«tuple<int, uint, uint>»──(22,2,2)-"
514 "«tuple<int, uint, uint>»──(23,1,0)-"
515 "«tuple<int, uint, uint>»──(24,0,13)-"
516 "«tuple<int, uint, uint>»──(25,10,11)-"
517 "«tuple<int, uint, uint>»──(26,9,9)-"
518 "«tuple<int, uint, uint>»──(27,8,7)-"
519 "«tuple<int, uint, uint>»──(28,7,5)-"
520 "«tuple<int, uint, uint>»──(29,6,3)-"
521 "«tuple<int, uint, uint>»──(30,5,1)"_expect);
522
523 CHECK (materialise (
524 zip
525 ( eachNum(10)
526 , explore(num31())
527 .expand ([](int i){ return NumIter{noneg(i-1),i}; })
528 , explore(num31())
529 .expand ([](int i){ return NumIter{noneg(i-2),i-1}; })
530 )
531 .expandAll() // ◁──────────┲━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ note the difference: expand triggered after the zip()
532 ) // ▽
533 == "«tuple<int, uint, uint>»──(10,1,1)-"
534 "«tuple<int, uint, uint>»──(10,0,4)-"
535 "«tuple<int, uint, uint>»──(10,4,2)-"
536 "«tuple<int, uint, uint>»──(10,3,0)-"
537 "«tuple<int, uint, uint>»──(10,2,7)-"
538 "«tuple<int, uint, uint>»──(10,1,5)-"
539 "«tuple<int, uint, uint>»──(10,0,3)-"
540 "«tuple<int, uint, uint>»──(10,7,1)-"
541 "«tuple<int, uint, uint>»──(10,6,10)-"
542 "«tuple<int, uint, uint>»──(10,5,8)-"
543 "«tuple<int, uint, uint>»──(10,4,6)-"
544 "«tuple<int, uint, uint>»──(10,3,4)-"
545 "«tuple<int, uint, uint>»──(10,2,2)-"
546 "«tuple<int, uint, uint>»──(10,1,0)-"
547 "«tuple<int, uint, uint>»──(10,0,13)-"
548 "«tuple<int, uint, uint>»──(10,10,11)-"
549 "«tuple<int, uint, uint>»──(10,9,9)-"
550 "«tuple<int, uint, uint>»──(10,8,7)-"
551 "«tuple<int, uint, uint>»──(10,7,5)-"
552 "«tuple<int, uint, uint>»──(10,6,3)-"
553 "«tuple<int, uint, uint>»──(10,5,1)"_expect);
554 }
555 };
556
557
558 LAUNCHER (IterZip_test, "unit common");
559
560
561}} // namespace lib::test
Enumerate all "numbers" within a range.
virtual void run(Arg)
auto buildIterTuple(ITS &&...iters)
Custom-Datatype 1: uses std::hash extension point.
Helpers typically used while writing tests.
#define LERR_(_NAME_)
Definition error.hpp:45
Collection of small helpers and convenience shortcuts for diagnostics & formatting.
unsigned int uint
Definition integral.hpp:29
Building tree expanding and backtracking evaluations within hierarchical scopes.
#define TYPE(_EXPR_)
Iterator builder to combine several iterables into a tuple sequence.
constexpr void forEach(TUP &&tuple, FUN fun)
Tuple iteration: perform some arbitrary operation on each element of a tuple.
constexpr auto mapEach(TUP &&tuple, FUN fun)
Apply some arbitrary function onto all elements of a tuple.
Implementation namespace for support and library code.
auto explore(IT &&srcSeq)
start building a IterExplorer by suitably wrapping the given iterable source.
auto zip(ITS &&...iters)
Build a tuple-combining iterator builder.
Definition iter-zip.hpp:138
auto izip(ITS &&...iters)
tuple-combining iterator prefixed by index sequence
Definition iter-zip.hpp:152
NumIter< INT > eachNum(INT start=std::numeric_limits< INT >::min(), INT end=std::numeric_limits< INT >::max())
convenience function to iterate "each number"
Test runner and basic definitions for tests.
constexpr NUM noneg(NUM val)
cut a numeric value to be >=0
Definition util.hpp:83
string showHash(size_t hash, uint showBytes) noexcept
renders the size_t in hex, optionally only trailing bytes
string join(COLL &&coll, string const &delim=", ")
enumerate a collection's contents, separated by delimiter.
bool isnil(lib::time::Duration const &dur)
Simplistic test class runner.
#define LAUNCHER(_TEST_CLASS_, _GROUPS_)
Definition run.hpp:116
A collection of frequently used helper functions to support unit testing.
#define VERIFY_ERROR(ERROR_ID, ERRONEOUS_STATEMENT)
Macro to verify that a statement indeed raises an exception.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...