Lumiera 0.pre.04
»edit your freedom«
Loading...
Searching...
No Matches
split-splice-test.cpp
Go to the documentation of this file.
1/*
2 SplitSplice(Test) - verify interval splicing
3
4 Copyright (C)
5 2023, 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
32#include "lib/test/run.hpp"
34#include "lib/format-cout.hpp"
35#include "lib/format-util.hpp"
36#include "lib/format-string.hpp"
37#include "lib/split-splice.hpp"
38#include "lib/nocopy.hpp"
39#include "lib/util.hpp"
40
41#include <optional>
42#include <utility>
43#include <string>
44#include <list>
45
46
47namespace lib {
48namespace test {
49
50 using util::_Fmt;
51 using util::isnil;
52 using util::getAdr;
54 using std::string;
55 using std::move;
56
57 namespace {// Test Fixture....
58
65 struct Seg
67 {
68 int start;
69 int after;
70 bool empty;
71
73 {
74 check -= id;
75 if (id) --cnt;
76 }
77
78 Seg (int s, int a, bool nil=false)
79 : start{s}
80 , after{a}
81 , empty{nil}
82 , id{++idGen}
83 {
84 ++cnt;
85 check += id;
86 }
87
89 Seg (Seg const& ref, int s, int a)
90 : start{s}
91 , after{a}
92 , empty{ref.empty}
93 , id{ref.id}
94 {
95 ++cnt;
96 check += id;
97 }
98
100 Seg (Seg&& rr)
101 : start{rr.start}
102 , after{rr.after}
103 , empty{rr.empty}
104 , id{0}
105 {
106 std::swap (id, rr.id);
107 }//transfer identity
108
109 operator string() const
110 {
111 return _Fmt{"[%i%s%i["}
112 % start
113 % (empty? "~":"_")
114 % after
115 ;
116 }
117
118
119 //-- Diagnostics --
121 static uint idGen;
122 static size_t cnt;
123 static size_t check;
124 };
125
126 // Storage for static ID-Generator
127 size_t Seg::check{0};
128 size_t Seg::cnt{0};
129 uint Seg::idGen{0};
130
131 const int SMIN = -100;
132 const int SMAX = +100;
133
143 struct SegL
144 : std::list<Seg>
145 {
146 SegL (std::initializer_list<int> breaks)
147 {
148 int p = SMIN;
149 bool bound = true;
150 for (int b : breaks)
151 {
152 emplace_back (p,b, bound);
153 bound = false;
154 p = b;
155 }
156 emplace_back(p,SMAX, true);
157 }
158
160 : SegL({})
161 { }
162
163 // using standard copy
164
165 operator string() const
166 {
167 return renderContent() + assess();
168 }
169
170 bool
171 isValid() const
172 {
173 return isnil (this->assess());
174 }
175
176 string
178 {
179 return "├"+util::join(*this,"")+"┤";
180 }
181
182 string
183 assess() const
184 {
185 string diagnosis;
186 if (empty())
187 diagnosis = "!empty!";
188 else
189 {
190 if (front().start != SMIN)
191 diagnosis += "missing-lower-bound!";
192 if (back().after != SMAX)
193 diagnosis += "missing-upper-bound!";
194 int p = SMIN;
195 for (auto const& s : *this)
196 {
197 if (s.start != p)
198 diagnosis += _Fmt{"!gap_%i<>%i_!"} % p % s.start;
199 if (s.start == s.after)
200 diagnosis += _Fmt{"!degen_%i_!"} % s.start;
201 if (s.start > s.after)
202 diagnosis += _Fmt{"!order_%i>%i_!"} % s.start % s.after;
203 p = s.after;
204 }
205 }
206 return diagnosis;
207 }
208 };
209
210
211
212
213 /* ======= Split/Splice-Algo Setup ======= */
214
215 using OptInt = std::optional<int>;
216 using Iter = SegL::iterator;
217
231 auto
232 invokeSplitSplice (SegL& segs, OptInt startNew, OptInt afterNew)
233 {
234 /*---configure-contextually-bound-Functors--------------------*/
235 auto getStart = [](Iter elm) -> int { return elm->start; };
236 auto getAfter = [](Iter elm) -> int { return elm->after; };
237 auto createSeg= [&](Iter pos, int start, int after) -> Iter { return segs.emplace (pos, start, after); };
238 auto emptySeg = [&](Iter pos, int start, int after) -> Iter { return segs.emplace (pos, start, after, true); };
239 auto cloneSeg = [&](Iter pos, int start, int after, Iter src) -> Iter { return segs.emplace (pos, *src, start, after); };
240 auto discard = [&](Iter pos, Iter after) -> Iter { return segs.erase (pos,after); };
241
242
243 lib::splitsplice::Algo splicer{ getStart
244 , getAfter
245 , createSeg
246 , emptySeg
247 , cloneSeg
248 , discard
249 , SMAX
250 , segs.begin(),segs.end()
251 , startNew, afterNew
252 };
253 splicer.determineRelations();
254 return splicer.performSplitSplice();
255 }
256 }//(End)Test Fixture
257
258
259
260
261 /****************************************************************************/
270 class SplitSplice_test : public Test
271 {
272
273 virtual void
274 run (Arg)
275 {
281
282 // no memory leaked
283 CHECK (0 == Seg::check);
284 CHECK (0 == Seg::cnt);
285 }
286
287
291 void
293 {
294 SegL segmentation;
295 CHECK (segmentation == "├[-100~100[┤"_expect);
296
297 OptInt startNew{5},
298 afterNew{23};
299
300 invokeSplitSplice (segmentation, startNew, afterNew);
301
302 // The given segmentation was modified by side-effect
303 // - a new segment [5...23[ has been inserted in the middle
304 // - suitably adapted empty predecessor and successor segments
305 CHECK (segmentation == "├[-100~5[[5_23[[23~100[┤"_expect);
306
307 // The modified segmentation still seamlessly covers the whole axis
308 CHECK (segmentation.isValid());
309 }
310
311
312
314 void
316 {
317 CHECK (0 == Seg::check);
318 Seg::idGen = 0;
319 {
320 Seg x{1,3}; // a segment 1 (inclusive) to 3 (exclusive)
321 Seg u{2,4,true}; // an "empty" segment 2 (incl) to 4 (excl)
322 CHECK (x == "[1_3["_expect);
323 CHECK (u == "[2~4["_expect); // "empty" interval is marked with '~' in string stylisation
324 CHECK (3 == Seg::check);
325 CHECK (2 == Seg::cnt);
326
327 Seg z{move(u)};
328 CHECK (z == "[2~4["_expect);
329 CHECK (3 == Seg::check);
330 CHECK (2 == Seg::cnt); // the "dead" instance u is not counted
331 CHECK (0 == u.id); // (its ID has been reset to zero in move-ctor)
332 CHECK (2 == z.id);
333
334 SegL l1; // default ctor always adds an empty base segment -100 ... +100
335 SegL l2{3};
336 SegL l3{5,-5,10};
337 CHECK (l1 == "├[-100~100[┤"_expect);
338 CHECK (l2 == "├[-100~3[[3~100[┤"_expect);
339 CHECK (l3 == "├[-100~5[[5_-5[[-5_10[[10~100[┤!order_5>-5_!"_expect);
340
341 CHECK (l1.isValid());
342 CHECK (l2.isValid());
343 CHECK (not l3.isValid()); // l3 violates validity condition, because segment [5 ... -5[ is reversed
344 CHECK (l3.assess() == "!order_5>-5_!"_expect);
345
346 CHECK ( 9 == Seg::cnt ); // 9 objects are alive
347 CHECK ( 9 == Seg::idGen); // ID generator sticks at 9
348 CHECK (45 == Seg::check); // checksum 1+..+9
349
350 l3.erase(l3.begin());
351 CHECK (l3.assess() == "missing-lower-bound!!gap_-100<>5_!!order_5>-5_!"_expect);
352 CHECK ( 8 == Seg::cnt ); // also one object less alive
353
354 l3.begin()->after = 5; // manipulate first segment to make it degenerate (empty
355 CHECK (l3.renderContent() == "├[5_5[[-5_10[[10~100[┤"_expect);
356 CHECK (l3.assess() == "missing-lower-bound!!gap_-100<>5_!!degen_5_!!gap_5<>-5_!"_expect);
357 l3.clear();
358 CHECK (l3.assess() == "!empty!"_expect);
359
360 CHECK ( 5 == Seg::cnt );
361 CHECK ( 9 == Seg::idGen);
362 CHECK (15 == Seg::check);
363 }
364 // all objects go out of scope
365 CHECK (0 == Seg::cnt );
366 CHECK (0 == Seg::check);
367 CHECK (9 == Seg::idGen);
368 }
369
370
371
375 void
377 {
378 auto testCase = [](SegL segmentation
379 ,int startNew
380 ,int afterNew
381 ,ExpectString expectedResult)
382 {
383 OptInt startSpec{startNew},
384 afterSpec{afterNew};
385
386 invokeSplitSplice (segmentation, startSpec, afterSpec);
387 CHECK (segmentation == expectedResult);
388 CHECK (segmentation.isValid());
389 };
391 testCase (SegL{}, -23,24, "├[-100~-23[[-23_24[[24~100[┤"_expect); // simple segment into empty axis
392
393 // insert smaller segment
394 testCase (SegL{5,10}, 2,3, "├[-100~2[[2_3[[3~5[[5_10[[10~100[┤"_expect); // smaller segment left spaced off
395 testCase (SegL{5,10}, 4,5, "├[-100~4[[4_5[[5_10[[10~100[┤"_expect); // left adjacent
396 testCase (SegL{5,10}, 4,8, "├[-100~4[[4_8[[8_10[[10~100[┤"_expect); // left overlapping
397 testCase (SegL{5,10}, 5,8, "├[-100~5[[5_8[[8_10[[10~100[┤"_expect); // left inside justified
398 testCase (SegL{5,10}, 6,8, "├[-100~5[[5_6[[6_8[[8_10[[10~100[┤"_expect); // smaller segment complete inside
399 testCase (SegL{5,10}, 7,10, "├[-100~5[[5_7[[7_10[[10~100[┤"_expect); // right inside justified
400 testCase (SegL{5,10}, 9,13, "├[-100~5[[5_9[[9_13[[13~100[┤"_expect); // right overlapping
401 testCase (SegL{5,10}, 10,13, "├[-100~5[[5_10[[10_13[[13~100[┤"_expect); // right adjacent
402 testCase (SegL{5,10}, 13,23, "├[-100~5[[5_10[[10~13[[13_23[[23~100[┤"_expect); // right spaced off
403
404 // insert identical segment
405 testCase (SegL{5,10}, 5,10, "├[-100~5[[5_10[[10~100[┤"_expect); // identical size replacement
406
407 // insert larger segment
408 testCase (SegL{5,10}, 3,10, "├[-100~3[[3_10[[10~100[┤"_expect); // larger segment right aligned
409 testCase (SegL{5,10}, 3,23, "├[-100~3[[3_23[[23~100[┤"_expect); // larger segment overarching
410 testCase (SegL{5,10}, 5,23, "├[-100~5[[5_23[[23~100[┤"_expect); // larger segment left aligned
411 }
412
413
414
418 void
420 {
421 auto testCase = [](SegL segmentation
422 ,OptInt startNew // Note: these are optional...
423 ,OptInt afterNew
424 ,ExpectString expectedResult)
425 {
426 invokeSplitSplice (segmentation, startNew, afterNew);
427 CHECK (segmentation == expectedResult);
428 CHECK (segmentation.isValid());
429 };
430 auto x = std::nullopt;
432 testCase (SegL{}, 3,2, "├[-100~2[[2_3[[3~100[┤"_expect); // flipped interval spec is reoriented
434 testCase (SegL{}, 3,x, "├[-100~3[[3_100[┤"_expect); // expanded until domain end
435 testCase (SegL{}, x,5, "├[-100_5[[5~100[┤"_expect); // expanded to start of domain
437 testCase (SegL{4,6}, 5,x, "├[-100~4[[4_5[[5_6[[6~100[┤"_expect); // expanded until end of enclosing segment
438 testCase (SegL{4,6}, x,5, "├[-100~4[[4_5[[5_6[[6~100[┤"_expect); // expanded to start of enclosing segment
440 testCase (SegL{4,6}, 3,x, "├[-100~3[[3_4[[4_6[[6~100[┤"_expect); // expanded to fill gap to next segment
441 testCase (SegL{4,6}, x,3, "├[-100_3[[3~4[[4_6[[6~100[┤"_expect); // expanded to cover predecessor completely
442 testCase (SegL{4,6}, 4,x, "├[-100~4[[4_6[[6~100[┤"_expect); // expanded to cover (replace) successor
443 testCase (SegL{4,6}, x,4, "├[-100_4[[4_6[[6~100[┤"_expect); // expanded to cover (replace) predecessor
445 testCase (SegL{4,6}, 7,x, "├[-100~4[[4_6[[6~7[[7_100[┤"_expect); // shorten successor and expand new segment to end of successor (=domain end)
446 testCase (SegL{4,6}, x,7, "├[-100~4[[4_6[[6_7[[7~100[┤"_expect); // fill gap between predecessor and given new segment end
447 testCase (SegL{4,6}, 6,x, "├[-100~4[[4_6[[6_100[┤"_expect); // expand to cover (replace) the following segment until domain end
448 testCase (SegL{4,6}, x,6, "├[-100~4[[4_6[[6~100[┤"_expect); // expanded to cover (replace) the preceding segment
450 testCase (SegL{}, x,x, "├[-100_100[┤"_expect); // without any specification, the whole domain is covered
451 testCase (SegL{4}, x,x, "├[-100~4[[4_100[┤"_expect); // otherwise, without any spec the last segment is replaced
452 testCase (SegL{4,6}, x,x, "├[-100~4[[4_6[[6_100[┤"_expect);
454 testCase (SegL{4,5,6,8}, 3,6, "├[-100~3[[3_6[[6_8[[8~100[┤"_expect); // spanning and thus replacing multiple segments
455 testCase (SegL{4,5,6,8}, 4,6, "├[-100~4[[4_6[[6_8[[8~100[┤"_expect);
456 testCase (SegL{4,5,6,8}, 4,7, "├[-100~4[[4_7[[7_8[[8~100[┤"_expect);
457 testCase (SegL{4,5,6,8}, 3,7, "├[-100~3[[3_7[[7_8[[8~100[┤"_expect);
458 testCase (SegL{4,5,6,8}, 3,8, "├[-100~3[[3_8[[8~100[┤"_expect);
459 testCase (SegL{4,5,6,8}, 4,8, "├[-100~4[[4_8[[8~100[┤"_expect);
460 testCase (SegL{4,5,6,8}, 4,9, "├[-100~4[[4_9[[9~100[┤"_expect);
461 testCase (SegL{4,5,6,8}, 5,9, "├[-100~4[[4_5[[5_9[[9~100[┤"_expect);
462 testCase (SegL{4,5,6,8}, 5,x, "├[-100~4[[4_5[[5_6[[6_8[[8~100[┤"_expect);
463 testCase (SegL{4,5,7,8}, x,6, "├[-100~4[[4_5[[5_6[[6_7[[7_8[[8~100[┤"_expect);
464 }
465
466
467
474 void
476 {
477 SegL segs{2,6};
478 CHECK (segs == "├[-100~2[[2_6[[6~100[┤"_expect);
479
480 Iter s = segs.begin();
481 CHECK (s->start == -100);
482 CHECK (s->after == 2);
483 uint id1 = s->id;
484 void* adr1 = &(*s);
485 ++s;
486 CHECK (s->start == 2);
487 CHECK (s->after == 6);
488 uint id2 = s->id;
489 void* adr2 = &(*s);
490 ++s;
491 CHECK (s->start == 6);
492 CHECK (s->after == 100);
493 uint id3 = s->id;
494 void* adr3 = &(*s);
495
496 auto [p,n,a] = invokeSplitSplice (segs, 3,4);
497 CHECK (5 == segs.size());
498 CHECK (segs == "├[-100~2[[2_3[[3_4[[4_6[[6~100[┤"_expect);
499
500 s = segs.begin();
501 CHECK (s->start == -100);
502 CHECK (s->after == 2);
503 CHECK (s->id == id1);
504 CHECK (adr1 == getAdr(*s));
505 CHECK (s != p);
506 ++s;
507 CHECK (s == p);
508 CHECK (s->start == 2);
509 CHECK (s->after == 3);
510 CHECK (s->id == id2);
511 CHECK (adr2 != getAdr(*s)); // this is the first part of the split segment (new allocation)
512 ++s;
513 CHECK (s != p);
514 CHECK (s == n);
515 CHECK (s->start == 3);
516 CHECK (s->after == 4);
517 CHECK (s->id != id1);
518 CHECK (s->id != id2);
519 CHECK (s->id != id3);
520 CHECK (adr2 != getAdr(*s));
521 ++s;
522 CHECK (s != n);
523 CHECK (s != a);
524 CHECK (s->start == 4);
525 CHECK (s->after == 6);
526 CHECK (s->id != id1);
527 CHECK (s->id == id2);
528 CHECK (s->id != id3);
529 CHECK (adr2 != getAdr(*s)); // this is the second part of the split segment (new allocation)
530 ++s;
531 CHECK (s == a);
532 CHECK (s->start == 6);
533 CHECK (s->after == 100);
534 CHECK (s->id != id1);
535 CHECK (s->id != id2);
536 CHECK (s->id == id3);
537 CHECK (adr3 == getAdr(*s));
538 ++s;
539 CHECK (s == segs.end());
540 }
541 };
542
543 LAUNCHER (SplitSplice_test, "unit common");
544
545
546}} // namespace lib::test
Implementation of »SplitSplice« algorithm.
Helper to produce better diagnostic messages when comparing to an expected result string.
Types marked with this mix-in may be moved but not copied.
Definition nocopy.hpp:50
A front-end for using printf-style formatting.
Automatically use custom string conversion in C++ stream output.
Front-end for printf-style string template interpolation.
Collection of small helpers and convenience shortcuts for diagnostics & formatting.
unsigned int uint
Definition integral.hpp:29
auto invokeSplitSplice(SegL &segs, OptInt startNew, OptInt afterNew)
Perform the »SplitSplice« Algorithm to splice a new Segment into the given segmentation of the intege...
Implementation namespace for support and library code.
Test runner and basic definitions for tests.
bool isSameObject(A const &a, B const &b)
compare plain object identity, based directly on the referee's memory identities.
Definition util.hpp:421
const void * getAdr(X &x)
extract address but strip any type info
Definition util.hpp:377
string join(COLL &&coll, string const &delim=", ")
enumerate a collection's contents, separated by delimiter.
bool isnil(lib::time::Duration const &dur)
Mix-Ins to allow or prohibit various degrees of copying and cloning.
Simplistic test class runner.
#define LAUNCHER(_TEST_CLASS_, _GROUPS_)
Definition run.hpp:116
Generic algorithm to splice a new segment into a seamless segmentation of intervals.
Test-Segmentation comprised of a sequence of Seg entries.
Test Dummy: a "segment" representing an integer interval.
Seg(Seg &&rr)
move-init: causes source-ref to be invalidated
Seg(Seg const &ref, int s, int a)
create a clone, but modify bounds
A collection of frequently used helper functions to support unit testing.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...