sclass LineComp_PairIndex { bool verbose; new Map pairInfos; MultiSetMap byCount = multiSetMap_innerTreeSet_outerRevTreeMap(); new LinkedHashMap chains; class PairInfo { IntPair pair; Bucket bucket; new LinkedHashSet nodes; public bool equals(O o) { if (o cast PairInfo) ret pair.equals(o.pair); false; } public int hashCode() { ret pair.hashCode(); } } // buckets Bucket oneBucket = new Bucket(1), highest = oneBucket; // 1-bucket is lowest class Bucket { Bucket higher, lower; int level; // number of duplicates of each IntPair in this bucket new LinkedHashSet pairs; *(int *level) {} void delete { if (higher != null) higher.lower = lower; else highest = lower; if (lower != null) lower.higher = higher; else lowest = higher; } Bucket oneHigher() { if (higher != null && higher.level == level+1) ret higher; Bucket b = new(level+1); b.lower = this; b.higher = higher; higher = b; if (highest == this) highest = b; ret b; } void add(PairInfo pair) { pairs.add(pair); pair.bucket = this; } void movePairOneHigher(IntPair pair) { pairs.remove(pair); oneHigher().add(pair); } void movePairOneLower(IntPair pair) { pairs.remove(pair); if (this == oneBucket) forgetPair(pair); else oneLower().add(pair); } } Bucket oneBucket() { ret oneBucket; } // a "chain" of nodes (one input file) class Ch { Node tail; *(L l) { fOr (int i : l) add(i); } L toList() { new L l; Node node = tail; while (node != null) { l.add(node.value); node = node.prev; } ret reverseInPlace(l); } void add(int i) { new Node n; n.ch = this; n.value = i; n.prev = tail; if (tail != null) tail.next = n; tail = n; addToIndex(n.prev); } } class Node { Ch ch; int value; Node prev, next; int a() { ret value; } int b() { ret next == null ? -1 : next.value; } IntPair pair() { ret next == null ? null : IntPair(a(), b()); } } IntPair nodeToPair(Node n) { ret n?.pair(); } // add node to pair index void addToIndex(Node n) { IntPair p = nodeToPair(n); if (p == null) ret; int count = nodes.getSize(p); nodes.add(p, n); if (count != 0) byCount.remove(count, p); byCount.put(count+1, p); } // remove node from pair index void removeFromIndex(Node n) { IntPair p = nodeToPair(n); if (p == null) ret; int count = nodes.getSize(p); if (count == 0) fail("Can't remove pair " + p); nodes.remove(p, n); byCount.remove(count, p); if (--count > 0) byCount.put(count, p); } IntPair mostPopularDuplicate() { ret toInt(firstKey(byCount)) < 2 ? null : firstValue(byCount); } int numberOfDuplicates() { ret byCount.size()-l(byCount.get(1)); } int getCount(IntPair p) { ret nodes.getSize(p); } void replacePair(int pa, int pb, int replaceWith) { IntPair p = IntPair(pa, pb); Set set = nodes.get(p); for (Node n : cloneList(set)) { continue if n.a() != pa || n.b() != pb; // nodes might have been deleted or changed replacePair(n, replaceWith); } } void replacePair(Node node, int replaceWith) { removeFromIndex(node.prev); removeFromIndex(node); removeFromIndex(node.next); node.value = replaceWith; deleteNode(node.next); addToIndex(node.prev); addToIndex(node); } void deleteNode(Node node) { if (node.next != null) node.next.prev = node.prev; else node.ch.tail = node.prev; if (node.prev != null) node.prev.next = node.next; node.value = -1; // mark invalid } void newChain(S version, L encoding) { chains.put(version, new Ch(encoding)); } PairInfo getPairInfo(IntPair pair) { ret getOrCreate(pairInfos, pair, () -> { new PairInfo pi; pi.pair = pair; pi.bucket = oneBucket; }); } void forgetPair(IntPair pair) { pair.bucket = null; } }