diff --git a/simra.py b/simra.py
index baf971395d30dd1fbcad759c9793710646fc5e5f..452c5f0323ff098f6c1f1bb550cebdf83bd28303 100755
--- a/simra.py
+++ b/simra.py
@@ -1,10 +1,12 @@
-/#!/usr/bin/env python3
+#!/usr/bin/env python3
 import collections
+import copy
 from functools import reduce
 from itertools import count
 import tkinter as TK
 
 # ################################################################
+# General utilities
 
 def dict_filter(dictionary, keys):
     """
@@ -12,7 +14,17 @@ def dict_filter(dictionary, keys):
     """
     return dict((key, dictionary[key]) for key in keys)
 
+def integers(n):
+    """
+    Return the list of integers from 0 (included) to n-1 (included).
+    """
+    return list(range(n))
+
+def flatten(l):
+    return [item for sublist in l for item in sublist]
+
 # ################################################################
+# Automata network themselves
 
 class Node:
     """
@@ -37,13 +49,17 @@ class Node:
         self.reset_state() # Sets self.state
     
     def reset_state(self):
-        self.state = self.init_state.copy()
+        self.state = self.init_state
     
     def add_dependency(self, new_dependency):
         self.dependencies.append(new_dependency)
     
     def update(self, neigh_vals):
-        self.state = self.update_func(neigh_vals)
+        f = self.update_func
+        self.state = f(neigh_vals)
+
+    def __repr__(self):
+        return "{{node {}: {}}}".format(self.name, self.state)
 
 class AN:
     """
@@ -59,11 +75,11 @@ class AN:
 
     Other fields:
     up      -- Stands for Update Pointer.
-               It is an index in update_mode thant “points” to the next update
+               It is an index in update_mode that “points” to the next update
                to perform. Note that foo.up can be publicly read and written.
                A modulo len(update_mode) is implicitly applied, so it is safe
                to do, e.g., foo.up +=1 on an update.
-    history -- A circular buffer of deep copies of the previous values of _node
+    history -- A circular buffer of deep copies of the previous values of _nodes
                (see below).
                It can be publicly accessed. Please do not alter the history
                by yourself (Stalin did nothing right).
@@ -85,12 +101,17 @@ class AN:
     @property
     def nodes(self):
         """Return the set of all nodes."""
-        return self._nodes.items()
+        return self._nodes.values()
 
     @nodes.setter
     def nodes(self, new_nodes):
         self._nodes = {}
         for node in new_nodes: self.add_node(node)
+    
+    @property
+    def state(self):
+        """Return a dictionary mapping node names to node states."""
+        return dict((name, self._nodes[name].state) for name in self._nodes.keys())
 
     def add_node(self, node):
         """Adds a new node to the AN."""
@@ -122,11 +143,18 @@ class AN:
             new_history.append(self.history[-(i+1)])
         self.history = new_history
     
+    def __repr__(self):
+        result = "{AN \n"
+        for node in self.nodes:
+            result += "    " + str(node) + "\n"
+        result += "}"
+        return result
+
     # ################################################################
     
     def reset_state(self):
         """Set each node to its initial value, and the up to 0."""
-        new_nodes = self._nodes.deep_copy()
+        new_nodes = self._nodes.copy()
         for node in new_nodes:
             node.reset_state()
         
@@ -136,9 +164,9 @@ class AN:
 
     def update(self):
         """Update the AN once."""
-        new_nodes = self._nodes.deep_copy()
+        new_nodes = copy.deepcopy(self._nodes)
         for n in self.update_mode[self.up]:
-            local_view = dict_filter(self.nodes, self.nodes[n].dependencies)
+            local_view = dict_filter(self.state, self._nodes[n].dependencies)
             new_nodes[n].update(local_view)
         
         self.history.append(self.nodes)
@@ -147,7 +175,7 @@ class AN:
 
     def unupdate(self):
         """Roll back one step of the AN, within limits of history_len."""
-        self._nodes = self.history.pop()
+        self._nodes = copy.deepcopy(self.history.pop())
         self.up -= 1
     
     def run(self, steps):
@@ -160,6 +188,112 @@ class AN:
                 self.update()
 
 # ################################################################
+# Update modes
+
+def parallel_mode(node_names):
+    """Return a parallel update mode."""
+    return [node_names]
+
+def local_clocks(period, deltas):
+    """
+    Return a local clocks update mode.
+
+    period -- The global period update.
+    deltas -- A dictionary mapping node names to phases.
+    """
+    result = [] * global_periods
+    for k in deltas.keys():
+        result[deltas[k]].append(k)
+    return result
+
+def is_block_seq(mode):
+    """
+    Return whether an update mode is block sequential
+    (no node is updated twice).
+    """
+    already_seen = set()
+    for node_name in flatten(mode):
+        if node_name in already_seen: return False
+        else: already_seen.add(name)
+    return True
+
+def block_seq(mode):
+    """
+    Checks that `mode` is block-sequential. If so, return it.
+    If not, raise a ValueError exception. Useful for functoinal programming.
+    """
+    if not is_block_seq(mode): raise ValueError()
+    return mode
+
+def periodic_mode(mode):
+    """
+    The identity function, with another name.
+    Useful for functionnal programming.
+    """
+    return mode
+
+# ################################################################
+# Trivial nodes
+
+def node_fixpoint(name, init_val=0):
+    def identity(args):
+        for k in args.keys():
+            return args[k]
+    return Node(name, identity, [name], init_val)
+
+def node_constant(name, value=0, init_val=value):
+    def constant(args):
+        nonlocal value
+        return value
+    return Node(name, constant, [], init_val)
+
+# ################################################################
+# Cycle ANs
+
+def cycle_update_func(sign=True):
+    """
+    Return an update function for a node in a cycle.
+    All update functions assume only one neighbour.
+    If sign=True, the returned update function is identity.
+    If sign=False, the returned update function is negation.
+    """
+    def positive(neighs):
+        for k in neighs.keys():
+            return neighs[k]
+    
+    def negative(neighs):
+        for k in neighs.keys():
+            return not neighs[k]
+
+    if sign: return positive
+    else:    return negative
+
+def cycle(signs, init_vals=None, update_mode=None):
+    """
+    Return a cyclic AN.
+    Node names are 0,…,n-1, with n=len(signs). Node i depends on node i-1 mod n.
+    signs       -- signs[i] is True if i depends positively on i-1, False if
+                   negatively
+    init_vals   -- list of initial values (booleans); defaults to all True
+    update_mode -- a periodic update mode; defaults to parallel
+    """
+    num_nodes = len(signs)
+    node_names = integers(num_nodes)
+    
+    if not init_vals:
+        init_vals = [True]*num_nodes
+    if not update_mode:
+        update_mode = parallel_mode(node_names)
+    
+    nodes = []
+    for (sign, init_val, i) in zip(signs, init_vals, count()):
+        update_func = cycle_update_func(sign)
+        node = Node(i, update_func, [(i-1)%num_nodes], init_val)
+        nodes.append(node)
+    return AN(nodes, update_mode)
+
+# ################################################################
+# And-not symmetric networks
 
 def land(b1, b2):
     """Like `and`, but a function (usable with `reduce`)"""
@@ -182,19 +316,19 @@ def and_not_update_func(dep_list):
 
 def and_not(dep_lists, init_vals, update_mode):
     """
-    Builds an and-not network.
+    Return an and-not network.
     Nodes are named 1, …, N where N=len(dep_lists)==len(init_vals).
-    dep_lists[0] and init_vals[0] are ignored.
 
-    dep_lists[i] is the list of neighbours of i (so a list of integers).
-    A negative neighbour means a negated neighbour.
+    dep_lists   -- dep_lists[0] is ignored; dep_lists[i] is the list of
+                   neighbours of i (so a list of integers).
+                   A negative neighbour means a negated neighbour.
+
     For example if dep_list[1] contains 4, then 1 depends positively on 4;
     if dep_list[1] contains -4, then 1 depends negatively on 4.
 
-    init_vals is a list of booleans, so that init_vals[i] is the initial
-    value of node i.
-
-    update_mode is a periodic update mode, i.e. a list of sets of integers.
+    init_vals   -- init_vals[0] is ignored; init_vals[i] is the initial value of
+                   node i
+    update_mode -- periodic update mode, i.e. a list of sets of integers
     """
     nodes = []
     for (dep_list, init_val, i) in zip(dep_lists[1:], init_vals[1:], count(1)):
@@ -234,12 +368,6 @@ example_update = [
 
 # ################################################################
 
-def parallel_mode(AN):
-    """Return a parallel update mode for AN."""
-    return [[node.name for node in AN.nodes]]
-
-# ################################################################
-
 def button_press(event):
     print("clicked at ({},{})".format(event.x, event.y))