From 171516875707462e62fca17ea206798beae175c8 Mon Sep 17 00:00:00 2001
From: Erwan Jahier <erwan.jahier@univ-grenoble-alpes.fr>
Date: Mon, 10 Jul 2023 11:51:17 +0200
Subject: [PATCH] test: add the Hoepman ring orientation algo (not yet
 finished: the legitimate predicate is not rigth)

---
 salut/test/ring-orientation/Makefile          |  34 ++++++
 salut/test/ring-orientation/Makefile.dot      |   1 +
 salut/test/ring-orientation/Makefile.inc      |   1 +
 salut/test/ring-orientation/config.ml         |   1 +
 salut/test/ring-orientation/dune              |   1 +
 salut/test/ring-orientation/dune-project      |   1 +
 salut/test/ring-orientation/dune-workspace    |   0
 salut/test/ring-orientation/p.lus             | 115 ++++++++++++++++++
 salut/test/ring-orientation/p.ml              |   1 +
 .../ring_orientation_oracle.lus               |  65 ++++++++++
 salut/test/ring-orientation/state.lus         |  74 +++++++++++
 salut/test/ring-orientation/state.ml          |   1 +
 salut/test/ring-orientation/verify.lus        |  28 +++++
 test/ring-orientation/Makefile                |  23 ++++
 test/ring-orientation/Makefile.dot            |   1 +
 test/ring-orientation/Makefile.inc            |   1 +
 test/ring-orientation/config.ml               |   6 +
 test/ring-orientation/dune                    |  59 +++++++++
 test/ring-orientation/dune-project            |   1 +
 test/ring-orientation/dune-workspace          |   0
 test/ring-orientation/p.ml                    | 109 +++++++++++++++++
 test/ring-orientation/some_session            |   7 ++
 test/ring-orientation/state.ml                |  15 +++
 23 files changed, 545 insertions(+)
 create mode 100644 salut/test/ring-orientation/Makefile
 create mode 120000 salut/test/ring-orientation/Makefile.dot
 create mode 120000 salut/test/ring-orientation/Makefile.inc
 create mode 120000 salut/test/ring-orientation/config.ml
 create mode 120000 salut/test/ring-orientation/dune
 create mode 120000 salut/test/ring-orientation/dune-project
 create mode 100644 salut/test/ring-orientation/dune-workspace
 create mode 100644 salut/test/ring-orientation/p.lus
 create mode 120000 salut/test/ring-orientation/p.ml
 create mode 100644 salut/test/ring-orientation/ring_orientation_oracle.lus
 create mode 100644 salut/test/ring-orientation/state.lus
 create mode 120000 salut/test/ring-orientation/state.ml
 create mode 100644 salut/test/ring-orientation/verify.lus
 create mode 100644 test/ring-orientation/Makefile
 create mode 120000 test/ring-orientation/Makefile.dot
 create mode 120000 test/ring-orientation/Makefile.inc
 create mode 100644 test/ring-orientation/config.ml
 create mode 100644 test/ring-orientation/dune
 create mode 120000 test/ring-orientation/dune-project
 create mode 100644 test/ring-orientation/dune-workspace
 create mode 100644 test/ring-orientation/p.ml
 create mode 100644 test/ring-orientation/some_session
 create mode 100644 test/ring-orientation/state.ml

diff --git a/salut/test/ring-orientation/Makefile b/salut/test/ring-orientation/Makefile
new file mode 100644
index 00000000..3949e640
--- /dev/null
+++ b/salut/test/ring-orientation/Makefile
@@ -0,0 +1,34 @@
+TOPOLOGY ?= ring5
+DOT2LUSFLAGS ?=
+SASA_ALGOS := p.ml
+SASAFLAGS += --central-daemon
+DECO_PATTERN="0-:p.ml"
+
+
+include ./Makefile.inc
+include ./Makefile.dot
+
+##############################################################################
+# Non-regression tests
+
+test: kind2-test ring5.lurette  ring15.lurette
+
+clean: genclean
+
+kind2-test:
+	../run-kind2.sh ring 3 ok central uint8 Bitwuzla
+
+##############################################################################
+# Other examples of use
+
+## Some examples of use of ../Makefile.inc
+
+# run a simulation with luciole
+simu: ring7.simu
+
+# make diring4.simu
+kind2: ring7.kind2
+
+##############################################################################
+
+-include ./Makefile.untrack
diff --git a/salut/test/ring-orientation/Makefile.dot b/salut/test/ring-orientation/Makefile.dot
new file mode 120000
index 00000000..618c8ab0
--- /dev/null
+++ b/salut/test/ring-orientation/Makefile.dot
@@ -0,0 +1 @@
+../Makefile.dot
\ No newline at end of file
diff --git a/salut/test/ring-orientation/Makefile.inc b/salut/test/ring-orientation/Makefile.inc
new file mode 120000
index 00000000..b9a83c9d
--- /dev/null
+++ b/salut/test/ring-orientation/Makefile.inc
@@ -0,0 +1 @@
+../Makefile.inc
\ No newline at end of file
diff --git a/salut/test/ring-orientation/config.ml b/salut/test/ring-orientation/config.ml
new file mode 120000
index 00000000..25afd9a8
--- /dev/null
+++ b/salut/test/ring-orientation/config.ml
@@ -0,0 +1 @@
+../../../test/ring-orientation/config.ml
\ No newline at end of file
diff --git a/salut/test/ring-orientation/dune b/salut/test/ring-orientation/dune
new file mode 120000
index 00000000..a1ef974a
--- /dev/null
+++ b/salut/test/ring-orientation/dune
@@ -0,0 +1 @@
+../../../test/dune2copy
\ No newline at end of file
diff --git a/salut/test/ring-orientation/dune-project b/salut/test/ring-orientation/dune-project
new file mode 120000
index 00000000..f539fb46
--- /dev/null
+++ b/salut/test/ring-orientation/dune-project
@@ -0,0 +1 @@
+../../../test/dune-project2copy
\ No newline at end of file
diff --git a/salut/test/ring-orientation/dune-workspace b/salut/test/ring-orientation/dune-workspace
new file mode 100644
index 00000000..e69de29b
diff --git a/salut/test/ring-orientation/p.lus b/salut/test/ring-orientation/p.lus
new file mode 100644
index 00000000..5e57a8b0
--- /dev/null
+++ b/salut/test/ring-orientation/p.lus
@@ -0,0 +1,115 @@
+-- Time-stamp: <modified the 22/06/2023 (at 16:05) by Erwan Jahier>
+
+-----------------------------------------------------------
+--Step and Enable functions:
+function p_enable<<const degree:int>>(q : state; neighbors : neigh^degree)
+returns (enabled : bool^actions_number);
+var
+  na : state^degree;
+  p, r : state;
+let
+  na = map<<state,degree>>(neighbors);
+  p = na[0];
+  r = na[1];
+
+
+  enabled = [
+  -- A (0)
+      p.color_0 and     q.color_0 and     r.color_0,
+  -- B (1)
+      p.color_0 and not q.color_0  and    r.color_0
+     and  q.phase_pos, -- not in Figure 3, but necessary to converge!
+  -- C (2)
+  not p.color_0 and not q.color_0 and not r.color_0,
+  --D (3)
+  not p.color_0 and     q.color_0 and not r.color_0
+  and q.phase_pos , -- ditto
+
+  -- Epr (4)
+   (     p.color_0 and     p.phase_pos and
+          q.color_0 and not q.phase_pos and
+      not r.color_0 and not r.phase_pos),
+
+  -- Erp (5)
+  ((not p.color_0 and not p.phase_pos and
+          q.color_0 and not q.phase_pos and
+          r.color_0 and     r.phase_pos)),
+
+  -- Fpr (6)
+   ( not p.color_0 and     p.phase_pos and
+      not q.color_0 and not q.phase_pos and
+          r.color_0 and not r.phase_pos),
+
+  -- Frp (7)
+  ((    p.color_0 and not p.phase_pos and
+      not q.color_0 and not q.phase_pos and
+      not r.color_0 and     r.phase_pos)),
+
+  -- G
+  ( (     p.color_0 and not p.phase_pos and
+          q.color_0 and not q.phase_pos and
+      not r.color_0       )
+ or ((not p.color_0 and
+          q.color_0 and not q.phase_pos and
+          r.color_0 and not r.phase_pos))),
+  -- H
+  ( (     p.color_0 and     p.phase_pos and
+          q.color_0 and     q.phase_pos and
+      not r.color_0       )
+ or ((not p.color_0 and
+          q.color_0 and     q.phase_pos and
+          r.color_0 and     r.phase_pos))),
+  -- I
+  ( ( not p.color_0 and not p.phase_pos and
+      not q.color_0 and not q.phase_pos and
+          r.color_0       )
+ or ((    p.color_0 and
+      not q.color_0 and not q.phase_pos and
+      not r.color_0 and not r.phase_pos))),
+  -- J
+  ( ( not p.color_0 and     p.phase_pos and
+      not q.color_0 and     q.phase_pos and
+          r.color_0       )
+ or ((    p.color_0 and
+      not q.color_0 and     q.phase_pos and
+      not r.color_0 and     r.phase_pos)))
+];
+tel;
+
+function p_step<<const d:int>>(
+  q : state;
+  neighbors : neigh^d;
+  a : action)
+returns (new_q : state);
+var
+  na : state^d;
+  p, r : state;
+let
+  na = map<<state,d>>(neighbors);
+  p = na[0];
+  r = na[1];
+  new_q = if a = A then state { color_0 = false ; phase_pos = false ; orient_left = q.orient_left }
+     else if a = B then state { color_0 = false ; phase_pos = false ; orient_left = q.orient_left }
+     else if a = C then state { color_0 = true  ; phase_pos = false ; orient_left = q.orient_left }
+     else if a = D then state { color_0 = true  ; phase_pos = false ; orient_left = q.orient_left }
+
+     else if a = Epr then state { color_0 = false ; phase_pos = true  ; orient_left = orient(p,r) }
+     else if a = Erp then state { color_0 = false ; phase_pos = true  ; orient_left = orient(r,p) }
+     else if a = Fpr then state { color_0 = true  ; phase_pos = true  ; orient_left = orient(p,r) }
+     else if a = Frp then state { color_0 = true  ; phase_pos = true  ; orient_left = orient(r,p) }
+
+     else if a = G then state { color_0 = true  ; phase_pos = true  ; orient_left = q.orient_left }
+     else if a = H then state { color_0 = true  ; phase_pos = false ; orient_left = q.orient_left }
+     else if a = I then state { color_0 = false ; phase_pos = true  ; orient_left = q.orient_left }
+     else (* a = J *)   state { color_0 = false ; phase_pos = false ; orient_left = q.orient_left };
+tel;
+
+function orient(p,r : state) returns (res : bool);
+let
+  res = if     p.phase_pos and not r.phase_pos then true else false;
+ --       if not p.phase_pos and     r.phase_pos then false else
+ --       if     p.phase_pos and     r.phase_pos then false else
+ --       (* not p.phase_pos and not r.phase_pos *)   false
+
+
+tel;
\ No newline at end of file
diff --git a/salut/test/ring-orientation/p.ml b/salut/test/ring-orientation/p.ml
new file mode 120000
index 00000000..c3e34af2
--- /dev/null
+++ b/salut/test/ring-orientation/p.ml
@@ -0,0 +1 @@
+../../../test/ring-orientation/p.ml
\ No newline at end of file
diff --git a/salut/test/ring-orientation/ring_orientation_oracle.lus b/salut/test/ring-orientation/ring_orientation_oracle.lus
new file mode 100644
index 00000000..374b7a3f
--- /dev/null
+++ b/salut/test/ring-orientation/ring_orientation_oracle.lus
@@ -0,0 +1,65 @@
+
+
+include "../../lib/utils.lus"
+
+
+node ring_orientation_oracle(
+  legitimate : bool;
+--  ocaml_cost : real;
+  ocaml_enabled : bool^actions_number^card;
+  active : bool^actions_number^card;
+  ocaml_config : state^card;
+  round:bool;
+  round_nb:int;
+)
+returns (ok : bool);
+var
+  lustre_config : state^card;
+  lustre_enabled : bool^actions_number^card;
+--  lustre_cost:int;
+  lustre_round:bool;
+  lustre_round_nb:int;
+let
+  ok, lustre_config, lustre_enabled, lustre_round, lustre_round_nb =
+     ring_orientation_oracle_debug(legitimate, ocaml_enabled,active, ocaml_config, round, round_nb);
+
+tel;
+
+node ring_orientation_oracle_debug(
+  legitimate : bool;
+--  ocaml_cost : real;
+  ocaml_enabled : bool^actions_number^card;
+  active : bool^actions_number^card;
+  ocaml_config : state^card;
+  round:bool;
+  round_nb:int;
+)
+returns (ok : bool;
+  lustre_config : state^card;
+  lustre_enabled : bool^actions_number^card;
+--  lustre_cost:int;
+  lustre_round:bool;
+  lustre_round_nb:int
+);
+
+
+let
+  lustre_config, lustre_enabled, lustre_round, lustre_round_nb =
+    topology(active -> pre active, -- ignored at the first step
+             ocaml_config -- used at the first step only
+             );
+
+  ok = lustre_enabled = ocaml_enabled
+       -- compare the sasa dot interpretation and the salut dot to lustre compilation
+       and lustre_config = ocaml_config
+
+   --    and round = lustre_round
+   --    and round_nb = lustre_round_nb
+;
+tel
+
+function to_state(color_0, phase_pos, orient_left :bool)
+returns ( res : state);
+let
+  res = state {  color_0 = color_0; phase_pos = phase_pos; orient_left = orient_left };
+tel;
diff --git a/salut/test/ring-orientation/state.lus b/salut/test/ring-orientation/state.lus
new file mode 100644
index 00000000..3e3aaf2c
--- /dev/null
+++ b/salut/test/ring-orientation/state.lus
@@ -0,0 +1,74 @@
+-- Time-stamp: <modified the 03/07/2023 (at 16:46) by Erwan Jahier>
+
+type state = { color_0 : bool ; phase_pos : bool ; orient_left : bool };
+
+type action = enum { A , B , C , D , Epr , Erp , Fpr , Frp , G , H , I , J };
+const actions_number = 12;
+
+function action_of_int(i : int) returns (a : action);
+let
+  a = if i = 0 then A else
+      if i = 1 then B else
+      if i = 2 then C else
+      if i = 3 then D else
+      if i = 4 then Epr else
+      if i = 5 then Erp else
+      if i = 6 then Fpr else
+      if i = 7 then Frp else
+      if i = 8 then G else
+      if i = 9 then H else
+      if i = 10 then I else J;
+tel;
+
+-- This legitimate function is not good enough to be able to prove closure
+-- Indeed, the orientation field migth be oriented by chance, while the other
+-- fields (that are used in guards) are not in a legitimate configuration.
+-- Adding  L3 property (p16) in the definition of legitimate should be enough
+-- to fix this problem.
+function legitimate<<const actions_number:int; const card:int>>(
+         enables : bool^actions_number^card; config: state^card)
+returns (res : bool);
+var
+  orientations : bool ^ card;
+  one_token : bool;
+let
+  orientations = map<<get_orientation,card>>(config);
+  one_token = nary_xor<<card>>(map<<nary_or<<actions_number>>,card>> (enables));
+  res = one_token and all_equal <<bool,card>>(orientations);
+tel;
+function get_orientation(s:state) returns (res:bool);
+let
+  res = s.orient_left;
+tel;
+
+(* XXX finish me
+function L3 <<const an:int;const card:int;const n:int>>(config: state^card) returns (res : bool);
+let
+  res = with n = 0
+        then true
+        else L3<<an,card,n>>
+tel;
+function I3 <<const an:int;const n:int>>(config: state^card) returns (res : bool);
+let
+  res = with n =
+        then ???
+        else
+            zl(config[n])
+
+tel;
+function O3 <<const an:int;const n:int>>(config: state^card) returns (res : bool);
+let
+  res = with n = 0
+        then ???
+        else
+tel;
+
+-- zero left
+function zl(s:state)  returns (res:bool); let res = s.color_0 and s.orient_left tel;
+function zpl(s:state) returns (res:bool); let res = zl(s) and s.phase_pos tel;
+function zml(s:state) returns (res:bool); let res = zl(s) and not s.phase_pos tel;
+-- one left
+function ol(s:state)  returns (res:bool); let res = (not s.color_0) and s.orient_left tel;
+function opl(s:state) returns (res:bool); let res = ol(s) and s.phase_pos tel;
+function oml(s:state) returns (res:bool); let res = ol(s) and not s.phase_pos tel;
+*)
\ No newline at end of file
diff --git a/salut/test/ring-orientation/state.ml b/salut/test/ring-orientation/state.ml
new file mode 120000
index 00000000..b648093a
--- /dev/null
+++ b/salut/test/ring-orientation/state.ml
@@ -0,0 +1 @@
+../../../test/ring-orientation/state.ml
\ No newline at end of file
diff --git a/salut/test/ring-orientation/verify.lus b/salut/test/ring-orientation/verify.lus
new file mode 100644
index 00000000..48685d9f
--- /dev/null
+++ b/salut/test/ring-orientation/verify.lus
@@ -0,0 +1,28 @@
+-- hint: call me with ../run-kind2.sh
+include "daemon_hyp.lus"
+
+const worst_case = card * card;
+node verify(active : bool^actions_number^card; init_config : state^card)
+returns (ok : bool);
+var
+  config : state^card;
+  enabled : bool^actions_number^card;
+  legitimate, closure, converge_worst_case,worst_case_is_tigth : bool;
+  steps : int;
+  round:bool; round_nb:int;
+let
+  assert(true -> daemon<<actions_number,card>> (active, pre enabled));
+  assert(config = init_config -> true);
+  steps = 0 -> (pre(steps) + 1);
+  config, enabled, round, round_nb = topology(active, init_config);
+  -- stability = mutual exclusion: exactly 1 node is enabled at a time
+  legitimate = legitimate<<actions_number,card>>(enabled,config);
+
+  closure = steps > 1 => (pre(legitimate) => legitimate);
+
+  converge_worst_case = (round_nb >= worst_case) => legitimate;
+  worst_case_is_tigth = (round_nb >= worst_case-1) => legitimate;
+  ok =
+    -- closure is wrong because legitimate needs to be more specific (cf state.lus)
+    converge_worst_case ; -- closure and;
+tel;
\ No newline at end of file
diff --git a/test/ring-orientation/Makefile b/test/ring-orientation/Makefile
new file mode 100644
index 00000000..c6838f11
--- /dev/null
+++ b/test/ring-orientation/Makefile
@@ -0,0 +1,23 @@
+# Time-stamp: <modified the 21/06/2023 (at 12:04) by Erwan Jahier>
+
+DECO_PATTERN="0-:p.ml"
+-include ./Makefile.dot
+-include ./Makefile.inc
+
+##############################################################################
+# Non-regression tests
+
+test:  grid5.gm_test grid5.rdbg-test clean
+
+# update golden-master tests (if necessary)
+utest:
+	make grid5.ugm_test || echo "grid4 ok"
+
+##############################################################################
+# Other examples of use
+
+rdbg: grid5.dot grid5.ml
+	rdbg --sasa -sut "sasa grid5.dot"
+
+clean: genclean
+	rm -f grid*.* diring*.* clique*.* er*.*
diff --git a/test/ring-orientation/Makefile.dot b/test/ring-orientation/Makefile.dot
new file mode 120000
index 00000000..618c8ab0
--- /dev/null
+++ b/test/ring-orientation/Makefile.dot
@@ -0,0 +1 @@
+../Makefile.dot
\ No newline at end of file
diff --git a/test/ring-orientation/Makefile.inc b/test/ring-orientation/Makefile.inc
new file mode 120000
index 00000000..b9a83c9d
--- /dev/null
+++ b/test/ring-orientation/Makefile.inc
@@ -0,0 +1 @@
+../Makefile.inc
\ No newline at end of file
diff --git a/test/ring-orientation/config.ml b/test/ring-orientation/config.ml
new file mode 100644
index 00000000..54c8032f
--- /dev/null
+++ b/test/ring-orientation/config.ml
@@ -0,0 +1,6 @@
+
+let potential = None (* None => only -sd, -cd, -lcd, -dd, or -custd are possible *)
+let fault = None (* None => the simulation stop once a legitimate configuration is reached *)
+let init_search_utils = None (* To provide to use --init-search *)
+
+let legitimate = None (* XXX this is wrong: FIXME *)
diff --git a/test/ring-orientation/dune b/test/ring-orientation/dune
new file mode 100644
index 00000000..70215208
--- /dev/null
+++ b/test/ring-orientation/dune
@@ -0,0 +1,59 @@
+;; dune file to build the cmxs (resp the cma) needed by sasa (resp rdbgui4sasa)
+
+(library
+ (name user_algo_files) ;; a fake name because "%{env:topology=ring4}" is rejected
+ (libraries algo)
+; (wrapped false) ; so that user_algo_files is not used in the .cm*
+ (library_flags -linkall)
+)
+
+;; copy the <topology>.dot to the build dir if it already exists
+;; or create it via make otherwise
+(rule
+ (mode (promote (until-clean)))
+ (target %{env:topology=ring4}.dot)
+ (deps
+  (:makefile Makefile)
+  (:makefiledot Makefile.dot)
+ )
+ (action
+  (bash "[ -f ../../%{env:topology=ring4}.dot ] && cp ../../%{env:topology=ring4}.dot . || \
+            make %{env:topology=ring4}.dot")
+ )
+)
+
+;; generate the registration file (out of the dot file)
+(rule
+ (target %{env:topology=ring4}.ml)
+ (deps (:dotfile ./%{env:topology=ring4}.dot))
+ (action
+  (bash "sasa -reg %{dotfile}")
+ )
+)
+
+(rule
+ (mode (promote (until-clean)))
+ (action
+  (progn
+    ;; give them back the rigth name
+    (copy user_algo_files.cmxs %{env:topology=ring4}.cmxs)
+    (copy user_algo_files.cmxa  %{env:topology=ring4}.cmxa)
+    (copy user_algo_files.cma  %{env:topology=ring4}.cma)
+    (copy user_algo_files.a  %{env:topology=ring4}.a)
+    ;; we can have only one registering file in the build dir
+    (bash "rm %{env:topology=ring4}.ml")
+   )
+ )
+ (deps (:src ./%{env:topology=ring4}.dot))
+)
+
+;; The glue lustre code between the oracle and the topology
+(rule
+ (target %{env:topology=ring4}_oracle.lus)
+ (mode (promote (until-clean)))
+ (deps
+  (:dotfile ./%{env:topology=ring4}.dot)
+  (:cmxs ./%{env:topology=ring4}.cmxs)
+  )
+ (action (bash "sasa -glos %{dotfile} -o $(basename $(realpath $PWD/../..))"))
+ )
diff --git a/test/ring-orientation/dune-project b/test/ring-orientation/dune-project
new file mode 120000
index 00000000..1c5c1f50
--- /dev/null
+++ b/test/ring-orientation/dune-project
@@ -0,0 +1 @@
+../dune-project2copy
\ No newline at end of file
diff --git a/test/ring-orientation/dune-workspace b/test/ring-orientation/dune-workspace
new file mode 100644
index 00000000..e69de29b
diff --git a/test/ring-orientation/p.ml b/test/ring-orientation/p.ml
new file mode 100644
index 00000000..fbbc86c2
--- /dev/null
+++ b/test/ring-orientation/p.ml
@@ -0,0 +1,109 @@
+(* Time-stamp: <modified the 07/07/2023 (at 15:24) by Erwan Jahier> *)
+
+(* Uniform deterministic self-stabilizing ring-orientation on odd-length rings
+  Hoepman, Jaap-Henk
+  WDAG'1994
+
+  hyp: central daemon
+ *)
+
+open Algo
+open State
+
+let (init_state: int -> string -> 'st) =
+  fun _nl _ ->
+  { color_0 = Random.bool () ; phase_pos = Random.bool () ; orient_left =  Random.bool () }
+
+let (enable_f: 'st -> 'st neighbor list -> action list) =
+  fun q nl ->
+  assert (card() mod 2 = 1); (* Odd rings only *)
+    match List.map state nl with
+    |  [p; r]  -> (match p,q,r with
+        | { color_0 = true  ; _ }, { color_0 = true  ; _ },                    { color_0 = true  ; _ } -> ["a"]
+        | { color_0 = true  ; _ }, { color_0 = false ; phase_pos = true ; _ }, { color_0 = true  ; _ } -> ["b"]
+        | { color_0 = false ; _ }, { color_0 = false ; _ },                    { color_0 = false ; _ } -> ["c"]
+        | { color_0 = false ; _ }, { color_0 = true  ; phase_pos = true; _  }, { color_0 = false ; _ } -> ["d"]
+
+        | { color_0 = true  ; phase_pos = true ; _ },
+          { color_0 = true  ; phase_pos = false ; _ },
+          { color_0 = false ; phase_pos = false ; _ }  -> ["e_pr"]
+
+        | { color_0 = false ; phase_pos = false ; _ },
+          { color_0 = true  ; phase_pos = false ; _ },
+          { color_0 = true  ; phase_pos = true ; _ } -> ["e_rp"]
+
+        | { color_0 = false ; phase_pos = true ; _ },
+          { color_0 = false ; phase_pos = false ; _ },
+          { color_0 = true  ; phase_pos = false ; _ } -> ["f_pr"]
+
+        | { color_0 = true  ; phase_pos = false ; _ },
+          { color_0 = false ; phase_pos = false ; _ },
+           { color_0 = false ; phase_pos = true ; _ } -> ["f_rp"]
+
+        | { color_0 = true  ; phase_pos = false ; _ },
+          { color_0 = true  ; phase_pos = false ; _ },
+          { color_0 = false ;  _ }
+        | { color_0 = false ;  _ },
+          { color_0 = true  ; phase_pos = false ; _ },
+          { color_0 = true  ; phase_pos = false ; _ } -> ["g"]
+
+        | { color_0 = true  ; phase_pos = true ; _ },
+          { color_0 = true  ; phase_pos = true ; _ },
+          { color_0 = false ;  _ }
+        | { color_0 = false ;  _ },
+          { color_0 = true  ; phase_pos = true ; _ },
+          { color_0 = true  ; phase_pos = true ; _ } -> ["h"]
+
+        | { color_0 = false  ; phase_pos = false ; _ },
+          { color_0 = false  ; phase_pos = false ; _ },
+          { color_0 = true ; _ }
+        | { color_0 = true ; _ },
+          { color_0 = false  ; phase_pos = false ; _ },
+          { color_0 = false  ; phase_pos = false ; _ } -> ["i"]
+
+        | { color_0 = false  ; phase_pos = true ; _ },
+          { color_0 = false  ; phase_pos = true ; _ },
+          { color_0 = true ; _ }
+        | { color_0 = true ; _ },
+          { color_0 = false  ; phase_pos = true ; _ },
+          { color_0 = false  ; phase_pos = true ; _ } -> ["j"]
+
+        | _ -> []
+      )
+    | _ -> failwith "only works on rings"
+
+(*        | { color_0 = cp ; phase_pos = pp ; _ },
+          { color_0 = cq ; phase_pos = pq ; _ },
+          { color_0 = cr ; phase_pos = pr ; _ } ->
+           let c2s c = if c then "0" else "1" in
+          let p2s c = if c then "+" else "-" in
+          failwith (Printf.sprintf "Missing case: %s%s %s%s %s%s "
+                      (c2s cp) (p2s pp) (c2s cq) (p2s pq) (c2s cr) (p2s pr) )
+
+ *)
+
+let orientation = function
+  | [  { phase_pos = true ; _} ; { phase_pos = false; _ }  ] -> true
+  | [  { phase_pos = false ; _} ; { phase_pos = true ; _ } ] -> false
+  | [  { phase_pos = true ; _} ; { phase_pos = true; _ } ] -> assert false
+  | [  { phase_pos = false ; _} ; { phase_pos = false; _ } ] -> assert false
+  | _ -> assert false
+
+let (step_f : 'st -> 'st neighbor list -> action -> 'st ) =
+  fun e nl ->
+  function
+  | "a" -> { e with color_0 = false ; phase_pos = false }
+  | "b" -> { e with color_0 = false ; phase_pos = false }
+  | "c" -> { e with color_0 = true ; phase_pos = false }
+  | "d" -> { e with color_0 = true ; phase_pos = false }
+
+  | "e_pr" -> { color_0 = false ; phase_pos = true ; orient_left = orientation (List.map state nl) }
+  | "e_rp" -> { color_0 = false ; phase_pos = true ; orient_left = orientation (List.map state nl |> List.rev) }
+  | "f_pr" -> { color_0 = true ; phase_pos = true  ; orient_left = orientation (List.map state nl) }
+  | "f_rp" -> { color_0 = true ; phase_pos = true  ; orient_left = orientation (List.map state nl |> List.rev) }
+
+  | "g" -> { e with color_0 = true ; phase_pos = true }
+  | "h" -> { e with color_0 = true ; phase_pos = false }
+  | "i" -> { e with color_0 = false ; phase_pos = true }
+  | "j" -> { e with color_0 = false ; phase_pos = false }
+  | _ -> e
diff --git a/test/ring-orientation/some_session b/test/ring-orientation/some_session
new file mode 100644
index 00000000..96b69b57
--- /dev/null
+++ b/test/ring-orientation/some_session
@@ -0,0 +1,7 @@
+n
+n
+n
+n
+wait 3
+print_event !e;;
+
diff --git a/test/ring-orientation/state.ml b/test/ring-orientation/state.ml
new file mode 100644
index 00000000..2f4a722b
--- /dev/null
+++ b/test/ring-orientation/state.ml
@@ -0,0 +1,15 @@
+(* Time-stamp: <modified the 29/06/2023 (at 16:45) by Erwan Jahier> *)
+
+type t = { color_0 : bool ; phase_pos : bool ; orient_left : bool }
+
+let to_string s =
+  Printf.sprintf "color_0=%b phase_pos=%b orient_left=%b" s.color_0 s.phase_pos s.orient_left
+
+let (of_string: (string -> t) option) =
+  Some (fun s ->
+      Scanf.sscanf s "color_0=%B phase_pos=%B orient_left=%B"
+        (fun color_0 phase_pos orient_left -> { color_0 ; phase_pos ; orient_left  }))
+
+let copy x = x
+
+let actions = ["a";"b";"c";"d";"e_pr";"e_rp";"f_pr";"f_rp";"g";"h";"i";"j"]
-- 
GitLab