sclass StringClustersWithIDs { new L clusters; !include #1025301 // change()/onChange() sclass Cluster { GlobalID globalID = aGlobalIDObject(); Set synonyms = ciSet(); *() {} *(S s) { synonyms.add(s); } toString { ret "Cluster " + globalID + ": " + synonyms; } bool add(S s) { ret synonyms.add(s); } } L searchForCluster(S s) { ret filter(clusters, c -> contains(c.synonyms, s)); } Cluster clusterWith(S s) { ret first(searchForCluster(s)); } void add(Cluster cluster) { clusters.add(cluster); change(); } void remove(Cluster cluster) { clusters.remove(cluster); change(); } // also adds to list Cluster newCluster(S s) { Cluster c = new(s); add(c); ret c; } // also adds to list Cluster newCluster(Cl synonyms) { new Cluster c; addAll(c.synonyms, synonyms); add(c); ret c; } // also merges clusters void addPair(S a, S b) { Cluster c1 = clusterWith(a), c2 = clusterWith(b); if (c1 != null) if (c2 != null) mergeClusters(c1, c2); else { c1.add(b); change(); } else if (c2 != null) { c2.add(a); change(); } else newCluster(ll(a, b)); } void mergeClusters(Cluster a, Cluster b) { if (a == b) ret; a.synonyms.addAll(b.synonyms); remove(b); } // either return s or all synonyms from the first cluster containing s Set extend(S s) { Cluster c = clusterWith(s); ret c == null ? litciset(s) : c.synonyms; } int totalCount() { ret intSum(map(clusters, c -> l(c.synonyms))); } bool eqicOrInSameCluster(S a, S b) { if (eqic(a, b)) true; if (a == null || b == null) false; for (Cluster c : clusters) if (contains(c.synonyms, a) && contains(c.synonyms, b)) true; false; } }