Add missing impl tests and automated testing from makefile
authorJakob Cornell <jakob@jcornell.net>
Mon, 9 Sep 2019 00:49:59 +0000 (19:49 -0500)
committerJakob Cornell <jakob@jcornell.net>
Mon, 9 Sep 2019 00:49:59 +0000 (19:49 -0500)
46 files changed:
.gitignore
algorithms/dijkstra/impl/java/Graph.java [deleted file]
algorithms/dijkstra/impl/java/Main.java [deleted file]
algorithms/dijkstra/impl/python3/dijkstra.py [deleted file]
algorithms/dijkstra/impl/python3/usage.py [deleted file]
algorithms/dijkstra/topic.tex
algorithms/hull-2d/impl/java/Hull.java [deleted file]
algorithms/hull-2d/impl/java/Main.java [deleted file]
algorithms/hull-2d/impl/python3/hull.py [deleted file]
algorithms/hull-2d/impl/python3/usage.py [deleted file]
algorithms/hull-2d/topic.tex
algorithms/network-flows/impl/java-junk/FlowNet.java [deleted file]
algorithms/network-flows/impl/java-junk/Main.java [deleted file]
algorithms/network-flows/impl/java-junk/usage [deleted file]
algorithms/network-flows/impl/python3/flows.py [deleted file]
algorithms/network-flows/topic.tex
config.py
contributors
doc.tex
impl/dijkstra/java/Graph.java [new file with mode: 0644]
impl/dijkstra/java/Test.java [new file with mode: 0644]
impl/dijkstra/java/Usage.java [new file with mode: 0644]
impl/dijkstra/java/makefile [new file with mode: 0644]
impl/dijkstra/python3/dijkstra.py [new file with mode: 0644]
impl/dijkstra/python3/makefile [new file with mode: 0644]
impl/dijkstra/python3/test.py [new file with mode: 0644]
impl/dijkstra/python3/usage.py [new file with mode: 0644]
impl/hull-2d/java/Hull.java [new file with mode: 0644]
impl/hull-2d/java/Test.java [new file with mode: 0644]
impl/hull-2d/java/Usage.java [new file with mode: 0644]
impl/hull-2d/java/makefile [new file with mode: 0644]
impl/hull-2d/python3/hull.py [new file with mode: 0644]
impl/hull-2d/python3/makefile [new file with mode: 0644]
impl/hull-2d/python3/test.py [new file with mode: 0644]
impl/hull-2d/python3/usage.py [new file with mode: 0644]
impl/network-flows/java-junk/FlowNet.java [new file with mode: 0644]
impl/network-flows/java-junk/Main.java [new file with mode: 0644]
impl/network-flows/java-junk/usage [new file with mode: 0644]
impl/network-flows/python3/flows.py [new file with mode: 0644]
impl/network-flows/python3/makefile [new file with mode: 0644]
impl/network-flows/python3/test.py [new file with mode: 0644]
impl/network-flows/python3/usage.py [new file with mode: 0644]
makefile
readme [deleted file]
readme.md [new file with mode: 0644]
util/run_all_tests.py [new file with mode: 0644]

index 16be8f2193eadbfc2e56f06968aca064a36735a2..fd6aad7fa7789c8d23a86afd469f09f91ee60277 100644 (file)
@@ -1 +1,2 @@
-/output/
+output/
+__pycache__/
diff --git a/algorithms/dijkstra/impl/java/Graph.java b/algorithms/dijkstra/impl/java/Graph.java
deleted file mode 100644 (file)
index eefd961..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-import java.util.*;
-
-public class Graph {
-       public Set<Node> nodes = new HashSet<>();
-
-       // only needed for repeated search
-       public void reset() {
-               for (Node n : nodes) {
-                       n.seen = false;
-                       n.dist = Long.MAX_VALUE;
-                       n.prev = null;
-               }
-       }
-
-       public void search(Node root) {
-               Set<Node> q = new HashSet<>();
-               root.dist = 0l;
-               q.add(root);
-
-               while (!q.isEmpty()) {
-                       Node a = Collections.min(q);
-                       q.remove(a);
-                       if (a.seen) continue;
-                       a.seen = true;
-                       for (Node b : a.adj.keySet()) {
-                               long d = a.dist + a.adj.get(b);
-                               if (d < b.dist) {
-                                       b.dist = d;
-                                       b.prev = a;
-                                       q.add(b);
-                               }
-                       }
-               }
-       }
-}
-
-class Node implements Comparable<Node> {
-       public boolean seen;
-       public long dist = Long.MAX_VALUE;
-       public Node prev;
-       public Map<Node, Long> adj = new HashMap<>();
-
-       public boolean equals(Object o) {
-               return dist == ((Node) o).dist;
-       }
-
-       public int compareTo(Node n) {
-               return Long.compare(dist, n.dist);
-       }
-}
diff --git a/algorithms/dijkstra/impl/java/Main.java b/algorithms/dijkstra/impl/java/Main.java
deleted file mode 100644 (file)
index 8ca56a7..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-import java.util.Collections;
-
-public class Main {
-       public static void main(String[] args) {
-               Graph graph = new Graph();
-               Node a = new Node();
-               Node b = new Node();
-               Node c = new Node();
-               Node d = new Node();
-               Node e = new Node();
-               Node f = new Node();
-
-               // add each node to `graph.nodes'
-               Collections.addAll(graph.nodes, new Node[] {a,b,c,d,e,f});
-
-               // make edges between nodes
-               a.adj.put(c, 6l);
-               b.adj.put(a, 9l);
-               c.adj.put(a, 6l);
-               d.adj.put(b, 2l);
-               d.adj.put(c, 11l);
-               e.adj.put(b, 14l);
-               e.adj.put(d, 9l);
-               e.adj.put(f, 7l);
-               f.adj.put(c, 15l);
-               f.adj.put(d, 10l);
-
-               // search the whole graph, starting at node `e'
-               graph.search(e);
-               System.out.println(a.dist);
-               System.out.println(a.prev == b);
-               // be sure to reset the graph before performing another search
-               graph.reset();
-       }
-}
diff --git a/algorithms/dijkstra/impl/python3/dijkstra.py b/algorithms/dijkstra/impl/python3/dijkstra.py
deleted file mode 100644 (file)
index 3f8e6db..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-import math
-
-class Node:
-       def __init__(s, data = None):
-               s.data = data
-               s.adj = {}
-
-       def search(s):
-               dists = {s: 0}
-               prev = {}
-               expl = set()
-               q = {s}
-               while q:
-                       a = min(q, key = lambda n: dists.get(n, math.inf))
-                       q.remove(a)
-                       if a not in expl:
-                               expl.add(a)
-                               for n in a.adj:
-                                       d = dists[a] + a.adj[n]
-                                       if d < dists.get(n, math.inf):
-                                               dists[n] = d
-                                               prev[n] = a
-                                               q.add(n)
-               return (dists, prev)
diff --git a/algorithms/dijkstra/impl/python3/usage.py b/algorithms/dijkstra/impl/python3/usage.py
deleted file mode 100644 (file)
index 6000afd..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-a = Node()
-b = Node('foo bar')
-c = Node()
-d = Node([1, 2, 3])
-e = Node()
-
-# each node has an adjacency `dict'; each key is the destination of an outward edge, and the corresponding value is the edge cost
-a.adj[b] = 5
-b.adj[c] = 5
-c.adj[d] = 5
-a.adj[d] = 30
-
-(dists, prev) = a.search()
-assert e not in dists
-assert dists[d] == 15
-assert prev[c] == b
index 28e4d351d34f783173996d9c84da6b2719f7e507..35d4d8e9e0d8c763f008e66cb6b705a623755fd9 100644 (file)
@@ -1,5 +1,5 @@
 {
-\newcommand{\BasePath}{\ProjRootPrefix/algorithms/dijkstra}
+\newcommand{\ImplPath}{\ProjRootPrefix/impl/dijkstra}
 
 \TopicHeader{Weighted Shortest Path: Dijkstra's Agorithm}
 
 
        \TopicSubHeader{Python 3}
 
-               \inputminted{python}{\BasePath/impl/python3/dijkstra.py}
+               \inputminted{python}{\ImplPath/python3/dijkstra.py}
 
                The \texttt{Node} constructor accepts a \texttt{data} parameter.
                Any value can be passed in, and it will be stored in the node to facilitate associating the nodes with data specific to the problem.
                Here's a usage example:
 
-               \inputminted{python}{\BasePath/impl/python3/usage.py}
+               \inputminted{python}{\ImplPath/python3/usage.py}
 
                In this implementation, two dictionaries are returned which provide the distance and previous-node information.
                To see if a node is reachable, simply check whether the node is a key in the first (distance) dictionary.
        \TopicSubHeader{Java}
 
                \centerline{\texttt{Graph.java}}
-               \inputminted{java}{\BasePath/impl/java/Graph.java}
+               \inputminted{java}{\ImplPath/java/Graph.java}
 
                The code may be used as follows.
                If you need to attach some data to the nodes, you could add a field to the \texttt{Node} class or use a \texttt{Map} to keep track of the association.
 
-               \inputminted{java}{\BasePath/impl/java/Main.java}
+               \inputminted{java}{\ImplPath/java/Usage.java}
 
                The visited flag, previous-node links, and distances are accessible as fields on the \texttt{Node} objects after the search.
 }
diff --git a/algorithms/hull-2d/impl/java/Hull.java b/algorithms/hull-2d/impl/java/Hull.java
deleted file mode 100644 (file)
index dbc0f2a..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-import java.util.*;
-
-public class Hull {
-       static long dir(Point a, Point b, Point c) {
-               return (b.x - a.x)*(c.y - a.y) - (b.y - a.y)*(c.x - a.x);
-       }
-       static List<Point> hull(Set<Point> pts) {
-               if (pts.isEmpty()) {
-                       return new ArrayList<>();
-               }
-               Point s = Collections.min(pts, new Comparator<Point>() {
-                       public int compare(Point a, Point b) {
-                               int yCmp = Long.compare(a.y, b.y);
-                               return yCmp == 0 ? Long.compare(a.x, b.x) : yCmp;
-                       }
-               });
-               LinkedList<Point> srt = new LinkedList<>(pts);
-               srt.remove(s);
-               Collections.sort(srt, new Comparator<Point>() {
-                       double cos(Point p) {
-                               return (p.x - s.x) / Math.sqrt((s.x - p.x)*(s.x - p.x) + (s.y - p.y)*(s.y - p.y));
-                       }
-                       public int compare(Point a, Point b) {
-                               return -Double.compare(cos(a), cos(b));
-                       }
-               });
-               srt.addFirst(s);
-               if (srt.size() < 3) {
-                       return srt;
-               }
-               LinkedList<Point> out = new LinkedList<>(srt.subList(0, 3));
-               for (Point p : srt.subList(3, srt.size())) {
-                       while (dir(out.get(out.size() - 2), out.get(out.size() - 1), p) <= 0) {
-                               out.removeLast();
-                       }
-                       out.add(p);
-               }
-               return out;
-       }
-}
-
-class Point {
-       long x;
-       long y;
-       Point(long x, long y) {
-               this.x = x;
-               this.y = y;
-       }
-}
diff --git a/algorithms/hull-2d/impl/java/Main.java b/algorithms/hull-2d/impl/java/Main.java
deleted file mode 100644 (file)
index b7b61cc..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-import java.util.*;
-
-public class Main {
-       public static void main(String[] args) {
-               Point a = new Point(0, 0);
-               Point b = new Point(-2, 2);
-               Point c = new Point(0, 4);
-               Point d = new Point(2, 2);
-               Point e = new Point(0, 2);
-
-               List<Point> hull = Hull.hull(new HashSet<>(Arrays.asList(new Point[] {a, b, c, d, e})));
-               assert hull.equals(Arrays.asList(new Point[] {a, d, c, b}));
-       }
-}
diff --git a/algorithms/hull-2d/impl/python3/hull.py b/algorithms/hull-2d/impl/python3/hull.py
deleted file mode 100644 (file)
index c4ddd9d..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-import math
-
-def hull(points):
-       if not points:
-               return []
-       sx, sy = s = min(points, key = lambda p: p[::-1])
-       cos = lambda p: (p[0] - sx) / math.sqrt((sx - p[0])**2 + (sy - p[1])**2)
-       dir_ = lambda ax, ay, bx, by, cx, cy: (bx - ax)*(cy - ay) - (by - ay)*(cx - ax)
-       points = [s] + sorted(points - {s}, key = cos, reverse = True)
-       stack = points[:3]
-       for p in points[3:]:
-               while dir_(*stack[-2], *stack[-1], *p) <= 0:
-                       stack.pop()
-               stack.append(p)
-       return stack
diff --git a/algorithms/hull-2d/impl/python3/usage.py b/algorithms/hull-2d/impl/python3/usage.py
deleted file mode 100644 (file)
index d8fff87..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-points = {(0,0), (-2,2), (0,4), (2,2), (0,2)}
-assert hull(points) == [(0,0), (2,2), (0,4), (-2,2)]
index ca7efc8a485a4e17d7f15a08e47fdc8ac8688d26..4eb9cc283582d096a3aeedf4d0edc4f5b3282440 100644 (file)
@@ -1,10 +1,10 @@
 {
-\newcommand{\BasePath}{\ProjRootPrefix/algorithms/hull-2d}
+\newcommand{\ImplPath}{\ProjRootPrefix/impl/hull-2d}
 
 \TopicHeader{Convex Hull (2D)}
 
        Convex hull algorithms are given a collection of points and output a subset of just the outermost points.
-       Formally, the output is the points of a polygon within which lie all points in the input.
+       Formally, the output is the points of a convex polygon within which lie all points in the input.
 
        The implementations here use a technique called Graham scan to compute the convex hull with time complexity $O(n \log n)$, where $n$ is the number of input points.
        The output points are ordered: the bottom-most (and then leftmost) point appears first, then the remaining hull points in counterclockwise order.
        \TopicSubHeader{Java}
 
                \centerline{\texttt{Hull.java}}
-               \inputminted{java}{\BasePath/impl/java/Hull.java}
+               \inputminted{java}{\ImplPath/java/Hull.java}
 
                This may be used as follows:
 
-               \inputminted{java}{\BasePath/impl/java/Main.java}
+               \inputminted{java}{\ImplPath/java/Usage.java}
 
        \TopicSubHeader{Python 3}
 
-               \inputminted{python}{\BasePath/impl/python3/hull.py}
+               \inputminted{python}{\ImplPath/python3/hull.py}
 
                To run the algorithm, pass a set of $(x, y)$ pairs (\texttt{tuple}s) to \texttt{hull}.
 
                A sample usage:
 
-               \inputminted{python}{\BasePath/impl/python3/usage.py}
+               \inputminted{python}{\ImplPath/python3/usage.py}
 }
diff --git a/algorithms/network-flows/impl/java-junk/FlowNet.java b/algorithms/network-flows/impl/java-junk/FlowNet.java
deleted file mode 100644 (file)
index de0f1b6..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-import java.util.*;
-
-class FlowNet {
-       Map<Node, Boolean> nodes = new HashMap<>();
-       long value;
-       Node source, sink;
-
-       void addNode(Node n) {
-               nodes.put(n, false);
-       }
-
-       Edge edgeFor(Node one, Node two) {
-               return one.edgeTo.containsKey(two) ? one.edgeTo.get(two) : two.edgeTo.get(one);
-       }
-
-       long findMax() {
-               long value = 0;
-
-               AP augPath = getPath();
-               while (augPath != null) {
-                       Node from, to = augPath.path.pop();
-                       while (!augPath.path.isEmpty()) {
-                               from = to;
-                               to = augPath.path.pop();
-                               edgeFor(from, to).sendTo(to, augPath.value);
-                       }
-                       value += augPath.value;
-                       augPath = getPath();
-               }
-
-               return value;
-       }
-
-       // for min cut
-       Set<Node> srcSide() {
-               Set<Node> srcSide = new HashSet<>();
-               for (Node n : nodes.keySet()) {
-                       if (nodes.get(n)) {
-                               srcSide.add(n);
-                       }
-               }
-               return srcSide;
-       }
-
-       AP getPath() {
-               Map<Node, Node> prev = new HashMap<>();
-               Queue<Node> q = new LinkedList<>();
-               q.offer(source);
-               prev.put(source, null);
-               while (!q.isEmpty()) {
-                       Node n = q.poll();
-                       for (Node other : n.adj) {
-                               Edge e = edgeFor(n, other);
-                               if (e.capTo(other) > 0 && !prev.containsKey(other)) {
-                                       prev.put(other, n);
-                                       q.offer(other);
-                               }
-                       }
-               }
-               for (Node n : nodes.keySet()) {
-                       nodes.put(n, prev.keySet().contains(n));
-               }
-
-               if (nodes.get(sink)) {
-                       LinkedList<Node> path = new LinkedList<>();
-                       long value = Long.MAX_VALUE;
-
-                       Node n = sink, p = prev.get(n);
-                       path.push(n);
-                       while (p != null) {
-                               value = Math.min(value, edgeFor(p, n).capTo(n));
-                               path.push(p);
-                               n = p;
-                               p = prev.get(p);
-                       }
-
-                       AP ap = new AP();
-                       ap.path = path;
-                       ap.value = value;
-                       return ap;
-               } else {
-                       return null;
-               }
-       }
-
-       class AP {
-               LinkedList<Node> path;
-               long value;
-       }
-}
-
-class Node {
-       Set<Node> adj = new HashSet<>();
-       Map<Node, Edge> edgeTo = new HashMap<>();
-}
-
-class Edge {
-       Node from, to;
-       long cap, flow;
-
-       Edge(Node f, Node t, long c) {
-               from = f;
-               to = t;
-               cap = c;
-               f.adj.add(t);
-               t.adj.add(f);
-               f.edgeTo.put(t, this);
-       }
-
-       long capTo(Node n) {
-               return n == to ? cap - flow : flow;
-       }
-
-       void sendTo(Node n, long amt) {
-               flow += n == to ? amt : -amt;
-       }
-}
diff --git a/algorithms/network-flows/impl/java-junk/Main.java b/algorithms/network-flows/impl/java-junk/Main.java
deleted file mode 100644 (file)
index 1410824..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-public class Main {
-       public static void main(String[] args) {
-               FlowNet net = new FlowNet();
-               Node s = new Node();
-               Node t = new Node();
-               Node u = new Node();
-               Node v = new Node();
-               for (Node n : new Node[] {s,t,u,v}) {
-                       net.addNode(n);
-               }
-               net.source = s;
-               net.sink = t;
-               new Edge(s, u, 20);
-               new Edge(s, v, 10);
-               new Edge(u, v, 30);
-               new Edge(u, t, 10);
-               new Edge(v, t, 20);
-               System.out.println(net.findMax());
-               for (Node n : net.srcSide()) {
-                       char name = 0;
-                       if (n.equals(s)) name = 's';
-                       if (n.equals(t)) name = 't';
-                       if (n.equals(u)) name = 'u';
-                       if (n.equals(v)) name = 'v';
-                       System.out.println(name);
-               }
-       }
-}
diff --git a/algorithms/network-flows/impl/java-junk/usage b/algorithms/network-flows/impl/java-junk/usage
deleted file mode 100644 (file)
index 008896d..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-Usage:
-
-       FlowNet net = new FlowNet();
-
-       Node s = new Node();
-       Node t = new Node();
-       Node u = new Node();
-       Node v = new Node();
-       for (Node n : new Node[] {s,t,u,v}) {
-               net.addNode(n);
-       }
-       net.source = s;
-       net.sink = t;
-
-       new Edge(s, u, 20);
-       new Edge(s, v, 10);
-       new Edge(u, v, 30);
-       new Edge(u, t, 10);
-       new Edge(v, t, 20);
-
-       System.out.println(net.findMax());
-       System.out.println(net.srcSide());
diff --git a/algorithms/network-flows/impl/python3/flows.py b/algorithms/network-flows/impl/python3/flows.py
deleted file mode 100644 (file)
index 41ac788..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-from collections import deque
-
-class Edge:
-       def __init__(s, frm, to, cap):
-               s.frm = frm
-               s.to = to
-               s.cap = cap
-               s.flow = 0
-
-       def cap_to(s, node):
-               return {s.frm: s.flow, s.to: s.cap - s.flow}[node]
-
-       def add_to(s, node, amt):
-               s.flow += {s.frm: -amt, s.to: amt}[node]
-
-       def other(s, node):
-               return {s.frm: s.to, s.to: s.frm}[node]
-
-class Node:
-       def __init__(s, data = None):
-               s.data = data
-               s.edges = set()
-
-       @staticmethod
-       def edge(f, t, cap):
-               e = Edge(f, t, cap)
-               f.edges.add(e)
-               t.edges.add(e)
-
-def ff(nodes, src, sink):
-       def path():
-               prev = {}
-               q = deque([src])
-               while q:
-                       n = q.popleft()
-                       for e in n.edges:
-                               o = e.other(n)
-                               if o not in prev and e.cap_to(o) > 0:
-                                       prev[o] = e
-                                       q.append(o)
-               d = sink
-               p = deque()
-               while d in prev and d is not src:
-                       p.appendleft((prev[d], d))
-                       d = prev[d].other(d)
-               return p if d is src else []
-
-       p = path()
-       val = 0
-       while p:
-               amt = min(e.cap_to(d) for e, d in p)
-               val += amt
-               for e, d in p:
-                       e.add_to(d, amt)
-               p = path()
-
-       cut_src = set()
-       w = [src]
-       while w:
-               n = w.pop()
-               cut_src.add(n)
-               for e in n.edges:
-                       o = e.other(n)
-                       if o not in cut_src and e.cap_to(o) != 0:
-                               w.append(o)
-
-       return (val, cut_src)
index ed9685f1afe0823bc40db65176dbfd5c8cdefdfb..1dd1a68a1f9a0e2ff7db1b1a6444108508cf485a 100644 (file)
@@ -1,5 +1,5 @@
 {
-\newcommand{\BasePath}{\ProjRootPrefix/algorithms/network-flows}
+\newcommand{\ImplPath}{\ProjRootPrefix/impl/network-flows}
 
 \TopicHeader{Network Flows: Edmonds--Karp}
 
 
        \TopicSubHeader{Python 3}
 
-               \inputminted{python}{\BasePath/impl/python3/flows.py}
+               \inputminted{python}{\ImplPath/python3/flows.py}
+
+               To use the code:
+
+               \inputminted{python}{\ImplPath/python3/usage.py}
+
+               The \texttt{cut\_src} variable here is a set of node objects representing the source side of the minimum cut.
+               If the set \texttt{n} contains all nodes in the network, the sink side is computed by \texttt{n - cut\_src}.
 }
index 928f34df04eed0b177e0e8ddce6c680290eed4c4..67eb52ea3ec8da3b6bbac30f0288ed77f562f0e4 100644 (file)
--- a/config.py
+++ b/config.py
@@ -1,16 +1,12 @@
 r'''
 This file is responsible for configuring the document build.
 Some/all parameters are passed to LaTeX as generated code, which is probably a bad idea, but oh well.
-
-The keys in the `config' dictionary are the command names to assign in LaTeX.
-So if I use `FooBar' in the dictionary, then the command `\FooBar' is available to the LaTeX code.
-Note that an error will be raised if one of these names matches an existing LaTeX command.
 '''
 
 config = {
        # whether to insert page breaks before reference items to ease lookup
        'RefPageBrk': False,
-       # languages for which to exclude implementations (java, python3)
+       # languages for which to exclude implementations (java, python3) (to be implemented)
        'exclude_langs': [],
 }
 
index 1d362a43e879bafcc8d868fc8969713bb4be912e..ba95e4b6b21a4c432446cfd3cd744d668b03b626 100644 (file)
@@ -1,5 +1,5 @@
 Contributors
-       - Jakob Cornell (creator): LaTeX code, build system, Python/Java algorithm implementations
+       - Jakob Cornell, creator: LaTeX code, build system, Python/Java algorithm implementations
 
 Attribution
        - Dijkstra's Algorithm implementations and complexity analysis based on content here: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm (reused under CC-BY-SA)
diff --git a/doc.tex b/doc.tex
index 17a16e4324330c185d9c542951d58675e882a2ac..231c01676f9d5d91499b1bb0f7528aa0abccc63b 100644 (file)
--- a/doc.tex
+++ b/doc.tex
@@ -4,7 +4,7 @@
 \usepackage[margin=1in]{geometry} % for margin adjustment
 \usepackage{parskip} % replaces paragraph indentation with vertical space
 \usepackage{float} % used for captions on code listings
-\usepackage{ifthen} % for conditional inclusion (page breaks)
+\usepackage{ifthen} % for conditional compilation
 
 \newcommand{\ProjRootPrefix}{..}
 
diff --git a/impl/dijkstra/java/Graph.java b/impl/dijkstra/java/Graph.java
new file mode 100644 (file)
index 0000000..eefd961
--- /dev/null
@@ -0,0 +1,50 @@
+import java.util.*;
+
+public class Graph {
+       public Set<Node> nodes = new HashSet<>();
+
+       // only needed for repeated search
+       public void reset() {
+               for (Node n : nodes) {
+                       n.seen = false;
+                       n.dist = Long.MAX_VALUE;
+                       n.prev = null;
+               }
+       }
+
+       public void search(Node root) {
+               Set<Node> q = new HashSet<>();
+               root.dist = 0l;
+               q.add(root);
+
+               while (!q.isEmpty()) {
+                       Node a = Collections.min(q);
+                       q.remove(a);
+                       if (a.seen) continue;
+                       a.seen = true;
+                       for (Node b : a.adj.keySet()) {
+                               long d = a.dist + a.adj.get(b);
+                               if (d < b.dist) {
+                                       b.dist = d;
+                                       b.prev = a;
+                                       q.add(b);
+                               }
+                       }
+               }
+       }
+}
+
+class Node implements Comparable<Node> {
+       public boolean seen;
+       public long dist = Long.MAX_VALUE;
+       public Node prev;
+       public Map<Node, Long> adj = new HashMap<>();
+
+       public boolean equals(Object o) {
+               return dist == ((Node) o).dist;
+       }
+
+       public int compareTo(Node n) {
+               return Long.compare(dist, n.dist);
+       }
+}
diff --git a/impl/dijkstra/java/Test.java b/impl/dijkstra/java/Test.java
new file mode 100644 (file)
index 0000000..69a1a40
--- /dev/null
@@ -0,0 +1,58 @@
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Collections;
+
+public class Test {
+       public static void main(String[] args) {
+               Graph graph = new Graph();
+               Node a = new Node();
+               Node b = new Node();
+               Node c = new Node();
+               Node d = new Node();
+               Node e = new Node();
+               Node f = new Node();
+
+               Collections.addAll(graph.nodes, new Node[] {a,b,c,d,e,f});
+
+               a.adj.put(c, 6l);
+               b.adj.put(a, 9l);
+               c.adj.put(a, 6l);
+               d.adj.put(b, 2l);
+               d.adj.put(c, 11l);
+               e.adj.put(b, 14l);
+               e.adj.put(d, 9l);
+               e.adj.put(f, 7l);
+               f.adj.put(c, 15l);
+               f.adj.put(d, 10l);
+
+               graph.search(e);
+
+               Map<Node, Long> actDists = new HashMap<>();
+               for (Node n : graph.nodes) {
+                       actDists.put(n, n.dist);
+               }
+               Map<Node, Long> expDists = new HashMap<>();
+               expDists.put(a, 20l);
+               expDists.put(b, 11l);
+               expDists.put(c, 20l);
+               expDists.put(d, 9l);
+               expDists.put(e, 0l);
+               expDists.put(f, 7l);
+               assert actDists.equals(expDists);
+
+               Map<Node, Node> actPrevs = new HashMap<>();
+               for (Node n : graph.nodes) {
+                       actPrevs.put(n, n.prev);
+               }
+               Map<Node, Node> expPrevs = new HashMap<>();
+               expPrevs.put(a, b);
+               expPrevs.put(b, d);
+               expPrevs.put(c, d);
+               expPrevs.put(d, e);
+               expPrevs.put(e, null);
+               expPrevs.put(f, e);
+               assert actPrevs.equals(expPrevs);
+
+               System.out.println("Pass");
+       }
+}
diff --git a/impl/dijkstra/java/Usage.java b/impl/dijkstra/java/Usage.java
new file mode 100644 (file)
index 0000000..ebf88bb
--- /dev/null
@@ -0,0 +1,29 @@
+import java.util.Collections;
+
+public class Usage {
+       public static void main(String[] args) {
+               Graph graph = new Graph();
+               Node a = new Node();
+               Node b = new Node();
+               Node c = new Node();
+
+               // add nodes to graph
+               graph.nodes.add(a);
+               graph.nodes.add(b);
+               graph.nodes.add(c);
+
+               a.adj.put(b, 5l); // make an edge from `a' to `b' of weight 5
+               b.adj.put(c, 3l);
+               c.adj.put(a, 10l);
+               c.adj.put(b, 0l);
+
+               // search the whole graph, starting at node `a'
+               graph.search(a);
+
+               System.out.println(b.dist);
+               System.out.println(c.prev == b);
+
+               // be sure to reset the graph before performing another search
+               graph.reset();
+       }
+}
diff --git a/impl/dijkstra/java/makefile b/impl/dijkstra/java/makefile
new file mode 100644 (file)
index 0000000..9ab38df
--- /dev/null
@@ -0,0 +1,10 @@
+.PHONY: test
+
+SOURCES := Graph.java Test.java Usage.java
+
+test: output/Test.class
+       @java -enableassertions -cp output Test
+
+output/Test.class: $(SOURCES)
+       @mkdir -p output
+       javac -d output Test.java Usage.java
diff --git a/impl/dijkstra/python3/dijkstra.py b/impl/dijkstra/python3/dijkstra.py
new file mode 100644 (file)
index 0000000..3f8e6db
--- /dev/null
@@ -0,0 +1,24 @@
+import math
+
+class Node:
+       def __init__(s, data = None):
+               s.data = data
+               s.adj = {}
+
+       def search(s):
+               dists = {s: 0}
+               prev = {}
+               expl = set()
+               q = {s}
+               while q:
+                       a = min(q, key = lambda n: dists.get(n, math.inf))
+                       q.remove(a)
+                       if a not in expl:
+                               expl.add(a)
+                               for n in a.adj:
+                                       d = dists[a] + a.adj[n]
+                                       if d < dists.get(n, math.inf):
+                                               dists[n] = d
+                                               prev[n] = a
+                                               q.add(n)
+               return (dists, prev)
diff --git a/impl/dijkstra/python3/makefile b/impl/dijkstra/python3/makefile
new file mode 100644 (file)
index 0000000..9827325
--- /dev/null
@@ -0,0 +1,6 @@
+.PHONY: test
+
+SOURCES := dijkstra.py test.py
+
+test: $(SOURCES)
+       @python3 test.py
diff --git a/impl/dijkstra/python3/test.py b/impl/dijkstra/python3/test.py
new file mode 100644 (file)
index 0000000..444637f
--- /dev/null
@@ -0,0 +1,35 @@
+from dijkstra import *
+
+a, b, c, d, e, f = [Node() for _ in range(6)]
+
+a.adj[c] = 6
+b.adj[a] = 9
+c.adj[a] = 6
+d.adj[b] = 2
+d.adj[c] = 11
+e.adj[b] = 14
+e.adj[d] = 9
+e.adj[f] = 7
+f.adj[c] = 15
+f.adj[d] = 10
+
+(dists, prev) = e.search()
+
+assert dists == {
+       a: 20,
+       b: 11,
+       c: 20,
+       d: 9,
+       e: 0,
+       f: 7,
+}
+
+assert prev == {
+       a: b,
+       b: d,
+       c: d,
+       d: e,
+       f: e,
+}
+
+print("Pass")
diff --git a/impl/dijkstra/python3/usage.py b/impl/dijkstra/python3/usage.py
new file mode 100644 (file)
index 0000000..1556caf
--- /dev/null
@@ -0,0 +1,15 @@
+a = Node()
+b = Node('foo bar')
+c = Node()
+d = Node([1, 2, 3])
+e = Node()
+
+a.adj[b] = 5 # make an edge from `a' to `b' with weight 5
+b.adj[c] = 5
+c.adj[d] = 5
+a.adj[d] = 30
+
+(dists, prev) = a.search() # search the graph outward from `a'
+
+assert dists[d] == 15
+assert prev[c] == b
diff --git a/impl/hull-2d/java/Hull.java b/impl/hull-2d/java/Hull.java
new file mode 100644 (file)
index 0000000..6b99aa9
--- /dev/null
@@ -0,0 +1,49 @@
+import java.util.*;
+
+public class Hull {
+       static class Point {
+               long x;
+               long y;
+               Point(long x, long y) {
+                       this.x = x;
+                       this.y = y;
+               }
+       }
+
+       static long dir(Point a, Point b, Point c) {
+               return (b.x - a.x)*(c.y - a.y) - (b.y - a.y)*(c.x - a.x);
+       }
+       static List<Point> hull(Set<Point> pts) {
+               if (pts.isEmpty()) {
+                       return new ArrayList<>();
+               }
+               Point s = Collections.min(pts, new Comparator<Point>() {
+                       public int compare(Point a, Point b) {
+                               int yCmp = Long.compare(a.y, b.y);
+                               return yCmp == 0 ? Long.compare(a.x, b.x) : yCmp;
+                       }
+               });
+               LinkedList<Point> srt = new LinkedList<>(pts);
+               srt.remove(s);
+               Collections.sort(srt, new Comparator<Point>() {
+                       double cos(Point p) {
+                               return (p.x - s.x) / Math.sqrt((s.x - p.x)*(s.x - p.x) + (s.y - p.y)*(s.y - p.y));
+                       }
+                       public int compare(Point a, Point b) {
+                               return -Double.compare(cos(a), cos(b));
+                       }
+               });
+               srt.addFirst(s);
+               if (srt.size() < 3) {
+                       return srt;
+               }
+               LinkedList<Point> out = new LinkedList<>(srt.subList(0, 3));
+               for (Point p : srt.subList(3, srt.size())) {
+                       while (dir(out.get(out.size() - 2), out.get(out.size() - 1), p) <= 0) {
+                               out.removeLast();
+                       }
+                       out.add(p);
+               }
+               return out;
+       }
+}
diff --git a/impl/hull-2d/java/Test.java b/impl/hull-2d/java/Test.java
new file mode 100644 (file)
index 0000000..868f782
--- /dev/null
@@ -0,0 +1,16 @@
+import java.util.*;
+
+public class Test {
+       public static void main(String[] args) {
+               Hull.Point a = new Hull.Point(0, 0);
+               Hull.Point b = new Hull.Point(-2, 2);
+               Hull.Point c = new Hull.Point(0, 4);
+               Hull.Point d = new Hull.Point(2, 2);
+               Hull.Point e = new Hull.Point(0, 2);
+
+               List<Hull.Point> hull = Hull.hull(new HashSet<>(Arrays.asList(new Hull.Point[] {a, b, c, d, e})));
+               assert hull.equals(Arrays.asList(new Hull.Point[] {a, d, c, b}));
+
+               System.out.println("Pass");
+       }
+}
diff --git a/impl/hull-2d/java/Usage.java b/impl/hull-2d/java/Usage.java
new file mode 100644 (file)
index 0000000..e045ee6
--- /dev/null
@@ -0,0 +1,21 @@
+import java.util.*;
+
+public class Usage {
+       public static void main(String[] args) {
+               Hull.Point a = new Hull.Point(0, 3);
+               Hull.Point b = new Hull.Point(-2, 2);
+               Hull.Point c = new Hull.Point(1, -6);
+
+               // construct a set of points for input
+               Set<Hull.Point> points = new HashSet<>(Arrays.asList(new Hull.Point[] {a, b, c}));
+               // or manually:
+               Set<Hull.Point> points2 = new HashSet<>();
+               points2.add(a);
+               points2.add(b);
+               points2.add(c);
+
+               List<Hull.Point> out = Hull.hull(points);
+               System.out.println(out.get(0) == c);
+               System.out.println(out.contains(b));
+       }
+}
diff --git a/impl/hull-2d/java/makefile b/impl/hull-2d/java/makefile
new file mode 100644 (file)
index 0000000..e950b22
--- /dev/null
@@ -0,0 +1,10 @@
+.PHONY: test
+
+SOURCES := Hull.java Test.java Usage.java
+
+test: output/Test.class
+       @java -enableassertions -cp output Test
+
+output/Test.class: $(SOURCES)
+       @mkdir -p output
+       javac -d output Test.java Usage.java
diff --git a/impl/hull-2d/python3/hull.py b/impl/hull-2d/python3/hull.py
new file mode 100644 (file)
index 0000000..c4ddd9d
--- /dev/null
@@ -0,0 +1,15 @@
+import math
+
+def hull(points):
+       if not points:
+               return []
+       sx, sy = s = min(points, key = lambda p: p[::-1])
+       cos = lambda p: (p[0] - sx) / math.sqrt((sx - p[0])**2 + (sy - p[1])**2)
+       dir_ = lambda ax, ay, bx, by, cx, cy: (bx - ax)*(cy - ay) - (by - ay)*(cx - ax)
+       points = [s] + sorted(points - {s}, key = cos, reverse = True)
+       stack = points[:3]
+       for p in points[3:]:
+               while dir_(*stack[-2], *stack[-1], *p) <= 0:
+                       stack.pop()
+               stack.append(p)
+       return stack
diff --git a/impl/hull-2d/python3/makefile b/impl/hull-2d/python3/makefile
new file mode 100644 (file)
index 0000000..ecb77a0
--- /dev/null
@@ -0,0 +1,6 @@
+.PHONY: test
+
+SOURCES := hull.py test.py
+
+test: $(SOURCES)
+       @python3 test.py
diff --git a/impl/hull-2d/python3/test.py b/impl/hull-2d/python3/test.py
new file mode 100644 (file)
index 0000000..8976d21
--- /dev/null
@@ -0,0 +1,6 @@
+from hull import *
+
+points = {(0,0), (-2,2), (0,4), (2,2), (0,2)}
+assert hull(points) == [(0,0), (2,2), (0,4), (-2,2)]
+
+print("Pass")
diff --git a/impl/hull-2d/python3/usage.py b/impl/hull-2d/python3/usage.py
new file mode 100644 (file)
index 0000000..d8fff87
--- /dev/null
@@ -0,0 +1,2 @@
+points = {(0,0), (-2,2), (0,4), (2,2), (0,2)}
+assert hull(points) == [(0,0), (2,2), (0,4), (-2,2)]
diff --git a/impl/network-flows/java-junk/FlowNet.java b/impl/network-flows/java-junk/FlowNet.java
new file mode 100644 (file)
index 0000000..de0f1b6
--- /dev/null
@@ -0,0 +1,117 @@
+import java.util.*;
+
+class FlowNet {
+       Map<Node, Boolean> nodes = new HashMap<>();
+       long value;
+       Node source, sink;
+
+       void addNode(Node n) {
+               nodes.put(n, false);
+       }
+
+       Edge edgeFor(Node one, Node two) {
+               return one.edgeTo.containsKey(two) ? one.edgeTo.get(two) : two.edgeTo.get(one);
+       }
+
+       long findMax() {
+               long value = 0;
+
+               AP augPath = getPath();
+               while (augPath != null) {
+                       Node from, to = augPath.path.pop();
+                       while (!augPath.path.isEmpty()) {
+                               from = to;
+                               to = augPath.path.pop();
+                               edgeFor(from, to).sendTo(to, augPath.value);
+                       }
+                       value += augPath.value;
+                       augPath = getPath();
+               }
+
+               return value;
+       }
+
+       // for min cut
+       Set<Node> srcSide() {
+               Set<Node> srcSide = new HashSet<>();
+               for (Node n : nodes.keySet()) {
+                       if (nodes.get(n)) {
+                               srcSide.add(n);
+                       }
+               }
+               return srcSide;
+       }
+
+       AP getPath() {
+               Map<Node, Node> prev = new HashMap<>();
+               Queue<Node> q = new LinkedList<>();
+               q.offer(source);
+               prev.put(source, null);
+               while (!q.isEmpty()) {
+                       Node n = q.poll();
+                       for (Node other : n.adj) {
+                               Edge e = edgeFor(n, other);
+                               if (e.capTo(other) > 0 && !prev.containsKey(other)) {
+                                       prev.put(other, n);
+                                       q.offer(other);
+                               }
+                       }
+               }
+               for (Node n : nodes.keySet()) {
+                       nodes.put(n, prev.keySet().contains(n));
+               }
+
+               if (nodes.get(sink)) {
+                       LinkedList<Node> path = new LinkedList<>();
+                       long value = Long.MAX_VALUE;
+
+                       Node n = sink, p = prev.get(n);
+                       path.push(n);
+                       while (p != null) {
+                               value = Math.min(value, edgeFor(p, n).capTo(n));
+                               path.push(p);
+                               n = p;
+                               p = prev.get(p);
+                       }
+
+                       AP ap = new AP();
+                       ap.path = path;
+                       ap.value = value;
+                       return ap;
+               } else {
+                       return null;
+               }
+       }
+
+       class AP {
+               LinkedList<Node> path;
+               long value;
+       }
+}
+
+class Node {
+       Set<Node> adj = new HashSet<>();
+       Map<Node, Edge> edgeTo = new HashMap<>();
+}
+
+class Edge {
+       Node from, to;
+       long cap, flow;
+
+       Edge(Node f, Node t, long c) {
+               from = f;
+               to = t;
+               cap = c;
+               f.adj.add(t);
+               t.adj.add(f);
+               f.edgeTo.put(t, this);
+       }
+
+       long capTo(Node n) {
+               return n == to ? cap - flow : flow;
+       }
+
+       void sendTo(Node n, long amt) {
+               flow += n == to ? amt : -amt;
+       }
+}
diff --git a/impl/network-flows/java-junk/Main.java b/impl/network-flows/java-junk/Main.java
new file mode 100644 (file)
index 0000000..1410824
--- /dev/null
@@ -0,0 +1,28 @@
+public class Main {
+       public static void main(String[] args) {
+               FlowNet net = new FlowNet();
+               Node s = new Node();
+               Node t = new Node();
+               Node u = new Node();
+               Node v = new Node();
+               for (Node n : new Node[] {s,t,u,v}) {
+                       net.addNode(n);
+               }
+               net.source = s;
+               net.sink = t;
+               new Edge(s, u, 20);
+               new Edge(s, v, 10);
+               new Edge(u, v, 30);
+               new Edge(u, t, 10);
+               new Edge(v, t, 20);
+               System.out.println(net.findMax());
+               for (Node n : net.srcSide()) {
+                       char name = 0;
+                       if (n.equals(s)) name = 's';
+                       if (n.equals(t)) name = 't';
+                       if (n.equals(u)) name = 'u';
+                       if (n.equals(v)) name = 'v';
+                       System.out.println(name);
+               }
+       }
+}
diff --git a/impl/network-flows/java-junk/usage b/impl/network-flows/java-junk/usage
new file mode 100644 (file)
index 0000000..008896d
--- /dev/null
@@ -0,0 +1,22 @@
+Usage:
+
+       FlowNet net = new FlowNet();
+
+       Node s = new Node();
+       Node t = new Node();
+       Node u = new Node();
+       Node v = new Node();
+       for (Node n : new Node[] {s,t,u,v}) {
+               net.addNode(n);
+       }
+       net.source = s;
+       net.sink = t;
+
+       new Edge(s, u, 20);
+       new Edge(s, v, 10);
+       new Edge(u, v, 30);
+       new Edge(u, t, 10);
+       new Edge(v, t, 20);
+
+       System.out.println(net.findMax());
+       System.out.println(net.srcSide());
diff --git a/impl/network-flows/python3/flows.py b/impl/network-flows/python3/flows.py
new file mode 100644 (file)
index 0000000..a904a32
--- /dev/null
@@ -0,0 +1,68 @@
+from collections import deque
+
+class Edge:
+       def __init__(s, frm, to, cap):
+               s.frm = frm
+               s.to = to
+               s.cap = cap
+               s.flow = 0
+
+       def cap_to(s, node):
+               return {s.frm: s.flow, s.to: s.cap - s.flow}[node]
+
+       def add_to(s, node, amt):
+               s.flow += {s.frm: -amt, s.to: amt}[node]
+
+       def other(s, node):
+               return {s.frm: s.to, s.to: s.frm}[node]
+
+class Node:
+       def __init__(s, data = None):
+               s.data = data
+               s.edges = set()
+
+       @staticmethod
+       def edge(f, t, cap):
+               e = Edge(f, t, cap)
+               f.edges.add(e)
+               t.edges.add(e)
+               return e
+
+def ek(nodes, src, sink):
+       def path():
+               prev = {}
+               q = deque([src])
+               while q:
+                       n = q.popleft()
+                       for e in n.edges:
+                               o = e.other(n)
+                               if o not in prev and e.cap_to(o) > 0:
+                                       prev[o] = e
+                                       q.append(o)
+               d = sink
+               p = deque()
+               while d in prev and d is not src:
+                       p.appendleft((prev[d], d))
+                       d = prev[d].other(d)
+               return p if d is src else []
+
+       p = path()
+       val = 0
+       while p:
+               amt = min(e.cap_to(d) for e, d in p)
+               val += amt
+               for e, d in p:
+                       e.add_to(d, amt)
+               p = path()
+
+       cut_src = set()
+       w = [src]
+       while w:
+               n = w.pop()
+               cut_src.add(n)
+               for e in n.edges:
+                       o = e.other(n)
+                       if o not in cut_src and e.cap_to(o) != 0:
+                               w.append(o)
+
+       return (val, cut_src)
diff --git a/impl/network-flows/python3/makefile b/impl/network-flows/python3/makefile
new file mode 100644 (file)
index 0000000..ef1e50a
--- /dev/null
@@ -0,0 +1,6 @@
+.PHONY: test
+
+SOURCES := flows.py test.py
+
+test: $(SOURCES)
+       @python3 test.py
diff --git a/impl/network-flows/python3/test.py b/impl/network-flows/python3/test.py
new file mode 100644 (file)
index 0000000..d68b85d
--- /dev/null
@@ -0,0 +1,21 @@
+from flows import *
+
+src, a, b, c, d, e, f, sink = nodes = [Node() for _ in range(8)]
+
+src_to_a = Node.edge(src, a, 4)
+Node.edge(src, b, 4)
+Node.edge(src, c, 2)
+Node.edge(a, d, 3)
+Node.edge(b, e, 3)
+Node.edge(c, e, 3)
+Node.edge(c, f, 1)
+Node.edge(d, sink, 4)
+Node.edge(e, sink, 5)
+Node.edge(f, sink, 5)
+
+val, cut_src = ek(nodes, src, sink)
+
+assert val == 8
+assert src_to_a.cap_to(src) == 3
+
+print("Pass")
diff --git a/impl/network-flows/python3/usage.py b/impl/network-flows/python3/usage.py
new file mode 100644 (file)
index 0000000..2c6a33e
--- /dev/null
@@ -0,0 +1,15 @@
+# pass a value here to store data in the node (optional)
+src = Node('foo')
+a = Node('bar')
+b = Node()
+sink = Node(1234)
+
+Node.edge(src, a, 5) # add an edge from `src' to `a' with capacity 5
+Node.edge(a, b, 3)
+edge = Node.edge(b, sink, 6) # can capture an edge object for later use
+
+val, cut_src = ek(nodes, src, sink)
+
+assert val == 3 # flow value
+
+print(edge.cap_to(b)) # amount of flow from `b' to `sink' in the saturated network
index 6edc49fed082b182d56576ed126614885e886486..af0e338526f684ace89fb9dcdd4728b4ef44e640 100644 (file)
--- a/makefile
+++ b/makefile
@@ -1,5 +1,6 @@
-# defensively depend on all files in the `algorithms' tree
+# These are more than the files we really need to depend on, but keep things simple.
 ALG_FILES := $(shell find algorithms -type f)
+IMPL_FILES := $(shell find impl -type f)
 
 BUILD_CMD := ( cd output && pdflatex -shell-escape ../doc.tex )
 
@@ -10,13 +11,16 @@ doc: output/doc.pdf
 output/latex-defines: config.py
        python3 config.py
 
-output/doc.pdf: makefile output/latex-defines doc.tex $(ALG_FILES)
+output/doc.pdf: makefile output/latex-defines doc.tex $(ALG_FILES) $(IMPL_FILES)
        # build twice for table of contents
        $(BUILD_CMD)
        $(BUILD_CMD)
 
+test:
+       @python3 util/run_all_tests.py
+
 clean:
        rm -rf output
        mkdir output
 
-.PHONY: clean doc all
+.PHONY: all doc test clean
diff --git a/readme b/readme
deleted file mode 100644 (file)
index 0059876..0000000
--- a/readme
+++ /dev/null
@@ -1,20 +0,0 @@
-# Overview
-
-This project aims to (ultimately) provide a reference document covering any information useful to participants in college programming contests, which typically only allow printed materials. This primarily includes algorithm implementations and documentation, but the project may at some point support building custom selections of language and standard library documentation. The text and programming language coverage are to some extent tailored to use by Oberlin College students.
-
-# Build
-
-The following are currently needed to build the document using the Make build file:
-       - Make
-       - Python 3
-       - `pdflatex' LaTeX compiler
-       - LaTeX packages (see `doc.tex')
-       - Unix shell environment (typical MacOS or Linux should work)
-
-Currently all LaTeX-related requirements are provided by the TeX Live distribution.
-
-To configure the document build, take a look at `config.py', which contains a configuration dictionary and a mechanism for passing values to LaTeX. To start the build, just run the command `make' from the project root. The document is placed at `output/doc.pdf'.
-
-# Contribution, attribution, and redistribution
-
-This document is intended to be freely editable and redistributable. For more information, see the `license' file. Please ensure that copyright and license restrictions are compatible with this project when adding algorithm implementations based on others' work.
diff --git a/readme.md b/readme.md
new file mode 100644 (file)
index 0000000..0fa4a92
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,30 @@
+# Overview
+
+This project aims to (ultimately) provide a reference document covering any information useful to participants in college programming contests, which typically only allow printed materials. This primarily includes algorithm implementations and documentation, but the project may at some point support building custom selections of language and standard library documentation. The text and programming language coverage are to some extent tailored to use by Oberlin College students.
+
+# Building the document
+
+The following are currently needed to build the document using the Make build file:
+       - Make
+       - Python 3
+       - `pdflatex` LaTeX compiler
+       - LaTeX packages (see `doc.tex`)
+       - Unix shell environment (typical MacOS or Linux should work)
+
+Currently all LaTeX-related requirements are provided by the TeX Live distribution.
+
+To configure the document build, take a look at `config.py`, which contains a configuration dictionary and a mechanism for passing values to LaTeX. To start the build, just run the command `make` from the project root. The document is placed at `output/doc.pdf`.
+
+In the future, maybe some LaTeX template system could be used to make things cleaner.
+
+# Working with algorithm implementations
+
+Algorithm implementations are found in the `impl` directory. Each implementation includes a usage sample (`usage.py`/`Usage.java`) and a test program (`test.py`/`Test.java`). The usage files are included in the document to show the interface the implementations use.
+
+The test program can be run to verify the implementation works on a nontrivial test case. Just invoke `make` from the directory with the source files. You'll either see "Pass" or an indication of where the test failed.
+
+Note that the document pulls both the source files and the usage files from this location, so references in LaTeX need to be updated if file names are changed.
+
+# Contribution, attribution, and redistribution
+
+This document is intended to be freely editable and redistributable. For more information, see the `license` file. Please ensure that copyright and license restrictions are compatible with this project when adding algorithm implementations based on others' work.
diff --git a/util/run_all_tests.py b/util/run_all_tests.py
new file mode 100644 (file)
index 0000000..148e18b
--- /dev/null
@@ -0,0 +1,9 @@
+import subprocess
+from pathlib import Path
+
+for alg in Path('impl').iterdir():
+       if alg.is_dir():
+               for lang in alg.iterdir():
+                       if lang.is_dir() and lang.joinpath('makefile').exists():
+                               print('{}: {}'.format(alg.name, lang.name))
+                               subprocess.run(['make', '--no-print-directory', '-C', str(lang)])