Source file ltsa.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
(**********************************************************************)
(*                                                                    *)
(*                              LASCAr                                *)
(*                                                                    *)
(*  Copyright (c) 2017-present, Jocelyn SEROT.  All rights reserved.  *)
(*                                                                    *)
(*  This source code is licensed under the license found in the       *)
(*  LICENSE file in the root directory of this source tree.           *)
(*                                                                    *)
(**********************************************************************)

open Utils

type Dot.graph_style +=
     | Circular   (** Circular layout (circo); default is layered *)
     | NoAttr     (** Do not draw state attributes *)
     | NoLabel    (** Do not draw transition label *)
   
module type STATE = OrderedTypeExt.T

module type LABEL = OrderedTypeExt.T

module type ATTR = sig
  type t
  val to_string: t -> string
end

module type T = sig

  type state (** The type of state identifiers *)
  type label (** The type of transition labels *)
  type attr  (** The type of state attributes *)

  type transition = state * label * state

  type itransition = label * state

  type t (** The type of Labeled Transition Systems *)

  module State : STATE with type t = state
  module Label : LABEL with type t = label
  module Attr : ATTR with type t = attr
  module States : SetExt.S with type elt = state
  module Attrs : Map.S with type key = state
  module Tree : Tree.S with type node = state and type edge = label

  val states: t -> States.t
  val states': t -> (state * attr) list
  val istates: t -> States.t
  val istates': t -> state list
(*   val attrs: t -> Attr.t Attrs.t *)
  val transitions: t -> transition list
  val itransitions: t -> itransition list

  val string_of_state: state -> string
  val string_of_label: label -> string
  val string_of_attr: attr -> string

  val is_state: t -> state -> bool
  val is_init_state: t -> state -> bool
  val is_reachable: t -> state -> bool
  val is_transition: t -> transition -> bool

  val succs: t -> state -> States.t
  val succs': t -> state -> (state * label) list
  val preds: t -> state -> States.t
  val preds': t -> state -> (state * label) list
  val succs_hat: t -> state -> States.t
  val preds_hat: t -> state -> States.t

  val attr_of: t -> state -> attr

  val empty: t

  val create: states:(state * attr) list -> itrans:(label * state) list -> trans:(state * label * state) list -> t

  val add_state: state * attr -> t -> t

  exception Invalid_state of state

  val add_transition: state * label * state -> t -> t
  
  val add_itransition: label * state -> t -> t

  val remove_state: state -> t -> t
  
  val iter_states: (state -> attr -> unit) -> t -> unit
  val fold_states: (state-> attr -> 'a -> 'a) -> t -> 'a -> 'a
  val iter_transitions: (transition -> unit) -> t -> unit
  val fold_transitions: (transition -> 'a -> 'a) -> t -> 'a -> 'a
  val iter_itransitions: (itransition -> unit) -> t -> unit
  val fold_itransitions: (itransition -> 'a -> 'a) -> t -> 'a -> 'a


  val fold_succs: t -> state -> (state -> label -> 'a -> 'a) -> 'a -> 'a
  val iter_succs: t -> state -> (state -> label -> unit) -> unit
  val fold_preds: t -> state -> (state -> label -> 'a -> 'a) -> 'a -> 'a
  val iter_preds: t -> state -> (state -> label -> unit) -> unit

  val map_state: (state -> state) -> t -> t
  val map_attr: (attr -> attr) -> t -> t
  val map_label: (label -> label) -> t -> t
  val clean: t -> t
  val unwind: int -> t -> Tree.t list

  val dot_output: string
               -> ?fname:string
               -> ?options:Utils.Dot.graph_style list
               -> ?marked_states:(state * Utils.Dot.node_style) list
               -> ?extra_nodes:(string * Utils.Dot.node_style) list
               -> ?implicit_transitions:transition list
               -> t
               -> unit

  val dot_output_oc: string
               -> out_channel
               -> ?options:Utils.Dot.graph_style list
               -> ?marked_states:(state * Utils.Dot.node_style) list
               -> ?extra_nodes:(string * Utils.Dot.node_style) list
               -> ?implicit_transitions:transition list
               -> t
               -> unit

  val dot_output_execs: string -> ?fname:string -> ?options:Utils.Dot.graph_style list -> int -> t -> unit

  val tex_output: string -> ?fname:string -> ?listed_transitions:label list option -> t -> unit

  (* val dump: t -> unit  (\* for debug only *\) *)
end


module Make (S: STATE) (L: LABEL) (A: ATTR) =
struct

  module State = S
  module Label = L
  module Attr = A

  type state = S.t
  type label = L.t
  type attr = A.t

  type transition = state * label * state
  type itransition = label * state

  module States = SetExt.Make (State)
  module Attrs = Map.Make(struct type t = state let compare = compare end)

  module Tree =
    Tree.Make(
        struct
          type node = state
          type edge = label
          let string_of_node = State.to_string 
          let string_of_edge = Label.to_string 
        end)


  module Transition = struct
    type t = transition
    let compare = compare
    let to_string (q1,l,q2) = "(" ^ State.to_string q1 ^ "," ^ Label.to_string l ^ "," ^ State.to_string q2 ^ ")"
  end

  module D = SetExt.Make(Transition)
  module Q = States
  module H = Attrs

  type t = {
      states: Q.t;
      attrs: attr H.t;
      irel: label H.t;
      rel: D.t
      }

  let string_of_state = State.to_string
  let string_of_label = Label.to_string
  let string_of_attr = Attr.to_string

  let empty = { states = Q.empty; attrs = H.empty; irel = H.empty; rel = D.empty }

  let add_state (q,a) s = { s with states = Q.add q s.states; attrs = H.add q a s.attrs }

  let remove_state q s = {
    states = Q.remove q s.states;
    attrs = H.remove q s.attrs;
    irel = H.filter (fun q' _ -> State.compare q q' <> 0) s.irel;
    rel = D.filter (function (q',_,q'') -> State.compare q q' <> 0 && State.compare q q'' <> 0) s.rel 
    }

  let attr_of s q = H.find q s.attrs

  exception Invalid_state of state

  let check_state q qs = if not (Q.mem q qs) then raise (Invalid_state q)

  let add_transition ((q1,l,q2) as t) s = 
    check_state q1 s.states;
    check_state q2 s.states;
    { s with rel = D.add t s.rel }

  let add_itransition (l,q) s = 
    check_state q s.states;
    { s with irel = H.add q l s.irel }

  let create ~states:qs ~itrans:ts0 ~trans:ts =
    empty |> (function s -> List.fold_left (Misc.flip add_state) s qs)
          |> (function s -> List.fold_left (Misc.flip add_transition) s ts)
          |> (function s -> List.fold_left (Misc.flip add_itransition) s ts0)

  let states s =  s.states 
  let states' s =  H.bindings s.attrs
(*   let attrs s =  s.attrs  *)
  let istates s =  H.fold (fun q _ acc -> Q.add q acc) s.irel Q.empty
  let istates' s =  Q.elements (istates s)
  let itransitions s =  H.fold (fun q l z -> (l,q)::z) s.irel []
  let transitions s = D.elements s.rel

  let iter_states f s = H.iter f s.attrs
  let fold_states f s z = H.fold f s.attrs z

  let iter_transitions f s = D.iter f s.rel
  let fold_transitions f s z = D.fold f s.rel z

  let iter_itransitions f s = H.iter (fun q l -> f (l,q)) s.irel
  let fold_itransitions f s z = H.fold (fun q l z -> f (l,q) z) s.irel z

  let is_state s q = Q.mem q s.states
  let is_init_state s q = H.mem q s.irel
  let is_transition s t = D.mem t s.rel

  let fold_succs s q f z = D.fold (fun (src,lbl,dst) acc -> if S.compare src q = 0 then f dst lbl acc else acc) s.rel z
  let fold_preds s q f z = D.fold (fun (src,lbl,dst) acc -> if S.compare dst q = 0 then f src lbl acc else acc) s.rel z
  let iter_succs s q f = D.iter (fun (src,lbl,dst) -> if S.compare src q = 0 then f dst lbl) s.rel
  let iter_preds s q f = D.iter (fun (src,lbl,dst) -> if S.compare dst q = 0 then f src lbl) s.rel

  let trav_acc s f q0s =
    let step qs = Q.union qs (Q.fold (fun q acc -> Q.union acc (f s q)) qs Q.empty) in
    Misc.iter_fix Q.equal step q0s

  let succs s q = fold_succs s q (fun q' _ acc -> Q.add q' acc) Q.empty
  let preds s q = fold_preds s q (fun q' _ acc -> Q.add q' acc) Q.empty
  let succs' s q = fold_succs s q (fun q' l acc -> (q',l)::acc) [] 
  let preds' s q = fold_preds s q (fun q' l acc -> (q',l)::acc) []

  let succs_hat s q = trav_acc s succs (Q.singleton q)
  let preds_hat s q = trav_acc s preds (Q.singleton q)

  let reachable_states s = trav_acc s succs (istates s)

  let is_reachable s q = Q.mem q (reachable_states s)

  let map_state f s =
    { states = Q.map f s.states;
      attrs = H.fold (fun q a acc -> H.add (f q) a acc) s.attrs H.empty;
      rel = D.map (function (q1,l,q2) -> f q1, l, f q2) s.rel;
      irel = H.fold (fun q l acc -> H.add (f q) l acc) s.irel H.empty }

  let map_attr f s =
    { s with attrs = H.map f s.attrs }

  let map_label f s =
    { s with rel = D.map (function (q1,l,q2) -> q1, f l, q2) s.rel;
             irel = H.map f s.irel }
      
  let clean s =
    let rs = reachable_states s in
    { states = rs;
      attrs = H.filter (fun q _ -> Q.mem q rs) s.attrs;
      rel = D.filter (function (q,l,q') -> Q.mem q rs && Q.mem q' rs) s.rel;
      irel = H.filter (fun q _ -> Q.mem q rs) s.irel }

  let unwind depth s = 
    let rec unwind level q =
      if level < depth then
        Tree.Node(q, List.map (fun (q,l) -> unwind (level+1) q, l) (succs' s q))
      else 
        Tree.Empty
    in
    List.map (unwind 0) (Q.elements (istates s))

  let dump m = (* For debug only *)
    Printf.printf "states=%s\n" (Q.to_string m.states);
    Printf.printf "attrs=%s\n"
      (ListExt.to_string (function (q,a) -> State.to_string q ^ "->" ^ Attr.to_string a) "," (H.bindings m.attrs));
    Printf.printf "rel=%s\n" (D.to_string m.rel);
    Printf.printf "irel=%s\n"
      (ListExt.to_string (function (q,l) -> Label.to_string l ^ "->" ^ State.to_string q) "," (H.bindings m.irel))
    
  let dot_output_oc name oc ?(options=[]) ?(marked_states=[]) ?(extra_nodes=[]) ?(implicit_transitions=[]) m = 
    let rankdir = if List.mem Utils.Dot.RankdirLR options then "LR" else "UD" in
    let module K = Map.Make (State) in
    let default_node_style = { Utils.Dot.node_shape = "circle"; Utils.Dot.node_style = "solid" } in
    let node_id i = name ^ "_" ^ string_of_int i in
    let ini_id = name ^ "_ini" in
    let extra_id j = name ^ "_extra" ^ string_of_int j in
    let extra_idxs = ListExt.range Misc.id 0 (List.length extra_nodes -1) in
    let ndescs, nbn = 
      fold_states
        (fun s a (acc,n) ->
          let style = try ListExt.assoc ~cmp:State.compare s marked_states with Not_found -> default_node_style in
          let lbl =
            if List.mem NoAttr options 
            then string_of_state s
            else string_of_state s  ^ "\\n" ^ string_of_attr a in
          K.add s (node_id n,lbl,style) acc, n+1)
        m
        (K.empty, 0) in 
    let ndesc q =
      try K.find q ndescs 
      with Not_found -> failwith ("Ltsa.dot_output: cannot find state " ^ (string_of_state q)) in
    let dump_extra_node i (lbl, {Utils.Dot.node_shape=sh; Utils.Dot.node_style=st}) =
      Printf.fprintf oc "%s [label = \"%s\", shape = %s, style = %s]\n" (extra_id i) lbl sh st in
    let dump_state q a =
      let id, lbl, style = ndesc q in
      Printf.fprintf oc "%s [label = \"%s\", shape = %s, style = %s]\n" id lbl (style.Utils.Dot.node_shape) (style.Utils.Dot.node_style) in
    let dump_itransition (l,q) =
      let id, _, _ = ndesc q in
      if List.mem NoLabel options then
        Printf.fprintf oc "%s -> %s;\n" ini_id id
      else
        Printf.fprintf oc "%s -> %s [label=\"%s\"];\n" ini_id id (Label.to_string l) in
    let dump_transition ((q,l,q') as t) =
      if not (List.mem t implicit_transitions) then 
        let id, _, _ = ndesc q in
        let id', _, _ = ndesc q' in
        if List.mem NoLabel options then
          Printf.fprintf oc "%s -> %s;\n" id id'
        else
          Printf.fprintf oc "%s -> %s [label = \"%s\"];\n" id id' (Label.to_string l) in
    let layout, mindist =
      if List.mem Circular options then "circo", 1.5 else "dot", 1.0 in
    if List.mem Utils.Dot.SubGraph options then 
      Printf.fprintf oc "subgraph cluster_%s {\nlabel = %s;\nrankdir = %s;\nsize = \"8.5,11\";\nlabel = \"%s\"\n center = 1;\n nodesep = \"0.350000\"\n ranksep = \"0.400000\"\n fontsize = 14;\nmindist=\"%1.1f\"\n" name name rankdir name mindist
    else
      Printf.fprintf oc "digraph %s {\nlayout = %s;\nrankdir = %s;\nsize = \"8.5,11\";\nlabel = \"\"\n center = 1;\n nodesep = \"0.350000\"\n ranksep = \"0.400000\"\n fontsize = 14;\nmindist=\"%1.1f\"\n" name layout rankdir mindist;
    Printf.fprintf oc "%s [shape=point; label=\"\"; style = invis]\n" ini_id;
    List.iteri dump_extra_node extra_nodes;
    if List.length extra_nodes > 0 then begin
      (* The followning lines force the corresponding nodes to lie on the same rank *)
        Printf.fprintf oc "%s -> %s [style=invis]\n" ini_id (ListExt.to_string extra_id " -> " extra_idxs);
        Printf.fprintf oc "{rank=same; %s, %s}\n" ini_id (ListExt.to_string extra_id ", " extra_idxs)
        end;
    iter_states dump_state m;
    iter_itransitions dump_itransition m;
    iter_transitions dump_transition m;
    Printf.fprintf oc "}\n"

  let dot_output name ?(fname="") ?(options=[]) ?(marked_states=[]) ?(extra_nodes=[]) ?(implicit_transitions=[]) m = 
    let fname = match fname with "" -> name ^ ".dot" | _ -> fname in
    let oc = open_out fname in
    dot_output_oc
      name oc ~options:options ~marked_states:marked_states ~extra_nodes:extra_nodes ~implicit_transitions:implicit_transitions m


  let dot_output_execs name ?(fname="") ?(options=[]) depth s = 
    let fname = match fname with "" -> name ^ ".dot" | _ -> fname in
    match unwind depth s with
    | [] -> Tree.dot_output name fname Tree.Empty 
    | [t] -> Tree.dot_output name fname ~options:options t
    | _ -> failwith "Ltsa.dot_output_execs: not [yet] supported: output of multi-rooted exec trees"

  let succs_l s q l =
    fold_succs s q (fun s' l' acc -> if L.compare l l' = 0 then (s',l')::acc else acc) []

  let tex_dump oc title s listed_transitions =
    let labels = match listed_transitions with
      None ->
        let module LL = Set.Make(L) in
        LL.elements (D.fold (fun (_,l,_) ls -> LL.add l ls) s.rel LL.empty)
    | Some lbls -> lbls in
    Printf.fprintf oc "%% Generated automatically by Lascar OCaml library\n" ;
    Printf.fprintf oc "$\\begin{array}[c]{|c|";
    List.iter (function l -> Printf.fprintf oc "c|") labels;
    Printf.fprintf oc "}\n";
    Printf.fprintf oc "\\hline\n";
    Printf.fprintf oc "\\text{%s} " title;
    List.iter (function l -> Printf.fprintf oc " & %s" (Label.to_string l)) labels;
    Printf.fprintf oc "\\\\ \\hline\n";
    let dest q l = 
      match succs_l s q l with
        [] -> "-"
      | xs -> ListExt.to_string (function (q,l) -> string_of_state q) "," xs in
    let dump_state q =
      Printf.fprintf oc "%s%s " (if is_init_state s q then "\\rightarrow " else "") (string_of_state q);
      List.iter (function l -> Printf.fprintf oc " & %s" (dest q l)) labels;
      Printf.fprintf oc "\\\\ \\hline\n" in
    Q.iter dump_state s.states;
    Printf.fprintf oc "\\end{array}$\n"

  let tex_output name ?(fname="") ?(listed_transitions=None) a =
    let fname = match fname with "" -> name ^ ".tex" | _ -> fname in
    let oc = open_out fname in
    let title = StringExt.escape_car '_' name in
    tex_dump oc title a listed_transitions;
    close_out oc;
    Printf.printf "Wrote file %s\n" fname

end

module Trans (S1: T) (S2: T) =
struct
  let map fs fl fa s1 =
    let add_states s = S1.fold_states (fun q a acc -> S2.add_state (fs q, fa a) acc) s1 s in
    let add_transitions s = S1.fold_transitions (fun (q,l,q') acc -> S2.add_transition (fs q, fl l, fs q') acc) s1 s in
    let add_itransitions s = S1.fold_itransitions (fun (l,q') acc -> S2.add_itransition (fl l, fs q') acc) s1 s in
    S2.empty |> add_states |> add_transitions |> add_itransitions
end

module Product (S1: T) (S2: T) =
struct
  module S = OrderedTypeExt.Pair (S1.State) (S2.State)
  module L = OrderedTypeExt.Either (S1.Label) (S2.Label)
  module A = Stringable.Pair (S1.Attr) (S2.Attr)
  module R = Make(S)(L)(A)
  include R
  let add_states s1 s2 s =
    let states' = ListExt.prod (fun (q1,a1) (q2,a2) -> (q1,q2),(a1,a2)) (S1.states' s1) (S2.states' s2) in
    List.fold_left (fun acc (q,a) -> R.add_state (q,a) acc) s states'
  let add_transitions sync s1 s2 s =
    let trans' =
      (* [trans' = [ ((q1,q2)--(l1,.)-->(q1',q2) | (q1,l1,q1') \in R1, q2 \in Q2 ]
                 @ [ ((q1,q2)--(.,l2)-->(q1,q2') | (q2,l2,q2') \in R2, q1 \in Q1 ]
                 @ [ ((q1,q2)--(l1,l2)-->(q1',q2') | (q1,l1,q1') \in R1, (q2,l2,q2') \in R2] *)
        List.map (function ((q1,l1,q1'),q2) -> ((q1,q2),(Some l1,None),(q1',q2)))
                 (ListExt.cart_prod2 (S1.transitions s1) (List.map fst (S2.states' s2)))
      @ List.map (function ((q2,l2,q2'),q1) -> ((q1,q2),(None,Some l2),(q1,q2')))
                 (ListExt.cart_prod2 (S2.transitions s2) (List.map fst (S1.states' s1)))
      @ List.map (function ((q1,l1,q1'),(q2,l2,q2')) -> ((q1,q2),(Some l1,Some l2),(q1',q2')))
                 (ListExt.cart_prod2 (S1.transitions s1) (S2.transitions s2)) in
    List.fold_left (fun acc ((q,l,q') as t) -> if sync l then R.add_transition t acc else acc) s trans'
  let add_itransitions s1 s2 s =
    let itrans' =
      (* [itrans' = [ --(l1,l2)-->(q1,q2) | (l1,q1) \in I1, (l2,q2) \in I2 ] *)
      List.map (function ((l1,q1),(l2,q2)) -> ((Some l1,Some l2),(q1,q2)))
                 (ListExt.cart_prod2 (S1.itransitions s1) (S2.itransitions s2)) in
    List.fold_left (fun acc ((l,q) as t) -> R.add_itransition t acc) s itrans'
  let synch_product sync s1 s2 =
    R.empty |> (add_states s1 s2)
            |> (add_transitions sync s1 s2)
            |> (add_itransitions s1 s2)
  let synchronized_product sync_set = synch_product (function l -> List.mem l sync_set)
  let free_product = synch_product (function _ -> true)
  let asynchronous_product = synch_product (function (Some _, None) | (None, Some _)-> true | _ -> false)
  let synchronous_product = synch_product (function (Some _, Some _) -> true | _ -> false)
end

module Product3 (S1: T) (S2: T) (S3: T) =
struct
  module S = OrderedTypeExt.Triplet (S1.State) (S2.State) (S3.State)
  module L = OrderedTypeExt.Either3 (S1.Label) (S2.Label) (S3.Label)
  module A = Stringable.Triplet (S1.Attr) (S2.Attr) (S3.Attr)
  module R = Make(S)(L)(A)
  include R
  let add_states s1 s2 s3 s =
    let states' = ListExt.prod3 (fun (q1,a1) (q2,a2) (q3,a3) -> (q1,q2,q3),(a1,a2,a3)) (S1.states' s1) (S2.states' s2) (S3.states' s3) in
    List.fold_left (fun acc (q,a) -> R.add_state (q,a) acc) s states'
  let add_transitions sync s1 s2 s3 s =
    let states1 = List.map fst (S1.states' s1) in
    let states2 = List.map fst (S2.states' s2) in
    let states3 = List.map fst (S3.states' s3) in
    let trans' =
      (* [trans' = [ ((q1,q2,q3)--(l1,.,.)-->(q1',q2,q3) | (q1,l1,q1') \in R1, q2 \in Q2, q3 \in Q3 ]
                 @ [ ((q1,q2,q3)--(.,l2,.)-->(q1,q2',q3) | (q2,l2,q2') \in R2, q1 \in Q1, q3 \in Q3 ]
                 @ [ ((q1,q2,q3)--(.,.,l3)-->(q1,q2,q3') | (q3,l3,q3') \in R3, q1 \in Q1, q2 \in Q2 ]
                 @ [ ((q1,q2,q3)--(l1,l2,.)-->(q1',q2',q3) | (q1,l1,q1') \in R1, (q2,l2,q2') \in R2, q3 \in Q3 ]
                 @ [ ((q1,q2,q3)--(l1,.,l3)-->(q1',q2,q3') | (q1,l1,q1') \in R1, (q3,l3,q3') \in R3, q2 \in Q2 ]
                 @ [ ((q1,q2,q3)--(.,l2,l3)-->(q1,q2',q3') | (q2,l2,q2') \in R2, (q3,l3,q3') \in R3, q1 \in Q1 ]
                 @ [ ((q1,q2,q3)--(l1,l2,l3)-->(q1',q2',q3') | (q1,l1,q1') \in R1, (q2,l2,q2') \in R2, (q3,l3,q3') ] *)
        List.map (function ((q1,l1,q1'),q2,q3) -> ((q1,q2,q3),(Some l1,None,None),(q1',q2,q3)))
                 (ListExt.cart_prod3 (S1.transitions s1) states2 states3)
      @ List.map (function (q1,(q2,l2,q2'),q3) -> ((q1,q2,q3),(None,Some l2,None),(q1,q2',q3)))
                 (ListExt.cart_prod3 states1 (S2.transitions s2) states3)
      @ List.map (function (q1,q2,(q3,l3,q3')) -> ((q1,q2,q3),(None,None,Some l3),(q1,q2,q3')))
                 (ListExt.cart_prod3 states1 states2 (S3.transitions s3))
      @ List.map (function ((q1,l1,q1'),(q2,l2,q2'),q3) -> ((q1,q2,q3),(Some l1,Some l2,None),(q1',q2',q3)))
                 (ListExt.cart_prod3 (S1.transitions s1) (S2.transitions s2) states3)
      @ List.map (function ((q1,l1,q1'),q2,(q3,l3,q3')) -> ((q1,q2,q3),(Some l1,None,Some l3),(q1',q2,q3')))
                 (ListExt.cart_prod3 (S1.transitions s1) states2 (S3.transitions s3))
      @ List.map (function (q1,(q2,l2,q2'),(q3,l3,q3')) -> ((q1,q2,q3),(None,Some l2,Some l3),(q1,q2',q3')))
                 (ListExt.cart_prod3 states1 (S2.transitions s2) (S3.transitions s3))
      @ List.map (function ((q1,l1,q1'),(q2,l2,q2'),(q3,l3,q3')) -> ((q1,q2,q3),(Some l1,Some l2,Some l3),(q1',q2',q3')))
                 (ListExt.cart_prod3 (S1.transitions s1) (S2.transitions s2) (S3.transitions s3)) in
    List.fold_left (fun acc ((q,l,q') as t) -> if sync l then R.add_transition t acc else acc) s trans'
  let add_itransitions s1 s2 s3 s =
    let itrans' =
      (* [itrans' = [ --(l1,l2,l3)-->(q1,q2,q3) | (l1,q1) \in I1, (l2,q2) \in I2, (l3,q3) \in I3 ] *)
      List.map (function ((l1,q1),(l2,q2),(l3,q3)) -> ((Some l1,Some l2,Some l3),(q1,q2,q3)))
                 (ListExt.cart_prod3 (S1.itransitions s1) (S2.itransitions s2) (S3.itransitions s3)) in
    List.fold_left (fun acc ((l,q) as t) -> R.add_itransition t acc) s itrans'
  let synch_product sync s1 s2 s3 =
    R.empty |> (add_states s1 s2 s3)
            |> (add_transitions sync s1 s2 s3)
            |> (add_itransitions s1 s2 s3)
  let synchronized_product sync_set = synch_product (function l -> List.mem l sync_set)
  let free_product = synch_product (function _ -> true)
  let asynchronous_product = synch_product (function (Some _, None, None) | (None, Some _, None) | (None, None, Some _) -> true | _ -> false)
  let synchronous_product = synch_product (function (Some _, Some _, Some _) -> true | _ -> false)
end


module type Merge = sig
  type state
  type label
  type attr
  val merge_state: state * state -> state
  val merge_label: label option * label option -> label
  val merge_attr: attr * attr -> attr
end
  
module IProduct (S: T) (M: Merge with type state=S.state and type label=S.label and type attr=S.attr) = struct
  include S
  module P = Product(S)(S)
  module U = Trans(P)(S)
  let tr = U.map M.merge_state M.merge_label M.merge_attr
  let free_product s1 s2 = tr (P.free_product s1 s2)
  let synch_product sync s1 s2 = tr (P.synch_product sync s1 s2)
  let synchronized_product sync s1 s2 = tr (P.synchronized_product sync s1 s2)
  let asynchronous_product s1 s2 = tr (P.asynchronous_product s1 s2)
  let synchronous_product s1 s2 = tr (P.synchronous_product s1 s2)
end

module type Merge3 = sig
  type state
  type label
  type attr
  val merge_state: state * state * state -> state
  val merge_label: label option * label option * label option -> label
  val merge_attr: attr * attr * attr -> attr
end
  
module IProduct3 (S: T) (M: Merge3 with type state=S.state and type label=S.label and type attr=S.attr) = struct
  include S
  module P = Product3(S)(S)(S)
  module U = Trans(P)(S)
  let tr = U.map M.merge_state M.merge_label M.merge_attr
  let free_product s1 s2 s3 = tr (P.free_product s1 s2 s3)
  let synch_product sync s1 s2 s3 = tr (P.synch_product sync s1 s2 s3)
  let synchronized_product sync s1 s2 s3 = tr (P.synchronized_product sync s1 s2 s3)
  let asynchronous_product s1 s2 s3 = tr (P.asynchronous_product s1 s2 s3)
  let synchronous_product s1 s2 s3 = tr (P.synchronous_product s1 s2 s3)
end