2 * resolve detected trains into initial state
9 /*---------- main resolution processing ----------*/
13 * Notation and background:
16 * D \subset S set of segments where we have detection
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
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')
26 * We want to find a mapping R(t)
27 * R(t) \elem { N, E, H } t \elem T, N(t') = \null
29 * A(t) = (R(t))(t) segments allocated to train t
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.
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.
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.
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).
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.
52 * 1. Initially we set \all_{t} R(t) = N.
54 * Optimality: This is minimal for U = \null, as Rank(R) = 0.
56 * 2. Then we search for lack of coverage, ie violations of D \subset U.
57 * If none are found we are done.
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.
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.
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.)
75 * So the rank increase by 1 is is minimal for U2.
77 * Proof elements: R(t) = E is demonstrated by
78 * every d meeting these conditions.
80 * 3b. Alternatively, hope that d \elem H(t')
82 * If so set R2(t') = H
83 * \all_{t+} R2(t+) = N where R(t+) = E.
85 * Optimality: in the case we are now dealing with, we
86 * didn't succeed with the test for 3a, so either:
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.
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.
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)
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.
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
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)
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
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.
126 * The rank is increased: we increase Count(R,H) and set
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.
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
137 * plus the clashes d'' as for 3b(iii).
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.
146 /* values for Train->resolution: */
150 #define RESOLUTION_CHARS "NEH"
152 /* During the main algorithm:
153 * We record R in tra->resolution,
154 * U in segi->mark0 and D in segi->res_detect
156 #define iselem_u mark0
158 static int resmain_getmovpos(TrackLocation *t, TrackAdvanceContext *c,
159 MovPosComb *use_io) {
161 const char *pn= d->i->pname;
163 if (d->home && d->home->resolution == RR_H) {
164 *use_io= 0; /* a bit kludgey */
165 DPRINTF(resolving,main, "getmovpos %s H\n", pn);
166 } else if (d->owner) {
167 *use_io= d->movposcomb;
168 DPRINTF(resolving,main, "getmovpos %s E %d\n", pn, *use_io);
169 } else if (!d->res_movposset) {
171 DPRINTF(resolving,main, "getmovpos %s N -1\n", pn);
172 } else if (d->motion) {
173 *use_io= movpos_change_intent(d->motion);
174 DPRINTF(resolving,main, "getmovpos %s M %u\n", pn, *use_io);
179 static MovPosComb just_getmovpos(Segment *d, TrackAdvanceContext *tac) {
185 r= resmain_getmovpos(&tloc,tac,&target); assert(!r);
189 static int resolve_complete_main(void) {
190 int problems, phase, nextphase;
193 TRAIN_ITERVARS(tplus);
195 SEGMENT_ITERVARS(d1);
196 SEGMENT_ITERVARS(dplus);
199 FOR_TRAIN(t,NOOP,NOOP) t->resolution= RR_N;
201 for (phase=0; phase<3; phase=nextphase) {
203 DPRINTF(resolving,alg, "iteration %c\n", "EHX"[phase]);
205 DPRINTF(resolving,alg, " calculate-u\n");
207 FOR_SEGMENT(d,NOOP,NOOP) { /* calculate U */
210 #define ADDTO_U_EH(homeowner,HH_HE,string) \
211 if (d->homeowner && d->homeowner->resolution == HH_HE) { \
212 DPRINTF(resolving,alg, " covered %s " string " %s\n", \
213 di->pname, d->homeowner->pname); \
216 ADDTO_U_EH(home, RR_H, "at-home");
217 if (d->movposcomb >= 0)
218 ADDTO_U_EH(owner, RR_E, "as-expected");
221 d->iselem_u= updated;
224 DPRINTF(resolving,alg, " searching\n");
225 FOR_SEGMENT(d, NOOP, NOOP) {
226 if (!(d->res_detect && !d->iselem_u))
228 /* 3. we have a violation of D \subset U, namely d */
230 DPRINTF(resolving,alg, " violation %s\n", di->pname);
232 if (d->owner) { /* 3a perhaps */
234 DPRINTF(resolving,alg, " expected %s\n", t->pname);
237 DPRINTF(resolving,alg, " expected-is-not-addressable\n");
241 if (t->resolution == RR_H) {
242 DPRINTF(resolving,alg, " expected-is-at-home\n");
246 if (d->movposcomb < 0) {
247 DPRINTF(resolving,alg, " track-unknown-position\n");
251 if (t->resolution == RR_E)
252 /* we added it in this sweep and have found another d */
254 assert(t->resolution == RR_N);
256 /* check E(t) \disjoint U */
258 FOR_SEGMENT(d1, NOOP, NOOP) {
259 if (d1->owner == t && d1->iselem_u) {
260 FOR_TRAIN(t2, NOOP, NOOP) {
261 if (t2->resolution == RR_H && d1->owner == t2) {
262 DPRINTF(resolving,alg, " clash %s %s\n",
263 d1i->pname, t2->pname);
270 DPRINTF(resolving,alg, " expected-has-clashes\n");
278 DPRINTF(resolving,alg, " supposing %s as-expected\n", t->pname);
283 if (d->home) { /* 3b then */
287 Train *t1= d->home; /* t' st d \elem H(t') */
288 DPRINTF(resolving,alg, " home %s\n", t1->pname);
291 DPRINTF(resolving,alg, " home-is-not-addressable\n");
295 DPRINTF(resolving,alg, " reset-expecteds\n");
296 FOR_TRAIN(tplus, NOOP,NOOP) {
297 if (tplus->resolution == RR_E) {
298 DPRINTF(resolving,alg, " supposing %s absent\n", tplus->pname);
299 tplus->resolution= RR_N;
303 /* Now must trim U to correspond to our setting of R(t+)=N
304 * so that any other implied Hs are spotted. */
305 FOR_SEGMENT(dplus, NOOP,NOOP) {
306 Train *tplus= dplus->owner;
307 if (!(tplus && tplus->resolution == RR_E)) continue;
311 t1->resolution= RR_H;
314 DPRINTF(resolving,alg, " supposing %s at-home\n", t1->pname);
323 ouprintf("resolution inexplicable %s\n", di->pname);
329 FOR_TRAIN(t,NOOP,NOOP) {
330 if (t->resolution == RR_H)
333 FOR_SEGMENT(d,NOOP,NOOP) {
334 if (d->home && d->home->resolution == RR_H)
335 d->tr_backwards= d->ho_backwards;
338 TrackAdvanceContext tac;
340 tac.getmovpos= resmain_getmovpos;
342 FOR_SEGMENT(d,NOOP,NOOP) {
343 if (problems) return problems;
347 target= just_getmovpos(d,&tac);
349 if (d->i->invertible) {
350 Segment *interferer= segment_interferes_simple(&tac,d);
352 actual_inversions_start();
353 d->seg_inverted= interferer->seg_inverted
354 ^ d->i->interferes_polarity_opposed;
355 actual_inversions_segment(d);
356 actual_inversions_done();
360 if (d->i->n_poscombs>1) {
363 movpos_unreserve(d->motion);
366 if (!d->res_movposset)
369 ErrorCode ec= movpos_reserve(d,-1,&d->motion,target,-1);
371 ouprintf("resolution movpos-change-failed %s/%s %s\n",
372 d->i->pname, d->i->poscombs[target].pname,
380 assert(!d->i->n_movfeats);
389 /*---------- heads and tails of trains, final placement ----------*/
391 #define resfin_ours mark1
394 Distance hardallow, min, max;
396 Segment *lastdetect, *hardend;
400 TrackAdvanceContext tc; /* first! */
404 Distance atlastdetect;
406 FindEndConstraint constraints[2];
408 } FindEndUserContext;
410 static int findends_getmovpos(TrackLocation *t, TrackAdvanceContext *c,
411 MovPosComb *use_io) {
412 const char *pn= t->seg->i->pname;
413 if (t->seg->motion) *use_io= movpos_change_intent(t->seg->motion);
415 DPRINTF(resolving,ends, " getmovpos %s fails\n", pn);
418 DPRINTF(resolving,ends, " getmovpos %s -> %d\n", pn, *use_io);
422 static int constraint_nextseg(TrackLocation *t, struct TrackAdvanceContext *c,
423 MovPosComb *mpc, const TrackLocation *before) {
425 FindEndUserContext *u= (void*)c;
427 DPRINTF(resolving,ends, " constraint_nextseg %c"
428 " %s%s remain=%d dist=INF-%d det=%d ours=%d owner=%s home=%s\n",
430 t->backwards?"-":"", t->seg->i->pname, t->remain,
431 TL_DIST_INF - c->distance,
434 t->seg->owner ? t->seg->owner->pname : "-",
435 t->seg->home ? t->seg->home->pname : "-");
437 if (!t->seg->resfin_ours) return 'o';
438 r= findends_getmovpos(t,0,mpc); if (r) return r;
440 const SegPosCombInfo *pci= &t->seg->i->poscombs[*mpc];
443 if (t->seg == u->startpoint) return 'l';
444 const SegmentLinkInfo *rlink= &pci->link[!t->backwards];
445 if (before->seg != &segments[rlink->next]) {
446 DPRINTF(resolving,ends, " j mismatch %s %s\n",
447 before->seg->i->pname, info_segments[rlink->next].pname);
451 if (t->seg->res_detect) {
452 u->atlastdetect= c->distance;
454 /* wind back to start of this segment which is necessary if this
455 * we are looking rearwards */
456 u->atlastdetect += (pci->dist - t->remain);
457 u->lastdetect= t->seg;
462 static int constraint_trackend(TrackLocation *t,
463 struct TrackAdvanceContext *c) {
467 static void end_startpoint(FindEndUserContext *u) {
470 u->tc.distance= TL_DIST_INF;
471 u->tc.nextseg= constraint_nextseg;
472 u->tc.getmovpos= findends_getmovpos;
473 u->tc.trackend= constraint_trackend;
476 r= trackloc_set_exactinto(&u->t, &u->tc,
477 u->startpoint, u->startpoint->tr_backwards,
482 static void end_constraints(FindEndUserContext *u) {
483 FindEndConstraint lim;
484 Train *tra= u->train;
488 u->tc.distance= TL_DIST_INF;
494 r= trackloc_reverse_exact(&u->t,&u->tc);
498 FindEndConstraint *cons= &u->constraints[u->rear];
500 cons->hardwhy= trackloc_advance(&u->t,&u->tc);
502 assert(cons->hardwhy);
503 assert(u->lastdetect);
504 assert(u->atlastdetect >= 0);
506 Distance nose= MARGIN_NOSE +
507 ((tra->backwards ^ u->rear) ? tra->head : tra->tail);
509 lim.min= TL_DIST_INF - u->atlastdetect;
510 lim.max= (TL_DIST_INF - u->tc.distance) - nose;
516 cons->max= -lim.min + ceil(tra->detectable * MARGIN_TRAINLENGTH);
517 cons->min= -lim.max + ceil(tra->detectable * MARGIN_TRAINLENGTH);
519 cons->lastdetect= u->lastdetect;
520 cons->hardend= u->t.seg;
522 if (cons->min > cons->max) {
523 ouprintf("resolution implausible %s %s overhang %d %d\n",
524 cons->lastdetect->i->pname, tra->pname,
525 cons->min - cons->max, nose);
529 DPRINTF(resolving,ends, " lims %c %s,%s,%c dist=INF-%d"
530 " lim=%d..%d out=%d..%d\n",
532 cons->lastdetect->i->pname, u->t.seg->i->pname, cons->hardwhy,
533 TL_DIST_INF - u->tc.distance,
534 lim.min, lim.max, cons->min, cons->max);
537 static int resolve_complete_ends_train(Train *tra) {
539 FindEndUserContext u;
540 const SegPosCombInfo *pci;
543 switch (tra->resolution) {
550 DPRINTF1(resolving,ends, "%s %c",
551 tra->pname, RESOLUTION_CHARS[tra->resolution]);
553 memset(&u,0,sizeof(u));
556 FOR_SEGMENT(seg,NOOP,NOOP) {
557 seg->resfin_ours= tra ==
558 (tra->resolution==RR_H ? seg->home : seg->owner);
559 if (!seg->resfin_ours) continue;
560 DPRINTF2(" %s%s", seg->tr_backwards?"-":"", seg->i->pname);
561 if (seg->res_detect) {
566 assert(u.startpoint);
569 /* There are four pieces of information we have:
570 * The rearmost detection, the rearmost end of ownership
571 * the foremost detection, the foremost end of ownership
573 * From each we compute a range of locations. These are
574 * in the form of a minimum and maximum distance of the
575 * foredetect ahead of 0 into the startpoint.
579 for (u.rear=0; u.rear<2; u.rear++)
584 if ((wrongness= u.constraints[0].min - u.constraints[1].max) > 0) {
585 for (u.rear=1; u.rear>0; u.rear--)
586 ouprintf("resolution implausible %s %s over-detect %d\n",
587 u.constraints[u.rear].lastdetect->i->pname,
588 tra->pname, wrongness);
591 if ((wrongness= u.constraints[1].min - u.constraints[0].max) > 0) {
592 for (u.rear=1; u.rear>0; u.rear--)
593 ouprintf("resolution implausible %s %s cramped %d\n",
594 u.constraints[u.rear].hardend->i->pname,
595 tra->pname, wrongness);
599 int min= MAX(u.constraints[0].min, u.constraints[1].min);
600 int max= MIN(u.constraints[0].max, u.constraints[1].max);
601 DPRINTF(resolving,ends, " lims a %d..%d problems=%d\n",
602 min, max, u.problems);
608 assert(max > 0); /* startpoint is detected so foredet is there or later */
610 /* Now we just need to turn this into the canonical format. */
615 r= trackloc_advance(&u.t, &u.tc); assert(!r);
617 tra->foredetect= u.t.seg;
618 tra->foredetect->tr_backwards= u.t.backwards;
620 r= trackloc_getlink(&u.t, &u.tc, &pci,0,0); assert(!r);
621 tra->maxinto= pci->dist - u.t.remain;
622 tra->uncertainty= max - min;
624 report_train_position(tra);
625 /* We don't lay the train out now since the moveable features
626 * are not yet in place. We can defer it, though, as it is guaranteed
632 static int resolve_complete_ends(void) {
637 FOR_TRAIN(tra,NOOP,NOOP)
638 problems += resolve_complete_ends_train(tra);
645 /*---------- main resolutionentrypoints ----------*/
647 void resolve_begin(void) {
649 actual_inversions_start();
652 if (segi->invertible)
653 actual_inversions_segment(seg);
655 seg->seg_inverted= 0;
656 if (segi->n_poscombs==1)
659 actual_inversions_done();
662 int resolve_complete(void) {
668 problems= resolve_complete_main();
670 problems += resolve_complete_ends();
673 ouprintf("resolution problems %d\n",problems);
678 if (seg->owner && seg->owner->resolution == RR_N)
681 MovPosChange *reservation= seg->motion;
682 if (!reservation) continue;
684 MovPosComb target= movpos_change_intent(reservation);
685 ec= movpos_change(seg, target, -1, reservation); assert(!ec);
689 speedmanager_reset_train(tra);
694 void resolve_motioncheck(void) {
700 assert(sta_state == Sta_Finalising);
703 if (seg->moving) return;
706 if (tra->resolution == RR_N) continue;
708 mgettimeofday(&tnow);
709 tra->plan_lookahead_nsegs= INT_MAX;
710 ec= predict(tra,tnow, PREDF_OLDPLAN, 0,0, 0,
711 0,(char*)"resolution confirmation");
716 if (seg->i->n_poscombs==1) continue;
717 if (seg->res_movposset) continue;
718 assert(!seg->motion);
722 sta_finalising_done();