Lumiera  0.pre.03
»edit your freedom«
iter-zip-test.cpp
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"
23 #include "lib/test/test-helper.hpp"
25 #include "lib/format-util.hpp"
26 #include "lib/util.hpp"
27 
28 #include <array>
29 #include <vector>
30 
31 
32 namespace lib {
33 namespace test{
34 
35  using util::join;
36  using util::isnil;
37  using util::noneg;
38  using LERR_(ITER_EXHAUST);
39  using lib::meta::forEach;
40  using lib::meta::mapEach;
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
86  run (Arg)
87  {
88  simpleUsage();
89  test_Fixture();
92 
97  }
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  CHECK (TYPE (refr(five)) == "int&"_expect);
166  CHECK (TYPE (refr(5 )) == "int&"_expect);
167 
168  auto t2r = mapEach (t2, refr);
169  CHECK (t2r == "«tuple<char&, char&>»──(6,6)"_expect ); // function yields references, which are placed into res-tuple
170 
171  forEach (t2r, [](auto& v){ v +=23; });
172  CHECK (t2r == "«tuple<char&, char&>»──(M,M)"_expect ); // apply operation with side-effect to the last res-tuple t2r
173  CHECK (t2 == "«tuple<char, char&>»──(M,M)"_expect ); // the referred src-tuple t2 is also affected
174  CHECK (t2f == "«tuple<char, char>»──(6,6)"_expect ); // (while previously constructed t2f holds values unaffected)
175  CHECK (t1 == "«tuple<uint, double, char>»──(42,1.61803,7)"_expect ); // the first elm in t2 was bound by value, so no side-effect
176  CHECK (t1ff == "«tuple<uint, double, char>»──(42,1.61803,M)"_expect ); // but the second elm in t2 was bound by ref to t1ff
177  }
178 
179 
180  template<typename...ITS>
181  auto
182  buildIterTuple (ITS&& ...iters)
183  {
184  return make_tuple (lib::explore (std::forward<ITS> (iters)) ...);
185  }
186 
187 
189  void
191  {
192  // let's start with the basics...
193  // We can use lib::explore() to construct a suitable iterator,
194  // and thus we can apply it to each var-arg and place the results into a tuple
195  auto arry = std::array{3u,2u,1u};
196  auto iTup = buildIterTuple (num5(), arry);
197 
198  CHECK (TYPE(iTup) == "tuple<IterExplorer<iter_explorer::BaseAdapter<NumIter<int> > >, "
199  "IterExplorer<iter_explorer::BaseAdapter<iter_explorer::StlRange<array<uint, 3ul>&> > > >"_expect);
200 
201  // and we can use them as iterators...
202  auto iterate_it = [](auto& it){ ++it; };
203  auto access_val = [](auto& it){ return *it; };
204 
205  forEach (iTup, iterate_it);
206  auto vTup = mapEach (iTup, access_val);
207  CHECK (vTup == "«tuple<int, uint>»──(1,2)"_expect);
208 
209  using ITup = decltype(iTup);
210 
211  // Next step: define a »product iterator«
212  // by mapping down each of the base operations onto the tuple elements
213  struct ProductCore
214  {
215  ITup iters_;
216 
217  ProductCore(ITup&& iterTup)
218  : iters_{move (iterTup)}
219  { }
220 
221  /* === »state core« protocol API === */
222  bool
223  checkPoint() const
224  {
225  bool active{true}; // note: optimiser can unroll this
226  forEach (iters_, [&](auto& it){ active = active and bool(it); });
227  return active;
228  }
229 
230  ITup&
231  yield() const
232  {
233  return unConst(iters_); // ◁─────────────── note: we expose the iterator-tuple itself as »product«
234  }
235 
236  void
237  iterNext()
238  {
239  forEach (iters_, [](auto& it){ ++it; });
240  }
241  };
242 
243  // ....and now we're essentially set!
244  // use library building blocks to construct a tuple-iter-explorer...
245  auto ii = explore (ProductCore{buildIterTuple (num5(), arry)})
246  .transform ([&](ITup& iTup){ return mapEach (iTup, access_val); })
247  ;
248 
249  // demonstrate the composed pipeline type...
250  CHECK (TYPE(ii) == "IterExplorer<"
251  "IterableDecorator<"
252  "CheckedCore<"
253  "iter_explorer::Transformer<" // ◁──────────────────────────────── the top-layer is a Transformer (to access the value from each src-iter)
254  "iter_explorer::BaseAdapter<"
255  "IterableDecorator<" // ◁──────────────────────────── the product-iterator we constructed
256  "CheckedCore<" // ◁──────────────────────────── ....and using the given ProductCore as »state core«
257  "IterZip_test::demo_construction()::ProductCore> > >, "
258  "tuple<int, uint> " // ◁──────────────────────────────── back to top-layer: result-type of the Transformer
259  "> "
260  "> "
261  "> "
262  ">"_expect);
263 
264  // ....
265  // This is indeed a valid iterator,
266  // that can be iterated for three steps
267  // (limited by the shorter sequence from the array)
268  // (first value from num5(), second from the array)
269  CHECK (materialise (ii) == "«tuple<int, uint>»──(0,3)-"
270  "«tuple<int, uint>»──(1,2)-"
271  "«tuple<int, uint>»──(2,1)"_expect);
272  }
273 
274 
275 
276 
280  void
282  {
283  CHECK (materialise (
284  zip (num31(), num32(), num33())
285  )
286  == "«tuple<uint&, uint&, uint&>»──(1,2,3)-"
287  "«tuple<uint&, uint&, uint&>»──(4,5,6)-"
288  "«tuple<uint&, uint&, uint&>»──(7,8,9)-"
289  "«tuple<uint&, uint&, uint&>»──(10,11,12)-"
290  "«tuple<uint&, uint&, uint&>»──(13,14,15)"_expect);
291 
292  CHECK (materialise(
293  izip (num31(), num32(), num33())
294  )
295  == "«tuple<ulong, uint&, uint&, uint&>»──(0,1,2,3)-"
296  "«tuple<ulong, uint&, uint&, uint&>»──(1,4,5,6)-"
297  "«tuple<ulong, uint&, uint&, uint&>»──(2,7,8,9)-"
298  "«tuple<ulong, uint&, uint&, uint&>»──(3,10,11,12)-"
299  "«tuple<ulong, uint&, uint&, uint&>»──(4,13,14,15)"_expect);
300 
301 
302  auto s6 = std::array{1,1,2,3,5,8};
303  auto s3 = {3,2,1};
304  auto s0 = eachNum(5u,5u);
305  CHECK (TYPE(s6) == "array<int, 6ul>"_expect );
306  CHECK (TYPE(s3) == "initializer_list<int>"_expect );
307  CHECK (TYPE(s0) == "NumIter<uint>"_expect );
308 
309  CHECK (materialise (
310  zip (s6,s6,s6,eachNum('a'))
311  )
312  == "«tuple<int&, int&, int&, char>»──(1,1,1,a)-"
313  "«tuple<int&, int&, int&, char>»──(1,1,1,b)-"
314  "«tuple<int&, int&, int&, char>»──(2,2,2,c)-"
315  "«tuple<int&, int&, int&, char>»──(3,3,3,d)-"
316  "«tuple<int&, int&, int&, char>»──(5,5,5,e)-"
317  "«tuple<int&, int&, int&, char>»──(8,8,8,f)"_expect);
318 
319  CHECK (materialise (
320  zip (s6,s3,s6,eachNum('a'))
321  )
322  == "«tuple<int&, int const&, int&, char>»──(1,3,1,a)-"
323  "«tuple<int&, int const&, int&, char>»──(1,2,1,b)-"
324  "«tuple<int&, int const&, int&, char>»──(2,1,2,c)"_expect);
325 
326  CHECK (isnil (s0));
327  CHECK (materialise (
328  zip (s0,s3,s6,eachNum('a'))
329  )
330  == ""_expect);
331 
332  CHECK (materialise (
333  zip (eachNum('a'),eachNum(-1),s0,s0)
334  )
335  == ""_expect);
336 
337  CHECK (materialise (
338  zip (eachNum('a'),eachNum(-1),s3,s0)
339  )
340  == ""_expect);
341 
342  CHECK (materialise (
343  zip (eachNum('a'),eachNum(-1),s3,s3)
344  )
345  == "«tuple<char, int, int const&, int const&>»──(a,-1,3,3)-"
346  "«tuple<char, int, int const&, int const&>»──(b,0,2,2)-"
347  "«tuple<char, int, int const&, int const&>»──(c,1,1,1)"_expect);
348 
349  // a wild mix of data sources,
350  // including infinite and virtual ones....
351  CHECK (materialise (
352  izip (s6 // a STL container given by reference
353  ,explore(s6).filter([](int i){ return i%2; }) // IterExplorer pipeline with filtering
354  ,numS<17,170>().transform(hexed) // IterExplorer pipeline with transformer and object value result
355  ,eachNum((1+sqrt(5))/2) // a Lumiera iterator which happens to be almost inexhaustible
356  ,explore(s3).asIterSource() // an IterSource, which is a virtual (OO) iterator interface
357  )
358  )
359  == "«tuple<ulong, int&, int&, string&, double, int const&>»──(0,1,1,AA,1.618034,3)-"
360  "«tuple<ulong, int&, int&, string&, double, int const&>»──(1,1,1,BB,2.618034,2)-"
361  "«tuple<ulong, int&, int&, string&, double, int const&>»──(2,2,3,CC,3.618034,1)"_expect);
362  }
363 
364 
365 
367  void
369  {
370  auto vec = std::vector{1,5};
371  auto arr = std::array{2,3};
372 
373  // Case-1 ------
374  auto i1 = izip (vec,arr);
375 
376  CHECK (*i1 == "«tuple<ulong, int&, int&>»──(0,1,2)"_expect ); // initial state points to the first elements, prefixed with index≡0
377  get<1>(*i1) = 5; // manipulate through the exposed reference
378  CHECK (*i1 == "«tuple<ulong, int&, int&>»──(0,5,2)"_expect ); // effect of manipulation is visible
379 
380  CHECK (join(vec) == "5, 5"_expect ); // manipulation indeed flipped the first element in the vector
381  CHECK (join(arr) == "2, 3"_expect ); // (while the array remains unaffected)
382 
383  // Case-2 ------
384  auto i2 = izip (explore(vec).transform([](uint v){ return v-1; }) // this time the first iterator is a pipeline with a transformer
385  ,arr); // while the second one is again a direct iteration of the array
386 
387  CHECK (*i2 == "«tuple<ulong, uint&, int&>»──(0,4,2)"_expect ); // again can see the first elements, and the effect of the transformer
388  get<0>(*i2) = 9; // manipulate complete result tuple
389  get<1>(*i2) = 9;
390  get<2>(*i2) = 9;
391  CHECK (*i2 == "«tuple<ulong, uint&, int&>»──(9,9,9)"_expect ); // effect of the manipulation is visible
392 
393  ++i2; // ...but iteration re-uses the internal result-tuple storage
394  CHECK (*i2 == "«tuple<ulong, uint&, int&>»──(1,4,3)"_expect ); // and so the effect of the manipulation seems gone
395  CHECK (join(vec) == "5, 5"_expect ); // ...which is in fact true for the vector, due to the transformer
396  CHECK (join(arr) == "9, 3"_expect ); // ...while the array could be reached through the reference
397  }
398 
399 
400 
401 
408  void
410  {
411  // for reference: this is the base data.......
412  CHECK (materialise (
413  zip (num31(), num32(), num33())
414  )
415  == "«tuple<uint&, uint&, uint&>»──(1,2,3)-"
416  "«tuple<uint&, uint&, uint&>»──(4,5,6)-"
417  "«tuple<uint&, uint&, uint&>»──(7,8,9)-"
418  "«tuple<uint&, uint&, uint&>»──(10,11,12)-"
419  "«tuple<uint&, uint&, uint&>»──(13,14,15)"_expect);
420 
421  // transform the tuple into another data value
422  CHECK (materialise (
423  zip (num31(), num32(), num33())
424  . transform([](auto& it){ auto [a,b,c] = *it;
425  return a+b+c;
426  })
427  )
428  == "6-15-24-33-42"_expect);
429 
430  // filter tuples based on inspecting contents
431  CHECK (materialise (
432  zip (num31(), num32(), num33())
433  . filter ([](auto& it){ auto [a,b,c] = *it;
434  return not ((a+b+c) % 2);
435  })
436  )
437  == "«tuple<uint&, uint&, uint&>»──(1,2,3)-"
438  "«tuple<uint&, uint&, uint&>»──(7,8,9)-"
439  "«tuple<uint&, uint&, uint&>»──(13,14,15)"_expect);
440 
441  // reduce with accessor and std::plus
442  CHECK (zip (num31(), num32(), num33())
443  . reduce ([](auto& it){ auto [a,b,c] = *it;
444  return a+b+c;
445  })
446  == 6+15+24+33+42);
447  }
448 
449 
450 
467  void
469  {
470  CHECK (materialise (
471  num31()
472  )
473  == "1-4-7-10-13"_expect);
474 
475  CHECK (materialise (
476  explore(num31())
477  .expand ([](int i){ return NumIter{noneg(i-1),i}; })
478  .expandAll()
479  )
480  == "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);
481 
482  CHECK (materialise (
483  explore(num31())
484  .expand ([](int i){ return NumIter{noneg(i-2),i-1}; })
485  .expandAll()
486  )
487  == "1-4-2-0-7-5-3-1-10-8-6-4-2-0-13-11-9-7-5-3-1"_expect);
488 
489  CHECK (materialise (
490  zip
491  ( eachNum(10)
492  , explore(num31())
493  .expand ([](int i){ return NumIter{noneg(i-1),i}; })
494  .expandAll() // ◁────────────────────────────────────────────── expand triggered in source pipeline, before the zip()
495  , explore(num31())
496  .expand ([](int i){ return NumIter{noneg(i-2),i-1}; })
497  .expandAll()
498  )
499  )
500  == "«tuple<int, uint, uint>»──(10,1,1)-"
501  "«tuple<int, uint, uint>»──(11,0,4)-"
502  "«tuple<int, uint, uint>»──(12,4,2)-"
503  "«tuple<int, uint, uint>»──(13,3,0)-"
504  "«tuple<int, uint, uint>»──(14,2,7)-"
505  "«tuple<int, uint, uint>»──(15,1,5)-"
506  "«tuple<int, uint, uint>»──(16,0,3)-"
507  "«tuple<int, uint, uint>»──(17,7,1)-"
508  "«tuple<int, uint, uint>»──(18,6,10)-"
509  "«tuple<int, uint, uint>»──(19,5,8)-"
510  "«tuple<int, uint, uint>»──(20,4,6)-"
511  "«tuple<int, uint, uint>»──(21,3,4)-"
512  "«tuple<int, uint, uint>»──(22,2,2)-"
513  "«tuple<int, uint, uint>»──(23,1,0)-"
514  "«tuple<int, uint, uint>»──(24,0,13)-"
515  "«tuple<int, uint, uint>»──(25,10,11)-"
516  "«tuple<int, uint, uint>»──(26,9,9)-"
517  "«tuple<int, uint, uint>»──(27,8,7)-"
518  "«tuple<int, uint, uint>»──(28,7,5)-"
519  "«tuple<int, uint, uint>»──(29,6,3)-"
520  "«tuple<int, uint, uint>»──(30,5,1)"_expect);
521 
522  CHECK (materialise (
523  zip
524  ( eachNum(10)
525  , explore(num31())
526  .expand ([](int i){ return NumIter{noneg(i-1),i}; })
527  , explore(num31())
528  .expand ([](int i){ return NumIter{noneg(i-2),i-1}; })
529  )
530  .expandAll() // ◁──────────┲━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ note the difference: expand triggered after the zip()
531  ) // ▽
532  == "«tuple<int, uint, uint>»──(10,1,1)-"
533  "«tuple<int, uint, uint>»──(10,0,4)-"
534  "«tuple<int, uint, uint>»──(10,4,2)-"
535  "«tuple<int, uint, uint>»──(10,3,0)-"
536  "«tuple<int, uint, uint>»──(10,2,7)-"
537  "«tuple<int, uint, uint>»──(10,1,5)-"
538  "«tuple<int, uint, uint>»──(10,0,3)-"
539  "«tuple<int, uint, uint>»──(10,7,1)-"
540  "«tuple<int, uint, uint>»──(10,6,10)-"
541  "«tuple<int, uint, uint>»──(10,5,8)-"
542  "«tuple<int, uint, uint>»──(10,4,6)-"
543  "«tuple<int, uint, uint>»──(10,3,4)-"
544  "«tuple<int, uint, uint>»──(10,2,2)-"
545  "«tuple<int, uint, uint>»──(10,1,0)-"
546  "«tuple<int, uint, uint>»──(10,0,13)-"
547  "«tuple<int, uint, uint>»──(10,10,11)-"
548  "«tuple<int, uint, uint>»──(10,9,9)-"
549  "«tuple<int, uint, uint>»──(10,8,7)-"
550  "«tuple<int, uint, uint>»──(10,7,5)-"
551  "«tuple<int, uint, uint>»──(10,6,3)-"
552  "«tuple<int, uint, uint>»──(10,5,1)"_expect);
553  }
554  };
555 
556 
557  LAUNCHER (IterZip_test, "unit common");
558 
559 
560 }} // namespace lib::test
auto explore(IT &&srcSeq)
start building a IterExplorer by suitably wrapping the given iterable source.
auto izip(ITS &&...iters)
tuple-combining iterator prefixed by index sequence
Definition: iter-zip.hpp:152
bool filter(Placement< DummyMO > const &candidate)
a filter predicate to pick some objects from a resultset.
Definition: run.hpp:40
#define VERIFY_ERROR(ERROR_ID, ERRONEOUS_STATEMENT)
Macro to verify that a statement indeed raises an exception.
NUM constexpr noneg(NUM val)
cut a numeric value to be >=0
Definition: util.hpp:83
Helpers typically used while writing tests.
Iterator builder to combine several iterables into a tuple sequence.
Implementation namespace for support and library code.
Enumerate all "numbers" within a range.
Simplistic test class runner.
int reduce(Gtk::Button &icon)
attempt to reduce space consumption
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
A collection of frequently used helper functions to support unit testing.
Custom-Datatype 1: uses std::hash extension point.
Collection of small helpers and convenience shortcuts for diagnostics & formatting.
void forEach(TUP &&tuple, FUN fun)
Tuple iteration: perform some arbitrary operation on each element of a tuple.
Building tree expanding and backtracking evaluations within hierarchical scopes.
auto mapEach(TUP &&tuple, FUN fun)
Apply some arbitrary function onto all elements of a tuple.
NumIter< INT > eachNum(INT start=std::numeric_limits< INT >::min(), INT end=std::numeric_limits< INT >::max())
convenience function to iterate "each number"
auto zip(ITS &&...iters)
Build a tuple-combining iterator builder.
Definition: iter-zip.hpp:138