diff --git a/simra.py b/simra.py
index 06185d3aa3e0a5f3bab83534923176f50b83ff72..baf971395d30dd1fbcad759c9793710646fc5e5f 100755
--- a/simra.py
+++ b/simra.py
@@ -1,6 +1,10 @@
-#!/usr/bin/env python3
-import tkinter as TK
+/#!/usr/bin/env python3
 import collections
+from functools import reduce
+from itertools import count
+import tkinter as TK
+
+# ################################################################
 
 def dict_filter(dictionary, keys):
     """
@@ -8,13 +12,11 @@ def dict_filter(dictionary, keys):
     """
     return dict((key, dictionary[key]) for key in keys)
 
-def flatten(l):
-    """Given a list of lists l, concatenate all elements of l."""
-    return [item for sublist in l for item in sublist]
+# ################################################################
 
 class Node:
     """
-    An Automata Network Node.
+    An automata network node.
 
     Constructor fields:
     name         -- Any hashable value, used for printing and identification
@@ -159,6 +161,85 @@ class AN:
 
 # ################################################################
 
+def land(b1, b2):
+    """Like `and`, but a function (usable with `reduce`)"""
+    return b1 and b2
+
+def and_not_update_func(dep_list):
+    """
+    Return an update function for a node in an and_not network.
+    If dep_list is [1, -2, 3, -4], the node has positive neighbours 1, 3
+    and negative neighbours 2, 4.
+    """
+    def node_update_func(args):
+        nonlocal dep_list
+        l = []
+        for n in dep_list:
+            if n<0: l.append(not args[-n])
+            else: l.append(args[n])
+        return reduce(land, l)
+    return node_update_func
+
+def and_not(dep_lists, init_vals, update_mode):
+    """
+    Builds 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.
+    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.
+    """
+    nodes = []
+    for (dep_list, init_val, i) in zip(dep_lists[1:], init_vals[1:], count(1)):
+        update_func = ant_not_update_func(dep_list)
+        deps = [abs(d) for d in dep_list]
+        node = Node(i, update_func, deps, init_val)
+        nodes.append(node)
+    return AN(nodes[1:], update_mode)
+
+# ################################################################
+
+example_deps = [
+    None, #ignored
+    [3, 5],
+    [4, 6],
+    [1, 7],
+    [2, 7],
+    [1, -8],
+    [2, -8],
+    [3, 4, 9],
+    [-5, -6, -9, -10],
+    [-8, 13, 14],
+    [11, 12],
+    [10],
+    [10],
+    [9, 14],
+    [9, 13],
+]
+
+example_init_vals = [
+    None, #ignored
+]
+
+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))