예제 #1
0
def internal_san_checker():
    sys.stderr.write(
        "Starting internal sanity check with {} classes.\n".format(
            len(classes)))
    ProgressBar.create(len(classes))
    for clas in classes:
        if len(clas) < 2:
            continue
        print('Sanity checking the class  %s' % str(clas))
        for l in range(1, check_len + 1):
            for p in PermSet(l):
                last = None
                for i in clas:
                    if last is None:
                        last = p.avoids(MeshPatt.unrank(classpatt, mps[i]))
                        continue
                    av = p.avoids(MeshPatt.unrank(classpatt, mps[i]))
                    if av != last:
                        print('Noooooooooooooooo')
                        print(MeshPatt.unrank(classpatt, mps[i - 1]))
                        print('')
                        print(mps[i])
                        return
                    last = av
        ProgressBar.progress()
    ProgressBar.finish()
    print("Sanity check completed.")
예제 #2
0
    def coincify(self, maxdepth=3, multbox=True, q_check=True, force_len=None):
        cnt = len(self.mps)
        n = self.mps.n

        sys.stderr.write('Shading lemma coincifier\n')
        ProgressBar.create(len(self.mps))
        for mpi, mp in enumerate(self.mps):
            ProgressBar.progress()

            if True:
                for mpj in range(mpi+1, len(self.mps)):
                    mp2 = self.mps[mpj]
                    if self.uf.find(self.mps[mp]) != self.uf.find(self.mps[mp2]) and tsa5_coincident(mp, mp2, maxdepth, multbox=multbox, q_check=q_check, force_len=force_len):
                        self.uf.unite(self.mps[mp], self.mps[mp2])
            else:
                poss = []
                for (i,j) in mp.non_pointless_boxes():
                    if (i,j) not in mp.mesh:
                        # if all_points_all_dir(mp, (i,j), maxdepth, cut=True):
                        mp2 = mp.shade((i,j))
                        if self.uf.find(self.mps[mp]) != self.uf.find(self.mps[mp2]) and tsa5_coincident(mp, mp2, maxdepth, multbox=multbox, q_check=q_check, force_len=force_len):
                            self.uf.unite(self.mps[mp], self.mps[mp2])

            #         for sh in mp.can_shade((i,j)):
            #             if simultaneous and (i,j) not in mp.mesh:
            #                 poss.append((sh, set([(i,j)])))
            #             if not simultaneous:
            #                 self.uf.unite(self.mps[mp], self.mps[mp.shade((i,j))])
            #
            # if simultaneous:
            #     for i in range(n+1):
            #         for j in range(n+1):
            #             for di in range(-1,2):
            #                 for dj in range(-1,2):
            #                     if (di == 0) == (dj == 0):
            #                         continue
            #                     i2,j2 = (i+di, j+dj)
            #                     if 0 <= i2 < n+1 and 0 <= j2 < n+1:
            #                         for sh in mp.can_shade2((i,j),(i2,j2)):
            #                             poss.append((sh, set([(i,j),(i2,j2)])))
                #
                # def bt(at, nm, used):
                #     if at == len(poss):
                #         mp2 = mp.shade(nm)
                #         self.uf.unite(self.mps[mp], self.mps[mp2])
                #         return
                #
                #     bt(at + 1, nm, used)
                #     if poss[at][0] not in used and not (nm & poss[at][1]):
                #         bt(at + 1, nm | poss[at][1], used | set([poss[at][0]]))
                #
                # bt(0, set(), set())
        ProgressBar.finish()
예제 #3
0
    def __init__(self, n, patt=None):
        self.n = n
        self.patt = patt

        self.mps = []
        self.idx = {}

        sys.stderr.write('Generating mesh patterns\n')
        ProgressBar.create(2**((n+1)*(n+1)) * (factorial(n) if patt is None else 1))
        for i,mp in enumerate(MeshPatterns(n, patt)):
            ProgressBar.progress()
            self.mps.append(mp)
            self.idx[mp] = i
        ProgressBar.finish()
예제 #4
0
    def __init__(self, n, patt=None):
        self.n = n
        self.patt = patt

        self.mps = []
        self.idx = {}

        sys.stderr.write('Generating mesh patterns\n')
        ProgressBar.create(2**((n + 1) * (n + 1)) *
                           (factorial(n) if patt is None else 1))
        for i, mp in enumerate(gen_meshpatts(n, patt)):
            ProgressBar.progress()
            self.mps.append(mp)
            self.idx[mp] = i
            assert MeshPatt.unrank(patt, i) == mp
        ProgressBar.finish()
예제 #5
0
def filter_binary(patterns, cpatt):
    binarypatterns = set(patterns)
    for l in range(len(cpatt), len(cpatt) * 2 + 1):
        sys.stderr.write("Permutations of length {}\n".format(l))
        ProgressBar.create(factorial(l))
        for perm in perm_set.of_length(l):
            ProgressBar.progress()
            poss = []
            for res in cpatt.occurrences_in(perm):
                con = set(perm[i] for i in res)
                colcnt = 0
                col = [-1]*len(perm)
                for v in perm:
                    if v in con:
                        colcnt += 1
                    else:
                        col[v] = colcnt
                rowcnt = 0
                row = [-1]*len(perm)
                for v in range(len(perm)):
                    if v in con:
                        rowcnt += 1
                    else:
                        row[v] = rowcnt
                # bad is the set of boxes that contain points and can not be shaded
                bad = set( (u,v) for u,v in zip(col,row) if u != -1 )
                # cur is the set of boxes that can be shaded
                cur = set( (u,v) for u in range(len(cpatt)+1) for v in range(len(cpatt)+1) if (u,v) not in bad )
                poss.append(shad_to_binary(cur, len(cpatt) + 1))

            occurring = set()
            nonbinary = set()
            for binpatt in binarypatterns:
                for occ in poss:
                    if is_subset(binpatt, occ):
                        if binpatt in occurring:
                            nonbinary.add(binpatt)
                            # print(MeshPatt.unrank(cpatt, binpatt))
                            # print(perm)
                            break
                        else:
                            occurring.add(binpatt)
            binarypatterns -= nonbinary
        ProgressBar.finish()

    return binarypatterns
예제 #6
0
    def take_closure(self):
        it = 0
        while True:
            it += 1
            changed = False
            sys.stderr.write('Shading lemma closure (no %d)\n' % (it))
            cnt = len(self.mps)
            ss = {}
            for i in range(cnt):
                ss.setdefault(self.uf.find(i),[])
                ss[self.uf.find(i)].append(i)

            ProgressBar.create(len(ss))
            for _,v in sorted(ss.items(),key=lambda k: min(k[1])):
                ProgressBar.progress()

                minima = []
                maxima = []
                for i in v:
                    mp_cur = self.mps[i]
                    is_maximal = True
                    is_minimal = True
                    for j in v:
                        if i == j:
                            continue
                        mp = self.mps[j]
                        if mp_cur.mesh <= mp.mesh:
                            is_maximal = False
                        if mp.mesh <= mp_cur.mesh:
                            is_minimal = False
                    if is_maximal:
                        maxima.append(mp_cur)
                    if is_minimal:
                        minima.append(mp_cur)

                for mn in minima:
                    for mx in maxima:
                        if mn.mesh <= mx.mesh:
                            for add in subsets(list(mx.mesh - mn.mesh)):
                                mid = mn.shade(set(add))
                                if self.uf.unite(self.mps[mn], self.mps[mid]):
                                    changed = True
            ProgressBar.finish()
            if not changed:
                break
예제 #7
0
    def coincify(self, simultaneous=False):
        cnt = len(self.mps)
        n = self.mps.n

        sys.stderr.write('Shading lemma coincifier\n')
        ProgressBar.create(len(self.mps))
        for mp in self.mps:
            ProgressBar.progress()

            poss = []
            for (i,j) in mp.non_pointless_boxes():
                # # Was
                # for i in range(n+1):
                #     for j in range(n+1):
                for sh in mp.can_shade((i,j)):
                    if simultaneous and (i,j) not in mp.mesh:
                        poss.append((sh, set([(i,j)])))
                    if not simultaneous:
                        self.uf.unite(self.mps[mp], self.mps[mp.shade((i,j))])

            if simultaneous:
                for i in range(n+1):
                    for j in range(n+1):
                        for di in range(-1,2):
                            for dj in range(-1,2):
                                if (di == 0) == (dj == 0):
                                    continue
                                i2,j2 = (i+di, j+dj)
                                if 0 <= i2 < n+1 and 0 <= j2 < n+1:
                                    for sh in mp.can_shade2((i,j),(i2,j2)):
                                        poss.append((sh, set([(i,j),(i2,j2)])))

                def bt(at, nm, used):
                    if at == len(poss):
                        mp2 = mp.shade(nm)
                        self.uf.unite(self.mps[mp], self.mps[mp2])
                        return

                    bt(at + 1, nm, used)
                    if poss[at][0] not in used and not (nm & poss[at][1]):
                        bt(at + 1, nm | poss[at][1], used | set([poss[at][0]]))

                bt(0, set(), set())
        ProgressBar.finish()
예제 #8
0
if len(sys.argv) > 2:
    # avoidance = [ Perm(map(int, p)) for p in map(list, sys.argv[2].split())]
    avoidance = [ Perm(map(int, p)) for p in map(list, sys.argv[2:]) ]
perm_set = PermSet.avoiding(avoidance)
pattern_ranks = [ i for i in range(2**((len(cpatt) + 1)**2)) ]

################################################################################

internal_check = True
external_check = False
print_clas = True
binarypatterns = filter_binary(pattern_ranks, cpatt)

if internal_check:
    sys.stderr.write("Internal checking\n")
    ProgressBar.create(len(binarypatterns))
    for patt in binarypatterns:
        ProgressBar.progress()
        mpatt = MeshPatt.unrank(cpatt, patt)
        for length in range(len(cpatt), len(cpatt) * 2 + 2):
            for perm in perm_set.of_length(length):
                if perm.count_occurrences_of(mpatt) > 1:
                    print("INTERNAL FAAAAAAAAAAAAAAAAAAAAAIIIIIIIIIIIIIIIIIIIIIIIIILLLLLLLLLLLLLLL")
                    print(MeshPatt.unrank(cpatt, patt))
                    sys.exit(1)
    ProgressBar.finish()
if external_check:
    for mpatt in gen_meshpatts(len(cpatt), cpatt):
        if mpatt.rank() in binarypatterns:
            continue
        if all(perm.count_occurrences_of(mpatt) == 1 for length in range(len(cpatt), len(cpatt) * 2 + 2) for perm in perm_set.of_length(length)):
예제 #9
0
import sys
from permuta import *
from permuta.misc import ProgressBar, factorial

cpatt = Perm(map(int, sys.argv[1].split()))
mpatt1 = MeshPatt.unrank(cpatt, int(sys.argv[2]))
mpatt2 = MeshPatt.unrank(cpatt, int(sys.argv[3]))
length = 9

if len(sys.argv) > 4:
    length = int(sys.argv[4])

for l in range(len(cpatt), length + 1):
    sys.stderr.write("Length {}\n".format(l))
    ProgressBar.create(factorial(l))
    for perm in PermSet(l):
        ProgressBar.progress()
        if perm.avoids(mpatt1) != perm.avoids(mpatt2):
            print(perm.avoids(mpatt1))
            print(perm.avoids(mpatt2))
            print("Avoidance of permutation {} differs.".format(perm))
            sys.exit(0)
    ProgressBar.finish()
예제 #10
0
from permuta import *
from permuta.misc import ProgressBar
import sys

# cpatt = Perm((0,1))
cpatt = Perm(map(int, sys.argv[1]))
# patts = [MeshPatt.unrank(cpatt, int(p)) for p in sys.stdin.readline().strip("[]\n").split(",")]
patts = [MeshPatt.unrank(cpatt, int(p)) for p in sys.stdin.readlines()]
# patts = [MeshPatt.unrank(cpatt, p) for p in [0, 23, 55, 151, 263, 269, 278, 284, 295, 301, 310, 316, 391, 397, 406, 412]]

sys.stderr.write("Comparing {} patterns with each other.\n".format(len(patts)))
ProgressBar.create(len(patts))

maximal = []
for i in range(len(patts)-1,-1,-1):
    ProgressBar.progress()
    ismax = True
    for j in range(len(patts)-1,-1,-1):
        if i == j:
            continue

        if patts[i].shading <= patts[j].shading:
            ismax = False
            break

    if ismax:
        maximal.append(patts[i])

ProgressBar.finish()

for p in maximal:
예제 #11
0


idx = {}
mps = []
groups = {}
groupid = {}
for mp in MeshPatterns(len(patt), patt):
    idx[mp] = len(mps)
    mps.append(mp)

active = None
for l in range(n+1, mxlen+1):
    print('length %d' % l)

    ProgressBar.create(factorial(l))
    mesh_perms = {}
    for perm in Permutations(l):
        ProgressBar.progress()
        # perm = Permutation([5,2,1,6,4,3,7])
        # perm = Permutation([5,2,11,1,8,6,4,9,3,10,7])
        idx = {}
        for i,x in enumerate(perm):
            idx[x] = i
        poss = []
        for res in containment(patt, perm):
            # print([ i-1 for i in res ])
            con = set(res)
            colcnt = 0
            col = [-1]*len(perm)
            for i,v in enumerate(perm):
예제 #12
0
    def brute_coincify_len(self, l, active):
        n = self.mps.n
        patt = self.mps.patt
        assert patt is not None
        cnt = len(self.mps)
        mesh_perms = {}

        sys.stderr.write('Permutations of length %d\n' % l)
        ProgressBar.create(factorial(l))
        for perm in Permutations(l):
            ProgressBar.progress()
            poss = []
            for res in containment(patt, perm):
                con = set(res)
                colcnt = 0
                col = [-1]*len(perm)
                for i,v in enumerate(perm):
                    if v in con:
                        colcnt += 1
                    else:
                        col[v-1] = colcnt
                rowcnt = 0
                row = [-1]*len(perm)
                for v in range(len(perm)):
                    if v+1 in con:
                        rowcnt += 1
                    else:
                        row[v] = rowcnt
                bad = set( (u,v) for u,v in zip(col,row) if u != -1 )
                cur = set( (u,v) for u in range(len(patt)+1) for v in range(len(patt)+1) if (u,v) not in bad )
                poss.append(cur)

            last = None
            maxima = []
            for cur in sorted(poss):
                if cur == last:
                    continue
                add = True
                for other in poss:
                    if cur < other:
                        add = False
                if add:
                    maxima.append(cur)
                last = cur
            perm_id = get_perm_id(perm)
            for m in maxima:
                m = tuple(sorted(m))
                mesh_perms.setdefault(m, [])
                mesh_perms[m].append(perm_id)
        ProgressBar.finish()

        max_shaded = {}
        for i in range(cnt):
            here = self.mps[i]
            if self.uf.find(i) not in max_shaded or len(here.mesh) > len(max_shaded[self.uf.find(i)]):
                max_shaded[self.uf.find(i)] = here

        cont = {}
        notcnt = 0
        sys.stderr.write('Brute supersets, active = %d\n' % len(active))
        ProgressBar.create(len(active))
        for i in active:
            perms = set()
            here = max_shaded[i]
            ProgressBar.progress()
            notcnt += 1
            # print(i, here)
            for nxt in supersets_of_mesh(n+1, here.mesh):
                nxt = tuple(sorted(nxt))
                if nxt in mesh_perms:
                    perms |= set(mesh_perms[nxt])
            # print(i, perms)
            # perms = tuple([ tuple(p) for p in sorted(perms) ])
            perms_id = get_perm_class_id(sorted(perms))
            cont.setdefault(perms_id, [])
            cont[perms_id].append(i)
        ProgressBar.finish()

        for _, v in cont.items():
            if len(v) == 1:
                active.remove(v[0])
        return cont
예제 #13
0
def brute_coincify_len(l, active, contsets, singles):
    global mps
    global classpatt
    patt = classpatt
    assert patt is not None
    mesh_perms = {}

    sys.stderr.write('Permutations of length %d\n' % l)
    ProgressBar.create(factorial(l))
    # For each permutation
    for perm in perm_set.of_length(l):
        ProgressBar.progress()
        poss = []
        # loop over the occurrences of the underlying pattern exactly once
        for res in patt.occurrences_in(perm):
            con = set(perm[i] for i in res)
            colcnt = 0
            col = [-1] * len(perm)
            for v in perm:
                if v in con:
                    colcnt += 1
                else:
                    col[v] = colcnt
            rowcnt = 0
            row = [-1] * len(perm)
            for v in range(len(perm)):
                if v in con:
                    rowcnt += 1
                else:
                    row[v] = rowcnt
            # bad is the set of boxes that contain points and can not be shaded
            bad = set((u, v) for u, v in zip(col, row) if u != -1)
            # cur is the set of boxes that can be shaded
            cur = set((u, v) for u in range(len(patt) + 1)
                      for v in range(len(patt) + 1) if (u, v) not in bad)
            poss.append(shad_to_binary(cur, len(patt) + 1))

        last = None
        maxima = []
        # compute the maximal sets of boxes that can be shaded
        for cur in sorted(poss):
            if cur == last:
                continue
            add = True
            for other in poss:
                if is_subset(cur, other) and cur != other:
                    add = False
                    break
            # pick out the maximal ones
            if add:
                maxima.append(cur)
            last = cur
        # for each maximal set, append the permutation to the list of
        # containing permutations any subset of the maximal set is then also
        # contained in the permutation
        perm_id = get_perm_id(perm)
        for m in maxima:
            mesh_perms.setdefault(m, [])
            mesh_perms[m].append(perm_id)
    ProgressBar.finish()

    cont = {}
    sys.stderr.write(
        'Compare mesh patterns with occurrences, active = %d\n'.format(
            len(active)))
    ProgressBar.create(sum(map(len, active)))

    # For each active class
    for (mpatts, contset) in zip(active, contsets):

        # We monitor whether the class will split or not
        for i in mpatts:
            ProgressBar.progress()
            perms = set()
            for (maxi, ps) in mesh_perms.items():
                if is_subset(mps[i], maxi):
                    perms |= set(ps)
            permset_id = get_perm_class_id(tuple(sorted(perms)))
            permid_vec = contset + (permset_id, )
            cont.setdefault(permid_vec, [])
            cont[permid_vec].append(i)

    ProgressBar.finish()
    nowactive = []
    nowcontset = []

    for cs, v in cont.items():
        if len(v) > 1:
            nowactive.append(v)
            nowcontset.append(cs)
        else:
            singles.append(v[0])
    return (nowactive, nowcontset)
예제 #14
0
def populate_rule_set(settings, rule_set):
    assert settings.sinput.is_classical

    # settings.logger.log('Generate allowed neighbors, overlap')
    # allowed_neighbors = find_allowed_neighbors(settings)

    settings.logger.log("Generate allowed neighbors, perm prop")
    allowed_neighbors_cpp = find_allowed_neighbors_classical_perm_prop(settings)

    ssets = set(settings.sets)
    n = settings.max_rule_size[0]
    m = settings.max_rule_size[1]

    for s in ssets:
        if type(s) is PointPermutationSet:
            pps = s
            break

    def generates_subset(rule):
        for l in range(settings.perm_bound + 1):
            for p in rule.generate_of_length(l, settings.sinput.permutations):
                if not settings.sinput.contains(p):
                    return False
        return True

    # Special case: the empty rule
    rule_set.add_rule(GeneratingRule({(0, 0): None}))

    settings.logger.log("Generating point rules")
    valid = TrieMap()
    cur = [[[None for j in range(m)] for i in range(n)]]
    for i in range(n - 1, -1, -1):
        for j in range(m - 1, -1, -1):
            settings.logger.log("Cell (%d,%d)" % (i, j))
            ProgressBar.create(len(cur))
            nxt = []
            for rule in cur:
                ProgressBar.progress()

                left = settings.max_non_empty - sum(rule[x][y] is not None for x in range(n) for y in range(m))
                at_most = settings.mn_at_most - sum(
                    0 if rule[x][y] is None else rule[x][y].min_length(settings.sinput.permutations)
                    for x in range(n)
                    for y in range(m)
                )

                ss = set([None, pps])
                for s in ss:
                    if s is not None:
                        if left == 0 or s.min_length(settings.sinput.permutations) > at_most:
                            continue
                    rule[i][j] = s

                    if j == 0:
                        found = False
                        for y in range(m):
                            if rule[i][y] is not None:
                                found = True
                                break
                        if not found:
                            continue

                    if rule[i][j] is not None and not generates_subset(GeneratingRule(rule)):
                        continue

                    is_done = True
                    for x in range(i, n):
                        for y in range(j):
                            if rule[x][y] is not None:
                                is_done = False
                                break
                        if not is_done:
                            break

                    if is_done:
                        for y in range(j, m):
                            found = False
                            for x in range(i, n):
                                if rule[x][y] is not None:
                                    found = True
                                    break
                            if not found:
                                is_done = False
                                break

                    if is_done and any(rule[i][y] is not None for y in range(j, m)):
                        key = [
                            (x, y, pps)
                            for x in range(n - 1, -1, -1)
                            for y in range(m - 1, -1, -1)
                            if rule[x][y] is not None
                        ]
                        valid[key] = True

                        si = i
                        sj = None
                        for y in range(m):
                            found = False
                            for x in range(si, n):
                                if rule[x][y] is not None:
                                    found = True
                                    break
                            if found:
                                sj = y
                                break
                        assert sj is not None
                        g = GeneratingRule({(x - si, y - sj): rule[x][y] for x in range(si, n) for y in range(sj, m)})
                        ok = True
                        if (si, sj) == (n - 1, m - 1) and not rule[si][sj].can_be_alone():
                            ok = False
                        if ok:
                            assert rule_set.add_rule(g) != RuleDeath.PERM_PROP

                    nxt.append([[rule[x][y] for y in range(m)] for x in range(n)])
            cur = nxt
            ProgressBar.finish()

    dag = settings.dag.get_transitive_reduction()
    order = dag.get_topological_order()
    settings.logger.log("Number of elements: %d" % len(order))
    ProgressBar.create(len(order) - 2)
    for s in order:
        if s is None or type(s) is PointPermutationSet:
            continue

        ProgressBar.clear()
        settings.logger.log("Promoting to: %s" % (s.description if s is not None else "empty set"))
        ProgressBar.draw()
        ProgressBar.draw()

        above = {a for a in dag.get_parents(s) if a is not None and a != s}
        rule = [[None for y in range(m)] for x in range(n)]

        def intersect(cur, pos):
            if settings.filter_rule_incrementally and not generates_subset(GeneratingRule(rule)):
                return None

            here = TrieNode()
            here.end = all(c.end for c in cur)
            if here.end:
                si = pos[0]
                sj = None
                for y in range(m):
                    found = False
                    for x in range(si, n):
                        if rule[x][y] is not None:
                            found = True
                            break
                    if found:
                        sj = y
                        break
                assert sj is not None
                g = GeneratingRule({(x - si, y - sj): rule[x][y] for x in range(si, n) for y in range(sj, m)})
                ok = True
                if (si, sj) == (n - 1, m - 1) and not rule[si][sj].can_be_alone():
                    ok = False
                if ok:
                    res = rule_set.add_rule(g)
                    if res == RuleDeath.PERM_PROP:
                        return None
                    elif res != RuleDeath.ALIVE:
                        here.end = False

            found = False
            for (i, j, s2) in reduce(lambda x, y: x & y, [set(c.down.keys()) for c in cur]):
                rule[i][j] = s2

                ok = True
                if ok:
                    # make sure the current type is an allowed neighbor of all (not necessarily immediate) neighbors
                    for ci in range(i, n):
                        for cj in range(m):
                            if (ci, cj) > (i, j):
                                di = signum(ci - i)
                                dj = signum(cj - j)
                                if s2 not in allowed_neighbors_cpp[(rule[ci][cj], -di, -dj)]:
                                    ok = False
                                    break
                        if not ok:
                            break
                if ok and type(s2) is not PointPermutationSet:
                    # check if any of my immediate neighbors are neither empty nor a point
                    # in that case, we can only put a point in the current box
                    for di in range(-1, 2):
                        for dj in range(-1, 2):
                            ci, cj = i + di, j + dj
                            if (ci, cj) > (i, j) and 0 <= ci < n and 0 <= cj < m:
                                if rule[ci][cj] is not None and type(rule[ci][cj]) is not PointPermutationSet:
                                    ok = False
                                    break
                        if not ok:
                            break

                if ok:
                    rest = intersect([c.down[(i, j, s2)] for c in cur], (i, j))
                    if rest is not None:
                        found = True
                        here.down[(i, j, s2)] = rest
                rule[i][j] = None

            if not found and not here.end:
                return None
            return here

        def promote(cur):
            for ((i, j, s2), nxt) in cur.down.items():
                rule[i][j] = s2
                promote(nxt)
                rule[i][j] = None
            add = {}
            for ((i, j, s2), nxt) in cur.down.items():
                rule[i][j] = s
                cur_mn = sum(
                    0 if rule[x][y] is None else rule[x][y].min_length(settings.sinput.permutations)
                    for x in range(n)
                    for y in range(m)
                )
                if cur_mn <= settings.mn_at_most and s2 in above and (i, j, s) not in cur.down and (i, j, s) not in add:
                    if all((i, j, a) in cur.down for a in above):

                        ok = True
                        if ok:
                            # make sure the current type is an allowed neighbor of all (not necessarily immediate) neighbors
                            for ci in range(i, n):
                                for cj in range(m):
                                    if (ci, cj) > (i, j):
                                        di = signum(ci - i)
                                        dj = signum(cj - j)
                                        if s not in allowed_neighbors_cpp[(rule[ci][cj], -di, -dj)]:
                                            ok = False
                                            break
                                if not ok:
                                    break
                        if ok:
                            # check if any of my immediate neighbors are neither empty nor a point
                            # in that case, we can only put a point in the current box
                            for di in range(-1, 2):
                                for dj in range(-1, 2):
                                    ci, cj = i + di, j + dj
                                    if (ci, cj) > (i, j) and 0 <= ci < n and 0 <= cj < m:
                                        if rule[ci][cj] is not None and type(rule[ci][cj]) is not PointPermutationSet:
                                            ok = False
                                            break
                                if not ok:
                                    break

                        if ok:
                            add[(i, j, s)] = intersect([cur.down[(i, j, a)] for a in above], (i, j))

                rule[i][j] = None
            for (k, v) in add.items():
                if v is not None:
                    cur.down[k] = v

        promote(valid.root)
        ProgressBar.progress()

    ProgressBar.finish()