diff --git a/examples/README.md b/examples/README.md
index 7835502ad..0e8c954b0 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -49,6 +49,9 @@ listed without any additional command line arguments.
| Example | Syntax (`parse`) | Types (`typecheck`) | Unit tests (`test`) | Apalache (`verify`) |
|---------|:----------------:|:-------------------:|:-------------------:|:-------------------:|
| [classic/distributed/ClockSync/clockSync3.qnt](./classic/distributed/ClockSync/clockSync3.qnt) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
+| [classic/distributed/ewd426/ewd426.qnt](./classic/distributed/ewd426/ewd426.qnt) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
+| [classic/distributed/ewd426/ewd426_3.qnt](./classic/distributed/ewd426/ewd426_3.qnt) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
+| [classic/distributed/ewd426/ewd426_4.qnt](./classic/distributed/ewd426/ewd426_4.qnt) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| [classic/distributed/ewd840/ewd840.qnt](./classic/distributed/ewd840/ewd840.qnt) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| [classic/distributed/LamportMutex/LamportMutex.qnt](./classic/distributed/LamportMutex/LamportMutex.qnt) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| [classic/distributed/Paxos/Paxos.qnt](./classic/distributed/Paxos/Paxos.qnt) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x:https://github.com/informalsystems/quint/issues/1284 |
diff --git a/examples/classic/distributed/ewd426/README.md b/examples/classic/distributed/ewd426/README.md
new file mode 100644
index 000000000..1fec43c4e
--- /dev/null
+++ b/examples/classic/distributed/ewd426/README.md
@@ -0,0 +1,30 @@
+# EWD426
+
+This folder contains Quint specifications for the three different self-stabilization algorithms proposed by Dijkstra in [EWD426](https://www.cs.utexas.edu/~EWD/transcriptions/EWD04xx/EWD426.html).
+1. A solution with K-state machines [ewd426.qnt](ewd426.qnt)
+2. A solution with three-state machines [ewd426_3.qnt](ewd426_3.qnt)
+3. A solution with four-state machines [ewd426_4.qnt](ewd426_4.qnt)
+
+Due to the presence of temporal properties and fairness, we need to use TLC to model check these specs. As the integration with TLC is not completed, we provide a script to automate running it for these specific specifications: [check_with_tlc.sh](check_with_tlc.sh).
+
+If you are trying to learn/understand these algorithms, we recommend playing with the Quint REPL. For that, pick one of the files (for example [ewd426.qnt](ewd426.qnt)) and run the following command in the terminal:
+``` sh
+quint -r ewd426.qnt::ewd426
+```
+
+This will open the REPL with the `ewd426` machine loaded. You can now interact with the machine and explore its states and transitions:
+
+``` bluespec
+Quint REPL 0.22.4
+Type ".exit" to exit, or ".help" for more information
+>>> init
+true
+>>> step
+true
+>>> show_token(system)
+Map(0 -> false, 1 -> true, 2 -> true, 3 -> false, 4 -> true, 5 -> false)
+>>> 5.reps(_ => step)
+true
+>>> show_token(system)
+Map(0 -> true, 1 -> false, 2 -> false, 3 -> false, 4 -> false, 5 -> false)
+```
diff --git a/examples/classic/distributed/ewd426/check_with_tlc.sh b/examples/classic/distributed/ewd426/check_with_tlc.sh
new file mode 100644
index 000000000..0314a81a2
--- /dev/null
+++ b/examples/classic/distributed/ewd426/check_with_tlc.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+# Files to process
+FILES=("ewd426.qnt" "ewd426_3.qnt" "ewd426_4.qnt")
+
+# Apalache jar path
+APALACHE_JAR="$HOME/.quint/apalache-dist-0.47.2/apalache/lib/apalache.jar"
+
+# Memory options for Java
+JAVA_OPTS="-Xmx8G -Xss515m"
+
+for file in "${FILES[@]}"; do
+ base_name="${file%.qnt}"
+ tla_file="${base_name}.tla"
+ cfg_file="${base_name}.cfg"
+
+ echo "Processing $file..."
+
+ # Step 1: Compile the .qnt file to .tla
+ quint compile --target=tlaplus "$file" --temporal=convergence,closure,persistence --invariant=tokenInv > "$tla_file"
+
+ if [[ $? -ne 0 ]]; then
+ echo "Error: Compilation failed for $file"
+ exit 1
+ fi
+
+ # Step 2: Fix init to be a predicate instead of an assignment
+ perl -0777 -i -pe "s/(.*)'\s+:= (.*_self_stabilization_.*?initial)/\1 = \2/s" "$tla_file"
+
+
+ if [[ $? -ne 0 ]]; then
+ echo "Error: Failed to edit $tla_file"
+ exit 1
+ fi
+
+ # Step 3: Create the .cfg file
+ cat < "$cfg_file"
+INIT
+q_init
+
+NEXT
+q_step
+
+PROPERTY
+q_temporalProps
+
+INVARIANT
+q_inv
+EOF
+
+ # Step 4: Run TLC with the required Apalache lib files and check output
+ output=$(java $JAVA_OPTS -cp "$APALACHE_JAR" tlc2.TLC -deadlock "$tla_file" 2>&1)
+
+ # Step 5: Delete the generated .tla and .cfg files
+ rm -f "$tla_file" "$cfg_file"
+
+ if echo "$output" | grep -q "Model checking completed. No error has been found."; then
+ echo "Model checking succeeded for $tla_file"
+ else
+ echo "Error: Model checking failed for $tla_file"
+ echo "$output"
+ exit 1
+ fi
+
+done
+
+echo "All files processed successfully."
diff --git a/examples/classic/distributed/ewd426/ewd426.qnt b/examples/classic/distributed/ewd426/ewd426.qnt
new file mode 100644
index 000000000..9607c645d
--- /dev/null
+++ b/examples/classic/distributed/ewd426/ewd426.qnt
@@ -0,0 +1,85 @@
+// -*- mode: Bluespec; -*-
+/**
+ * ewd426's Stabilizing Token Ring (EWD426)
+ * K state machine
+ * This implementation ensures that from some time on,
+ * exactly one token circulates in a set of nodes,
+ *
+ * Mahtab Norouzi, Josef Widder, Informal Systems, 2024-2025
+ */
+module self_stabilization {
+ // Number of nodes in the ring
+ const N: int
+ const K: int
+
+ /// Ensures the state space is larger than the number of nodes
+ assume _ = K >= N
+
+ val bottom = 0
+ val top = N
+
+ /// Mapping of node indices to their states
+ var system: int -> int
+
+ /// Check if a node has the token
+ pure def has_token(nodes: int -> int, index: int): bool =
+ if (index == bottom)
+ nodes.get(bottom) == nodes.get(top)
+ else
+ not(nodes.get(index) == nodes.get(index - 1))
+
+ /// Update the state of a specific node
+ pure def state_transition(nodes: int -> int, index: int): int =
+ if (not(has_token(nodes, index)))
+ nodes.get(index)
+ else if (index == bottom)
+ (nodes.get(bottom) + 1) % K
+ else
+ nodes.get(index - 1)
+
+ /// Initialize all nodes with non-deterministic states
+ action init = all {
+ nondet initial = 0.to(N).setOfMaps(0.to(K - 1)).oneOf()
+ system' = initial
+ }
+
+ /// Pick a single active node non-deterministically and update its state
+ action step = {
+ nondet node = 0.to(N).filter(i => has_token(system, i)).oneOf()
+ system' = system.set(node, state_transition(system, node))
+ }
+
+ /// Pick several active nodes non-deterministically and update their state.
+ /// Closer to the distributed demon is discussed in EWD 391. We are not
+ /// considering interleaving in the execution of state_transition here
+ action distributed_step = {
+ nondet nodes = 0.to(N).filter(i => has_token(system, i)).powerset().exclude(Set()).oneOf()
+ system' = nodes.fold(system, (s, x) => s.set(x, state_transition(system, x)))
+ }
+
+ // Pure function to count how many tokens exist
+ pure def count_tokens(nodes: int -> int): int = {
+ 0.to(N).filter(i => has_token(nodes, i)).size()
+ }
+
+ // Temporal properties
+ temporal convergence = step.weakFair(Set(system)) implies eventually(count_tokens(system) == 1)
+ temporal closure = always(count_tokens(system) == 1 implies always(count_tokens(system) == 1))
+ temporal persistence = step.weakFair(Set(system)) implies eventually(always(count_tokens(system) == 1))
+
+ // Invariant
+ def tokenInv = count_tokens(system) > 0
+
+ /// to better see the token in the repl
+ pure def show_token(nodes: int -> int): int -> bool =
+ nodes.keys().mapBy(i => has_token(nodes, i))
+}
+
+module ewd426 {
+ import self_stabilization(N = 5, K = 7).*
+}
+
+module broken_ewd426 {
+ // This should break the assumption of K >= N. See #1182.
+ import self_stabilization(N = 3, K = 2).*
+}
diff --git a/examples/classic/distributed/ewd426/ewd426_3.qnt b/examples/classic/distributed/ewd426/ewd426_3.qnt
new file mode 100644
index 000000000..a4d3b9fc9
--- /dev/null
+++ b/examples/classic/distributed/ewd426/ewd426_3.qnt
@@ -0,0 +1,102 @@
+// -*- mode: Bluespec; -*-
+/**
+ * ewd426's Stabilizing Token Ring (EWD426)
+ * 3 state machine
+ * This implementation ensures that from some time on,
+ * exactly one token circulates in a set of nodes.
+ *
+ * Mahtab Norouzi, Josef Widder, Informal Systems, 2024-2025
+ */
+module self_stabilization_three_state {
+ // Number of nodes in the ring
+ const N: int
+
+ val bottom = 0
+ val top = N
+
+ // Variable: Mapping of node indices to their states
+ var system: int -> int
+
+ pure def has_token(nodes: int -> int, index: int): bool =
+ val s = nodes.get(index)
+ if (index == bottom)
+ val r = nodes.get(1)
+ (s + 1) % 3 == r
+ else if (index == top)
+ val l = nodes.get(N - 1)
+ val r = nodes.get(bottom)
+ l == r and not((l + 1) % 3 == s)
+ else
+ val l = nodes.get(index - 1)
+ val r = nodes.get(index + 1)
+ or {
+ (s + 1) % 3 == l,
+ (s + 1) % 3 == r
+ }
+
+ // Transition function for the three-state machine
+ pure def state_transition(nodes: int -> int, index: int): int =
+ val s = nodes.get(index)
+ if (not(has_token(nodes, index)))
+ s
+ else if (index == bottom)
+ (s + 2) % 3 // (s - 1) % 3
+ else if (index == top)
+ val l = nodes.get(N - 1)
+ (l + 1) % 3
+ else
+ val l = nodes.get(index - 1)
+ val r = nodes.get(index + 1)
+ if ((s + 1) % 3 == l)
+ // Not 100% sure what ewd426 wants here. Might be non-determinism
+ // between the two options. We make it deterministic here.
+ if ((s + 1) % 3 == r)
+ r
+ else
+ l
+ else if ((s + 1) % 3 == r)
+ r
+ else
+ s
+
+ /// Initialize all nodes with random states
+ action init = all {
+ nondet initial = 0.to(N).setOfMaps(0.to(2)).oneOf()
+ system' = initial
+ }
+
+ /// Pick a single active node non-deterministically and update its state
+ action step = {
+ nondet node = 0.to(N).filter(i => has_token(system, i)).oneOf()
+ system' = system.set(node, state_transition(system, node))
+ }
+
+ /// Pick several active nodes non-deterministically and update their state.
+ /// Closer to the distributed demon is discussed in EWD 391. We are not
+ /// considering interleaving in the execution of state_transition here
+ action distributed_step = {
+ nondet nodes = 0.to(N).filter(i => has_token(system, i)).powerset().exclude(Set()).oneOf()
+ system' = nodes.fold(system, (s, x) => s.set(x, state_transition(system, x)))
+ }
+
+ // Pure function to count how many tokens exist
+ pure def count_tokens(nodes: int -> int): int = {
+ to(0, N).filter(i => has_token(nodes, i)).size()
+ }
+
+ // Temporal properties
+ temporal convergence = step.weakFair(Set(system)) implies eventually(count_tokens(system) == 1)
+ temporal closure = always(count_tokens(system) == 1 implies always(count_tokens(system) == 1))
+ temporal persistence = step.weakFair(Set(system)) implies eventually(always(count_tokens(system) == 1))
+
+ // Invariant
+ def tokenInv = count_tokens(system) > 0
+
+ /// to better see the token in the repl
+ pure def show_token(nodes: int -> int): int -> bool =
+ nodes.keys().mapBy(i => has_token(nodes, i))
+}
+
+module ewd426_3 {
+ import self_stabilization_three_state(N = 5).*
+}
diff --git a/examples/classic/distributed/ewd426/ewd426_4.qnt b/examples/classic/distributed/ewd426/ewd426_4.qnt
new file mode 100644
index 000000000..962bfabd6
--- /dev/null
+++ b/examples/classic/distributed/ewd426/ewd426_4.qnt
@@ -0,0 +1,109 @@
+// -*- mode: Bluespec; -*-
+/**
+ * ewd426's Stabilizing Token Ring (EWD426)
+ * 4 state machine
+ * This implementation ensures that from some time on,
+ * exactly one token circulates in a set of nodes,
+ *
+ * Mahtab Norouzi, Josef Widder, Informal Systems, 2024-2025
+ */
+module self_stabilization_four_state {
+ const N: int
+
+ val bottom = 0
+ val top = N
+
+ type State = {
+ x: bool,
+ up: bool
+ }
+
+ var system: int -> State
+
+ pure def has_token(nodes: int -> State, index: int): bool =
+ val s = nodes.get(index)
+ if (index == bottom)
+ val r = nodes.get(1)
+ s.x == r.x and not(r.up)
+ else if (index == top)
+ val l = nodes.get(N - 1)
+ not(s.x == l.x)
+ else
+ val l = nodes.get(index - 1)
+ val r = nodes.get(index + 1)
+ or {
+ not(s.x == l.x),
+ and {
+ s.x == r.x,
+ s.up,
+ not(r.up)
+ }
+ }
+
+ // Pure function to handle the four-state transition
+ pure def state_transition(nodes: int -> State, index: int): State =
+ val s = nodes.get(index)
+ if (not(has_token(nodes, index)))
+ s
+ else if (index == bottom)
+ { x: not(s.x), up: s.up }
+ else if (index == top)
+ { x: not(s.x), up: s.up }
+ else
+ val l = nodes.get(index - 1)
+ val r = nodes.get(index + 1)
+ if (not(s.x == l.x))
+ { x: not(s.x), up: true }
+ else
+ // and {s.x == r.x, s.up, not(r.up)}
+ { x: s.x, up: false }
+
+ action init = all {
+ val allState = Set(
+ { x: false, up: false },
+ { x: false, up: true },
+ { x: true, up: false },
+ { x: true, up: true }
+ )
+ nondet initial = 0.to(N).setOfMaps(allState).oneOf()
+ // "For the bottom machine upS = true by definition, for the top machine upS = false by definition"
+ system' = initial
+ .setBy(bottom, s => { ...s, up: true })
+ .setBy(top, s => { ...s, up: false })
+ }
+
+ /// Pick a single active node non-deterministically and update its state
+ action step = {
+ nondet node = 0.to(N).filter(i => has_token(system, i)).oneOf()
+ system' = system.set(node, state_transition(system, node))
+ }
+
+ /// Pick several active nodes non-deterministically and update their state.
+ /// Closer to the distributed demon is discussed in EWD 391. We are not
+ /// considering interleaving in the execution of state_transition here
+ action distributed_step = {
+ nondet nodes = 0.to(N).filter(i => has_token(system, i)).powerset().exclude(Set()).oneOf()
+ system' = nodes.fold(system, (s, x) => s.set(x, state_transition(system, x)))
+ }
+
+ // Pure function to count how many tokens exist
+ pure def count_tokens(nodes: int -> State): int = {
+ 0.to(N).filter(i => has_token(nodes, i)).size()
+ }
+
+ // Temporal properties
+ temporal convergence = step.weakFair(Set(system)) implies eventually(count_tokens(system) == 1)
+ temporal closure = always(count_tokens(system) == 1 implies always(count_tokens(system) == 1))
+ temporal persistence = step.weakFair(Set(system)) implies eventually(always(count_tokens(system) == 1))
+
+ // Invariant
+ def tokenInv = count_tokens(system) > 0
+
+ /// to better see the token in the repl
+ pure def show_token(nodes: int -> State): int -> bool =
+ nodes.keys().mapBy(i => has_token(nodes, i))
+}
+
+module ewd426_4 {
+ import self_stabilization_four_state(N = 5).*
+}