/*
* resolve detected trains into initial state
*/
-/*
- * Algorithm:
- *
- * Notation and background:
- *
- * S set of segments
- * D \subset S set of segments where we have detection
- *
- * T set of trains
- * H(t) \subset S home range for train t \elem T
- * E(t) \subset S set of segments where train t last expected
- *
- * H(t) \disjoint H(t') for t != t'
- * E(t) \disjoint E(t') for t != t'
- * but not necessarily E(t) \disjoint H(t')
- *
- * We want to find a mapping R(t)
- * R(t) \elem { N, E, H } t \elem T, N(t') = \null
- * giving
- * A(t) = (R(t))(t) segments allocated to train t
- * U = \union_{t} A(t)
- * satisfing
- * A(t) \disjoint A(t') for t != t' `disjoincy'
- * D \subset U `coverage'
- * and the solution is somehow optimal or at least not badly suboptimal.
- *
- * We compute R incrementally, maintaining disjoincy,,
- * while increasing U. At each stage R is an optimal solution
- * to the problem posed by D' = U, and we maintain this invariant.
- *
- * We define an optimality ordering on R by counting occurrences of
- * H, E, and H in the range, in that order. Ie,
- * Count(R,x) = |{ t: R(t) = x }|
- * Rank(R) = Count(R,H) * (|T|+1) + Count(R,E)
- * and compare Ranks numerically, lower being better.
- *
- * So we mean that for any incrementally computed R, there is no
- * R' satisfying the same subproblem D' = U for which Rank(R') < Rank(R).
- *
- * For the benefit of client programs, we record the key elements of
- * the proofs that we generate for non-N values of R.
- *
- * 1. Initially we set \all_{t} R(t) = N.
- *
- * Optimality: This is minimal for U = \null, as Rank(R) = 0.
- *
- * 2. Then we search for lack of coverage, ie violations of D \subset U.
- * If none are found we are done.
- *
- * 3. Let d \elem D but d !\elem U.
- * We will now calculate new R which we will call R2
- * giving new U which we will call U2.
- *
- * 3a. Hope that \exists{t} st d \elem E(t) with
- * E(t) \disjoint U and R(t) = N.
- * If so set R2(t) = E giving a solution to U2.
- *
- * Optimality: if d \elem D then we need U2 to include d.
- * That means d \elem E(t) or d \elem H(t'). No E(t')
- * other than E(t) can help since they are disjoint, and
- * we would certainly prefer adding E(t) to adding H(t').
- * (Adding H(t') and removing some other H(t'') doesn't
- * work either because the Hs are disjoint, so no
- * H can stand in for any other.)
- *
- * So the rank increase by 1 is is minimal for U2.
- *
- * Proof elements: R(t) = E is demonstrated by
- * every d meeting these conditions.
- *
- * 3b. Alternatively, hope that d \elem H(t')
- *
- * If so set R2(t') = H
- * \all_{t+} R2(t+) = N where R(t+) = E.
- *
- * Optimality: in the case we are now dealing with, we
- * didn't succeed with the test for 3a, so either:
- *
- * (i) There is no t where d \elem E(t), so R(t') = H is
- * essential because no solution without R(t') = H has d \elem U2.
- *
- * (ii) There is t where d \elem E(t) but R(t) = H.
- * We have therefore already proved that R(t) cannot be E.
- *
- * (iii) There is t where d \elem E(t) but E(t) !\disjoint U
- * In this case, consider a clash d' between E(t) and U
- * d' \elem U, d' \elem E(t)
- *
- * This clash at d' is preventing us covering d with E(t).
- * E's can't clash since they are disjoint so it must be
- * a clash with some H. But we have no unavoidable H's
- * in our solution to U, so this solution to U2 has no
- * unavoidable H's either.
- *
- * Or to put it algebraically,
- * d' != d, because d !\elem U.
- * d' \elem A(t'') for some t'' since d' \elem U.
- * If R(t'') = E, d' \elem E(t'') but E's are disjoint.
- * So R(t'') = H, which must have been unavoidable by our
- * inductive premise. Thus for U2, R2(t'') = H is also
- * unavoidable.
- *
- * Proof elements: R(t') = H is demonstrated by
- * every d meeting these conditions
- * together with for each such d
- * the alternative train t if applicable
- * (there can be only one)
- * plus in case (iii)
- * every clash d' between E(t) and U
- * plus for each such d' the corresponding t''
- * (we record all this indexed by t so we can reuse it
- * if needed)
- *
- * R2 consists only of Hs and Ns. All of the Hs are necessary
- * for U2 by the above, so R2 is minimal for U2.
- *
- * The rank is increased: we increase Count(R,H) and set
- * Count(R,E) to 0.
- *
- * 3c. If neither of these is true, we are stuck and cannot
- * satisfy d. This is an error. We remove d from D
- * and continue anyway to see if we can find any more.
- *
- * Proof elements: Lack of solution is demonstrated
- * separately by every such d together with
- * for each d if d \elem E(t) for some t
- * that t
- * plus the clashes d'' as for 3b(iii).
- *
- * Termination: at each iteration 3a or 3b, we strictly increase Rank.
- * At each iteration 3c we reduce D.
- * Rank cannot exceed |T|*(|T|+1) and D starts out finite. So
- * eventually we must succeed or fail.
- *
- */
#include "realtime.h"
+#define NOOP (void)0
+
+/*---------- main resolution processing ----------*/
+ /*
+ * Algorithm:
+ *
+ * Notation and background:
+ *
+ * S set of segments
+ * D \subset S set of segments where we have detection
+ *
+ * T set of trains
+ * H(t) \subset S home range for train t \elem T
+ * E(t) \subset S set of segments where train t last expected
+ *
+ * H(t) \disjoint H(t') for t != t'
+ * E(t) \disjoint E(t') for t != t'
+ * but not necessarily E(t) \disjoint H(t')
+ *
+ * We want to find a mapping R(t)
+ * R(t) \elem { N, E, H } t \elem T, N(t') = \null
+ * giving
+ * A(t) = (R(t))(t) segments allocated to train t
+ * U = \union_{t} A(t)
+ * satisfing
+ * A(t) \disjoint A(t') for t != t' `disjoincy'
+ * D \subset U `coverage'
+ * and the solution is somehow optimal or at least not badly suboptimal.
+ *
+ * We compute R incrementally, maintaining disjoincy,,
+ * while increasing U. At each stage R is an optimal solution
+ * to the problem posed by D' = U, and we maintain this invariant.
+ *
+ * We define an optimality ordering on R by counting occurrences of
+ * H, E, and H in the range, in that order. Ie,
+ * Count(R,x) = |{ t: R(t) = x }|
+ * Rank(R) = Count(R,H) * (|T|+1) + Count(R,E)
+ * and compare Ranks numerically, lower being better.
+ *
+ * So we mean that for any incrementally computed R, there is no
+ * R' satisfying the same subproblem D' = U for which Rank(R') < Rank(R).
+ *
+ * For the benefit of client programs, we record the key elements of
+ * the proofs that we generate for non-N values of R.
+ *
+ * 1. Initially we set \all_{t} R(t) = N.
+ *
+ * Optimality: This is minimal for U = \null, as Rank(R) = 0.
+ *
+ * 2. Then we search for lack of coverage, ie violations of D \subset U.
+ * If none are found we are done.
+ *
+ * 3. Let d \elem D but d !\elem U.
+ * We will now calculate new R which we will call R2
+ * giving new U which we will call U2.
+ *
+ * 3a. Hope that \exists{t} st d \elem E(t) with
+ * E(t) \disjoint U and R(t) = N.
+ * If so set R2(t) = E giving a solution to U2.
+ *
+ * Optimality: if d \elem D then we need U2 to include d.
+ * That means d \elem E(t) or d \elem H(t'). No E(t')
+ * other than E(t) can help since they are disjoint, and
+ * we would certainly prefer adding E(t) to adding H(t').
+ * (Adding H(t') and removing some other H(t'') doesn't
+ * work either because the Hs are disjoint, so no
+ * H can stand in for any other.)
+ *
+ * So the rank increase by 1 is is minimal for U2.
+ *
+ * Proof elements: R(t) = E is demonstrated by
+ * every d meeting these conditions.
+ *
+ * 3b. Alternatively, hope that d \elem H(t')
+ *
+ * If so set R2(t') = H
+ * \all_{t+} R2(t+) = N where R(t+) = E.
+ *
+ * Optimality: in the case we are now dealing with, we
+ * didn't succeed with the test for 3a, so either:
+ *
+ * (i) There is no t where d \elem E(t), so R(t') = H is
+ * essential because no solution without R(t') = H has d \elem U2.
+ *
+ * (ii) There is t where d \elem E(t) but R(t) = H.
+ * We have therefore already proved that R(t) cannot be E.
+ *
+ * (iii) There is t where d \elem E(t) but E(t) !\disjoint U
+ * In this case, consider a clash d' between E(t) and U
+ * d' \elem U, d' \elem E(t)
+ *
+ * This clash at d' is preventing us covering d with E(t).
+ * E's can't clash since they are disjoint so it must be
+ * a clash with some H. But we have no unavoidable H's
+ * in our solution to U, so this solution to U2 has no
+ * unavoidable H's either.
+ *
+ * Or to put it algebraically,
+ * d' != d, because d !\elem U.
+ * d' \elem A(t'') for some t'' since d' \elem U.
+ * If R(t'') = E, d' \elem E(t'') but E's are disjoint.
+ * So R(t'') = H, which must have been unavoidable by our
+ * inductive premise. Thus for U2, R2(t'') = H is also
+ * unavoidable.
+ *
+ * Proof elements: R(t') = H is demonstrated by
+ * every d meeting these conditions
+ * together with for each such d
+ * the alternative train t if applicable
+ * (there can be only one)
+ * plus in case (iii)
+ * every clash d' between E(t) and U
+ * plus for each such d' the corresponding t''
+ * (we record all this indexed by t so we can reuse it
+ * if needed)
+ *
+ * R2 consists only of Hs and Ns. All of the Hs are necessary
+ * for U2 by the above, so R2 is minimal for U2.
+ *
+ * The rank is increased: we increase Count(R,H) and set
+ * Count(R,E) to 0.
+ *
+ * 3c. If neither of these is true, we are stuck and cannot
+ * satisfy d. This is an error. We remove d from D
+ * and continue anyway to see if we can find any more.
+ *
+ * Proof elements: Lack of solution is demonstrated
+ * separately by every such d together with
+ * for each d if d \elem E(t) for some t
+ * that t
+ * plus the clashes d'' as for 3b(iii).
+ *
+ * Termination: at each iteration 3a or 3b, we strictly increase Rank.
+ * At each iteration 3c we reduce D.
+ * Rank cannot exceed |T|*(|T|+1) and D starts out finite. So
+ * eventually we must succeed or fail.
+ *
+ */
+
/* values for Train->resolution: */
#define RR_N 0u
#define RR_E 1u
#define RR_H 2u
+#define RESOLUTION_CHARS "NEH"
-/* We record R in tra->resolution,
- * U in segi->mark0 and D in segi->res_detect */
+/* During the main algorithm:
+ * We record R in tra->resolution,
+ * U in segi->mark0 and D in segi->res_detect
+ */
#define iselem_u mark0
-#define resfin_done mark0
-
-void resolve_begin(void) {
- SEG_IV;
- actual_inversions_start();
- FOR_SEG {
- seg->res_detect= 0;
- seg->iselem_u= 0;
- if (segi->invertible)
- actual_inversions_segment(seg);
- else
- seg->seg_inverted= 0;
- }
- actual_inversions_done();
-}
-
-#define NOOP (void)0
-int resolve_complete(void) {
+static int resolve_complete_main(void) {
int problems, phase, nextphase;
TRAIN_ITERVARS(t);
TRAIN_ITERVARS(t2);
#define ADDTO_U_EH(homeowner,HH_HE,string) \
if (d->homeowner && d->homeowner->resolution == HH_HE) { \
- oprintf(DUPO("resolving") " covered @%s " string " %s\n", \
+ oprintf(DUPO("resolving") " covered %s " string " %s\n", \
di->pname, d->homeowner->pname); \
updated++; \
}
if (phase<2)
continue;
- oprintf(UPO, "resolution inexplicable @%s\n", di->pname);
+ oprintf(UPO, "resolution inexplicable %s\n", di->pname);
d->res_detect= 0;
problems++;
}
FOR_SEGMENT(d,NOOP,NOOP) {
if (problems)
- goto x_problems;
+ return problems;
d->iselem_u= 0;
MovPosComb target= -1;
if (d->home && d->home->resolution == RR_H) {
- d->owner= d->home;
d->tr_backwards= d->ho_backwards;
target= 0; /* a bit kludgey */
} else if (d->owner) {
d->tr_backwards ^= d->owner->backwards;
target= d->movposcomb;
- }
+ }
if (d->i->n_poscombs>1) {
d->movposcomb= -1;
if (target >= 0) {
- ErrorCode ec= movpos_change(d,target,-1,0);
+ assert(!d->motion);
+ ErrorCode ec= movpos_reserve(d,-1,&d->motion,target,-1);
if (ec) {
oprintf(UPO, "resolution movpos-change-failed %s/%s %s\n",
d->i->pname, d->i->poscombs[target].pname,
d->movposcomb= 0;
}
}
+ return problems;
+}
- if (problems) {
- x_problems:
- oprintf(UPO,"resolution problems %d\n",problems);
- return -1;
- }
+#undef iselem_u
- FOR_TRAIN(t,NOOP,NOOP)
- speedmanager_reset_train(t);
+/*---------- heads and tails of trains, final placement ----------*/
- return 0;
-}
+#define resfin_ours mark1
+#define resfin_done mark2
typedef struct {
Train *train;
int extraspace;
} FindEndUserContext;
+static int findends_getmovpos(TrackLocation *t, TrackAdvanceContext *c,
+ MovPosComb *use_io) {
+ const char *pn= t->seg->i->pname;
+ if (t->seg->motion) *use_io= movpos_change_intent(t->seg->motion);
+ if (*use_io<0) {
+ oprintf(DUPO("resolving") " ends getmovpos %s fails\n", pn);
+ return -1;
+ }
+// oprintf(DUPO("resolving") " ends getmovpos %s -> %d\n", pn, *use_io);
+ return 0;
+}
+
static int segdist(Segment *seg) {
- assert(seg->movposcomb >= 0);
- return seg->i->poscombs[seg->movposcomb].dist;
+ TrackLocation t;
+ int r;
+ MovPosComb mpc;
+
+ t.seg= seg;
+ mpc= seg->movposcomb;
+
+ r= findends_getmovpos(&t,0,&mpc);
+ assert(!r); assert(mpc >= 0);
+ return seg->i->poscombs[mpc].dist;
}
-static void finalise_callback_debug(const char *what, TrackLocation *t,
- struct TrackAdvanceContext *c) {
- oprintf(DUPO("resolving") " finalise %s"
- " %s%s owner=%s res_detect=%d resfin_done=%d\n",
+static void ends_callback_debug(const char *what, TrackLocation *t,
+ struct TrackAdvanceContext *c) {
+ oprintf(DUPO("resolving") " ends %s"
+ " %s%s det=%d ours=%d done=%d owner=%s home=%s\n",
what, t->backwards?"-":"", t->seg->i->pname,
- t->seg->owner ? t->seg->owner->pname : "0",
- t->seg->res_detect, t->seg->resfin_done);
+ t->seg->res_detect,
+ t->seg->resfin_ours,
+ t->seg->resfin_done,
+ t->seg->owner ? t->seg->owner->pname : "-",
+ t->seg->home ? t->seg->home->pname : "-");
}
static int findhead_nextseg(TrackLocation *t, struct TrackAdvanceContext *c,
MovPosComb *mpc_io, Segment *before) {
FindEndUserContext *u= c->u;
- finalise_callback_debug("findhead_nextseg",t,c);
+ ends_callback_debug("findhead_nextseg",t,c);
- if (t->seg->owner != u->train) return -1;
+ if (!t->seg->resfin_ours || t->seg->resfin_done) return -1;
u->furthest= t->seg;
if (!t->seg->res_detect) { u->extraspace= 0; return -1; }
u->train->foredetect= t->seg;
t->seg->tr_backwards= t->backwards;
- t->seg->res_detect= 0;
+ t->seg->resfin_done= 1;
return 0;
}
static int walkback_nextseg(TrackLocation *t, struct TrackAdvanceContext *c,
MovPosComb *mpc_io, Segment *before) {
- FindEndUserContext *u= c->u;
-
- finalise_callback_debug("walkback_nextseg",t,c);
+ ends_callback_debug("walkback_nextseg",t,c);
- if (t->seg->owner != u->train) return -1;
+ if (!t->seg->resfin_ours) return -1;
t->seg->tr_backwards= !t->backwards;
t->seg->resfin_done= 1;
return 0;
}
-static void resolve_train_finalise(Segment *startpoint) {
+static int resolve_complete_ends_train(Train *tra) {
SEG_IV;
- Train *tra;
TrackLocation t;
TrackAdvanceContext tc;
FindEndUserContext u;
int r;
+ Segment *startpoint;
- assert(startpoint->owner);
- tra= startpoint->owner;
+ switch (tra->resolution) {
+ case RR_H: break;
+ case RR_E: break;
+ case RR_N: return 0;
+ default: abort();
+ }
+
+ oprintf(DUPO("resolving") " ends %s %c\n",
+ tra->pname, RESOLUTION_CHARS[tra->resolution]);
+
+ startpoint= 0;
+
+ FOR_SEGMENT(seg,NOOP,NOOP) {
+ seg->resfin_done= 0;
+ seg->resfin_ours= tra ==
+ (tra->resolution==RR_H ? seg->home : seg->owner);
+ if (seg->resfin_ours && seg->res_detect)
+ startpoint= seg;
+ }
+
+ assert(startpoint);
t.seg= startpoint;
t.remain= 0;
tc.distance= TL_DIST_INF;
tc.nextseg= findhead_nextseg;
+ tc.getmovpos= findends_getmovpos;
tc.trackend= 0;
- tc.getmovpos= 0;
tc.u= &u;
u.train= tra;
u.extraspace= MARGIN_NOSE + tra->head;
u.furthest= 0;
- oprintf(DUPO("resolving") " finalise %s start=%s%s es=%d\n",
- tra->pname,
- t.backwards?"-":"", t.seg->i->pname,
- u.extraspace);
+ oprintf(DUPO("resolving") " ends start=%s%s es=%d\n",
+ t.backwards?"-":"", t.seg->i->pname, u.extraspace);
r= findhead_nextseg(&t,&tc,0,0); assert(!r);
trackloc_advance(&t,&tc);
- oprintf(DUPO("resolving") " finalise es=%d furthest=%s fdd(%s)=%d\n",
+ oprintf(DUPO("resolving") " ends es=%d furthest=%s fd=%s%s fd.dist=%d\n",
u.extraspace, u.furthest->i->pname,
- tra->foredetect->i->pname, segdist(tra->foredetect));
+ tra->foredetect->tr_backwards?"-":"", tra->foredetect->i->pname,
+ segdist(tra->foredetect));
tra->maxinto= segdist(tra->foredetect) - u.extraspace;
if (tra->maxinto < 0) tra->maxinto= 0;
tra->uncertainty= tra->maxinto;
- FOR_SEG {
- if (seg->owner == tra) seg->res_detect= 0;
- seg->resfin_done= 0;
- }
+ FOR_SEG seg->resfin_done= 0;
t.seg= tra->foredetect;
t.remain= segdist(tra->foredetect) - u.extraspace;
r= walkback_nextseg(&t,&tc,0,0); assert(!r);
trackloc_advance(&t,&tc);
- oprintf(DUPO("resolving") " finalise maxi=%d unc=%d;"
+ oprintf(DUPO("resolving") " ends maxi=%d unc=%d;"
" remaining distance=%d\n",
tra->maxinto, tra->uncertainty, tc.distance);
if (tc.distance) {
tra->uncertainty -= tc.distance;
- if (tra->uncertainty < 0)
- safety_panic(tra, t.seg, "resolved train location too small by %d!",
- -tra->uncertainty);
+ if (tra->uncertainty < 0) {
+ oprintf(UPO, "resolution mispositioned %s %s -%d\n",
+ tra->pname, t.seg->i->pname, -tra->uncertainty);
+ return 1;
+ }
}
FOR_SEG {
}
report_train_position(tra);
- report_train_ownerships(tra, u.furthest);
+ report_train_ownerships(tra, u.furthest, 0);
+
+ return 0;
+}
+
+static int resolve_complete_ends(void) {
+ TRA_IV;
+ int problems;
+
+ problems= 0;
+ FOR_TRAIN(tra,NOOP,NOOP)
+ problems += resolve_complete_ends_train(tra);
+ return problems;
+}
+
+#undef resfin_ours
+#undef resfin_done
+
+/*---------- main resolutionentrypoints ----------*/
+
+void resolve_begin(void) {
+ SEG_IV;
+ actual_inversions_start();
+ FOR_SEG {
+ seg->res_detect= 0;
+ if (segi->invertible)
+ actual_inversions_segment(seg);
+ else
+ seg->seg_inverted= 0;
+ }
+ actual_inversions_done();
+}
+
+int resolve_complete(void) {
+ int problems;
+ ErrorCode ec;
+ TRA_IV;
+ SEG_IV;
+
+ problems= resolve_complete_main();
+ if (!problems)
+ problems += resolve_complete_ends();
+
+ if (problems) {
+ oprintf(UPO,"resolution problems %d\n",problems);
+ return -1;
+ }
+
+ FOR_SEG {
+ if (seg->owner && seg->owner->resolution == RR_N)
+ seg->owner= 0;
+
+ MovPosChange *reservation= seg->motion;
+ if (!reservation) continue;
+ seg->motion= 0;
+ MovPosComb target= movpos_change_intent(reservation);
+ ec= movpos_change(seg, target, -1, reservation); assert(!ec);
+ }
+
+ FOR_TRA
+ speedmanager_reset_train(tra);
+
+ return 0;
}
-
void resolve_motioncheck(void) {
SEG_IV;
FOR_SEG
if (seg->moving) return;
- FOR_SEG
- if (seg->res_detect)
- resolve_train_finalise(seg);
-
sta_finalising_done();
}