def compute_J(protocol, valuation, disabled, F, mem, graph, vertices):
    key = (valuation, frozenset(disabled))

    if key in mem:
        return mem[key]

    newM = set()
    M = set()
    sF = set(F)
    distance = shortest_distance(graph, directed=True)
    while not (len(newM) == len(sF)):
        newM = nextH(newM, sF.difference(newM), distance, vertices)
        M = newM
        stable = (len(M) == 0)

        while not stable:
            to_remove = set()

            for pair in M:  # AB
                constraints = []

                for t in protocol.transitions:
                    if (t.post == pair):
                        constraints.append(([], [], t.pre))
                    elif len(set(pair) & t.postset) == 1:
                        p = tuple(set(pair) & t.postset)[0]  # E
                        q = pair.other(p)  # F
                        q_ = t.post.other(p)  # G

                        if (q_ != q):  # G != F
                            if p != q:  # E != F
                                constraints.append(([Var(q)], [Var(p)], t.pre))
                            elif p not in t.pre:  # E = F and E not in AB
                                constraints.append(([Var(p, True)], [], t.pre))

                formula = Formula(valuation, disabled | M)

                if not formula.tautology_check(constraints):
                    to_remove.add(pair)

            if len(to_remove) > 0:
                M -= to_remove
            else:
                stable = True
        if len(M) > 0:
            break

    mem[key] = M

    return M
def compute_J(protocol, valuation, disabled, F, mem):
    key = (valuation, frozenset(disabled))

    if key in mem:
        return mem[key]

    M = set(F)
    stable = (len(M) == 0)

    while not stable:
        to_remove = set()

        for pair in M:  # AB
            constraints = []

            for t in protocol.transitions:
                if (t.post == pair):
                    constraints.append(([], [], t.pre))
                elif len(set(pair) & t.postset) == 1:
                    p = tuple(set(pair) & t.postset)[0]  # E
                    q = pair.other(p)  # F
                    q_ = t.post.other(p)  # G

                    if (q_ != q):  # G != F
                        if p != q:  # E != F
                            constraints.append(([Var(q)], [Var(p)], t.pre))
                        elif p not in t.pre:  # E = F and E not in AB
                            constraints.append(([Var(p, True)], [], t.pre))

            formula = Formula(valuation, disabled | M)

            if not formula.tautology_check(constraints):
                to_remove.add(pair)

        if len(to_remove) > 0:
            M -= to_remove
        else:
            stable = True

    mem[key] = M

    return M