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