Lumiera 0.pre.04
»edit your freedom«
Loading...
Searching...
No Matches
split-splice.hpp
Go to the documentation of this file.
1/*
2 SPLIT-SPLICE.hpp - Algorithm to integrate a new interval into an axis-segmentation.
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
14
71#ifndef LIB_SPLIT_SPLICE_H
72#define LIB_SPLIT_SPLICE_H
73
74#include "lib/error.hpp"
75#include "lib/meta/function.hpp"
77#include "lib/nocopy.hpp"
78
79#include <optional>
80#include <array>
81
82
83namespace lib {
84
85 namespace error = lumiera::error;
86
87
88 namespace splitsplice {
89
90
98 template<class ORD
99 ,class POS
100 ,class START
101 ,class AFTER
102 ,class CREATE
103 ,class EMPTY
104 ,class CLONE
105 ,class DELETE
106 >
107 class Algo
109 {
110 /* ======= elementary operations ======= */
111
112 ASSERT_VALID_SIGNATURE (START, ORD(POS))
113 ASSERT_VALID_SIGNATURE (AFTER, ORD(POS))
114 ASSERT_VALID_SIGNATURE (CREATE, POS(POS,ORD,ORD))
115 ASSERT_VALID_SIGNATURE (EMPTY, POS(POS,ORD,ORD))
116 ASSERT_VALID_SIGNATURE (CLONE, POS(POS,ORD,ORD,POS))
117 ASSERT_VALID_SIGNATURE (DELETE, POS(POS,POS))
118
119 START getStart;
120 AFTER getAfter;
121 CREATE createSeg;
122 EMPTY emptySeg;
123 CLONE cloneSeg;
124 DELETE discard;
125
126 using OptORD = std::optional<ORD>;
127 const ORD AXIS_END;
128
129
130 /* ======= working state ======= */
131
133
135 { // can be assigned in one step (ORD may be immutable)
136 ORD start,
138 } b_;
139
146
149
150 public:
158 Algo (START fun_getStart
159 ,AFTER fun_getAfter
160 ,CREATE fun_createSeg
161 ,EMPTY fun_emptySeg
162 ,CLONE fun_cloneSeg
163 ,DELETE fun_discard
164 ,const ORD axisEnd
165 ,POS startAll, POS afterAll
166 ,OptORD start, OptORD after)
167 : getStart{fun_getStart}
168 , getAfter{fun_getAfter}
169 , createSeg{fun_createSeg}
170 , emptySeg{fun_emptySeg}
171 , cloneSeg{fun_cloneSeg}
172 , discard {fun_discard}
173 , AXIS_END{axisEnd}
174 , pred_{}
175 , succ_{}
176 , b_{establishSplitPoint (startAll,afterAll, start,after)}
177 {
178 // Postcondition: ordered start and end times
179 ENSURE (pred_ != afterAll);
180 ENSURE (succ_ != afterAll);
181 ENSURE (b_.start < b_.after);
182 ENSURE (getStart(pred_) <= b_.start);
183 ENSURE (b_.start <= getStart(succ_) or pred_ == succ_);
184 }
185
191 SegBounds
192 establishSplitPoint (POS startAll, POS afterAll
193 ,OptORD start, OptORD after)
194 { // nominal break point
195 ORD sep = start? *start
196 : after? *after
197 : AXIS_END;
198
199 // find largest Predecessor with start before separator
200 for (succ_ = startAll, pred_ = afterAll
201 ;succ_ != afterAll and getStart(succ_) < sep
202 ;++succ_)
203 {
204 pred_ = succ_;
205 }
206 REQUIRE (pred_ != succ_, "non-empty segmentation required");
207 if (succ_ == afterAll) succ_=pred_;
208 if (pred_ == afterAll) pred_=succ_; // separator touches bounds
209
210 // Stage-2 : establish start and end point of new segment
211
212 ORD startSeg = start? *start
213 : getAfter(pred_) < sep? getAfter(pred_)
214 : getStart(pred_);
215 ORD afterSeg = after? *after
216 : getStart(succ_) > sep? getStart(succ_)
217 : getAfter(succ_);
218 ENSURE (startSeg != afterSeg);
219 if (startSeg < afterSeg)
220 return {startSeg,afterSeg};
221 else
222 return {afterSeg,startSeg};
223 }
224
225
231 void
233 {
234 ORD startPred = getStart (pred_),
235 afterPred = getAfter (pred_);
236
237 if (startPred < b_.start)
238 {
239 if (afterPred < b_.start) opPred_ = INS_NOP;
240 else
241 if (afterPred == b_.start) opPred_ = SEAMLESS;
242 else
243 {
244 opPred_ = TRUNC;
245 if (afterPred > b_.after)
246 { // predecessor actually spans the new segment
247 // thus use it also as successor and truncate both (=SPLIT)
248 succ_ = pred_;
249 opSucc_ = TRUNC;
250 return;
251 } } }
252 else
253 {
254 REQUIRE (startPred == b_.start, "predecessor does not precede start point");
255 opPred_ = DROP;
256 if (b_.after < afterPred )
257 { // predecessor coincides with start of new segment
258 // thus use it rather as successor and truncate at start
259 succ_ = pred_;
260 opSucc_ = TRUNC;
261 return;
262 } }
263
264 ORD startSucc = getStart (succ_);
265 if (startSucc < b_.after)
266 {
267 while (getAfter(succ_) < b_.after)
268 ++succ_;
269 ASSERT (getStart(succ_) < b_.after // in case we dropped a successor completely spanned,
270 ,"seamless segmentation"); // even the next one must start within the new segment
271
272 if (b_.after == getAfter(succ_)) opSucc_ = DROP;
273 else
275 }
276 else
277 {
278 if (b_.after == startSucc) opSucc_ = SEAMLESS;
279 else opSucc_ = INS_NOP;
280 }
281 }
282
283
291 std::array<POS, 3>
293 {
294 POS refPred = pred_, refSucc = succ_;
295 REQUIRE (opPred_ != NIL and opSucc_ != NIL);
296
297 // deletions are done by skipping the complete range around the insertion point;
298 // thus to retain a predecessor or successor, this range has to be reduced
299 if (opPred_ == INS_NOP or opPred_ == SEAMLESS)
300 ++pred_;
301 if (opSucc_ == DROP or opSucc_ == TRUNC)
302 ++succ_;
303
304 // insert the new elements /before/ the range to be dropped, i.e. at pred_
305 POS insPos = pred_;
306 POS n = createSeg (insPos, b_.start, b_.after);
307 POS s = n;
308 //
309 // possibly adapt the predecessor
310 if (opPred_ == INS_NOP)
311 s = emptySeg (n, getAfter(refPred), b_.start);
312 else
313 if (opPred_ == TRUNC)
314 s = cloneSeg (n, getStart(refPred), b_.start, refPred);
315 //
316 // possibly adapt the successor
317 if (opSucc_ == INS_NOP)
318 emptySeg (insPos, b_.after, getStart(refSucc));
319 else
320 if (opSucc_ == TRUNC)
321 cloneSeg (insPos, b_.after, getAfter(refSucc), refSucc);
322
323 // finally discard superseded segments
324 POS e = discard (insPos, succ_);
325
326 // indicate the range where changes happened
327 return {s,n,e};
328 }
329 };
330
331 }//namespace splitsplace
332} // namespace lib
333#endif /*LIB_SPLIT_SPLICE_H*/
Implementation of »SplitSplice« algorithm.
std::array< POS, 3 > performSplitSplice()
Stage-4 of the algorithm performs the actual insert and deleting of segments.
SegBounds establishSplitPoint(POS startAll, POS afterAll, OptORD start, OptORD after)
Stage-1 and Stage-2 of the algorithm determine the insert point and establish the actual start and en...
void determineRelations()
Stage-3 of the algorithm works out the precise relation of the predecessor and successor segments to ...
std::optional< ORD > OptORD
struct lib::splitsplice::Algo::SegBounds b_
Algo(START fun_getStart, AFTER fun_getAfter, CREATE fun_createSeg, EMPTY fun_emptySeg, CLONE fun_cloneSeg, DELETE fun_discard, const ORD axisEnd, POS startAll, POS afterAll, OptORD start, OptORD after)
Setup for a single SplitSplice-operation to insert a new segment start to after.
Any copy and copy construction prohibited.
Definition nocopy.hpp:38
Lumiera error handling (C++ interface).
Metaprogramming tools for detecting and transforming function types.
#define ASSERT_VALID_SIGNATURE(_FUN_, _SIG_)
Macro for a compile-time check to verify the given generic functors or lambdas expose some expected s...
Definition function.hpp:316
Implementation namespace for support and library code.
STL namespace.
Mix-Ins to allow or prohibit various degrees of copying and cloning.
a family of time value like entities and their relationships.