sclass OccTree { int count; new HashMap followUp; OccTree parent; void add(L script) { OccTree node = this; ++count; for (A c : script) (node = node.getOrMakeFollowUp(c)).count++; } OccTree getOrMakeFollowUp(A c) { OccTree node = followUp.get(c); if (node == null) followUp.put(c, node = newChild()); ret node; } OccTree followUp(A a) { ret followUp.get(a); } Set followUpKeys() { ret keys(followUp); } A randomChoice() { MultiSet ms = new MultiSet(false); for (A c : keys(followUp)) ms.add(c, followUp.get(c).count); ret msOneOf(ms); } OccTree newChild() { new OccTree child; child.parent = this; ret child; } // size including all nodes int size() { int n = 1; for (OccTree t : values(followUp)) n += t.size(); ret n; } L> allLeaves() { new L l; collectLeaves(l); ret l; } void collectLeaves(L l) { if (isLeaf()) l.add(this); else for (OccTree t : values(followUp)) t.collectLeaves(l); } bool isLeaf() { ret isEmpty(followUp); } }