chiark / gitweb /
7c0b3b7b63fbf5f79ca9b4b970e316a30dc1e5a4
[trains.git] / hostside / resolve.c
1 /*
2  * resolve detected trains into initial state
3  */
4
5 #include "realtime.h"
6
7 #define NOOP (void)0
8
9 /*---------- main resolution processing ----------*/
10  /*
11   * Algorithm:
12   *
13   *  Notation and background:
14   *
15   *     S                       set of segments
16   *     D \subset S             set of segments where we have detection
17   *
18   *     T                       set of trains
19   *     H(t) \subset S          home range for train t \elem T
20   *     E(t) \subset S          set of segments where train t last expected
21   *
22   *     H(t) \disjoint H(t')   for t != t'
23   *     E(t) \disjoint E(t')   for t != t'
24   *     but not necessarily E(t) \disjoint H(t')
25   *
26   *  We want to find a mapping R(t)
27   *     R(t) \elem { N, E, H }          t \elem T,  N(t') = \null
28   *  giving
29   *     A(t) = (R(t))(t)                segments allocated to train t
30   *      U = \union_{t} A(t)
31   *  satisfing
32   *      A(t) \disjoint A(t')             for t != t'   `disjoincy'
33   *      D \subset U                                    `coverage'
34   *  and the solution is somehow optimal or at least not badly suboptimal.
35   *
36   * We compute R incrementally, maintaining disjoincy,,
37   * while increasing U.  At each stage R is an optimal solution
38   * to the problem posed by D' = U, and we maintain this invariant.
39   *
40   * We define an optimality ordering on R by counting occurrences of
41   * H, E, and H in the range, in that order.  Ie,
42   *      Count(R,x) = |{ t: R(t) = x }|
43   *     Rank(R) = Count(R,H) * (|T|+1) + Count(R,E)
44   * and compare Ranks numerically, lower being better.
45   *
46   * So we mean that for any incrementally computed R, there is no
47   * R' satisfying the same subproblem D' = U for which Rank(R') < Rank(R).
48   *
49   * For the benefit of client programs, we record the key elements of
50   * the proofs that we generate for non-N values of R.
51   *
52   *  1. Initially we set \all_{t} R(t) = N.
53   *
54   *     Optimality: This is minimal for U = \null, as Rank(R) = 0.
55   *
56   *  2. Then we search for lack of coverage, ie violations of D \subset U.
57   *     If none are found we are done.
58   *
59   *  3.  Let  d \elem D  but  d !\elem U.
60   *     We will now calculate new R which we will call R2
61   *     giving new U which we will call U2.
62   *
63   *  3a. Hope that  \exists{t} st d \elem E(t)  with
64   *      E(t) \disjoint U  and  R(t) = N.
65   *     If so set R2(t) = E giving a solution to U2.
66   *
67   *      Optimality: if d \elem D then we need U2 to include d.
68   *      That means d \elem E(t) or d \elem H(t').  No E(t')
69   *     other than E(t) can help since they are disjoint, and
70   *     we would certainly prefer adding E(t) to adding H(t').
71   *     (Adding H(t') and removing some other H(t'') doesn't
72   *     work either because the Hs are disjoint, so no
73   *     H can stand in for any other.)
74   *     
75   *     So the rank increase by 1 is is minimal for U2.
76   *
77   *     Proof elements: R(t) = E is demonstrated by
78   *     every d meeting these conditions.
79   *
80   *  3b.        Alternatively, hope that  d \elem H(t')
81   *
82   *     If so set  R2(t') = H
83   *                \all_{t+} R2(t+) = N where R(t+) = E.
84   *
85   *     Optimality: in the case we are now dealing with, we
86   *     didn't succeed with the test for 3a, so either:
87   *
88   *      (i)  There is no t where d \elem E(t), so R(t') = H is
89   *           essential because no solution without R(t') = H has d \elem U2.
90   *
91   *      (ii) There is t where d \elem E(t) but R(t) = H.
92   *           We have therefore already proved that R(t) cannot be E.
93   *
94   *      (iii) There is t where d \elem E(t) but E(t) !\disjoint U
95   *          In this case, consider a clash d' between E(t) and U
96   *              d' \elem U,  d' \elem E(t)
97   *
98   *          This clash at d' is preventing us covering d with E(t).
99   *          E's can't clash since they are disjoint so it must be
100   *          a clash with some H.  But we have no unavoidable H's
101   *          in our solution to U, so this solution to U2 has no
102   *          unavoidable H's either.
103   *
104   *          Or to put it algebraically,
105   *           d' != d, because d !\elem U.
106   *           d' \elem A(t'') for some t'' since d' \elem U.
107   *           If R(t'') = E, d' \elem E(t'') but E's are disjoint.
108   *            So R(t'') = H, which must have been unavoidable by our
109   *           inductive premise.  Thus for U2, R2(t'') = H is also
110   *           unavoidable.
111   *
112   *     Proof elements: R(t') = H is demonstrated by
113   *     every d meeting these conditions
114   *     together with for each such d
115   *         the alternative train t if applicable
116   *         (there can be only one)
117   *     plus in case (iii)
118   *         every clash d' between E(t) and U
119   *         plus for each such d' the corresponding t''
120   *          (we record all this indexed by t so we can reuse it
121   *           if needed)
122   *
123   *     R2 consists only of Hs and Ns.  All of the Hs are necessary
124   *     for U2 by the above, so R2 is minimal for U2.
125   *
126   *     The rank is increased: we increase Count(R,H) and set
127   *     Count(R,E) to 0.
128   *
129   *  3c. If neither of these is true, we are stuck and cannot
130   *     satisfy d.  This is an error.  We remove d from D
131   *     and continue anyway to see if we can find any more.
132   *
133   *     Proof elements: Lack of solution is demonstrated
134   *     separately by every such  d  together with
135   *     for each d if d \elem E(t) for some t
136   *         that t
137   *         plus the clashes d'' as for 3b(iii).
138   *
139   *  Termination: at each iteration 3a or 3b, we strictly increase Rank.
140   *  At each iteration 3c we reduce D.
141   *  Rank cannot exceed |T|*(|T|+1) and D starts out finite. So
142   *  eventually we must succeed or fail.
143   *
144   */
145
146 /* values for Train->resolution: */
147 #define RR_N 0u
148 #define RR_E 1u
149 #define RR_H 2u
150 #define RESOLUTION_CHARS "NEH"
151
152 /* During the main algorithm:
153  *  We record R in tra->resolution,
154  *  U in segi->mark0 and D in segi->res_detect
155  */
156 #define iselem_u mark0
157
158 static int resmain_getmovpos(TrackLocation *t, TrackAdvanceContext *c,
159                              MovPosComb *use_io) {
160   Segment *d= t->seg;
161   const char *pn= d->i->pname;
162
163   if (d->home && d->home->resolution == RR_H) {
164     d->tr_backwards= d->ho_backwards;
165     *use_io= 0; /* a bit kludgey */
166     DPRINTF(resolving,main, "getmovpos %s H\n", pn);
167   } else if (d->owner) {
168     d->tr_backwards ^= d->owner->backwards;
169     *use_io= d->movposcomb;
170     DPRINTF(resolving,main, "getmovpos %s E %d\n", pn, *use_io);
171   } else if (!d->res_movposset) {
172     *use_io= -1;
173     DPRINTF(resolving,main, "getmovpos %s N -1\n", pn);
174   } else if (d->motion) {
175     *use_io= movpos_change_intent(d->motion);
176     DPRINTF(resolving,main, "getmovpos %s M %u\n", pn, *use_io);
177   }
178   return 0;
179 }
180
181 static MovPosComb just_getmovpos(Segment *d, TrackAdvanceContext *tac) {
182   MovPosComb target;
183   TrackLocation tloc;
184   int r;
185
186   tloc.seg= d;
187   r= resmain_getmovpos(&tloc,tac,&target);  assert(!r);
188   return target;
189 }
190
191 static int resolve_complete_main(void) {
192   int problems, phase, nextphase;
193   TRAIN_ITERVARS(t);
194   TRAIN_ITERVARS(t2);
195   TRAIN_ITERVARS(tplus);
196   SEGMENT_ITERVARS(d);
197   SEGMENT_ITERVARS(d1);
198   SEGMENT_ITERVARS(dplus);
199
200   problems= 0;
201   FOR_TRAIN(t,NOOP,NOOP) t->resolution= RR_N;
202
203   for (phase=0; phase<3; phase=nextphase) {
204     nextphase= phase+1;
205     DPRINTF(resolving,alg, "iteration %c\n", "EHX"[phase]);
206
207     DPRINTF(resolving,alg, " calculate-u\n");
208
209     FOR_SEGMENT(d,NOOP,NOOP) { /* calculate U */
210       unsigned updated= 0;
211
212 #define ADDTO_U_EH(homeowner,HH_HE,string)                              \
213       if (d->homeowner && d->homeowner->resolution == HH_HE) {          \
214         DPRINTF(resolving,alg, "  covered %s " string " %s\n",  \
215                 di->pname, d->homeowner->pname);                        \
216         updated++;                                                      \
217       }
218       ADDTO_U_EH(home,  RR_H, "at-home");
219       if (d->movposcomb >= 0)
220         ADDTO_U_EH(owner, RR_E, "as-expected");
221
222       assert(updated<=1);
223       d->iselem_u= updated;
224     }
225
226     DPRINTF(resolving,alg, " searching\n");
227     FOR_SEGMENT(d, NOOP, NOOP) {
228       if (!(d->res_detect && !d->iselem_u))
229         continue;
230       /* 3. we have a violation of D \subset U, namely d */
231
232       DPRINTF(resolving,alg, "  violation %s\n", di->pname);
233
234       if (d->owner) { /* 3a perhaps */
235         t= d->owner;
236         DPRINTF(resolving,alg, "   expected %s\n", t->pname);
237
238         if (t->addr < 0) {
239           DPRINTF(resolving,alg, "    expected-is-not-addressable\n");
240           goto not_3a;
241         }
242
243         if (t->resolution == RR_H) {
244           DPRINTF(resolving,alg, "    expected-is-at-home\n");
245           goto not_3a;
246         }
247
248         if (d->movposcomb < 0) {
249           DPRINTF(resolving,alg, "    track-unknown-position\n");
250           goto not_3a;
251         }
252
253         if (t->resolution == RR_E)
254           /* we added it in this sweep and have found another d */
255           goto already_3a;
256         assert(t->resolution == RR_N);
257
258         /* check E(t) \disjoint U */
259         int clashes= 0;
260         FOR_SEGMENT(d1, NOOP, NOOP) {
261           if (d1->owner == t && d1->iselem_u) {
262             FOR_TRAIN(t2, NOOP, NOOP) {
263               if (t2->resolution == RR_H && d1->owner == t2) {
264                 DPRINTF(resolving,alg, "    clash %s %s\n",
265                         d1i->pname, t2->pname);
266                 clashes++;
267               }
268             }
269           }
270         }
271         if (clashes) {
272           DPRINTF(resolving,alg, "   expected-has-clashes\n");
273           goto not_3a;
274         }
275
276         /* Yay! 3a. */
277         t->resolution= RR_E;
278         nextphase= 0;
279       already_3a:
280         DPRINTF(resolving,alg, "   supposing %s as-expected\n", t->pname);
281         continue;
282       }
283     not_3a:
284
285       if (d->home) { /* 3b then */
286         if (phase<1)
287           continue;
288
289         Train *t1= d->home; /* t' st d \elem H(t') */
290         DPRINTF(resolving,alg, "   home %s\n", t1->pname);
291         
292         if (t1->addr < 0) {
293           DPRINTF(resolving,alg, "    home-is-not-addressable\n");
294           goto not_3b;
295         }
296
297         DPRINTF(resolving,alg, "   reset-expecteds\n");
298         FOR_TRAIN(tplus, NOOP,NOOP) {
299           if (tplus->resolution == RR_E) {
300             DPRINTF(resolving,alg, "   supposing %s absent\n", tplus->pname);
301             tplus->resolution= RR_N;
302             nextphase= 0;
303           }
304         }
305         /* Now must trim U to correspond to our setting of R(t+)=N
306          * so that any other implied Hs are spotted. */
307         FOR_SEGMENT(dplus, NOOP,NOOP) {
308           Train *tplus= dplus->owner;
309           if (!(tplus && tplus->resolution == RR_E)) continue;
310           dplus->iselem_u= 0;
311         }
312
313         t1->resolution= RR_H;
314         nextphase= 0;
315
316         DPRINTF(resolving,alg, "   supposing %s at-home\n", t1->pname);
317         continue;
318       }
319     not_3b:
320
321       /* Oh dear, 3c. */
322       if (phase<2)
323         continue;
324
325       ouprintf("resolution inexplicable %s\n", di->pname);
326       d->res_detect= 0;
327       problems++;
328     }
329   }
330     
331   TrackAdvanceContext tac;
332   MovPosComb target;
333   tac.getmovpos= resmain_getmovpos;
334
335   FOR_SEGMENT(d,NOOP,NOOP) {
336     if (problems) return problems;
337
338     d->iselem_u= 0;
339
340     target= just_getmovpos(d,&tac);
341
342     if (d->i->invertible) {
343       Segment *interferer= segment_interferes_simple(&tac,d);
344       if (interferer) {
345         actual_inversions_start();
346         d->seg_inverted= interferer->seg_inverted
347             ^ d->i->interferes_polarity_opposed;
348         actual_inversions_segment(d);
349         actual_inversions_done();
350       }
351     }
352
353     if (d->i->n_poscombs>1) {
354       if (target >= 0) {
355         if (!d->moving) {
356           movpos_unreserve(d->motion);
357           d->motion= 0;
358         }
359         if (!d->res_movposset)
360           d->movposcomb= -1;
361
362         ErrorCode ec= movpos_reserve(d,-1,&d->motion,target,-1);
363         if (ec) {
364           ouprintf("resolution movpos-change-failed %s/%s %s\n",
365                    d->i->pname, d->i->poscombs[target].pname,
366                    errorcodelist[ec]);
367           problems++;
368         } else {
369           d->res_movposset= 1;
370         }
371       }
372     } else {
373       assert(!d->i->n_movfeats);
374       d->movposcomb= 0;
375     }
376   }
377   return problems;
378 }
379
380 #undef iselem_u
381
382 /*---------- heads and tails of trains, final placement ----------*/
383
384 #define resfin_ours mark1
385
386 typedef struct {
387   Distance hardallow, min, max;
388   int hardwhy;
389   Segment *lastdetect, *hardend;
390 } FindEndConstraint;
391
392 typedef struct {
393   TrackAdvanceContext tc; /* first! */
394   TrackLocation t;
395   Train *train;
396   Segment *startpoint;
397   Distance atlastdetect;
398   Segment *lastdetect;
399   FindEndConstraint constraints[2];
400   int problems, rear;
401 } FindEndUserContext;
402
403 static int findends_getmovpos(TrackLocation *t, TrackAdvanceContext *c,
404                               MovPosComb *use_io) {
405   const char *pn= t->seg->i->pname;
406   if (t->seg->motion) *use_io= movpos_change_intent(t->seg->motion);
407   if (*use_io<0) {
408     DPRINTF(resolving,ends, "   getmovpos %s fails\n", pn);
409     return 'm';
410   }
411   DPRINTF(resolving,ends, "   getmovpos %s -> %d\n", pn, *use_io);
412   return 0;
413 }
414
415 static int constraint_nextseg(TrackLocation *t, struct TrackAdvanceContext *c,
416                               MovPosComb *mpc, const TrackLocation *before) {
417   int r;
418   FindEndUserContext *u= (void*)c;
419   
420   DPRINTF(resolving,ends, "  constraint_nextseg %c"
421           " %s%s remain=%d dist=INF-%d det=%d ours=%d owner=%s home=%s\n",
422           "frx"[u->rear],
423           t->backwards?"-":"", t->seg->i->pname, t->remain,
424           TL_DIST_INF - c->distance,
425           t->seg->res_detect,
426           t->seg->resfin_ours,
427           t->seg->owner ? t->seg->owner->pname : "-",
428           t->seg->home ?  t->seg->home->pname  : "-");
429
430   if (!t->seg->resfin_ours) return 'o';
431   r= findends_getmovpos(t,0,mpc);  if (r) return r;
432
433   const SegPosCombInfo *pci= &t->seg->i->poscombs[*mpc];
434
435   if (before) {
436     if (t->seg == u->startpoint) return 'l';
437     const SegmentLinkInfo *rlink= &pci->link[!t->backwards];
438     if (before->seg != &segments[rlink->next]) {
439       DPRINTF(resolving,ends, "   j mismatch %s %s\n",
440               before->seg->i->pname, info_segments[rlink->next].pname);
441       return 'j';
442     }
443   }
444   if (t->seg->res_detect) {
445     u->atlastdetect= c->distance;
446     if (!before)
447       /* wind back to start of this segment which is necessary if this
448        * we are looking rearwards */
449       u->atlastdetect += (pci->dist - t->remain);
450     u->lastdetect= t->seg;
451   }
452   return 0;
453 }
454
455 static int constraint_trackend(TrackLocation *t,
456                                struct TrackAdvanceContext *c) {
457   return 'e';
458 }
459
460 static void end_startpoint(FindEndUserContext *u) {
461   int r;
462
463   u->tc.distance= TL_DIST_INF;
464   u->tc.nextseg= constraint_nextseg;
465   u->tc.getmovpos= findends_getmovpos;
466   u->tc.trackend= constraint_trackend;
467   u->tc.u= 0;
468
469   r= trackloc_set_exactinto(&u->t, &u->tc,
470                             u->startpoint, u->startpoint->tr_backwards,
471                             0);
472   assert(!r);
473 }
474
475 static void end_constraints(FindEndUserContext *u) {
476   FindEndConstraint lim;
477   Train *tra= u->train;
478   int r;
479
480   end_startpoint(u);
481   u->tc.distance= TL_DIST_INF;
482
483   u->lastdetect= 0;
484   u->atlastdetect= -1;
485
486   if (u->rear) {
487     r= trackloc_reverse_exact(&u->t,&u->tc);
488     assert(!r);
489   }
490
491   FindEndConstraint *cons= &u->constraints[u->rear];
492
493   cons->hardwhy= trackloc_advance(&u->t,&u->tc);
494   
495   assert(cons->hardwhy);
496   assert(u->lastdetect);
497   assert(u->atlastdetect >= 0);
498
499   Distance nose= MARGIN_NOSE +
500     ((tra->backwards ^ u->rear) ? tra->head : tra->tail);
501
502   lim.min= TL_DIST_INF - u->atlastdetect;
503   lim.max= (TL_DIST_INF - u->tc.distance) - nose;
504
505   if (!u->rear) {
506     cons->min= lim.min;
507     cons->max= lim.max;
508   } else {
509     cons->max= -lim.min + ceil(tra->detectable * MARGIN_TRAINLENGTH);
510     cons->min= -lim.max + ceil(tra->detectable * MARGIN_TRAINLENGTH);
511   }
512   cons->lastdetect= u->lastdetect;
513   cons->hardend= u->t.seg;
514
515   if (cons->min > cons->max) {
516     ouprintf("resolution implausible %s %s overhang %d %d\n",
517              cons->lastdetect->i->pname, tra->pname,
518              cons->min - cons->max, nose);
519     u->problems++;
520   }
521
522   DPRINTF(resolving,ends, " lims %c %s,%s,%c dist=INF-%d"
523           " lim=%d..%d out=%d..%d\n",
524           "fr"[u->rear],
525           cons->lastdetect->i->pname, u->t.seg->i->pname, cons->hardwhy,
526           TL_DIST_INF - u->tc.distance,
527           lim.min, lim.max, cons->min, cons->max);
528 }
529
530 static int resolve_complete_ends_train(Train *tra) {
531   SEG_IV;
532   FindEndUserContext u;
533   const SegPosCombInfo *pci;
534   int r, DP;
535
536   switch (tra->resolution) {
537   case RR_H: break;
538   case RR_E: break;
539   case RR_N: return 0;
540   default: abort();
541   }
542
543   DPRINTF1(resolving,ends, "%s %c",
544            tra->pname, RESOLUTION_CHARS[tra->resolution]);
545
546   memset(&u,0,sizeof(u));
547   u.startpoint= 0;
548   
549   FOR_SEGMENT(seg,NOOP,NOOP) {
550     seg->resfin_ours= tra ==
551       (tra->resolution==RR_H ? seg->home : seg->owner);
552     if (!seg->resfin_ours) continue;
553     DPRINTF2(" %s%s", seg->tr_backwards?"-":"", seg->i->pname);
554     if (seg->res_detect) {
555       DPRINTF2("*");
556       u.startpoint= seg;
557     }
558   }
559   assert(u.startpoint);
560   DPRINTF2("\n");
561
562   /* There are four pieces of information we have:
563    * The rearmost detection, the rearmost end of ownership
564    * the foremost detection, the foremost end of ownership
565    *
566    * From each we compute a range of locations.  These are
567    * in the form of a minimum and maximum distance of the
568    * foredetect ahead of 0 into the startpoint.
569    */
570
571   u.train= tra;
572   for (u.rear=0; u.rear<2; u.rear++)
573     end_constraints(&u);
574
575   Distance wrongness;
576
577   if ((wrongness= u.constraints[0].min - u.constraints[1].max) > 0) {
578     for (u.rear=1; u.rear>0; u.rear--)
579       ouprintf("resolution implausible %s %s over-detect %d\n",
580                u.constraints[u.rear].lastdetect->i->pname,
581                tra->pname, wrongness);
582     u.problems++;
583   }
584   if ((wrongness= u.constraints[1].min - u.constraints[0].max) > 0) {
585     for (u.rear=1; u.rear>0; u.rear--)
586       ouprintf("resolution implausible %s %s cramped %d\n",
587                u.constraints[u.rear].hardend->i->pname,
588                tra->pname, wrongness);
589     u.problems++;
590   }
591
592   int min= MAX(u.constraints[0].min, u.constraints[1].min);
593   int max= MIN(u.constraints[0].max, u.constraints[1].max);
594   DPRINTF(resolving,ends, " lims a %d..%d problems=%d\n",
595           min, max, u.problems);
596
597   if (u.problems)
598     return u.problems;
599   
600   assert(min <= max);
601   assert(max > 0); /* startpoint is detected so foredet is there or later */
602
603   /* Now we just need to turn this into the canonical format. */
604
605   end_startpoint(&u);
606
607   u.tc.distance= max;
608   r= trackloc_advance(&u.t, &u.tc);  assert(!r);
609
610   tra->foredetect= u.t.seg;
611   tra->foredetect->tr_backwards= u.t.backwards;
612
613   r= trackloc_getlink(&u.t, &u.tc, &pci,0,0);  assert(!r);
614   tra->maxinto= pci->dist - u.t.remain;
615   tra->uncertainty= max - min;
616
617   report_train_position(tra);
618   /* We don't lay the train out now since the moveable features
619    * are not yet in place.  We can defer it, though, as it is guaranteed
620    * to work, now. */
621
622   return 0;
623 }
624
625 static int resolve_complete_ends(void) {
626   TRA_IV;
627   int problems;
628
629   problems= 0;
630   FOR_TRAIN(tra,NOOP,NOOP)
631     problems += resolve_complete_ends_train(tra);
632   return problems;
633 }
634
635 #undef resfin_ours
636 #undef resfin_done
637
638 /*---------- main resolutionentrypoints ----------*/
639
640 void resolve_begin(void) {
641   SEG_IV;
642   actual_inversions_start();
643   FOR_SEG {
644     seg->res_detect= 0;
645     if (segi->invertible)
646       actual_inversions_segment(seg);
647     else
648       seg->seg_inverted= 0;
649     if (segi->n_poscombs==1)
650       seg->movposcomb= 0;
651   }
652   actual_inversions_done();
653 }
654
655 int resolve_complete(void) {
656   int problems;
657   ErrorCode ec;
658   TRA_IV;
659   SEG_IV;
660
661   problems= resolve_complete_main();
662   if (!problems)
663     problems += resolve_complete_ends();
664
665   if (problems) {
666     ouprintf("resolution problems %d\n",problems);
667     return -1;
668   }
669
670   FOR_SEG {
671     if (seg->owner && seg->owner->resolution == RR_N)
672       seg->owner= 0;
673
674     MovPosChange *reservation= seg->motion;
675     if (!reservation) continue;
676     seg->motion= 0;
677     MovPosComb target= movpos_change_intent(reservation);
678     ec= movpos_change(seg, target, -1, reservation);  assert(!ec);
679   }
680
681   FOR_TRA
682     speedmanager_reset_train(tra);
683
684   return 0;
685 }
686
687 void resolve_motioncheck(void) {
688   ErrorCode ec;
689   SEG_IV;
690   TRA_IV;
691   struct timeval tnow;
692
693   assert(sta_state == Sta_Finalising);
694   
695   FOR_SEG
696     if (seg->moving) return;
697
698   FOR_TRA {
699     if (tra->resolution == RR_N) continue;
700     
701     mgettimeofday(&tnow);
702     tra->plan_lookahead_nsegs= INT_MAX;
703     ec= predict(tra,tnow, PREDF_OLDPLAN, 0,0, 0,
704                 0,(char*)"resolution confirmation");
705     assert(!ec);
706   }
707
708   FOR_SEG {
709     if (seg->i->n_poscombs==1) continue;
710     if (seg->res_movposset) continue;
711     assert(!seg->motion);
712     seg->movposcomb= -1;
713   }
714
715   sta_finalising_done();
716 }