Overture Tool

Formal Modelling in VDM

Overture Tool

Go to our GitHub Profile

Follow @overturetool

ATCSL

Author: Natsuki Terada

This example was developed by Natsuki Terada from the Japanese Railways Research Institute (RTRI) on a two year visit to IFAD in 2000 and 2001. It models a database for digital Automatic Train Control in Japan. More information can be found in:

Natsuki Terada, Formal Integrity Analysis of Digital ATC Database, In Proceedings of WCRR2001 (World Congress on Railway Research), 2001.

Natsuki Terada, Integrity Analysis of Digital ATC Database with Automatic Proofs, In VDM Workshop 3, Part of the FME 2002 conference, Copenhagen, Denmark, July 2002.

Natsuki Terada, Application of Formal Methods to Automatic Train Control Systems, In Proceedings of Symposium on Formal Methods for Railway Operation and Control Systems (FORMS 2003), 2003.

N. Terada and M. Fukuda, Application of Formal Methods to the Railway Signaling Systems, In Quarterly Report of RTRI, 2002, Vol 43, no 4, pp 169-174.

Properties Values
Language Version: vdm10

digitalATCdb.vdmsl

--
-- Model of Digital ATC Database(1 & 2 /3) -- Track Circuits and Routes
--
-- ver 1.   2000.4.11 by n. terada
-- ver 2.   2000.6.14 by n. terada
-- ver 2.3  2000.6.26 by n. terada
-- ver 3    2000.8.16 by n. terada
-- ver 4    2000.9.13 by n. terada
-- ver 4.5  2000.9.15 by n. terada
-- ver 4.6  2000.10.11 by n. terada
-- ver 4.61 2001.2.5 by n.terada
-- track circuits -- basic unit
--
types
TrackC::	joints : map Joint_id to Joint
		atc : map Direction to ATC
		td : TD
		atbt : ATBT
inv tc == card dom tc.joints > 1 and
        dom tc.atc = {<ADIR>, <BDIR>} and
	TD_Used_for_NonInsulated_TrackC(tc.td, tc.atbt, rng tc.joints) and
	(tc.atc(<ADIR>).used and tc.atc(<BDIR>).used => 
                tc.atc(<ADIR>).carrier <> tc.atc(<BDIR>).carrier);


--
-- joint -- connection between two track circuits
--

Joint_id = token;

Joint::	position : nat
	insulated : Insulation
	remark : Remark;


Insulation = bool; -- true if joint is insulated
Remark :: atc_terminal : map Direction to bool  -- true if atc is terminated
	  line_terminal : bool -- true if line terminated
inv rm == dom rm.atc_terminal = {<ADIR>, <BDIR>};

Direction = <ADIR> | <BDIR>;
--
-- carrier of track circuits
-- atc signal , td(train detection signal) 
--
ATC :: used : bool -- true if Digital ATC is used
       carrier : token;

TD :: used : bool  -- true if TD is used
      carrier : token;

ATBT = <AT> | <BT> | <NULL>;

functions
TD_Used_for_NonInsulated_TrackC : TD * ATBT * set of Joint -> bool
TD_Used_for_NonInsulated_TrackC(td, atbt, joints) ==
	(atbt = <NULL> <=> not td.used) and
	((exists j in set joints & not j.insulated) => td.used);

--
-- Collection of Track Circuits
--
types
TrackC_id = token;
TrackC_map = map TrackC_id to TrackC
inv tcs == forall tid in set dom tcs &
	        forall jid in set dom tcs(tid).joints &
			Only_One_Next_TrackC(tcs, tid, jid) and
                        forall tid2 in set dom tcs & tid <> tid2 =>
		        	Joint_and_Next_TrackC(tcs(tid), tcs(tid2), jid)
;

functions
Only_One_Next_TrackC: map TrackC_id to TrackC * TrackC_id * Joint_id -> bool
Only_One_Next_TrackC(tcs, tcid, jid) ==
	card {tid | tid in set dom tcs & 
                tid <> tcid and jid in set dom tcs(tid).joints} < 2;

Joint_and_Next_TrackC: TrackC * TrackC * Joint_id -> bool
Joint_and_Next_TrackC(tc1, tc2, jid) ==
	(jid in set dom tc1.joints and jid in set dom tc2.joints) =>
        (tc1.joints(jid) = tc2.joints(jid) and
         not tc1.joints(jid).remark.line_terminal and
         Is_wf_adjacent_signal(tc1, jid, tc2, jid, false));


-- 
-- condition related with signal
--
Is_wf_adjacent_signal: TrackC * Joint_id * TrackC * Joint_id * bool -> bool
Is_wf_adjacent_signal(tc1, jid1, tc2, jid2, dir_chng) ==
        jid1 in set dom tc1.joints and
        jid2 in set dom tc2.joints and
        Remark_Compatible(tc1.joints(jid1).remark, tc2.joints(jid2).remark,
                dir_chng) and
	ATC_Terminal_and_ATC_Used(tc1.atc(<ADIR>), tc2.atc(<BDIR>),
         tc1.joints(jid1).remark, tc2.atc(<ADIR>), tc2.atc(<BDIR>),
         tc2.joints(jid2).remark, dir_chng) and
	Adjacent_TD_Carrier_Differ(tc1.td, tc1.atbt, tc2.td, tc2.atbt,
                tc1.joints(jid1).insulated and tc2.joints(jid2).insulated);

Remark_Compatible : Remark * Remark * bool-> bool
Remark_Compatible(rm1, rm2, dir_chng) ==
        (not dir_chng => rm1.atc_terminal = rm2.atc_terminal) and
        (dir_chng =>
        (rm1.atc_terminal(<ADIR>) = rm2.atc_terminal(<BDIR>) and
         rm1.atc_terminal(<BDIR>) = rm2.atc_terminal(<ADIR>)));

ATC_Terminal_and_ATC_Used : ATC * ATC * Remark * ATC * ATC * Remark * bool
        -> bool
ATC_Terminal_and_ATC_Used(atcA1, atcB1, rm1, atcA2, atcB2, rm2, dir_chng) ==
        (not dir_chng =>
	(((atcA1.used <> atcA2.used) = rm1.atc_terminal(<ADIR>)) and
         ((atcB1.used <> atcB2.used) = rm1.atc_terminal(<BDIR>)))) and
        (dir_chng =>
	(((atcA1.used <> atcB2.used) = rm1.atc_terminal(<ADIR>)) and
         ((atcB1.used <> atcA2.used) = rm1.atc_terminal(<BDIR>))))

pre     Remark_Compatible(rm1, rm2, dir_chng);


Adjacent_TD_Carrier_Differ : TD * ATBT * TD * ATBT * Insulation -> bool
Adjacent_TD_Carrier_Differ(td1, atbt1, td2, atbt2, insulated) ==
	(insulated or
		td1.carrier <> td2.carrier and 
		(atbt1 <> atbt2 or atbt1 = <AT> and atbt2 = <AT>));
--
-- end of track circuit
--

--
-- paths in track circuits
--
types
Path::	tc : TrackC_id
	start : Joint_id
	endp : Joint_id
	length : nat
        used : map Direction to bool
        condition : set of Condition
inv p == p.start <> p.endp and
         dom p.used = {<ADIR>, <BDIR>} and
         (exists dr in set dom p.used & p.used(dr)) and
        (forall c1, c2 in set p.condition & Condition_not_Conflict(c1, c2));
-- start.position < endp.position is added at inv_Area

Condition ::    kind : Cond_Kind
                start : nat
                endp : nat
                speed : nat     -- only used for <LIMIT>
                permill : nat   -- should be integer or real
inv con == con.start < con.endp;

Cond_Kind = <LIMIT> | <GRADIENT> | <SECTION>;

functions
Condition_not_Conflict : Condition * Condition -> bool
Condition_not_Conflict(c1, c2) ==
        (c1 <> c2 and c1.kind = c2.kind and c1.kind <> <LIMIT>) =>
                 not Overlap(c1, c2);

Overlap : Condition * Condition -> bool
Overlap(c1, c2) ==
        (c1.start < c2.start and c2.start < c1.endp) or
        (c2.start < c1.start and c1.start < c2.endp) or
        (c1.start = c2.start)
;


types
Path_id = token;


Path_map = map Path_id to Path
inv ps == forall pid1, pid2 in set dom ps & pid1 <> pid2 => 
                (Not_Same_Path(ps(pid1), ps(pid2)) and
                 Not_Start_and_End(ps(pid1), ps(pid2)));

functions
Not_Same_Path : Path * Path -> bool
Not_Same_Path(p1, p2) ==
	not (p1.start = p2.start and p1.endp = p2.endp) and
        not (p1.start = p2.endp and p1.endp = p2.start);
-- It is possible to allow the case p1.tc <> p2.tc
-- but that is not practical, so it is not allowed here.


Not_Start_and_End : Path * Path -> bool
Not_Start_and_End(p1, p2) ==
        (p1.tc = p2.tc) =>
        (not p1.start = p2.endp and not p2.start = p1.endp);

--
-- route
--
types
Route::	dr : Direction
	paths : seq1 of Path_id;

Route_map = map Route_id to Route
;

Route_id = token;

--
-- Area -- corresponds to stations or intermediate part.
--

Area ::	trackcs : TrackC_map
	paths : Path_map
	routes : Route_map
        kind : Area_Kind
        max : MaxSpeed

inv mk_Area(trackcs, paths, routes, -, -) ==
	(forall p in set rng paths &
		Path_within_TrackC(trackcs, p) and
		Direction_Correct(trackcs, p)) and
	(forall r in set rng routes &
		Path_Exists(paths, r.paths, r.dr) and
		Exists_ATC_for_Route(trackcs, paths, r) and
		Route_not_Circular(paths, r) and
		Path_Connected(paths, r.paths, r.dr))
;

Area_Kind = <PLAIN> | <COMPLEX>;
MaxSpeed = token;

functions
Path_within_TrackC : TrackC_map * Path -> bool
Path_within_TrackC(trackcs, p) ==
	p.tc in set dom trackcs and
	p.start in set dom trackcs(p.tc).joints and
	p.endp in set dom trackcs(p.tc).joints
;
        


Direction_Correct : TrackC_map * Path -> bool
Direction_Correct(trackcs, p) ==
	let pstart = trackcs(p.tc).joints(p.start).position,
	    pend = trackcs(p.tc).joints(p.endp).position in
                pstart < pend and
                p.length = pend - pstart and
                forall c in set p.condition &
                        pstart <= c.start and c.endp <= pend
pre Path_within_TrackC(trackcs, p);

Path_Exists : Path_map * seq of Path_id * Direction -> bool
Path_Exists(paths, route, dr) ==
	forall pid in seq route & 
                pid in set dom paths and
                paths(pid).used(dr)
;

-- next function is related with signal
Exists_ATC_for_Route : TrackC_map * Path_map * Route -> bool
Exists_ATC_for_Route(trackcs, paths, r) ==
	forall pid in seq r.paths &
		paths(pid).tc in set dom trackcs and
		trackcs(paths(pid).tc).atc(r.dr).used
pre	Path_Exists(paths, r.paths, r.dr);

Route_not_Circular : Path_map * Route-> bool
Route_not_Circular(paths, r) ==
	forall i, j in set inds r.paths &
		i <> j => paths(r.paths(i)).tc <> paths(r.paths(j)).tc
pre	Path_Exists(paths, r.paths, r.dr);

Path_Connected : Path_map * seq of Path_id * Direction-> bool
Path_Connected(paths, route, dr) ==
	forall i in set inds route &
		(i + 1) in set inds route =>
                ((dr = <ADIR> =>
                        paths(route(i)).endp = paths(route(i+1)).start) and
                 (dr = <BDIR> =>
                        paths(route(i)).start = paths(route(i+1)).endp))

pre	Path_Exists(paths, route, dr);

--
-- end of invariant
--

--
-- Operation of Data Base (Area Level)
--
Add_TrackC : Area * TrackC_id * TrackC -> Area
Add_TrackC(ar, tcid, tc) ==
mu(ar, trackcs |-> ar.trackcs ++ {tcid |-> tc})
pre	tcid not in set dom ar.trackcs and
	forall jid in set dom tc.joints &
		Only_One_Next_TrackC(ar.trackcs, tcid, jid) and
                forall tcid1 in set dom ar.trackcs &
		Joint_and_Next_TrackC(tc, ar.trackcs(tcid1), jid)
post	tcid in set dom RESULT.trackcs and
        RESULT.trackcs = ar.trackcs ++ {tcid |-> tc} and
	RESULT.trackcs(tcid) = tc and
        RESULT.paths = ar.paths and
        RESULT.routes = ar.routes;

Del_TrackC : Area * TrackC_id -> Area
Del_TrackC(ar, tcid) ==
mu(ar, trackcs |-> {tcid} <-: ar.trackcs)
pre	tcid in set dom ar.trackcs and
	forall p in set rng ar.paths & p.tc <> tcid
post	tcid not in set dom RESULT.trackcs and
        RESULT.trackcs = {tcid} <-: ar.trackcs and
        RESULT.paths = ar.paths and
        RESULT.routes = ar.routes;


Add_Joint : Area * TrackC_id * Joint_id * Joint -> Area
Add_Joint(ar, tid, jid, joint) ==
	let tc = ar.trackcs(tid) in
	mu(ar, trackcs |-> ar.trackcs ++ {tid |->
		mu(tc, joints |-> tc.joints ++ {jid |-> joint})})
pre	tid in set dom ar.trackcs and
	let tc = ar.trackcs(tid) in
		jid not in set dom tc.joints and
		TD_Used_for_NonInsulated_TrackC(tc.td, tc.atbt, 
				rng tc.joints union {joint}) and
		Only_One_Next_TrackC(ar.trackcs, tid, jid) and
                forall tid1 in set dom ar.trackcs & tid1 <> tid =>
		Joint_and_Next_TrackC(ar.trackcs(tid1),
			mu(tc,joints |-> tc.joints ++ {jid |-> joint}), jid)
post	tid in set dom RESULT.trackcs and
	jid in set dom RESULT.trackcs(tid).joints and
        dom RESULT.trackcs = dom ar.trackcs and
        {tid} <-: RESULT.trackcs = {tid} <-: ar.trackcs and
        RESULT.trackcs(tid) = mu(ar.trackcs(tid), 
                joints |-> ar.trackcs(tid).joints ++ {jid |-> joint}) and
	RESULT.trackcs(tid).joints(jid) = joint and
	RESULT.trackcs(tid).atc = ar.trackcs(tid).atc and
        RESULT.trackcs(tid).td = ar.trackcs(tid).td and
        RESULT.trackcs(tid).atbt = ar.trackcs(tid).atbt and
        RESULT.paths = ar.paths and
        RESULT.routes = ar.routes
;

Del_Joint : Area * TrackC_id * Joint_id -> Area
Del_Joint(ar, tcid, jid) ==
	let tc = ar.trackcs(tcid) in
	mu(ar, trackcs |-> ar.trackcs ++
		{tcid |-> mu(tc, joints |-> {jid} <-: tc.joints)})
pre	tcid in set dom ar.trackcs and
	jid in set dom ar.trackcs(tcid).joints and
	card dom ar.trackcs(tcid).joints > 2 and
	(forall path in set rng ar.paths &
		path.tc <> tcid or
		jid <> path.start and jid <> path.endp)
post    tcid in set dom RESULT.trackcs and
        dom RESULT.trackcs = dom ar.trackcs and
        {tcid} <-: RESULT.trackcs = {tcid} <-: ar.trackcs and
        jid not in set dom RESULT.trackcs(tcid).joints and
        RESULT.trackcs(tcid) = mu(ar.trackcs(tcid), 
                joints |-> {jid} <-: ar.trackcs(tcid).joints) and
	RESULT.trackcs(tcid).atc = ar.trackcs(tcid).atc and
        RESULT.trackcs(tcid).td = ar.trackcs(tcid).td and
        RESULT.trackcs(tcid).atbt = ar.trackcs(tcid).atbt and
        RESULT.trackcs(tcid).joints = {jid} <-: ar.trackcs(tcid).joints and
        RESULT.paths = ar.paths and
        RESULT.routes = ar.routes
;

Add_Path : Area * Path_id * Path -> Area
Add_Path(ar, pid, path) ==
	mu(ar, paths |-> ar.paths ++ {pid |-> path})
pre	pid not in set dom ar.paths and
	Path_within_TrackC(ar.trackcs, path) and
	Direction_Correct(ar.trackcs, path) and
	forall p in set rng ar.paths & 
                Not_Same_Path(p, path) and Not_Start_and_End(p, path)
post	pid in set dom RESULT.paths and
        RESULT.paths = ar.paths ++ {pid |-> path} and
	RESULT.paths(pid) = path and
        RESULT.trackcs = ar.trackcs and
        RESULT.routes = ar.routes;


Del_Path : Area * Path_id -> Area
Del_Path(ar, pid) ==
	mu(ar, paths |-> {pid} <-: ar.paths)
pre	pid in set dom ar.paths and
	forall r in set rng ar.routes &	pid not in set elems r.paths
post	pid not in set dom RESULT.paths and
        RESULT.paths = {pid} <-: ar.paths and
        RESULT.trackcs = ar.trackcs and
        RESULT.routes = ar.routes;

Add_Route : Area * Route_id * Route -> Area
Add_Route (ar, rid, r) ==
mu(ar, routes |-> ar.routes ++ {rid |-> r})
pre	rid not in set dom ar.routes and
	Path_Exists(ar.paths, r.paths, r.dr) and
        Exists_ATC_for_Route(ar.trackcs, ar.paths, r) and
	Route_not_Circular(ar.paths, r) and
	Path_Connected(ar.paths, r.paths, r.dr)
post	rid in set dom RESULT.routes and
        RESULT.routes = ar.routes ++ {rid |-> r} and
	RESULT.routes(rid) = r and
        RESULT.trackcs = ar.trackcs and
        RESULT.paths = ar.paths;

Del_Route : Area * Route_id -> Area
Del_Route(ar, rid) ==
mu(ar, routes |-> {rid} <-: ar.routes)
pre	rid in set dom ar.routes
post	rid not in set dom RESULT.routes and
        RESULT.routes = {rid} <-: ar.routes and
        RESULT.trackcs = ar.trackcs and
        RESULT.paths = ar.paths;

Add_Condition : Area * Path_id * Condition -> Area
Add_Condition(ar, pid, con) ==
        let p = mu(ar.paths(pid), 
                condition |-> ar.paths(pid).condition union {con}) in
        mu(ar, paths |-> ar.paths ++ {pid |-> p})
pre     pid in set dom ar.paths and
        let p = ar.paths(pid) in
                ar.trackcs(p.tc).joints(p.start).position <= con.start and 
                con.endp <= ar.trackcs(p.tc).joints(p.endp).position and
                (forall c in set p.condition & Condition_not_Conflict(c, con))

post    pid in set dom RESULT.paths and
        dom RESULT.paths = dom ar.paths and
        {pid} <-: RESULT.paths = {pid} <-: ar.paths and
        RESULT.paths(pid) = mu(ar.paths(pid), 
                  condition |-> ar.paths(pid).condition union {con}) and
        RESULT.paths(pid).start = ar.paths(pid).start and
        RESULT.paths(pid).endp = ar.paths(pid).endp and
        RESULT.paths(pid).tc = ar.paths(pid).tc and
        RESULT.paths(pid).length = ar.paths(pid).length and
        RESULT.paths(pid).condition = ar.paths(pid).condition union {con} and
        RESULT.trackcs = ar.trackcs and
        RESULT.routes = ar.routes
;

Del_Condition : Area * Path_id * Cond_Kind * nat * nat -> Area
Del_Condition(ar, pid, kind, start, endp) ==
        let p = mu(ar.paths(pid), condition |-> 
                {l | l in set ar.paths(pid).condition & 
                 not (l.kind = kind and l.start = start and l.endp = endp)}) in
        mu(ar, paths |-> ar.paths ++ {pid |-> p})
pre     pid in set dom ar.paths and
        (exists c in set ar.paths(pid).condition &
                c.kind = kind and c.start = start and c.endp = endp)
post    pid in set dom RESULT.paths and
        dom RESULT.paths = dom ar.paths and
        {pid} <-: RESULT.paths = {pid} <-: ar.paths and
        RESULT.paths(pid) = mu(ar.paths(pid), condition |->
               {l | l in set ar.paths(pid).condition & 
               not (l.kind = kind and l.start = start and l.endp = endp)}) and
        RESULT.paths(pid).start = ar.paths(pid).start and
        RESULT.paths(pid).endp = ar.paths(pid).endp and
        RESULT.paths(pid).tc = ar.paths(pid).tc and
        RESULT.paths(pid).length = ar.paths(pid).length and
        RESULT.trackcs = ar.trackcs and
        RESULT.routes = ar.routes
;

--
-- end of Operation (Area level)
--


types
--
-- Line -- collection of Areas
--
--

Line :: areas : Area_map
	connect : Connect_map
--      ver : Version                --
--      edit_date : Date             -- These 4 fields are
--      valid_date : Date            -- to be used version control system.
--      locked : bool                --
inv mk_Line(areas, connect) == 
    forall c in set dom connect & 
        (forall n in set c & Area_Joint_Exists(areas, n)) and

        (forall n1, n2 in set c & n1 <> n2 =>
                Direction_for_Area_Joint(areas(n1.aid).paths, n1.no, 
                    areas(n2.aid).paths, n2.no, connect(c).chng_direction) and
               	let tc1 = areas(n1.aid).trackcs(n1.tcid),
                    tc2 = areas(n2.aid).trackcs(n2.tcid) in
                    Joint_Compatible(tc1.joints(n1.no), tc2.joints(n2.no),
				connect(c)) and
                    Is_wf_adjacent_signal(tc1, n1.no, tc2, n2.no,
				connect(c).chng_direction));

Area_map = map Area_id to Area;
Area_id = token;

-- (Plan)
-- Area_id :: lid : nat
--            aid : nat


--
-- connection between two areas
--
Area_Joint ::	aid : Area_id
		tcid : TrackC_id
		no : Joint_id;

Connect = set of Area_Joint
inv con == card con = 2 and
        forall a1, a2 in set con & a1 <> a2 => a1.aid <> a2.aid;


Connect_map = map Connect to Remark_Connect
inv con == forall a1, a2 in set dom con & a1 <> a2 => a1 inter a2 = {};


Remark_Connect :: chng_direction : bool
		  chng_distance : bool;

functions
Area_Joint_Exists : Area_map * Area_Joint -> bool
Area_Joint_Exists(areas, n) ==
	n.aid in set dom areas and
        n.tcid in set dom areas(n.aid).trackcs and
        n.no in set dom areas(n.aid).trackcs(n.tcid).joints and
        not areas(n.aid).trackcs(n.tcid).joints(n.no).remark.line_terminal and
        forall tcid in set dom areas(n.aid).trackcs & n.tcid <> tcid =>
                n.no not in set dom areas(n.aid).trackcs(tcid).joints;


Direction_for_Area_Joint : Path_map * Joint_id * Path_map * Joint_id * bool 
        -> bool
Direction_for_Area_Joint(pm1, n1, pm2, n2, chng_dir) ==
        forall p1 in set rng pm1, p2 in set rng pm2 &
        ((p1.start = n1 and p2.start = n2) => chng_dir) and
        ((p1.endp  = n1 and p2.endp  = n2) => chng_dir) and
        ((p1.start = n1 and p2.endp  = n2) => not chng_dir) and
        ((p1.endp  = n1 and p2.start = n2) => not chng_dir)
;


Joint_Compatible: Joint * Joint * Remark_Connect -> bool
Joint_Compatible(j1, j2, rm) ==
	j1.insulated = j2.insulated and
        ((j1.position <> j2.position) = rm.chng_distance) and
        Remark_Compatible(j1.remark, j2.remark, rm.chng_direction);

Add_Area : Line * Area_id * Area_Kind * MaxSpeed -> Line
Add_Area(ln, aid, kind, max) ==
mu(ln, areas |-> ln.areas ++ {aid |-> mk_Area({|->}, {|->}, {|->}, kind, max)})
pre	aid not in set dom ln.areas
post	aid in set dom RESULT.areas and
        RESULT.connect = ln.connect;

Change_Area : Line * Area_id * Area -> Line
Change_Area(ln, aid, area) ==
mu(ln, areas |-> ln.areas ++ {aid |-> area})
pre	aid in set dom ln.areas and
	inv_Line(mk_Line(ln.areas ++ {aid |-> area}, ln.connect))
post	RESULT.areas(aid) = area and
        RESULT.connect = ln.connect;


Del_Area : Line * Area_id -> Line
Del_Area(ln, aid) ==
mu(ln, areas |-> {aid} <-: ln.areas)
pre	aid in set dom ln.areas and
	(forall c in set dom ln.connect & 
                forall aj in set c & aj.aid <> aid)
post	aid not in set dom RESULT.areas and
        RESULT.connect = ln.connect;


Add_Connect : Line * Connect * Remark_Connect -> Line
Add_Connect(ln, con, r) ==
mu(ln, connect |-> ln.connect ++ {con |-> r})
pre	(forall c in set dom ln.connect & c inter con = {}) and
        (forall n in set con & Area_Joint_Exists(ln.areas, n)) and
                        
        (forall n1, n2 in set con & n1 <> n2 =>
                Direction_for_Area_Joint(ln.areas(n1.aid).paths, n1.no, 
                        ln.areas(n2.aid).paths, n2.no, r.chng_direction) and
        	let tc1 = ln.areas(n1.aid).trackcs(n1.tcid),
	            tc2 = ln.areas(n2.aid).trackcs(n2.tcid) in
        	Joint_Compatible(tc1.joints(n1.no), tc2.joints(n2.no), r) and
        	Is_wf_adjacent_signal(tc1,n1.no, tc2, n2.no, r.chng_direction))

post	con in set dom RESULT.connect and
        RESULT.connect = ln.connect ++ {con |-> r} and
	RESULT.connect(con) = r and
        RESULT.areas = ln.areas
;

Del_Connect : Line * Area_Joint -> Line
Del_Connect(ln, n) ==
mu(ln, connect |-> 
        {c |-> ln.connect(c) | c in set dom ln.connect & n not in set c})
pre	exists c in set dom ln.connect & n in set c
post	forall c in set dom RESULT.connect & n not in set c and
        RESULT.areas = ln.areas;
functions

Line_Add_TrackC : Line * Area_id * TrackC_id * TrackC -> Line
Line_Add_TrackC(ln, aid, tcid, tc) ==
       mu(ln, areas |-> ln.areas ++ 
                {aid |-> Add_TrackC(ln.areas(aid), tcid, tc)})
pre     aid in set dom ln.areas and
        pre_Add_TrackC(ln.areas(aid), tcid, tc) and
        (forall jid in set dom tc.joints &
         forall c in set dom ln.connect &
                forall n in set c &
                        n.aid = aid => n.no <> jid)
post    aid in set dom RESULT.areas and
        dom ln.areas = dom RESULT.areas and
        {aid} <-: RESULT.areas = {aid} <-: ln.areas and
        tcid in set dom RESULT.areas(aid).trackcs and
        RESULT.areas(aid).trackcs = 
                ln.areas(aid).trackcs ++ {tcid |-> tc} and
        RESULT.areas(aid).trackcs(tcid) = tc and
        RESULT.connect = ln.connect;

Line_Del_TrackC : Line * Area_id * TrackC_id -> Line
Line_Del_TrackC(ln, aid, tcid) ==
        mu(ln, areas |-> ln.areas ++
                {aid |-> Del_TrackC(ln.areas(aid), tcid)})
pre     aid in set dom ln.areas and
        pre_Del_TrackC(ln.areas(aid), tcid) and
        forall c in set dom ln.connect & forall aj in set c &
                        aj.aid = aid => aj.tcid <> tcid
post    aid in set dom RESULT.areas and
        dom ln.areas = dom RESULT.areas and
        {aid} <-: RESULT.areas = {aid} <-: ln.areas and
        RESULT.areas(aid).trackcs = {tcid} <-: ln.areas(aid).trackcs and
        tcid not in set dom RESULT.areas(aid).trackcs and
        RESULT.connect = ln.connect;

Line_Add_Joint : Line * Area_id * TrackC_id * Joint_id * Joint -> Line
Line_Add_Joint(ln, aid, tcid, jid, j) ==
        mu(ln, areas |-> ln.areas ++
                {aid |-> Add_Joint(ln.areas(aid), tcid, jid, j)})
pre     aid in set dom ln.areas and
        pre_Add_Joint(ln.areas(aid), tcid, jid, j) and
        (forall c in set dom ln.connect & forall n in set c &
                n.aid = aid => n.no <> jid)
post    aid in set dom RESULT.areas and
        tcid in set dom RESULT.areas(aid).trackcs and
        jid in set dom RESULT.areas(aid).trackcs(tcid).joints and
        dom RESULT.areas = dom ln.areas and
        {aid} <-: RESULT.areas = {aid} <-: ln.areas and
        dom RESULT.areas(aid).trackcs = dom ln.areas(aid).trackcs and
        {tcid} <-: RESULT.areas(aid).trackcs = 
                {tcid} <-: ln.areas(aid).trackcs and
        RESULT.areas(aid).trackcs(tcid).joints = 
                ln.areas(aid).trackcs(tcid).joints ++ {jid |-> j} and
        RESULT.areas(aid).trackcs(tcid).joints(jid) = j and
        RESULT.connect = ln.connect;

Line_Del_Joint : Line * Area_id * TrackC_id * Joint_id -> Line
Line_Del_Joint(ln, aid, tcid, jid) ==
        mu(ln, areas |-> ln.areas ++
                {aid |-> Del_Joint(ln.areas(aid), tcid, jid)})
pre     aid in set dom ln.areas and
        pre_Del_Joint(ln.areas(aid), tcid, jid) and
        forall c in set dom ln.connect & forall aj in set c &
                (aj.aid = aid and aj.tcid = tcid) => aj.no <> jid
post    aid in set dom RESULT.areas and
        tcid in set dom RESULT.areas(aid).trackcs and
        jid not in set dom RESULT.areas(aid).trackcs(tcid).joints and
        dom RESULT.areas = dom ln.areas and
        {aid} <-: RESULT.areas = {aid} <-: ln.areas and
        dom RESULT.areas(aid).trackcs = dom ln.areas(aid).trackcs and
        {tcid} <-: RESULT.areas(aid).trackcs = 
                        {tcid} <-: ln.areas(aid).trackcs and
        RESULT.areas(aid).trackcs(tcid).joints = 
                {jid} <-: ln.areas(aid).trackcs(tcid).joints and
        RESULT.connect = ln.connect;


Line_Add_Path : Line * Area_id * Path_id * Path -> Line
Line_Add_Path(ln, aid, pid, p) ==
       mu(ln, areas |-> ln.areas ++ 
                {aid |-> Add_Path(ln.areas(aid), pid, p)})
pre     aid in set dom ln.areas and
        pre_Add_Path(ln.areas(aid), pid, p) and
        forall c in set dom ln.connect &
                forall n1, n2 in set c & 
                (n1 <> n2 and n1.aid = aid) =>
                 Direction_for_Area_Joint({pid |-> p}, n1.no, 
                  ln.areas(n2.aid).paths , n2.no, ln.connect(c).chng_direction)
post    aid in set dom RESULT.areas and
        pid in set dom RESULT.areas(aid).paths and
        dom RESULT.areas = dom ln.areas and
        {aid} <-: RESULT.areas = {aid} <-: ln.areas and
        RESULT.areas(aid).paths = ln.areas(aid).paths ++ {pid |-> p} and
        RESULT.areas(aid).paths(pid) = p and
        RESULT.connect = ln.connect;

Line_Del_Path : Line * Area_id * Path_id -> Line
Line_Del_Path(ln, aid, pid) ==
        mu(ln, areas |-> ln.areas ++ {aid |-> Del_Path(ln.areas(aid), pid)})
pre     aid in set dom ln.areas and
        pre_Del_Path(ln.areas(aid), pid)
post    aid in set dom RESULT.areas and
        pid not in set dom RESULT.areas(aid).paths and
        dom RESULT.areas = dom ln.areas and
        {aid} <-: RESULT.areas = {aid} <-: ln.areas and
        RESULT.areas(aid).paths = {pid} <-: ln.areas(aid).paths and
        RESULT.connect = ln.connect;


Line_Add_Route : Line * Area_id * Route_id * Route -> Line
Line_Add_Route(ln, aid, rid, r) ==
        mu(ln, areas |-> ln.areas ++ {aid |-> Add_Route(ln.areas(aid), rid,r)})
pre     aid in set dom ln.areas and
        pre_Add_Route(ln.areas(aid), rid, r)
post    aid in set dom RESULT.areas and
        rid in set dom RESULT.areas(aid).routes and
        dom RESULT.areas = dom ln.areas and
        {aid} <-: RESULT.areas = {aid} <-: ln.areas and
        RESULT.areas(aid).routes = ln.areas(aid).routes ++ {rid |-> r} and
        RESULT.areas(aid).routes(rid) = r and
        RESULT.connect = ln.connect;

Line_Del_Route : Line * Area_id * Route_id -> Line
Line_Del_Route(ln, aid, rid) ==
        mu(ln, areas |-> ln.areas ++ {aid |-> Del_Route(ln.areas(aid), rid)})
pre     aid in set dom ln.areas and
        pre_Del_Route(ln.areas(aid), rid)
post    aid in set dom RESULT.areas and
        dom RESULT.areas = dom ln.areas and
        {aid} <-: RESULT.areas = {aid} <-: ln.areas and
        rid not in set dom RESULT.areas(aid).routes and
        RESULT.areas(aid).routes = {rid} <-: ln.areas(aid).routes and
        RESULT.connect = ln.connect;

Line_Add_Condition : Line * Area_id * Path_id * Condition-> Line
Line_Add_Condition(ln, aid, pid, con) ==
        mu(ln, areas |-> ln.areas ++
                        {aid |-> Add_Condition(ln.areas(aid), pid, con)})
pre     aid in set dom ln.areas and
        pre_Add_Condition(ln.areas(aid), pid, con);

Line_Del_Condition : Line * Area_id * Path_id * Cond_Kind * nat * nat-> Line
Line_Del_Condition(ln, aid, pid, kind, start, endp) ==
        mu(ln, areas |-> ln.areas ++ 
                {aid |-> Del_Condition(ln.areas(aid), pid, kind, start, endp)})
pre     aid in set dom ln.areas and
        pre_Del_Condition(ln.areas(aid), pid, kind, start, endp)
;

--
--  Digital ATC Database
--  Condition for "Completed" Database
--

functions
Is_wf_Line_DB : Line -> bool
Is_wf_Line_DB(ln) ==

        (forall aid in set dom ln.areas & let ar = ln.areas(aid) in
                Joint_Completed(ar.trackcs, aid, ln.connect) and
                Path_Exists_for_Joint(ar.trackcs, ar.paths) and
                Path_Exists_for_TrackC(ar.trackcs, ar.paths) and
                Route_Exists_for_Path(ar) and
                Path_Exists_before_Start(ar, aid, ln.connect) and
                Path_Exists_after_End(ar, aid, ln.connect) and
                Route_Exists_to_Terminal(ar, aid, ln.connect) and
                (ar.kind = <PLAIN> => Is_Plain_Area(ar, aid, ln.connect))) and

        Following_Path_Exists_at_Connect(ln) and
        Preceding_Path_Exists_at_Connect(ln) and
        One_Side_Unique_Path_at_Connection(ln);



Joint_Completed : TrackC_map * Area_id * Connect_map -> bool
Joint_Completed(trackcs, aid, connect) ==
        forall tid in set dom trackcs &
        let tc = trackcs(tid) in
                forall jid in set dom tc.joints &
                (not exists tcid in set dom trackcs & tcid <> tid and
                                jid in set dom trackcs(tcid).joints) =>
                (mk_Area_Joint(aid, tid, jid) in set dunion dom connect or
                 tc.joints(jid).remark.line_terminal)
;

Path_Exists_for_Joint : TrackC_map * Path_map -> bool
Path_Exists_for_Joint(trackcs, paths) ==
        forall tid in set dom trackcs &
        forall jid in set dom trackcs(tid).joints &
                (exists p in set rng paths & 
                        p.tc = tid and
                        (p.start = jid or p.endp = jid))
;

Path_Exists_for_TrackC : TrackC_map * Path_map -> bool
Path_Exists_for_TrackC(trackcs, paths) ==
        forall tid in set dom trackcs &
        forall dr in set dom trackcs(tid).atc &
                trackcs(tid).atc(dr).used => 
                        exists p in set rng paths & 
                                p.tc = tid and p.used(dr)
;

Route_Exists_for_Path : Area -> bool
Route_Exists_for_Path(ar) ==
        forall pid in set dom ar.paths &
        forall dr in set dom ar.paths(pid).used &
                ar.paths(pid).used(dr) =>
                ar.trackcs(ar.paths(pid).tc).atc(dr).used =>
                exists r in set rng ar.routes & 
                        r.dr = dr and pid in set elems r.paths;

Path_Exists_before_Start :  Area * Area_id * Connect_map -> bool
Path_Exists_before_Start(ar, aid, connect) ==
        forall pid in set dom ar.paths &
        let p = ar.paths(pid) in
        forall dr in set dom p.used &
                p.used(dr) =>
                (mk_Area_Joint(aid, p.tc, p.start) in set dunion dom connect or
                ar.trackcs(p.tc).joints(p.start).remark.line_terminal or
                ar.trackcs(p.tc).joints(p.start).remark.atc_terminal(dr) or
                exists pid1 in set dom ar.paths &
                        let p1 = ar.paths(pid1) in
                        p1.tc <> p.tc and
                        p1.used(dr) and
                        p1.endp = p.start)
;

Path_Exists_after_End :  Area * Area_id * Connect_map -> bool
Path_Exists_after_End(ar, aid, connect) ==
        forall pid in set dom ar.paths &
        let p = ar.paths(pid) in
        forall dr in set dom p.used &
                p.used(dr) =>
                (mk_Area_Joint(aid, p.tc, p.endp) in set dunion dom connect or
                ar.trackcs(p.tc).joints(p.endp).remark.line_terminal or
                ar.trackcs(p.tc).joints(p.endp).remark.atc_terminal(dr) or
                exists pid1 in set dom ar.paths &
                        let p1 = ar.paths(pid1) in
                        p1.tc <> p.tc and
                        p1.used(dr) and
                        p1.start = p.endp)
;

StartJoint : Path * Direction -> Joint_id
StartJoint(path, dr) ==
        if dr = <ADIR> then path.start else path.endp
post    (dr = <ADIR> => RESULT = path.start) and
        (dr = <BDIR> => RESULT = path.endp);

EndJoint : Path * Direction -> Joint_id
EndJoint(path, dr) ==
        if dr = <ADIR> then path.endp else path.start
post    (dr = <ADIR> => RESULT = path.endp) and
        (dr = <BDIR> => RESULT = path.start);


--
-- Route_Exists_to_Terminal means that Train can reach an Area_Joint or
--                                                    an end of track.
Route_Exists_to_Terminal : Area * Area_id * Connect_map -> bool
Route_Exists_to_Terminal(ar, aid, connect) ==
        forall rid in set dom ar.routes &
                let r = ar.routes(rid) in
                let pid = r.paths(len r.paths) in
                let jid  = EndJoint(ar.paths(pid), r.dr),
                    tcid = ar.paths(pid).tc in

                mk_Area_Joint(aid, tcid, jid) in set dunion dom connect or
                ar.trackcs(tcid).joints(jid).remark.line_terminal or
                ar.trackcs(tcid).joints(jid).remark.atc_terminal(r.dr) or
                Following_Route_Exists(ar.routes, rid) or
                Following_Path_Unique(ar.paths, pid, r.dr);


--
-- Following_Route_Exists1 :
-- On the last path, if a next route which includes following paths can 
-- be indicated, train can proceed with next route ID.
--

Following_Route_Exists : Route_map * Route_id -> bool
Following_Route_Exists(routes, rid) ==
        exists rid1 in set dom routes & 
                let r = routes(rid), r1 = routes(rid1) in
                r1.dr = r.dr and
                exists i in set inds r1.paths &
                        r1.paths(i) = r.paths(len r.paths) and
                        i < len r1.paths
pre     rid in set dom routes;

--
-- Unique_Next_Path :
-- On the last path, if there is only one next path possible,
--  trains can proceed to the next path.
--
Following_Path_Unique : Path_map * Path_id * Direction-> bool
Following_Path_Unique(paths, pid, dr) ==
        exists1 pid1 in set dom paths &
                paths(pid1).tc <> paths(pid).tc and
                paths(pid1).used(dr) and
                EndJoint(paths(pid), dr) = StartJoint(paths(pid1), dr)
pre     pid in set dom paths;


--
-- Plain Area, where from TrackC_id and direction, Route can be determined.
--

Is_Plain_Area : Area * Area_id * Connect_map-> bool
Is_Plain_Area(ar, aid, connect) ==
        (forall tcid in set dom ar.trackcs &
        forall dr in set dom ar.trackcs(tcid).atc &
        ar.trackcs(tcid).atc(dr).used =>
                exists1 rid in set dom ar.routes &
                        ar.routes(rid).dr = dr and
                        exists pid in seq ar.routes(rid).paths &
                        ar.paths(pid).tc = tcid) and
        (forall r in set rng ar.routes &
                let p = ar.paths(r.paths(len r.paths)) in
                let jid = EndJoint(p, r.dr) in
                mk_Area_Joint(aid, p.tc, jid) in set dunion dom connect or
                ar.trackcs(p.tc).joints(jid).remark.line_terminal or
                ar.trackcs(p.tc).joints(jid).remark.atc_terminal(r.dr));
                

--
-- One_Side_Unique_Path_at_Connection
--  At the connection it is not favorable that 
--  in both area paths are not unique.
-- Because it makes imposibble to indicate next path 
--                              in the former track circuit.

One_Side_Unique_Path_at_Connection : Line -> bool
One_Side_Unique_Path_at_Connection(ln) ==
        forall con in set dom ln.connect &
        forall n1, n2 in set con & n1 <> n2 =>
        forall dr in set {<ADIR>, <BDIR>} &
        card {p | p in set rng ln.areas(n1.aid).paths & 
                p.used(dr) and 
                EndJoint(p, dr) = n1.no} > 1 =>

                (ln.areas(n1.aid).trackcs(n1.tcid).joints(n1.no).
                    remark.atc_terminal(dr) or

                 let dr2 = if not ln.connect(con).chng_direction then dr
                                else if dr = <ADIR> then <BDIR> else <ADIR> in
                 card {p | p in set rng ln.areas(n2.aid).paths & 
                                p.used(dr2) and 
                                StartJoint(p, dr2) = n2.no} = 1);

Following_Path_Exists_at_Connect : Line -> bool
Following_Path_Exists_at_Connect(ln) ==
        forall con in set dom ln.connect &
        forall n1, n2 in set con & n1 <> n2 =>
        forall dr in set {<ADIR>, <BDIR>} &
        (exists p in set rng ln.areas(n1.aid).paths &
                p.used(dr) and
                EndJoint(p, dr) = n1.no) =>
        (ln.areas(n1.aid).trackcs(n1.tcid).joints(n1.no).
            remark.atc_terminal(dr) or
        (exists p2 in set rng ln.areas(n2.aid).paths &
                 let dr2 = if not ln.connect(con).chng_direction then dr
                                else if dr = <ADIR> then <BDIR> else <ADIR> in
                 p2.used(dr2) and
                 StartJoint(p2, dr2) = n2.no));

Preceding_Path_Exists_at_Connect : Line -> bool
Preceding_Path_Exists_at_Connect(ln) ==
        forall con in set dom ln.connect &
        forall n1, n2 in set con & n1 <> n2 =>
        forall dr in set {<ADIR>, <BDIR>} &
        (exists p in set rng ln.areas(n1.aid).paths &
                p.used(dr) and
                StartJoint(p, dr) = n1.no) =>
        (ln.areas(n1.aid).trackcs(n1.tcid).joints(n1.no).
            remark.atc_terminal(dr) or
        (exists p2 in set rng ln.areas(n2.aid).paths &
                let dr2 = if not ln.connect(con).chng_direction 
                          then dr
                          elseif dr = <ADIR> 
                          then <BDIR> 
                          else <ADIR> in
                p2.used(dr2) and
                EndJoint(p2, dr2) = n2.no));